import axios from 'axios'; class RequestHttp { constructor(config) { this.instance = axios.create(config); this.abortControllers = new Map(); this._setupInterceptors(); } _generateRequestKey(config) { const { method, url, params, data } = config; return `${method}-${url}-${JSON.stringify(params || {})}-${JSON.stringify(data || {})}`; } _addPendingRequest(config) { const requestKey = this._generateRequestKey(config); const controller = new AbortController(); config.signal = controller.signal; this.abortControllers.set(requestKey, controller); } _removePendingRequest(config, cancel = false) { const requestKey = this._generateRequestKey(config); if (this.abortControllers.has(requestKey)) { if (cancel) { this.abortControllers.get(requestKey).abort(); } this.abortControllers.delete(requestKey); } } _setupInterceptors() { // 请求拦截器 this.instance.interceptors.request.use( (config) => { // 防重复请求 if (config.cancelDuplicate !== false) { this._removePendingRequest(config, true); this._addPendingRequest(config); } // 注入 Token if (config.withToken !== false) { const token = localStorage.getItem('token'); if (token && config.headers) { config.headers.Authorization = `Bearer ${token}`; } } return config; }, (error) => { return Promise.reject(error); } ); // 响应拦截器 this.instance.interceptors.response.use( (response) => { this._removePendingRequest(response.config); const { data, config } = response; // 业务成功:脱壳返回 if (data.code === 200) { return data.data; } // 业务错误 if (!config.skipGlobalError) { console.error('Business Error:', data.message); } // Token 过期:仅在非 skipGlobalError 的请求上触发整站跳登录页, // 否则像登录接口(凭证错也是 401)会被强制 reload,覆盖掉本地 hint。 if (data.code === 401 && !config.skipGlobalError) { console.warn('Token已过期,请重新登录'); localStorage.removeItem('token'); window.location.href = '/login'; } return Promise.reject(new Error(data.message || 'Error')); }, (error) => { if (axios.isCancel(error)) { return Promise.reject('Request Canceled by Duplicate Prevention'); } if (error.config) { this._removePendingRequest(error.config); } const status = error.response?.status; let errorMsg = error.message; switch (status) { case 400: errorMsg = '请求参数错误'; break; case 401: errorMsg = '未授权,请重新登录'; break; case 403: errorMsg = '拒绝访问'; break; case 404: errorMsg = '请求地址不存在'; break; case 500: errorMsg = '服务器内部错误'; break; case 502: errorMsg = '网关错误'; break; case 503: errorMsg = '服务不可用'; break; case 504: errorMsg = '网关超时'; break; default: errorMsg = '网络连接异常'; } console.error('HTTP Error:', errorMsg); return Promise.reject(error); } ); } get(url, config) { return this.instance.get(url, config); } post(url, data, config) { return this.instance.post(url, data, config); } put(url, data, config) { return this.instance.put(url, data, config); } delete(url, config) { return this.instance.delete(url, config); } } export const http = new RequestHttp({ baseURL: process.env.VUE_APP_API_BASE || '', timeout: Number(process.env.VUE_APP_REQUEST_TIMEOUT) || 15000, cancelDuplicate: true, });