import axios from 'axios';
import { ElMessage, ElLoading } from 'element-plus';
const DEFAULT_CONFIG = {
showError: true,
showLoading: false,
loadingText: '加载中...',
showSuccess: false,
successText: '',
retry: 0,
retryDelay: 1000,
returnNon200Data: false,
cancelable: false,
timeout: 6000,
};
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
timeout: DEFAULT_CONFIG.timeout
});
let requestQueue = [];
let isRefreshing = false;
let loadingInstance = null;
const pendingCancel = new Map()
const addLoading = (config) => {
if (config.showLoading) {
if (!loadingInstance) {
loadingInstance = ElLoading.service({
lock: true,
text: config.loadingText || DEFAULT_CONFIG.loadingText,
background: 'rgba(0, 0, 0, 0.7)'
});
}
}
};
const removeLoading = () => {
if (loadingInstance) {
loadingInstance.close();
loadingInstance = null;
}
};
service.interceptors.request.use(
async (config) => {
config = { ...DEFAULT_CONFIG, ...config };
if(config.cancelable){
const controller = new AbortController();
config.signal = controller.signal
if(pendingCancel.has(config.url)){
let cancel = pendingCancel.has(config.url)
cancel()
pendingCancel.delete(config.url)
}
pendingCancel.set(config.url, controller.abort)
}
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
addLoading(config);
return config;
},
error => {
removeLoading();
return Promise.reject(error);
}
);
service.interceptors.response.use(
response => {
removeLoading();
const { config, data } = response;
if(config.cancelable && pendingCancel.has(config.url)){
pendingCancel.delete(config.url)
}
if (config.showSuccess) {
ElMessage.success({
message: config.successText || data.message || '操作成功',
duration: 1500
});
}
if (data.code !== 200 && !config.returnNon200Data) {
return Promise.reject(data);
}
return config.returnNon200Data ? data : data.data;
},
async error => {
removeLoading();
const { config, response } = error;
if(config.cancelable && pendingCancel.has(config.url)){
pendingCancel.delete(config.url)
}
if (axios.isCancel(error)) {
return Promise.reject({ type: 'cancel', message: '请求已取消' });
}
if (error.code === 'ECONNABORTED' && config.retry > 0) {
config.retry--;
await new Promise(resolve =>
setTimeout(resolve, config.retryDelay)
);
return service(config);
}
if (response?.status === 401) {
if (!isRefreshing) {
isRefreshing = true;
try {
const newToken = await refreshToken();
localStorage.setItem('token', newToken);
error.config.headers.Authorization = `Bearer ${newToken}`;
requestQueue.forEach(cb => cb(newToken));
requestQueue = [];
return service(error.config);
} catch (e) {
requestQueue = [];
localStorage.removeItem('token');
window.location.href = '/login';
} finally {
isRefreshing = false;
}
} else {
return new Promise(resolve => {
requestQueue.push(() => {
error.config.headers.Authorization = `Bearer ${localStorage.getItem('token')}`;
resolve(service(error.config));
});
});
}
}
if (config.showError) {
const message = response?.data?.message ||
error.message ||
'请求错误';
ElMessage.error(message);
}
return Promise.reject(error);
},
);
const refreshToken = async () => {
const refreshToken = localStorage.getItem('refreshToken');
const { data } = await axios.post('/auth/refresh', { refreshToken });
localStorage.setItem('token', data.accessToken);
return data.accessToken;
};
const createRequest = (method) => {
return (url, data, config) => {
const options = {
method,
url,
...config
};
if (method === 'get') {
options.params = data;
} else {
options.data = data;
}
return service(options);
};
};
export const http = {
get: createRequest('get'),
post: createRequest('post'),
put: createRequest('put'),
delete: createRequest('delete'),
cancel: (config) => config.cancel?.()
};
http.get('/api/data', { id: 1 }, {
showLoading: true,
loadingText: '正在加载数据...'
});
http.post('/api/submit', formData, {
showSuccess: true,
successText: '提交成功!'
});
官方文档