import axios, { type AxiosInstance, type AxiosRequestConfig, type AxiosResponse } from 'axios' import NProgress from 'nprogress' import CryptoJS from 'crypto-js' import { useUserStore } from '~/stores/user' import message from '~/utils/message' import loading from '~/utils/loading' import { InternalAxiosRequestConfig } from 'axios' interface CustomRequestConfig extends InternalAxiosRequestConfig { showLoading?: boolean, showError?: boolean } interface RequestConfig extends AxiosRequestConfig { showLoading?: boolean, showError?: boolean } interface ApiResponse { code: number message: string data: T } class Request { private instance: AxiosInstance constructor(config: AxiosRequestConfig) { this.instance = axios.create(config) // Request interceptor this.instance.interceptors.request.use( (config: InternalAxiosRequestConfig & { showLoading?: boolean }) => { const customConfig = config as CustomRequestConfig // NProgress runs by default for visual feedback NProgress.start() // Full screen blocking loading controlled by showLoading parameter if (customConfig.showLoading) { loading.show() } const userStore = useUserStore() customConfig.headers = customConfig.headers || {} if (userStore.token) { customConfig.headers['Authorization'] = 'Bearer ' + userStore.token } // Add Signature const timestamp = Date.now().toString() const secret = import.meta.env.VITE_API_SECRET || '' const sign = CryptoJS.MD5(timestamp + secret).toString() customConfig.headers['X-App-Sign'] = sign customConfig.headers['X-App-Timestamp'] = timestamp return customConfig }, (error) => { return Promise.reject(error) } ) // Response interceptor this.instance.interceptors.response.use( (response: AxiosResponse) => { const config = response.config as CustomRequestConfig NProgress.done() if (config.showLoading) { loading.hide() } const res = response.data if (res.code === 200) { return res.data } else { if (config.showError !== false) { message.error(res.message || 'Error') } return Promise.reject(new Error(res.message || 'Error')) } }, (error) => { const config = error.config as CustomRequestConfig NProgress.done() if (config?.showLoading) { loading.hide() } let msg = 'Network Error' if (error.response) { // 优先使用后端返回的错误信息 const backendMsg = error.response.data?.message switch (error.response.status) { case 401: msg = backendMsg || 'Unauthorized' const userStore = useUserStore() userStore.clearToken() userStore.clearUserInfo() window.location.href= '/' // userStore.logout() // router push login? break case 403: msg = backendMsg || 'Forbidden' break case 404: msg = backendMsg || 'Not Found' break case 429: msg = backendMsg || 'Too Many Requests' break case 500: msg = backendMsg || 'Internal Server Error' break default: msg = backendMsg || `Error: ${error.response.status}` } } else if (error.request) { msg = 'No response from server' } if (config?.showError !== false) { message.error(msg) } return Promise.reject(error) } ) } request(config: RequestConfig): Promise { return this.instance.request(config) } get(url: string, config?: RequestConfig): Promise { return this.instance.get(url, config) } post(url: string, data?: any, config?: RequestConfig): Promise { return this.instance.post(url, data, config) } } export default new Request({ baseURL: import.meta.env.VITE_API_BASE_URL, timeout: 10000, })