/** * HTTP请求封装 * 基于uni.request进行二次封装 */ import config from '@/config/api.config.js' class Request { constructor() { this.baseURL = config.baseURL this.timeout = config.timeout this.isRedirecting = false // 防止重复跳转登录页 } /** * 核心请求方法 * @param {Object} options 请求配置 * @returns {Promise} */ request(options) { // 显示loading if (options.loading !== false) { uni.showLoading({ title: options.loadingText || '加载中...', mask: true }) } // 打印请求信息(仅POST请求) if (options.method === 'POST') { console.log('=== HTTP POST 请求 ===') console.log('URL:', this.baseURL + options.url) console.log('Method:', options.method) console.log('请求数据:', options.data) console.log('请求头:', this.getHeaders(options.header)) } return new Promise((resolve, reject) => { uni.request({ url: this.baseURL + options.url, method: options.method || 'GET', data: options.data || {}, header: this.getHeaders(options.header), timeout: options.timeout || this.timeout, success: (res) => { // 隐藏loading if (options.loading !== false) { uni.hideLoading() } console.log('=== HTTP 响应 ===') console.log('状态码:', res.statusCode) console.log('响应数据:', res.data) // 处理响应 this.handleResponse(res, resolve, reject) }, fail: (err) => { // 隐藏loading if (options.loading !== false) { uni.hideLoading() } console.log('=== HTTP 请求失败 ===') console.log('错误信息:', err) // 处理错误 this.handleError(err, reject) } }) }) } /** * 获取请求头 * @param {Object} customHeader 自定义请求头 * @returns {Object} */ getHeaders(customHeader = {}) { // 获取token - 使用access_token作为key const token = uni.getStorageSync('access_token') || '' return { 'Content-Type': 'application/json', 'Blade-Auth': token ? `bearer ${token}` : '', 'Tenant-Id': '000000', ...customHeader } } /** * 处理响应数据 * @param {Object} res 响应对象 * @param {Function} resolve Promise resolve * @param {Function} reject Promise reject */ handleResponse(res, resolve, reject) { const data = res.data // 判断HTTP状态码 - 特别处理401 if (res.statusCode === 401) { this.handleTokenExpired() reject({ statusCode: 401, code: 401, message: '未登录或登录已过期' }) return } // 其他HTTP错误状态码 if (res.statusCode < 200 || (res.statusCode >= 300 && res.statusCode !== 304)) { this.showError('网络请求失败') reject({ statusCode: res.statusCode, code: res.statusCode, message: '网络请求失败' }) return } // 特殊处理:OAuth2 token接口直接返回数据(有access_token字段) if (data.access_token) { resolve(data) return } // 判断业务状态码 if (data.code === 200 || data.success === true) { // 请求成功,返回数据 resolve(data.data) } else { // 业务错误处理 const errorMsg = data.msg || data.message || data.error_description || '请求失败' // 特殊错误码处理 if (data.code === 401 || data.code === 403) { // token过期或无权限 this.handleTokenExpired() } this.showError(errorMsg) reject({ statusCode: res.statusCode, code: data.code, message: errorMsg, data: data.data }) } } /** * 处理请求错误 * @param {Object} err 错误对象 * @param {Function} reject Promise reject */ handleError(err, reject) { console.error('请求失败:', err) console.error('请求URL:', this.baseURL) let message = '网络请求失败' if (err.errMsg) { if (err.errMsg.includes('timeout')) { message = '请求超时,请检查网络' } else if (err.errMsg.includes('fail')) { message = '网络连接失败,请检查服务器地址' } } this.showError(message) reject({ code: undefined, message: '请求失败', data: undefined, error: err, url: this.baseURL }) } /** * 显示错误提示 * @param {String} message 错误信息 */ showError(message) { uni.showToast({ title: message, icon: 'none', duration: 2000 }) } /** * 处理token过期 */ handleTokenExpired() { console.log('=== handleTokenExpired 被调用 ===') console.log('isRedirecting:', this.isRedirecting) // 防止重复跳转 if (this.isRedirecting) { console.log('已经在跳转中,跳过') return } this.isRedirecting = true console.log('开始清除认证信息') // 清除所有认证信息 uni.removeStorageSync('access_token') uni.removeStorageSync('refresh_token') uni.removeStorageSync('userInfo') console.log('认证信息已清除') // 提示用户 console.log('显示Toast提示') uni.showToast({ title: '登录已过期,请重新登录', icon: 'none', duration: 1500 }) // 立即跳转,不等待Toast console.log('准备跳转到登录页') // 获取当前页面路径 const pages = getCurrentPages() console.log('当前页面栈:', pages) const currentPage = pages[pages.length - 1] const currentRoute = currentPage ? currentPage.route : '' console.log('当前页面路由:', currentRoute) // 如果当前不在登录页,才跳转 if (currentRoute !== 'pages/login/login') { console.log('开始执行跳转...') // 使用 setTimeout 确保在下一个事件循环执行 setTimeout(() => { this.isRedirecting = false uni.reLaunch({ url: '/pages/login/login', success: () => { console.log('✅ reLaunch 跳转成功') }, fail: (err) => { console.error('❌ reLaunch 失败:', err) // 如果reLaunch失败,尝试使用redirectTo uni.redirectTo({ url: '/pages/login/login', success: () => { console.log('✅ redirectTo 跳转成功') }, fail: (err2) => { console.error('❌ redirectTo 也失败:', err2) // 最后尝试navigateTo uni.navigateTo({ url: '/pages/login/login', success: () => { console.log('✅ navigateTo 跳转成功') }, fail: (err3) => { console.error('❌ navigateTo 也失败:', err3) console.error('所有跳转方式都失败了!') } }) } }) } }) }, 100) } else { console.log('当前已在登录页,不需要跳转') this.isRedirecting = false } } /** * GET请求 * @param {String} url 请求地址 * @param {Object} data 请求参数 * @param {Object} options 额外配置 * @returns {Promise} */ get(url, data = {}, options = {}) { return this.request({ url, method: 'GET', data, ...options }) } /** * POST请求 * @param {String} url 请求地址 * @param {Object|String} data 请求参数(可以是对象或字符串) * @param {Object} options 额外配置 * @returns {Promise} */ post(url, data = {}, options = {}) { return this.request({ url, method: 'POST', data, ...options }) } /** * PUT请求 * @param {String} url 请求��址 * @param {Object} data 请求参数 * @param {Object} options 额外配置 * @returns {Promise} */ put(url, data = {}, options = {}) { return this.request({ url, method: 'PUT', data, ...options }) } /** * DELETE请求 * @param {String} url 请求地址 * @param {Object} data 请求参数 * @param {Object} options 额外配置 * @returns {Promise} */ delete(url, data = {}, options = {}) { return this.request({ url, method: 'DELETE', data, ...options }) } /** * 文件上传 * @param {String} url 上传地址 * @param {String} filePath 文件路径 * @param {Object} formData 额外数据 * @returns {Promise} */ upload(url, filePath, formData = {}) { uni.showLoading({ title: '上传中...', mask: true }) return new Promise((resolve, reject) => { uni.uploadFile({ url: this.baseURL + url, filePath, name: 'file', formData, header: this.getHeaders(), success: (res) => { uni.hideLoading() const data = JSON.parse(res.data) if (data.code === 200) { resolve(data.data) } else { this.showError(data.msg || '上传失败') reject(data) } }, fail: (err) => { uni.hideLoading() this.showError('上传失败') reject(err) } }) }) } } // 导出实例 export default new Request()