import axios, { AxiosError } from "axios"; import { message } from "antd"; const TOKEN_KEY = "xly.access"; export function getToken(): string | null { return sessionStorage.getItem(TOKEN_KEY); } export function setToken(t: string): void { sessionStorage.setItem(TOKEN_KEY, t); } export function clearToken(): void { sessionStorage.removeItem(TOKEN_KEY); } export interface ApiEnvelope { code: number; msg: string; data: T; } export class ApiError extends Error { code: number; constructor(code: number, msg: string) { super(msg); this.code = code; } } export const http = axios.create({ baseURL: "/api", timeout: 15000, headers: { "Content-Type": "application/json" }, }); http.interceptors.request.use((config) => { const tok = getToken(); if (tok) { config.headers = config.headers ?? {}; (config.headers as Record).Authorization = `Bearer ${tok}`; } return config; }); http.interceptors.response.use( (response) => { const env = response.data as ApiEnvelope; if (env && typeof env.code === "number" && env.code !== 0) { message.error(env.msg || "请求失败"); throw new ApiError(env.code, env.msg || "请求失败"); } return response; }, (err: AxiosError) => { if (err.response?.status === 401) { clearToken(); // Redirect to /login. Use location to avoid coupling axios with React Router. if (window.location.pathname !== "/login") { window.location.href = "/login"; } return Promise.reject(new ApiError(401, "未登录或会话过期")); } const env = err.response?.data; const msg = env?.msg || err.message || "网络错误"; message.error(msg); return Promise.reject(new ApiError(env?.code ?? -1, msg)); } ); // Convenience: call http and unwrap envelope to data. export async function request( config: Parameters[0] ): Promise { const resp = await http.request>(config); return resp.data.data; }