/** * H5版本的请求工具 * 基于axios,兼容小程序的request接口 * 依赖:common.js (提供公共工具函数) */ // 环境配置 const env = { baseUrl: 'https://m.hfdcschool.com' // baseUrl: 'https://yx.newfeifan.cn' } // Loading 管理器 (H5版本) const loadingManager = { loadingCount: 0, loadingTimer: null, loadingElement: null, // 显示 loading show(options = {}) { const config = { title: '加载中...', mask: true, delay: 300, ...options } // 如果已经有 loading 在显示,只增加计数 if (this.loadingCount > 0) { this.loadingCount++ return } // 延迟显示,避免快速请求造成闪烁 this.loadingTimer = setTimeout(() => { if (this.loadingCount > 0) { this.createLoadingElement(config.title) } }, config.delay) this.loadingCount++ }, // 创建Loading元素 createLoadingElement(title) { if (this.loadingElement) return this.loadingElement = document.createElement('div') this.loadingElement.className = 'h5-loading-mask' this.loadingElement.innerHTML = `
${title}
` // 添加样式 const style = document.createElement('style') style.textContent = ` .h5-loading-mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.5); display: flex; align-items: center; justify-content: center; z-index: 10000; } .h5-loading-content { background: white; padding: 20px; border-radius: 8px; text-align: center; min-width: 120px; } .h5-loading-spinner { width: 30px; height: 30px; border: 3px solid #f3f3f3; border-top: 3px solid #40ac6d; border-radius: 50%; animation: h5-spin 1s linear infinite; margin: 0 auto 10px; } @keyframes h5-spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .h5-loading-text { color: #333; font-size: 14px; } ` document.head.appendChild(style) document.body.appendChild(this.loadingElement) }, // 隐藏 loading hide() { this.loadingCount = Math.max(0, this.loadingCount - 1) if (this.loadingCount === 0) { // 清除延迟显示的定时器 if (this.loadingTimer) { clearTimeout(this.loadingTimer) this.loadingTimer = null } // 移除 loading 元素 if (this.loadingElement) { document.body.removeChild(this.loadingElement) this.loadingElement = null } } }, // 强制隐藏所有 loading forceHide() { this.loadingCount = 0 if (this.loadingTimer) { clearTimeout(this.loadingTimer) this.loadingTimer = null } if (this.loadingElement) { document.body.removeChild(this.loadingElement) this.loadingElement = null } } } // 注意:getUrlParams, getDeviceInfo, getJSessionId, saveJSessionId // 这些函数已在 common.js 中定义,这里直接使用全局函数 const request = { async get(url, params = {}, options = {}) { return this.request(url, 'GET', params, options) }, async post(url, data = {}, options = {}) { return this.request(url, 'POST', data, options) }, async put(url, data = {}, options = {}) { return this.request(url, 'PUT', data, options) }, async delete(url, data = {}, options = {}) { return this.request(url, 'DELETE', data, options) }, async request(url, method, data, options = {}) { // 解析 loading 配置 let loadingConfig if (options.loading === false) { loadingConfig = false } else { loadingConfig = { show: true, title: '加载中...', mask: true, delay: 300, timeout: 10000, ...(typeof options.loading === 'object' ? options.loading : {}) } } // 解析请求配置 const requestConfig = { timeout: 15000, ...options.request } const shouldShowLoading = loadingConfig !== false && loadingConfig.show !== false // 显示 loading if (shouldShowLoading) { loadingManager.show(loadingConfig) } // 获取设备信息 const deviceInfo = getDeviceInfo() const devId = deviceInfo.deviceId || '' const sbmc = deviceInfo.model || '' // 处理URL,添加设备参数 const separator = url.includes('?') ? '&' : '?' const finalUrl = `${url}${separator}devId=${devId}&sbmc=${sbmc}` // 超时处理 let timeoutTimer = null if (shouldShowLoading && loadingConfig.timeout) { timeoutTimer = setTimeout(() => { loadingManager.hide() if (typeof showToastEffect !== 'undefined') { showToastEffect('请求超时', 3000, 'error') } else { // alert('请求超时') } }, loadingConfig.timeout) } try { // 配置axios请求 const axiosConfig = { url: `${env.baseUrl}${finalUrl}`, method: method.toLowerCase(), timeout: requestConfig.timeout, headers: {} } // 处理请求数据 if (method.toUpperCase() === 'GET') { axiosConfig.params = data } else { if (options.formData) { axiosConfig.headers['Content-Type'] = 'application/x-www-form-urlencoded' // 转换为表单格式 const formData = new URLSearchParams() Object.keys(data).forEach(key => { formData.append(key, data[key]) }) axiosConfig.data = formData } else { axiosConfig.headers['Content-Type'] = 'application/json' axiosConfig.data = data } } // 发送请求 const response = await axios(axiosConfig) // 清除超时定时器 if (timeoutTimer) { clearTimeout(timeoutTimer) } // 检查服务器处理错误 if (response.data && response.data.msg && response.data.msg.includes('页面执行时错误')) { throw new Error('服务器处理错误'); } // 检查没有服务授权 - 直接回到小程序 if (response.data && response.data.msg && response.data.msg.includes('没有服务授权')) { handleH5NoServiceAuth(response.data.msg); throw new Error(response.data.msg); } // 检查登录过期 - 根据实际响应体格式 if (response && ( response.data.errorcode === 1 || response.data.msg === '登录已失效,请重新登录' || response.data.message === '登录过期' || response.data.error === 'UNAUTHORIZED' )) { console.log('H5检测到登录过期,触发自动登录', response); handleH5LoginExpired(); throw new Error(response.msg || '登录过期'); } // 返回与小程序兼容的格式 return { data: response.data } } catch (error) { // 清除超时定时器 if (timeoutTimer) { clearTimeout(timeoutTimer) } // 处理错误 let errorMessage = '请求失败' if (error.response) { errorMessage = `请求失败: ${error.response.status}` } else if (error.request) { errorMessage = '网络连接失败' } else { errorMessage = error.message || '未知错误' } if (typeof showToastEffect !== 'undefined') { showToastEffect(errorMessage, 3000, 'error') } else { // alert(errorMessage) } throw error } finally { // 隐藏 loading if (shouldShowLoading) { loadingManager.hide() } } } } // H5登录过期处理 const handleH5LoginExpired = () => { console.log('🔒 H5处理登录过期'); // 获取yhsbToken,有token就自动登录,没有就跳转登录页 const userInfo = localStorage.getItem('userInfo'); let yhsbToken = ''; if (userInfo) { try { const userData = JSON.parse(userInfo); yhsbToken = userData.yhsbToken; } catch (e) { console.error('解析用户信息失败:', e); } } if (yhsbToken) { // 直接调用自动登录接口(和自动登录页面一样的逻辑) request.post( `/service?ssServ=ss.login&wdConfirmationCaptchaService=0&mdToken=${yhsbToken}`, { mdToken: yhsbToken }, { loading: false } ).then(response => { if (response && response.data) { console.log('✅ H5自动登录成功'); // 构建用户数据(和自动登录页面一样) const userData = { devId: response.data.devId, sbmc: response.data.sbmc, sessId: response.data.sessId, userId: response.data.userId, xm: response.data.xm, yhsbToken: response.data.yhsbToken, onlineToken: response.data.onlineToken }; // 保存用户信息到H5本地存储 if (window.h5UserApi) { window.h5UserApi.saveUserInfo(userData); } // 通知小程序更新token if (typeof callNative === 'function') { callNative('loginSuccess', 'H5自动登录成功', { success: true, userInfo: userData, isAutoLogin: true }); } // 刷新当前页面 setTimeout(() => window.location.reload(), 1000); } }).catch(error => { console.error('❌ H5自动登录失败:', error); window.location.href = '/page/login.html?from=expired'; }); } else { console.log('⚠️ 未找到yhsbToken,跳转登录页面'); window.location.href = '/page/login.html?from=expired'; } }; // H5没有服务授权处理 const handleH5NoServiceAuth = (errorMsg) => { // 显示错误提示 if (typeof showToastEffect !== 'undefined') { // showToastEffect('没有服务授权,即将返回', 2000, 'error'); } else { } // 延迟一下让用户看到提示,然后返回小程序 setTimeout(() => { // 通知小程序返回 if (typeof callNative === 'function') { callNative('noServiceAuth', '没有服务授权', { error: true, message: errorMsg, action: 'goBack' }); } else { // 降级处理:直接关闭页面 if (window.history.length > 1) { window.history.back(); } else { window.close(); } } }, 1500); }; // 导出到全局 window.request = request window.handleH5LoginExpired = handleH5LoginExpired window.handleH5NoServiceAuth = handleH5NoServiceAuth console.log('✅ H5 Request工具已加载')