diff --git a/utils/request.js b/utils/request.js index c13826f..20bf918 100644 --- a/utils/request.js +++ b/utils/request.js @@ -1,13 +1,17 @@ import { BASE_URL } from "./config"; -// 获取本地Token的同步方法 + +// 获取本地 Token 的同步方法 const getToken = () => { try { - return uni.getStorageSync('token') || null + return uni.getStorageSync('token') || null; } catch (e) { - console.error('获取本地Token失败:', e) - return null + console.error('获取本地Token失败:', e); + return null; } -} +}; + +// 是否正在跳转登录页,防止重复弹窗 +let isRedirectingToLogin = false; // 统一请求方法 export const request = (options) => { @@ -15,54 +19,106 @@ export const request = (options) => { // 合并请求头 const header = { 'Content-Type': 'application/json', - ...(options.header || {}), // 允许覆盖默认header - Authorization: getToken() ? `Bearer ${getToken()}` : undefined - } + ...(options.header || {}), // 允许覆盖默认 header + Authorization: getToken() ? `Bearer ${getToken()}` : undefined, + }; - // 过滤掉undefined的header项 + // 过滤掉 undefined 的 header 项 const filteredHeader = Object.fromEntries( Object.entries(header).filter(([_, v]) => v !== undefined) - ) + ); uni.request({ url: `${BASE_URL}${options.url}`, method: options.method || 'GET', data: options.data || {}, header: filteredHeader, + timeout: options.timeout || 10000, // 建议设置超时时间 success: (res) => { - // 处理401未授权 - if (res.statusCode === 401) { - uni.removeStorageSync('token') - // uni.showToast({ title: '请前往"个人中心页"登录', icon: 'none' }) - // uni.navigateTo({ url: '/pages/mine/index' }) - return reject(new Error('登录状态已过期')) + const { statusCode } = res; + + // 处理 401 未授权(登录过期/未登录) + if (statusCode === 401) { + // 防止多次弹窗和重复跳转 + if (!isRedirectingToLogin) { + isRedirectingToLogin = true; + + uni.removeStorageSync('token'); + + // 友好提示 + 延迟跳转 + uni.showToast({ + title: '暂未登录,请登录后使用', + icon: 'none', + duration: 1500, + }); + + setTimeout(() => { + isRedirectingToLogin = false; + uni.navigateTo({ + url: '/pages/mine/index', + fail: () => { + uni.reLaunch({ url: '/pages/mine/index' }); + } + }); + }, 1500); + } + + return reject(new Error('未登录或登录已过期')); } - // 处理其他错误状态码 - if (res.statusCode < 200 || res.statusCode >= 300) { - const errorMsg = res.data?.message || `请求失败 (${res.statusCode})` - uni.showToast({ title: errorMsg, icon: 'none' }) - return reject(new Error(errorMsg)) + // 处理其他非 2xx 状态码 + if (statusCode < 200 || statusCode >= 300) { + const errorMsg = res.data?.message || `请求失败 (HTTP ${statusCode})`; + uni.showToast({ title: errorMsg, icon: 'none', duration: 2000 }); + return reject(new Error(errorMsg)); } - resolve(res.data) + const responseData = res.data; + + if (responseData && typeof responseData === 'object' && 'code' in responseData) { + if (responseData.code !== 0) { + const bizMsg = responseData.message || '请求异常'; + uni.showToast({ title: bizMsg, icon: 'none' }); + return reject(new Error(bizMsg)); + } + // 成功:返回 data 字段 + resolve(responseData.data); + } else { + // 兼容没有统一响应格式的接口 + resolve(responseData); + } }, fail: (err) => { - const errorMsg = err.errMsg.includes('timeout') - ? '网络请求超时' - : '网络连接失败' - uni.showToast({ title: errorMsg, icon: 'none' }) - reject(new Error(errorMsg)) - } - }) - }) -} + const errMsg = err.errMsg || ''; + let errorMsg = '网络连接失败'; -// 封装常用方法 -export const get = (url, data, header) => request({ url, method: 'GET', data, header }) + if (errMsg.includes('timeout')) { + errorMsg = '网络请求超时,请检查网络'; + } else if (errMsg.includes('Network Error')) { + errorMsg = '网络异常,请检查网络设置'; + } -export const post = (url, data, header) => request({ url, method: 'POST', data, header }) + uni.showToast({ + title: errorMsg, + icon: 'none', + duration: 2000, + }); -export const put = (url, data, header) => request({ url, method: 'PUT', data, header }) + reject(new Error(errorMsg)); + }, + }); + }); +}; -export const del = (url, data, header) => request({ url, method: 'DELETE', data, header }) \ No newline at end of file +// 封装常用方法(支持传入额外配置,比如 timeout、skipAuth 等) +export const get = (url, data, header, options = {}) => + request({ url, method: 'GET', data, header, ...options }); + +export const post = (url, data, header, options = {}) => + request({ url, method: 'POST', data, header, ...options }); + +export const put = (url, data, header, options = {}) => + request({ url, method: 'PUT', data, header, ...options }); + +export const del = (url, data, header, options = {}) => + request({ url, method: 'DELETE', data, header, ...options }); \ No newline at end of file