|
@@ -1,25 +1,23 @@
|
|
|
import axios from "axios";
|
|
import axios from "axios";
|
|
|
import { ElMessage } from "element-plus";
|
|
import { ElMessage } from "element-plus";
|
|
|
import { getCurrentRouter } from "@/router/current";
|
|
import { getCurrentRouter } from "@/router/current";
|
|
|
|
|
+import store from "@/store";
|
|
|
|
|
|
|
|
-// 存储所有待取消的请求取消函数
|
|
|
|
|
let pendingRequests = [];
|
|
let pendingRequests = [];
|
|
|
|
|
|
|
|
-// 添加请求取消函数到队列
|
|
|
|
|
|
|
+const LOGIN_PATH = "/login";
|
|
|
|
|
+
|
|
|
const addPendingRequest = (cancelTokenSource) => {
|
|
const addPendingRequest = (cancelTokenSource) => {
|
|
|
- // 如果存在则先移除(避免重复)
|
|
|
|
|
removePendingRequest(cancelTokenSource);
|
|
removePendingRequest(cancelTokenSource);
|
|
|
pendingRequests.push(cancelTokenSource);
|
|
pendingRequests.push(cancelTokenSource);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-// 从队列中移除某个取消函数
|
|
|
|
|
const removePendingRequest = (cancelTokenSource) => {
|
|
const removePendingRequest = (cancelTokenSource) => {
|
|
|
pendingRequests = pendingRequests.filter(
|
|
pendingRequests = pendingRequests.filter(
|
|
|
(source) => source !== cancelTokenSource,
|
|
(source) => source !== cancelTokenSource,
|
|
|
);
|
|
);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-// 取消所有正在进行的请求
|
|
|
|
|
const cancelAllPendingRequests = () => {
|
|
const cancelAllPendingRequests = () => {
|
|
|
pendingRequests.forEach((source) => {
|
|
pendingRequests.forEach((source) => {
|
|
|
source.cancel("Token expired or unauthorized, request cancelled");
|
|
source.cancel("Token expired or unauthorized, request cancelled");
|
|
@@ -27,7 +25,49 @@ const cancelAllPendingRequests = () => {
|
|
|
pendingRequests = [];
|
|
pendingRequests = [];
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-// 创建 axios 实例
|
|
|
|
|
|
|
+const clearLoginState = () => {
|
|
|
|
|
+ localStorage.removeItem("token");
|
|
|
|
|
+ localStorage.removeItem("user_data");
|
|
|
|
|
+ localStorage.removeItem("HAN_SHAN_SHI_FAN_GOODS_HOUTAI_USETINFO");
|
|
|
|
|
+
|
|
|
|
|
+ store.commit("setStore", { userInfo: null });
|
|
|
|
|
+ store.commit("storePc/setStore", { formDefInfo: null });
|
|
|
|
|
+ store.commit("storeMobile/setStore", { formDefInfo: null });
|
|
|
|
|
+ store.dispatch("resetBreadcrumbTrail");
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const forceRedirectToLogin = () => {
|
|
|
|
|
+ if (window.__isRedirecting) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ window.__isRedirecting = true;
|
|
|
|
|
+ clearLoginState();
|
|
|
|
|
+ ElMessage.warning("未登录");
|
|
|
|
|
+
|
|
|
|
|
+ Promise.resolve()
|
|
|
|
|
+ .then(async () => {
|
|
|
|
|
+ const router = getCurrentRouter();
|
|
|
|
|
+
|
|
|
|
|
+ if (!router) {
|
|
|
|
|
+ window.location.hash = `#${LOGIN_PATH}`;
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (router.currentRoute.value.path !== LOGIN_PATH) {
|
|
|
|
|
+ await router.replace(LOGIN_PATH);
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ .catch(() => {
|
|
|
|
|
+ window.location.hash = `#${LOGIN_PATH}`;
|
|
|
|
|
+ })
|
|
|
|
|
+ .finally(() => {
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ window.__isRedirecting = false;
|
|
|
|
|
+ }, 1000);
|
|
|
|
|
+ });
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
const service = axios.create({
|
|
const service = axios.create({
|
|
|
baseURL: import.meta.env.VITE_API_BASE_URL || "/api",
|
|
baseURL: import.meta.env.VITE_API_BASE_URL || "/api",
|
|
|
headers: {
|
|
headers: {
|
|
@@ -35,12 +75,11 @@ const service = axios.create({
|
|
|
},
|
|
},
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
-// 请求拦截器:为每个请求生成 CancelToken
|
|
|
|
|
service.interceptors.request.use(
|
|
service.interceptors.request.use(
|
|
|
(config) => {
|
|
(config) => {
|
|
|
- // 创建 CancelToken 源
|
|
|
|
|
const source = axios.CancelToken.source();
|
|
const source = axios.CancelToken.source();
|
|
|
config.cancelToken = source.token;
|
|
config.cancelToken = source.token;
|
|
|
|
|
+ config.__cancelSource = source;
|
|
|
|
|
|
|
|
const token = localStorage.getItem("token");
|
|
const token = localStorage.getItem("token");
|
|
|
|
|
|
|
@@ -48,112 +87,41 @@ service.interceptors.request.use(
|
|
|
config.headers.satoken = `${token}`;
|
|
config.headers.satoken = `${token}`;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 存储取消函数
|
|
|
|
|
addPendingRequest(source);
|
|
addPendingRequest(source);
|
|
|
return config;
|
|
return config;
|
|
|
},
|
|
},
|
|
|
(error) => Promise.reject(error),
|
|
(error) => Promise.reject(error),
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
-// 响应拦截器
|
|
|
|
|
service.interceptors.response.use(
|
|
service.interceptors.response.use(
|
|
|
(response) => {
|
|
(response) => {
|
|
|
- // 请求完成后,从队列中移除该请求的取消函数
|
|
|
|
|
- const source = response.config.cancelToken?.source;
|
|
|
|
|
- if (source) {
|
|
|
|
|
- removePendingRequest(source);
|
|
|
|
|
|
|
+ if (response.config.__cancelSource) {
|
|
|
|
|
+ removePendingRequest(response.config.__cancelSource);
|
|
|
}
|
|
}
|
|
|
- // 正常处理响应
|
|
|
|
|
- if (response.data.code === 401) {
|
|
|
|
|
- // 触发401时,取消所有其他请求
|
|
|
|
|
- cancelAllPendingRequests();
|
|
|
|
|
- // 跳转登录(需要判断是否已经跳转,避免重复跳转)
|
|
|
|
|
- // 可以使用一个标志量防止多次执行
|
|
|
|
|
- if (!window.__isRedirecting) {
|
|
|
|
|
- window.__isRedirecting = true;
|
|
|
|
|
- const router = getCurrentRouter();
|
|
|
|
|
- localStorage.removeItem("token");
|
|
|
|
|
- localStorage.removeItem("user_data");
|
|
|
|
|
- localStorage.removeItem("HAN_SHAN_SHI_FAN_GOODS_HOUTAI_USETINFO");
|
|
|
|
|
-
|
|
|
|
|
- ElMessage.warning("未登录");
|
|
|
|
|
- if (router) {
|
|
|
|
|
- router.push("/login");
|
|
|
|
|
- } else {
|
|
|
|
|
- window.location.hash = "#/login";
|
|
|
|
|
- }
|
|
|
|
|
- // 完成后重置标志(可能需要延时)
|
|
|
|
|
- setTimeout(() => {
|
|
|
|
|
- window.__isRedirecting = false;
|
|
|
|
|
- }, 1000);
|
|
|
|
|
- }
|
|
|
|
|
- // 返回一个错误,让外层捕获,但也可以直接返回 Promise.reject
|
|
|
|
|
|
|
|
|
|
|
|
+ const responseData = response.data;
|
|
|
|
|
+
|
|
|
|
|
+ if (responseData?.code === 401 || responseData?.code === "401") {
|
|
|
|
|
+ cancelAllPendingRequests();
|
|
|
|
|
+ forceRedirectToLogin();
|
|
|
return Promise.reject(new Error("未授权"));
|
|
return Promise.reject(new Error("未授权"));
|
|
|
}
|
|
}
|
|
|
- return response.data;
|
|
|
|
|
|
|
+
|
|
|
|
|
+ return responseData;
|
|
|
},
|
|
},
|
|
|
(error) => {
|
|
(error) => {
|
|
|
- // 如果是主动取消的请求,不需要弹出错误提示
|
|
|
|
|
|
|
+ if (error.config?.__cancelSource) {
|
|
|
|
|
+ removePendingRequest(error.config.__cancelSource);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
if (axios.isCancel(error)) {
|
|
if (axios.isCancel(error)) {
|
|
|
- console.log("Request cancelled:", error.message);
|
|
|
|
|
- // 静默处理,不抛出给业务层
|
|
|
|
|
return Promise.reject(error);
|
|
return Promise.reject(error);
|
|
|
}
|
|
}
|
|
|
- // 其他错误处理
|
|
|
|
|
|
|
+
|
|
|
return Promise.reject(error);
|
|
return Promise.reject(error);
|
|
|
},
|
|
},
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
-// const service = axios.create({
|
|
|
|
|
-// baseURL: import.meta.env.VITE_API_BASE_URL || "/api",
|
|
|
|
|
-// headers: {
|
|
|
|
|
-// "Content-Type": "application/json",
|
|
|
|
|
-// },
|
|
|
|
|
-// });
|
|
|
|
|
-
|
|
|
|
|
-// service.interceptors.request.use(
|
|
|
|
|
-// (config) => {
|
|
|
|
|
-// const token = localStorage.getItem("token");
|
|
|
|
|
-
|
|
|
|
|
-// if (token) {
|
|
|
|
|
-// config.headers.satoken = `1${token}`;
|
|
|
|
|
-// }
|
|
|
|
|
-
|
|
|
|
|
-// return config;
|
|
|
|
|
-// },
|
|
|
|
|
-// (error) => Promise.reject(error),
|
|
|
|
|
-// );
|
|
|
|
|
-
|
|
|
|
|
-// service.interceptors.response.use(
|
|
|
|
|
-// (response) => {
|
|
|
|
|
-// const responseData = response.data;
|
|
|
|
|
-
|
|
|
|
|
-// if (responseData?.code === 401) {
|
|
|
|
|
-// localStorage.removeItem("token");
|
|
|
|
|
-// localStorage.removeItem("user_data");
|
|
|
|
|
-// if (!isLogout) {
|
|
|
|
|
-// isLogout = true;
|
|
|
|
|
-// ElMessage.warning("未登录");
|
|
|
|
|
-// }
|
|
|
|
|
-// router.push("/login").finally(() => {
|
|
|
|
|
-// isLogout = false;
|
|
|
|
|
-// });
|
|
|
|
|
-// }
|
|
|
|
|
-
|
|
|
|
|
-// return responseData;
|
|
|
|
|
-// },
|
|
|
|
|
-// (error) => {
|
|
|
|
|
-// const message =
|
|
|
|
|
-// error.response?.data?.message ||
|
|
|
|
|
-// error.response?.data?.msg ||
|
|
|
|
|
-// error.message ||
|
|
|
|
|
-// "Request Error";
|
|
|
|
|
-
|
|
|
|
|
-// return Promise.reject(new Error(message));
|
|
|
|
|
-// },
|
|
|
|
|
-// );
|
|
|
|
|
-
|
|
|
|
|
export const get = (url, params = {}, config = {}) =>
|
|
export const get = (url, params = {}, config = {}) =>
|
|
|
service.get(url, { params, ...config });
|
|
service.get(url, { params, ...config });
|
|
|
|
|
|