/** * 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;