init
This commit is contained in:
5
src/lib/http/constants.ts
Normal file
5
src/lib/http/constants.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export const HTTP_CONTENT_TYPE = {
|
||||
MULTIPART_FORM_DATA: "multipart/form-data",
|
||||
APPLICATION_FORM_URLENCODED: "application/x-www-form-urlencoded",
|
||||
APPLICATION_JSON: "application/json",
|
||||
} as const;
|
||||
2
src/lib/http/index.ts
Normal file
2
src/lib/http/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { default as http } from "./instance";
|
||||
export * from "./constants";
|
||||
20
src/lib/http/instance.ts
Normal file
20
src/lib/http/instance.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import axios from "axios";
|
||||
import { HTTP_CONTENT_TYPE } from "./constants";
|
||||
import { setupInterceptors } from "./interceptors";
|
||||
|
||||
const http = axios.create({
|
||||
timeout: 600_000,
|
||||
headers: {
|
||||
"X-Requested-With": "XMLHttpRequest",
|
||||
"Content-Type": HTTP_CONTENT_TYPE.APPLICATION_JSON,
|
||||
},
|
||||
baseURL: import.meta.env.VITE_API_URL,
|
||||
});
|
||||
|
||||
// 设置实例的原型
|
||||
// Object.setPrototypeOf(http, axios);
|
||||
|
||||
// 设置拦截器
|
||||
setupInterceptors(http);
|
||||
|
||||
export default http;
|
||||
98
src/lib/http/interceptors.ts
Normal file
98
src/lib/http/interceptors.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import type { AxiosInstance, InternalAxiosRequestConfig } from "axios";
|
||||
import { toast } from "sonner";
|
||||
import router from "@/router";
|
||||
|
||||
export const downloadFile = (res: any) => {
|
||||
const [, filename] = res.headers["content-disposition"]?.split(";");
|
||||
const link = document.createElement("a");
|
||||
link.download = decodeURIComponent(
|
||||
filename?.replace("filename=", "").replace(/\"/g, "").trim() || "",
|
||||
);
|
||||
link.style.display = "none";
|
||||
link.href = URL.createObjectURL(res.data);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
URL.revokeObjectURL(link.href);
|
||||
document.body.removeChild(link);
|
||||
};
|
||||
|
||||
export const setupInterceptors = (instance: AxiosInstance) => {
|
||||
// 请求拦截
|
||||
instance.interceptors.request.use(
|
||||
(config: InternalAxiosRequestConfig) => {
|
||||
console.info(
|
||||
"%c request ------>%o",
|
||||
"background:#4f46e5;color:#fff;",
|
||||
config,
|
||||
);
|
||||
// @ts-ignore
|
||||
config.headers = {
|
||||
...config.headers,
|
||||
// @ts-ignore
|
||||
Authorization: `Bearer ${useUserStore.getState().token}`,
|
||||
};
|
||||
return config;
|
||||
},
|
||||
(err: any) => {
|
||||
console.error(
|
||||
"%c request ------>%o",
|
||||
"background:#dc2626;color:#fff;",
|
||||
err,
|
||||
);
|
||||
return Promise.reject(err);
|
||||
},
|
||||
);
|
||||
|
||||
// 响应拦截
|
||||
instance.interceptors.response.use(
|
||||
(res: any) => {
|
||||
console.info(
|
||||
"%c response ------>%o",
|
||||
"background:#059669;color:#fff;",
|
||||
res,
|
||||
);
|
||||
|
||||
if (res.status !== 200) {
|
||||
return Promise.reject(res.data);
|
||||
}
|
||||
// 文件下载
|
||||
if (!res.headers["content-type"]?.startsWith("application/json")) {
|
||||
downloadFile(res);
|
||||
return Promise.resolve();
|
||||
}
|
||||
const { code, msg, errorData } = res.data;
|
||||
// 成功处理
|
||||
if (["200"].includes(code) || res.headers.code === 200) {
|
||||
return Promise.resolve(res.data);
|
||||
}
|
||||
// 未登录、登录超时、无权访问,重定向到登录页面
|
||||
if (["401", "402"].includes(code)) {
|
||||
toast.error(msg || "未登录/无权限");
|
||||
useUserStore.setState({ token: null });
|
||||
router.navigate({ to: "/login", replace: true });
|
||||
return Promise.reject();
|
||||
}
|
||||
// token变更
|
||||
if (code === "603") {
|
||||
useUserStore.setState({ token: errorData.token });
|
||||
return Promise.resolve();
|
||||
}
|
||||
// 失败统一弹窗处理
|
||||
if (["555", "666", "999", "401", "402", "700"].includes(code)) {
|
||||
toast.error(msg || "系统错误,请稍后再试");
|
||||
}
|
||||
return Promise.reject(res.data);
|
||||
},
|
||||
(err) => {
|
||||
console.error(
|
||||
"%c response ------>%o",
|
||||
"background:#dc2626;color:#fff;",
|
||||
err,
|
||||
);
|
||||
return Promise.reject(err);
|
||||
},
|
||||
);
|
||||
|
||||
return instance;
|
||||
};
|
||||
18
src/lib/utils.ts
Normal file
18
src/lib/utils.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { clsx, type ClassValue } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
// 将文件大小转换为人类可读的格式
|
||||
export function formatFileSize(bytes: number): string {
|
||||
if (bytes === undefined || bytes === null) return "-";
|
||||
if (bytes === 0) return "0 B";
|
||||
|
||||
const k = 1024;
|
||||
const sizes = ["B", "KB", "MB", "GB", "TB"];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
|
||||
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
|
||||
}
|
||||
Reference in New Issue
Block a user