/** * Axios请求工具 * 统一处理HTTP请求和响应 */ import axios from 'axios' import { ElMessage, ElLoading } from 'element-plus' import { getToken, removeToken } from '@/utils/auth' import router from '@/router' import globalErrorHandler, { ErrorTypes } from './errorHandler' // 创建axios实例 const service = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL || '/api', timeout: 15000, // 请求超时时间 headers: { 'Content-Type': 'application/json' } }) // 存储当前请求的loading实例 let loadingInstance = null // 存储当前请求数量 let requestCount = 0 /** * 显示loading */ const showLoading = () => { if (requestCount === 0) { loadingInstance = ElLoading.service({ lock: true, text: '加载中...', background: 'rgba(0, 0, 0, 0.7)' }) } requestCount++ } /** * 隐藏loading */ const hideLoading = () => { requestCount-- if (requestCount <= 0) { requestCount = 0 if (loadingInstance) { loadingInstance.close() loadingInstance = null } } } /** * 请求拦截器 */ service.interceptors.request.use( config => { // 显示loading(可选) if (config.showLoading !== false) { showLoading() } // 添加token到请求头 const token = getToken() if (token) { config.headers.Authorization = `Bearer ${token}` } // 添加请求ID用于追踪 config.headers['X-Request-ID'] = generateRequestId() // 添加时间戳防止缓存 if (config.method === 'get') { config.params = { ...config.params, _t: Date.now() } } return config }, error => { // 请求错误处理 hideLoading() globalErrorHandler.handleError(error, { type: ErrorTypes.NETWORK, context: 'request_interceptor', showMessage: true }) return Promise.reject(error) } ) /** * 响应拦截器 */ service.interceptors.response.use( response => { // 隐藏loading hideLoading() // 获取响应数据 const res = response.data // 根据后端约定的响应码处理 if (response.status === 200) { // 如果响应中包含code字段,根据code判断 if (res.code !== undefined) { // 成功响应 if (res.code === 200 || res.code === 0) { return res.data || res } // token过期或无效 else if (res.code === 401) { ElMessage.error('登录已过期,请重新登录') removeToken() router.push('/login') return Promise.reject(new Error('登录已过期')) } // 权限不足 else if (res.code === 403) { ElMessage.error('权限不足') return Promise.reject(new Error('权限不足')) } // 其他业务错误 else { const message = res.message || '服务器响应错误' ElMessage.error(message) return Promise.reject(new Error(message)) } } // 如果没有code字段,直接返回数据 else { return res } } else { // 处理非200状态码 return handleErrorResponse(response) } }, error => { // 隐藏loading hideLoading() // 处理响应错误 if (error.response) { // 服务器返回了响应,但状态码不在2xx范围内 return handleErrorResponse(error.response) } else if (error.request) { // 请求已发出,但没有收到响应 globalErrorHandler.handleNetworkError(error, { context: 'no_response', showMessage: true }) return Promise.reject(error) } else { // 请求配置出错 globalErrorHandler.handleError(error, { type: ErrorTypes.NETWORK, context: 'request_config', showMessage: true }) return Promise.reject(error) } } ) /** * 处理错误响应 * @param {Object} response - 响应对象 */ function handleErrorResponse(response) { const { status, data } = response // 使用全局错误处理器处理API错误 globalErrorHandler.handleApiError(response, { showMessage: true }) return Promise.reject(new Error(data?.message || `请求失败 (${status})`)) } /** * 生成请求ID */ function generateRequestId() { return Date.now().toString(36) + Math.random().toString(36).substr(2) } /** * 封装GET请求 * @param {string} url - 请求地址 * @param {Object} params - 请求参数 * @param {Object} config - 请求配置 */ export function get(url, params = {}, config = {}) { return service.get(url, { params, ...config }) } /** * 封装POST请求 * @param {string} url - 请求地址 * @param {Object} data - 请求数据 * @param {Object} config - 请求配置 */ export function post(url, data = {}, config = {}) { return service.post(url, data, config) } /** * 封装PUT请求 * @param {string} url - 请求地址 * @param {Object} data - 请求数据 * @param {Object} config - 请求配置 */ export function put(url, data = {}, config = {}) { return service.put(url, data, config) } /** * 封装DELETE请求 * @param {string} url - 请求地址 * @param {Object} config - 请求配置 */ export function del(url, config = {}) { return service.delete(url, config) } /** * 封装上传文件请求 * @param {string} url - 请求地址 * @param {FormData} formData - 表单数据 * @param {Object} config - 请求配置 */ export function upload(url, formData, config = {}) { return service.post(url, formData, { headers: { 'Content-Type': 'multipart/form-data' }, ...config }) } /** * 封装下载文件请求 * @param {string} url - 请求地址 * @param {Object} params - 请求参数 * @param {string} filename - 下载文件名 */ export function download(url, params = {}, filename = '') { return service.get(url, { params, responseType: 'blob' }).then(response => { // 创建下载链接 const blob = new Blob([response]) const downloadUrl = window.URL.createObjectURL(blob) const link = document.createElement('a') link.href = downloadUrl // 设置下载文件名 link.download = filename || `download_${Date.now()}` // 触发下载 document.body.appendChild(link) link.click() document.body.removeChild(link) // 释放URL对象 window.URL.revokeObjectURL(downloadUrl) return response }) } /** * 取消请求 * @param {string} url - 请求地址 */ export function cancelRequest(url) { // 这里可以实现取消特定请求的逻辑 // 例如使用CancelToken或AbortController } /** * 取消所有请求 */ export function cancelAllRequests() { // 这里可以实现取消所有请求的逻辑 // 例如存储所有请求的CancelToken或AbortController } export default service