Files
vue3-element-admin/src/utils/request.ts
2025-02-12 13:40:27 +08:00

115 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import axios, { type InternalAxiosRequestConfig, type AxiosResponse } from "axios";
import qs from "qs";
import { useUserStoreHook } from "@/store/modules/user";
import { ResultEnum } from "@/enums/ResultEnum";
import { getToken } from "@/utils/auth";
import router from "@/router";
// 创建 axios 实例
const service = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_API,
timeout: 50000,
headers: { "Content-Type": "application/json;charset=utf-8" },
paramsSerializer: (params) => qs.stringify(params),
});
// 请求拦截器
service.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
const accessToken = getToken();
// 如果 Authorization 设置为 no-auth则不携带 Token用于登录、刷新 Token 等接口
if (config.headers.Authorization !== "no-auth" && accessToken) {
config.headers.Authorization = accessToken;
} else {
delete config.headers.Authorization;
}
return config;
},
(error) => Promise.reject(error)
);
// 响应拦截器
service.interceptors.response.use(
(response: AxiosResponse) => {
// 如果响应是二进制流则直接返回用于下载文件、Excel 导出等
if (response.config.responseType === "blob") {
return response;
}
const { code, data, msg } = response.data;
if (code === ResultEnum.SUCCESS) {
return data;
}
ElMessage.error(msg || "系统出错");
return Promise.reject(new Error(msg || "Error"));
},
async (error) => {
console.error("request error", error); // for debug
// 非 2xx 状态码处理 401、403、500 等
const { config, response } = error;
if (response) {
const { code, msg } = response.data;
if (code === ResultEnum.ACCESS_TOKEN_INVALID) {
// Token 过期,刷新 Token
return handleTokenRefresh(config);
} else if (code === ResultEnum.REFRESH_TOKEN_INVALID) {
return Promise.reject(new Error(msg || "Error"));
} else {
ElMessage.error(msg || "系统出错");
}
}
return Promise.reject(error.message);
}
);
export default service;
// 是否正在刷新标识,避免重复刷新
let isRefreshing = false;
// 因 Token 过期导致的请求等待队列
const waitingQueue: Array<() => void> = [];
// 刷新 Token 处理
async function handleTokenRefresh(config: InternalAxiosRequestConfig) {
return new Promise((resolve) => {
// 封装需要重试的请求
const retryRequest = () => {
config.headers.Authorization = getToken();
resolve(service(config));
};
waitingQueue.push(retryRequest);
if (!isRefreshing) {
isRefreshing = true;
// 刷新 Token
useUserStoreHook()
.refreshToken()
.then(() => {
// 依次重试队列中所有请求, 重试后清空队列
waitingQueue.forEach((callback) => callback());
waitingQueue.length = 0;
})
.catch((error: any) => {
console.log("handleTokenRefresh error", error);
// 刷新 Token 失败,跳转登录页
ElNotification({
title: "提示",
message: "您的会话已过期,请重新登录",
type: "info",
});
useUserStoreHook()
.clearUserData()
.then(() => {
router.push("/login");
});
})
.finally(() => {
isRefreshing = false;
});
}
});
}