07ee86dc65
- Fix 48x @typescript-eslint/no-explicit-any: err: any → err: unknown with type narrowing - Fix 3x @typescript-eslint/no-unused-vars: remove unused imports/variables - Fix 2x react-refresh/only-export-components: add eslint-disable comments - Fix 1x react-hooks/exhaustive-deps: wrap in useCallback - 28 files modified across app/ and src/pages/ - ESLint now passes with 0 errors + 0 warnings (--max-warnings 0)
84 lines
2.5 KiB
TypeScript
84 lines
2.5 KiB
TypeScript
/**
|
||
* 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;
|