/** * API 客户端配置 * 封装 Axios 实例,配置拦截器和 Token 管理 */ import axios, { AxiosError, InternalAxiosRequestConfig } from 'axios'; import { message } from 'antd'; import { useAuthStore } from '@/store/authStore'; // 创建 Axios 实例 const apiClient = axios.create({ baseURL: '/api/v1', timeout: 10000, headers: { 'Content-Type': 'application/json', }, }); // 请求拦截器:添加 Token apiClient.interceptors.request.use( (config: InternalAxiosRequestConfig) => { const token = localStorage.getItem('access_token'); if (token && config.headers) { config.headers.Authorization = `Bearer ${token}`; } return config; }, (error: AxiosError) => { return Promise.reject(error); } ); // 响应拦截器:统一错误提示 + 处理未授权状态 apiClient.interceptors.response.use( (response) => response, async (error: AxiosError<{ detail?: string; message?: string; msg?: string }>) => { // 401 → 清除登录态 if (error.response?.status === 401) { useAuthStore.getState().clearAuth(); } // 提取后端返回的错误信息(detail / message / msg) const data = error.response?.data; const serverMsg = data?.detail || data?.message || data?.msg; let handled = false; if (error.code === 'ECONNABORTED' || error.message?.includes('timeout')) { message.error('请求超时,请检查网络后重试'); handled = true; } else if (!error.response) { message.error('网络连接异常,请检查网络设置'); handled = true; } else if (serverMsg) { message.error(serverMsg); handled = true; } else { const status = error.response?.status; if (status === 413) { message.error('文件过大,请缩小后重试'); handled = true; } else if (status === 415) { message.error('不支持的文件格式'); handled = true; } else if (status === 503) { message.error('服务暂不可用,请稍后再试'); handled = true; } else if (status && status >= 500) { message.error('服务器繁忙,请稍后再试'); handled = true; } // 其他 4xx 且无具体信息时不弹通用提示,由各组件自行处理 } // 标记已展示过提示,组件 onError 可据此跳过重复 toast if (handled) { // eslint-disable-next-line @typescript-eslint/no-explicit-any (error as any).__msgShown = true; } return Promise.reject(error); } ); export default apiClient;