import axios, { HttpStatusCode } from 'axios' import type { AxiosInstance, InternalAxiosRequestConfig, AxiosResponse, AxiosRequestConfig, } from 'axios' import { getToken, isUrl } from './common' import { ElMessage } from 'element-plus' import type { MessageParams } from 'element-plus' import useUserStore from '@/stores/modules/user' import storage from '@/utils/storage' interface RequestConfig extends AxiosRequestConfig { showErrorMessage?: boolean showSuccessMessage?: boolean } interface InternalRequestConfig extends InternalAxiosRequestConfig { showErrorMessage?: boolean showSuccessMessage?: boolean } interface requestResponse extends AxiosResponse { config: InternalRequestConfig } class Request { private instance: AxiosInstance constructor() { this.instance = axios.create({ baseURL: typeof import.meta.env.VITE_APP_BASE_URL === 'string' && import.meta.env.VITE_APP_BASE_URL.length > 0 ? (import.meta.env.VITE_APP_BASE_URL.substr(-1) == '/' ? import.meta.env.VITE_APP_BASE_URL : `${import.meta.env.VITE_APP_BASE_URL}/`) : '/', timeout: 0, headers: { 'Content-Type': 'application/json', lang: storage.get('lang') ?? 'zh-cn', }, }) // 全局请求拦截器 this.instance.interceptors.request.use( (config: InternalRequestConfig) => { // 携带token if (getToken()) { config.headers[import.meta.env.VITE_REQUEST_HEADER_TOKEN_KEY] = getToken() } return config }, (err: any) => { return Promise.reject(err) } ) // 全局响应拦截器 this.instance.interceptors.response.use( (response: requestResponse) => { if (response.request.responseType != 'blob') { const res = response.data if (res.code != 1) { this.handleAuthError(res.code) if (res.code != 401 && response.config.showErrorMessage !== false) this.showElMessage({ message: res.msg, type: 'error', dangerouslyUseHTMLString: true, duration: 5000, }) return Promise.reject(new Error(res.msg || 'Error')) } else { if (response.config.showSuccessMessage) ElMessage({ message: res.msg, type: 'success' }) return res } } return response.data }, (err: any) => { this.handleNetworkError(err) return Promise.reject(err) } ) } /** * 发送get请求 * @param url * @param config * @returns */ public get>( url: string, config?: RequestConfig ): Promise { if (!url || typeof url !== 'string') { console.error('Invalid URL for GET request:', url) return Promise.reject(new Error('Invalid URL')) } try { return this.instance.get(url, config) } catch (error) { console.error('Error in GET request:', error) return Promise.reject(error) } } /** * 发送post请求 * @param url * @param data * @param config * @returns */ public post, D = any>( url: string, data?: D, config?: RequestConfig ): Promise { if (!url || typeof url !== 'string') { console.error('Invalid URL for POST request:', url) return Promise.reject(new Error('Invalid URL')) } try { return this.instance.post(url, data, config) } catch (error) { console.error('Error in POST request:', error) return Promise.reject(error) } } /** * 发送get请求 * @param url * @param config * @returns */ public put, D = any>( url: string, data?: D, config?: RequestConfig ): Promise { return this.instance.put(url, data, config) } /** * 发送get请求 * @param url * @param config * @returns */ public delete>( url: string, config?: RequestConfig ): Promise { return this.instance.delete(url, config) } /** * 处理网络请求错误 * @param err */ private handleNetworkError(err: any) { let errMessage = '' if (err.response && err.response.status) { const errStatus = err.response.status switch (errStatus) { case 400: errMessage = '请求错误' break case 401: errMessage = '未授权,请登录' break case 403: errMessage = '拒绝访问' break case 404: let baseURL = ''; try { baseURL = err.response.config.baseURL && typeof err.response.config.baseURL === 'string' ? (isUrl(err.response.config.baseURL) ? err.response.config.baseURL : `${location.origin}${err.response.config.baseURL}`) : location.origin; } catch (e) { baseURL = location.origin; } errMessage = baseURL + '请求地址出错' break case 405: errMessage = '请求方法未允许' break case 408: errMessage = '请求超时' break case 409: errMessage = '资源冲突' break case 500: errMessage = '服务器内部错误' break case 501: errMessage = '服务未实现' break case 502: errMessage = '网关错误' break case 503: errMessage = '服务不可用' break case 504: errMessage = '网关超时' break case 505: errMessage = 'HTTP版本不受支持' break } } err.message.includes('timeout') && (errMessage = '请求超时') if (err.code == 'ERR_NETWORK') { let baseURL = ''; try { baseURL = err.config.baseURL && typeof err.config.baseURL === 'string' ? (isUrl(err.config.baseURL) ? err.config.baseURL : `${location.origin}${err.config.baseURL}`) : location.origin; } catch (e) { baseURL = location.origin; } errMessage = baseURL + '请求地址出错' } errMessage && this.showElMessage({ dangerouslyUseHTMLString: true, duration: 5000, message: errMessage, type: 'error', }) } private handleAuthError(code: number) { switch (code) { case 401: useUserStore().logout() break } } private messageCache = new Map() private showElMessage(options: MessageParams) { // 处理类型问题,安全地获取消息 const message = typeof options === 'string' ? options : (options as any).message; if (!message) return; const cacheKey = message; const cachedMessage = this.messageCache.get(cacheKey) if (!cachedMessage || Date.now() - cachedMessage.timestamp > 5000) { // 5秒内重复内容不再弹出,可自定义过期时间 this.messageCache.set(cacheKey, { timestamp: Date.now() }) ElMessage(options) } } } export default new Request()