import axios from 'axios' import type { AxiosResponse, AxiosError, InternalAxiosRequestConfig } from 'axios' import type { ApiResponse } from '@/api/types' import { ElLoading, ElNotification } from 'element-plus' import { getStorage, setStorage, removeStorage } from '@/utils/storage' import router from '../router' // 定义loading实例类型,用于控制loading的显示和关闭 let loadingInstance: ReturnType | null = null // 记录当前请求数量,防止多个请求重复显示/关闭loading let requestCount = 0 // 显示loading的方法 const showLoading = () => { if (requestCount === 0 && !loadingInstance) { loadingInstance = ElLoading.service({ lock: true, // 锁定屏幕滚动 fullscreen: true, // 全屏显示 }) } requestCount++ } // 关闭loading的方法 const hideLoading = () => { requestCount-- if (requestCount <= 0) { if (loadingInstance) { loadingInstance.close() loadingInstance = null } requestCount = 0 // 重置计数,防止负数 } } // 1. 环境自动识别 + 配置读取(核心修改:适配Vite环境变量) const env = import.meta.env const baseURL = env.VITE_API_BASE_URL || '' const timeout = Number(env.VITE_REQUEST_TIMEOUT) || 10000 // 2. 创建axios实例 const service = axios.create({ baseURL, timeout, headers: { 'Content-Type': 'application/json;charset=utf-8' }, }) // 3. 请求拦截器:直接使用 axios 内置类型 const requestInterceptor = (config: InternalAxiosRequestConfig) => { console.log('请求配置', config) // 显示loading(可通过config配置项控制是否显示,比如某些请求不需要loading) if (config.headers?.showLoading !== false) { showLoading() } // 添加token到请求头 const token = getStorage('token') if (token) { config.headers.token = token } return config } const requestErrorInterceptor = (error: AxiosError) => { // 请求拦截器出错时也要关闭loading ElNotification({ message: error.message || '未知的请求错误!', type: 'error', duration: 2000, }) hideLoading() return Promise.reject(error) } service.interceptors.request.use(requestInterceptor, requestErrorInterceptor) // 4. 响应拦截器 const responseInterceptor = (response: AxiosResponse>) => { //关闭loading(无论成功失败都要关) hideLoading() if (response.headers['new_token'] !== undefined) { setStorage('token', response.headers['new_token'] as string) } console.log('响应response', response) const { data } = response console.log('响应数据data', data) // 判断业务状态码 if (data.code == 200 || data.code == 0) { // 仅当业务成功时,才弹成功提示 ElNotification({ message: data.msg || '操作成功!', type: 'success', duration: 2000, customClass: 'global-notification', }) return data.data } else { switch (data.code) { case 1: if (!data.msg) { data.msg = '未知的错误!' } break case -101: data.msg = 'token错误!' removeStorage('token') router.push({ name: 'Login' }) break default: break; } // 其他业务失败:弹错误提示并拒绝Promise ElNotification({ message: data.msg || '未知的错误!', type: 'error', duration: 2000, customClass: 'global-notification', }) return Promise.reject(data) } } const responseErrorInterceptor = (error: AxiosError) => { // 响应失败,关闭loading hideLoading() // 分类处理不同类型的网络错误 let errMsg = '' if (error.message === 'Network Error') { errMsg = '网络异常,请检查您的网络连接!' } else if (error.code === 'ECONNABORTED') { errMsg = `请求超时!请求耗时超过 ${timeout / 1000} 秒` } else if (error.response) { // 有响应状态码的错误 const status = error.response.status switch (status) { case 401: errMsg = '登录状态过期,请重新登录!' // 可在此处添加跳转到登录页的逻辑 break case 403: errMsg = '您没有权限访问该资源!' break case 404: errMsg = '请求的资源不存在!' break case 500: errMsg = '服务器内部错误,请稍后重试!' break default: errMsg = (error.response?.data as ApiResponse)?.msg || `请求失败:${status}` } } else { errMsg = error.message || '服务器错误' } ElNotification({ message: errMsg, type: 'error', duration: 2000, customClass: 'global-notification', }) return Promise.reject(error) } service.interceptors.response.use(responseInterceptor, responseErrorInterceptor) // 5. 封装通用请求方法 export const request = { get(url: string, params?: unknown, config?: InternalAxiosRequestConfig): Promise { return service.get>(url, { params, ...config }).then((res) => { return res as T }) }, post(url: string, data?: unknown, config?: InternalAxiosRequestConfig): Promise { return service.post>(url, data, config).then((res) => { return res as T }) }, } export default service