// @/utils/request.js /** * 显示Toast提示(兼容uview-plus) * @param {string} message 提示内容 */ const showToast = (message) => { if (uni.$u?.toast) { uni.$u.toast(message) } else { uni.showToast({ title: message, icon: 'none' }) } } // 基础配置 - 从环境变量中获取baseURL const config = { baseURL: '/api', // 使用环境变量中的配置 timeout: 60000, header: { 'Content-Type': 'application/json', 'X-Business-System': 'medical-system', }, // 新增配置项 loginPage: '/pages/login/Login', // 登录页路径 tokenExpiredCode: 401, // 后端返回的Token过期状态码 noPermissionCode: 403, // 无权限状态码 // 添加白名单配置(不需要校验token的接口) whiteList: [ '/login', // 登录接口 // '/dingtalk/auth/login', // 钉钉授权登录接口 // '/dingtalk/auth/bind' // 钉钉授权登录绑定接口 ] } /** * 检查请求是否在白名单中 * @param {string} url 请求URL */ const isInWhiteList = (url) => { // 提取相对路径(移除baseURL)并忽略查询参数 const relativeUrl = url.replace(config.baseURL, '').split('?')[0]; return config.whiteList.some(path => { // 处理通配符情况 if (path.endsWith('/')) { return relativeUrl.startsWith(path) } // 精确匹配路径 return relativeUrl === path }) } /** * 跳转到登录页 */ const navigateToLogin = () => { uni.redirectTo({ url: config.loginPage + '?redirect=' + encodeURIComponent(getCurrentPagePath()) }) } /** * 获取当前页面路径(用于登录后跳回) */ const getCurrentPagePath = () => { const pages = getCurrentPages() return pages[pages.length - 1]?.route || '' } /** * 检查Token是否过期 * @param {number} statusCode HTTP状态码 */ const checkTokenExpired = (statusCode) => { if (statusCode === config.tokenExpiredCode) { showToast('登录已过期,请重新登录') navigateToLogin() return true } return false } /** * 请求拦截器 */ const requestInterceptor = (options) => { const token = uni.getStorageSync('token') // 如果请求在白名单中,直接放行 if (isInWhiteList(options.url)) { return options } // 如果未登录且不是白名单接口,跳转登录 // console.log(token,'token'); if (!token) { navigateToLogin() throw new Error('未登录') } // 添加Token到Header options.header = { ...options.header, 'Authorization': `Bearer ${token}` } return options } /** * 响应拦截器 */ const responseInterceptor = (response) => { const { statusCode, data } = response // Token过期处理 if (checkTokenExpired(statusCode)) { return Promise.reject(data) } // 无权限处理 if (statusCode === config.noPermissionCode) { showToast('无权限访问') return Promise.reject(data) } // 其他错误状态码 if (statusCode !== 200) { showToast(`请求失败: ${statusCode}`) return Promise.reject(data) } // 接口自定义错误码处理(假设data.code为0表示成功) if (data?.code !== 200) { showToast(data?.msg || '操作失败') return Promise.reject(data) } return data.data || data // 返回实际业务数据 } /** * 核心请求方法 */ const request = async (options) => { try { // 合并配置 options = { ...config, ...options, url: config.baseURL + options.url, header: { ...config.header, ...options.header } } // 请求拦截 options = await requestInterceptor(options) // 发起请求 return new Promise((resolve, reject) => { uni.request({ ...options, success: (res) => { resolve(responseInterceptor(res)) }, fail: (err) => { showToast('网络错误,请重试') reject(err) } }) }) } catch (err) { return Promise.reject(err) } } // 快捷方法封装 const http = { get(url, data = {}, options = {}) { return request({ url, data, method: 'GET', ...options }) }, post(url, data = {}, options = {}) { return request({ url, data, method: 'POST', ...options }) }, put(url, data = {}, options = {}) { return request({ url, data, method: 'PUT', ...options }) }, delete(url, data = {}, options = {}) { return request({ url, data, method: 'DELETE', ...options }) }, // 新增:上传文件方法 upload(url, filePath, name = 'file', formData = {}) { return request({ url, method: 'POST', filePath, name, formData, header: { 'Content-Type': 'multipart/form-data' } }) } } // 挂载到全局 uni.$uapi = http export default http