2025-07-17 21:56:53 +08:00
|
|
|
|
import axios, { AxiosInstance, AxiosRequestConfig, Method, AxiosResponse } from 'axios';
|
2025-07-17 22:19:37 +08:00
|
|
|
|
import { Toast } from 'antd-mobile';
|
2025-07-17 21:56:53 +08:00
|
|
|
|
|
2025-07-17 22:48:37 +08:00
|
|
|
|
const DEFAULT_DEBOUNCE_GAP = 1000;
|
2025-07-17 21:56:53 +08:00
|
|
|
|
const debounceMap = new Map<string, number>();
|
|
|
|
|
|
|
|
|
|
|
|
const instance: AxiosInstance = axios.create({
|
|
|
|
|
|
baseURL: (import.meta as any).env?.VITE_API_BASE_URL || '/api',
|
|
|
|
|
|
timeout: 10000,
|
|
|
|
|
|
headers: {
|
|
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
instance.interceptors.request.use(config => {
|
|
|
|
|
|
const token = localStorage.getItem('token');
|
|
|
|
|
|
if (token) {
|
|
|
|
|
|
config.headers = config.headers || {};
|
|
|
|
|
|
config.headers['Authorization'] = `Bearer ${token}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
return config;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
instance.interceptors.response.use(
|
|
|
|
|
|
(res: AxiosResponse) => {
|
2025-07-17 22:19:37 +08:00
|
|
|
|
const { code, success, msg } = res.data || {};
|
|
|
|
|
|
if (code === 200 || success) {
|
2025-07-17 21:56:53 +08:00
|
|
|
|
return res.data.data ?? res.data;
|
|
|
|
|
|
}
|
2025-07-17 22:19:37 +08:00
|
|
|
|
// 业务错误统一提示
|
|
|
|
|
|
Toast.show({ content: msg || '接口错误', position: 'top' });
|
|
|
|
|
|
// 分类处理
|
|
|
|
|
|
if (code === 401) {
|
|
|
|
|
|
// 未登录或登录失效
|
|
|
|
|
|
localStorage.removeItem('token');
|
|
|
|
|
|
} else if (code === 403) {
|
|
|
|
|
|
// 无权限
|
|
|
|
|
|
} else if (code === 500) {
|
|
|
|
|
|
// 服务端异常
|
|
|
|
|
|
}
|
|
|
|
|
|
return Promise.reject(msg || '接口错误');
|
2025-07-17 21:56:53 +08:00
|
|
|
|
},
|
|
|
|
|
|
err => {
|
2025-07-17 22:19:37 +08:00
|
|
|
|
// 网络错误、超时等
|
|
|
|
|
|
Toast.show({ content: err.message || '网络异常', position: 'top' });
|
2025-07-17 21:56:53 +08:00
|
|
|
|
return Promise.reject(err);
|
|
|
|
|
|
}
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2025-07-17 22:48:37 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @param url 接口地址
|
|
|
|
|
|
* @param data 请求参数
|
|
|
|
|
|
* @param method 请求方法
|
|
|
|
|
|
* @param config axios 配置
|
|
|
|
|
|
* @param debounceGap 防抖时间(毫秒),不传则用默认值
|
|
|
|
|
|
*/
|
2025-07-17 21:56:53 +08:00
|
|
|
|
export function request(
|
|
|
|
|
|
url: string,
|
|
|
|
|
|
data?: any,
|
|
|
|
|
|
method: Method = 'GET',
|
2025-07-17 22:48:37 +08:00
|
|
|
|
config?: AxiosRequestConfig,
|
|
|
|
|
|
debounceGap?: number
|
2025-07-17 21:56:53 +08:00
|
|
|
|
): Promise<any> {
|
2025-07-17 22:48:37 +08:00
|
|
|
|
const gap = typeof debounceGap === 'number' ? debounceGap : DEFAULT_DEBOUNCE_GAP;
|
2025-07-17 21:56:53 +08:00
|
|
|
|
const key = `${method}_${url}_${JSON.stringify(data)}`;
|
|
|
|
|
|
const now = Date.now();
|
|
|
|
|
|
const last = debounceMap.get(key) || 0;
|
2025-07-17 22:48:37 +08:00
|
|
|
|
if (gap > 0 && now - last < gap) {
|
2025-07-17 22:19:37 +08:00
|
|
|
|
Toast.show({ content: '请求过于频繁,请稍后再试', position: 'top' });
|
2025-07-17 21:56:53 +08:00
|
|
|
|
return Promise.reject('请求过于频繁,请稍后再试');
|
|
|
|
|
|
}
|
|
|
|
|
|
debounceMap.set(key, now);
|
|
|
|
|
|
|
|
|
|
|
|
const axiosConfig: AxiosRequestConfig = {
|
|
|
|
|
|
url,
|
|
|
|
|
|
method,
|
|
|
|
|
|
...config,
|
|
|
|
|
|
};
|
|
|
|
|
|
if (method.toUpperCase() === 'GET') {
|
|
|
|
|
|
axiosConfig.params = data;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
axiosConfig.data = data;
|
|
|
|
|
|
}
|
|
|
|
|
|
return instance(axiosConfig);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default request;
|