/**
* 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 = `
`
// 添加样式
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工具已加载')