Files
lilishop-ui/buyer/src/plugins/request.js
2026-07-13 14:30:09 +08:00

235 lines
6.4 KiB
JavaScript
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 from "axios";
import { ElLoading } from "element-plus";
import { Message, Modal } from "@/utils/message";
import Storage from "./storage";
import { router } from "../router/index.js";
import store from "../vuex/store";
import { handleRefreshToken } from "@/api/index";
import { v4 as uuidv4 } from "uuid";
const qs = require("qs");
export const buyerUrl =
process.env.NODE_ENV === "development"
? BASE.API_DEV.buyer
: BASE.API_PROD.buyer;
let isRefreshToken = 0;
const refreshToken = getTokenDebounce();
/** 会员中心(/home/*)页面使用局部 loading不启用全屏遮罩 */
function isMemberCenterRoute() {
try {
const path = router.currentRoute?.value?.path || "";
return path === "/home" || path.startsWith("/home/");
} catch {
return false;
}
}
const service = axios.create({
timeout: 10000,
baseURL: buyerUrl,
paramsSerializer: (params) =>
qs.stringify(params, {
arrayFormat: "repeat",
}),
});
service.interceptors.request.use(
(config) => {
const { loading } = config;
const isPutPost = config.method === "put" || config.method === "post";
const isJson = config.headers["Content-Type"] === "application/json";
const isFile = config.headers["Content-Type"] === "multipart/form-data";
if (isPutPost && isJson) {
config.data = JSON.stringify(config.data);
}
if (isPutPost && !isFile && !isJson) {
config.data = qs.stringify(config.data, {
arrayFormat: "repeat",
});
}
const method = (config.method || "get").toLowerCase();
const isReadRequest = method === "get" || method === "head";
const shouldShowLoading =
loading === true ||
(!isReadRequest && loading !== false && !isMemberCenterRoute());
if (typeof window !== "undefined" && shouldShowLoading) {
config.loading = ElLoading.service({ fullscreen: true });
}
let uuid = Storage.getItem("uuid");
if (!uuid) {
uuid = uuidv4();
Storage.setItem("uuid", uuid);
}
config.headers["uuid"] = uuid;
const accessToken = Storage.getItem("accessToken");
if (accessToken && (config.needToken || config.optionalToken)) {
config.headers["accessToken"] = accessToken;
try {
const jwtData = JSON.parse(
decodeURIComponent(
escape(
window
.atob(accessToken.split(".")[1].replace(/-/g, "+").replace(/_/g, "/"))
)
)
);
if (jwtData.exp < Math.round(new Date() / 1000)) {
refresh({ response: { config } });
}
} catch {
Storage.removeItem("accessToken");
Storage.removeItem("refreshToken");
}
}
return config;
},
(error) => Promise.reject(error)
);
function buildRejectError(error, errorData = {}) {
const message =
errorData.message ||
(error?.code === "ECONNABORTED" ? "连接超时,请稍候再试!" : "网络错误,请稍后再试!");
return Object.assign(new Error(message), { data: errorData });
}
async function refresh(error) {
const getTokenRes = await refreshToken();
if (getTokenRes === "success") {
if (error?.response?.config) {
error.response.config.headers.accessToken = Storage.getItem("accessToken");
return service(error.response.config);
}
router.go(0);
return new Promise(() => {});
}
Storage.removeItem("accessToken");
Storage.removeItem("refreshToken");
Storage.removeItem("userInfo");
Storage.setItem("cartNum", 0);
store.commit("SET_CARTNUM", 0);
const currentRoute = router.currentRoute.value;
Modal.confirm({
title: "请登录",
content: "请登录后执行此操作",
okText: "立即登录",
cancelText: "继续浏览",
onOk: () => {
router.push({
path: "/login",
query: {
rePath: currentRoute.path,
query: JSON.stringify(currentRoute.query),
},
});
},
onCancel: () => {
router.push("/");
Modal.remove();
},
});
return Promise.reject(new Error("登录已过期"));
}
service.interceptors.response.use(
async (response) => {
await closeLoading(response);
return response.data;
},
async (error) => {
if (typeof window === "undefined") return Promise.reject(error);
await closeLoading(error);
const errorResponse = error.response || {};
const errorData = errorResponse.data || {};
if (
errorResponse.status === 401 ||
errorResponse.status === 403 ||
error.response?.data?.code === 20004
) {
isRefreshToken++;
if (isRefreshToken === 1) {
isRefreshToken = 0;
return refresh(error);
}
return Promise.reject(buildRejectError(error, errorData));
}
if (errorResponse.status === 404) {
return Promise.reject(buildRejectError(error, { message: "请求的资源不存在" }));
}
if (error.message && !error.config?.skipMessage) {
const _message =
error.code === "ECONNABORTED"
? "连接超时,请稍候再试!"
: "网络错误,请稍后再试!";
Message.error(errorData.message || _message);
}
return Promise.reject(buildRejectError(error, errorData));
}
);
const closeLoading = (target) => {
if (!target.config || !target.config.loading) return true;
return new Promise((resolve) => {
setTimeout(() => {
target.config.loading.close();
resolve();
}, 200);
});
};
export const Method = {
GET: "get",
POST: "post",
PUT: "put",
DELETE: "delete",
};
export default function request(options) {
return service(options);
}
function getTokenDebounce() {
let lock = false;
let success = false;
return function () {
if (!lock) {
lock = true;
const oldRefreshToken = Storage.getItem("refreshToken");
handleRefreshToken(oldRefreshToken)
.then((res) => {
if (res.success) {
const { accessToken, refreshToken: newRefreshToken } = res.result;
Storage.setItem("accessToken", accessToken);
Storage.setItem("refreshToken", newRefreshToken);
success = true;
lock = false;
} else {
success = false;
lock = false;
}
})
.catch(() => {
success = false;
lock = false;
});
}
return new Promise((resolve) => {
const timer = setInterval(() => {
if (!lock) {
clearInterval(timer);
resolve(success ? "success" : "fail");
}
}, 500);
});
};
}