import axios, { AxiosInstance, AxiosRequestConfig, Method, AxiosResponse } from 'axios'; import { Toast } from 'antd-mobile'; const DEFAULT_DEBOUNCE_GAP = 1000; const debounceMap = new Map(); const instance: AxiosInstance = axios.create({ baseURL: (import.meta as any).env?.VITE_API_BASE_URL || '/api', timeout: 10000, headers: { 'Content-Type': 'application/json', }, }); instance.interceptors.request.use(config => { const token = localStorage.getItem('token'); if (token) { config.headers = config.headers || {}; config.headers['Authorization'] = `Bearer ${token}`; } return config; }); instance.interceptors.response.use( (res: AxiosResponse) => { const { code, success, msg } = res.data || {}; if (code === 200 || success) { return res.data.data ?? res.data; } // 业务错误统一提示 Toast.show({ content: msg || '接口错误', position: 'top' }); // 分类处理 if (code === 401) { // 未登录或登录失效 localStorage.removeItem('token'); } else if (code === 403) { // 无权限 } else if (code === 500) { // 服务端异常 } return Promise.reject(msg || '接口错误'); }, err => { // 网络错误、超时等 Toast.show({ content: err.message || '网络异常', position: 'top' }); return Promise.reject(err); } ); /** * @param url 接口地址 * @param data 请求参数 * @param method 请求方法 * @param config axios 配置 * @param debounceGap 防抖时间(毫秒),不传则用默认值 */ export function request( url: string, data?: any, method: Method = 'GET', config?: AxiosRequestConfig, debounceGap?: number ): Promise { const gap = typeof debounceGap === 'number' ? debounceGap : DEFAULT_DEBOUNCE_GAP; const key = `${method}_${url}_${JSON.stringify(data)}`; const now = Date.now(); const last = debounceMap.get(key) || 0; if (gap > 0 && now - last < gap) { Toast.show({ content: '请求过于频繁,请稍后再试', position: 'top' }); return Promise.reject('请求过于频繁,请稍后再试'); } debounceMap.set(key, now); const axiosConfig: AxiosRequestConfig = { url, method, ...config, }; if (method.toUpperCase() === 'GET') { axiosConfig.params = data; } else { axiosConfig.data = data; } return instance(axiosConfig); } export default request;