// HTTP请求配置和拦截器 // 基于uView-next的luch-request库实现 import { HTTP_CONFIG, checkApiAuth, checkAuthRequiredApi } from './config.js' import { clearAuthData, isUserLoggedIn, handleAuthRequired, tryAutoLogin } from '../utils/auth-helper.js' /** * 初始化HTTP配置 * @param {Object} vm Vue实例,用于访问vuex等全局状态 */ export default (vm) => { // 初始化请求配置 uni.$u.http.setConfig((config) => { config.baseURL = HTTP_CONFIG.baseURL config.timeout = HTTP_CONFIG.timeout config.header = { 'Content-Type': 'application/json;charset=UTF-8' } return config }) // 请求拦截器 uni.$u.http.interceptors.request.use((config) => { // 初始化请求拦截器时,会执行此方法,此时data为undefined,赋予默认{} config.data = config.data || {} config.custom = config.custom || {} // 自动检查接口是否需要鉴权(如果没有明确指定) if (config.custom.auth === undefined) { config.custom.auth = checkApiAuth(config.url) } // 检查是否为需要强制登录的接口 const isAuthRequiredApi = checkAuthRequiredApi(config.url) // 如果是需要强制登录的接口,检查登录状态 if (isAuthRequiredApi) { if (!isUserLoggedIn()) { console.log('访问需要鉴权的接口但未完成登录,跳转到登录流程:', config.url) handleAuthRequired() return Promise.reject({ message: '需要登录', code: 'AUTH_REQUIRED', url: config.url }) } } // 添加Authorization请求头 if (config.custom.auth !== false) { const token = uni.getStorageSync(HTTP_CONFIG.storageKeys.token) if (token) { config.header.Authorization = `Bearer ${token}` } } // 根据custom参数配置是否显示loading if (config.custom.loading !== false) { uni.showLoading({ title: config.custom.loadingText || '请稍候...', mask: true }) } return config }, config => { // 请求错误处理 // 如果是AUTH_REQUIRED错误,不显示loading if (config.code === 'AUTH_REQUIRED') { // 不显示loading,因为会跳转页面 return Promise.reject(config) } return Promise.reject(config) }) // 响应拦截器 uni.$u.http.interceptors.response.use((response) => { /* 对响应成功做点什么 可使用async await 做异步操作*/ // 隐藏loading uni.hideLoading() const data = response.data const custom = response.config?.custom // 统一的响应数据处理 if (data.code !== undefined) { // 如果服务端返回的状态码不等于0(成功),则reject() if (data.code !== 0) { // 如果没有显式定义custom的toast参数为false的话,默认对报错进行toast弹出提示 if (custom?.toast !== false) { uni.$u.toast(data.message || '请求失败') } // 特殊状态码处理 if (data.code === 401) { // token过期,清除本地认证信息并跳转到登录流程 clearAuthData() handleAuthRequired() return Promise.reject(data) } // 如果需要catch返回,则进行reject if (custom?.catch) { return Promise.reject(data) } else { // 否则返回一个pending中的promise,请求不会进入catch中 return new Promise(() => {}) } } } // 返回处理后的数据 return data.data !== undefined ? data.data : data }, (response) => { /* 对响应错误做点什么 (statusCode !== 200)*/ // 隐藏loading uni.hideLoading() const custom = response.config?.custom // 网络错误处理 let errorMessage = '网络错误,请检查网络连接' if (response.statusCode) { switch (response.statusCode) { case 400: errorMessage = '请求参数错误' break case 401: errorMessage = '未授权,请重新登录' clearAuthData() handleAuthRequired() break case 403: errorMessage = '拒绝访问' break case 404: errorMessage = '请求地址不存在' break case 500: errorMessage = '服务器内部错误' break case 502: errorMessage = '网关错误' break case 503: errorMessage = '服务不可用' break case 504: errorMessage = '网关超时' break default: errorMessage = `连接错误${response.statusCode}` } } // 显示错误提示 if (custom?.toast !== false) { uni.$u.toast(errorMessage) } console.error('请求错误:', response) return Promise.reject(response) }) }