import type { AxiosResponse, InternalAxiosRequestConfig } from "axios"; import axiosDef from "axios"; import Cookies from "js-cookie"; import { merge } from "lodash-es"; import type { CacheItem } from "./RequestCache"; export const TOKEN = "token"; const axios = axiosDef.create(); export interface AxiosRequestConfigWithCache extends InternalAxiosRequestConfig { cache?: CacheItem; } // 使用交叉类型扩展 AxiosResponse export type AxiosResponseCus = AxiosResponse & { config: AxiosRequestConfigWithCache; }; /** * setToken 选项接口 */ export interface SetTokenOptions { /** 是否同时设置 cookie */ hasCookie?: boolean; /** cookie 属性配置 */ cookieAttrs?: Cookies.CookieAttributes; } /** * getToken 选项接口 */ export interface GetTokenOptions { /** 是否从 cookie 获取 */ hasCookie?: boolean; } /** * 处理错误回调函数的选项接口 */ export interface HandleErrCbOptions { /** 直接替换回调函数 */ replaceErrCb?: boolean; /** 日志前缀 */ logPrefix?: string; } /** * 设置 token * @param token token 值 * @param opt 选项 */ export function setToken(token = "", opt?: SetTokenOptions) { const { hasCookie = false } = opt || {}; const _t = token || ""; if (hasCookie) { const { cookieAttrs } = opt || {}; const cookieOpt = merge( { expires: 365, }, cookieAttrs, ); Cookies.set(TOKEN, _t, cookieOpt); } localStorage.setItem(TOKEN, _t); setAxAuthorization(_t); } /** * 获取 token * @param opt 选项 * @returns token 值 */ export function getToken(opt?: GetTokenOptions): string { const { hasCookie = false } = opt || {}; let token = ""; if (hasCookie) { token = Cookies.get(TOKEN) || ""; if (token) { return token; } } token = localStorage.getItem(TOKEN); return token; } export const temp = { axiosList: [axios, axiosDef] }; /** * 设置 axios 列表,用于批量设置 axios 相关配置 * @param axiosList axios 实例列表 * @returns 更新后的 axios 实例列表 */ export function setAxios(axiosList: (typeof axios | typeof axiosDef)[]) { temp.axiosList = [...temp.axiosList, ...axiosList]; return temp.axiosList; } /** * axios 请求拦截器 * @param cb 成功回调 * @param errCb 错误回调 * @param opt 选项 */ export function setAxRequest( cb: ( config: InternalAxiosRequestConfig, ) => InternalAxiosRequestConfig | Promise, errCb?: (error: any) => Promise, opt?: HandleErrCbOptions, ) { temp.axiosList?.forEach((_ax) => { _ax.interceptors.request.use( cb, handleErrCb(errCb, { logPrefix: "Error axios response: ", ...opt }), ); }); } /** * 响应拦截器 * @param cb 成功回调 * @param errCb 错误回调 * @param opt 选项 */ export function setAxResponse( cb: (response: AxiosResponse) => AxiosResponse | Promise, errCb?: (error) => Promise, opt?: HandleErrCbOptions, ) { temp.axiosList?.forEach((_ax) => { _ax.interceptors.response.use( cb, handleErrCb(errCb, { logPrefix: "Error axios response: ", ...opt }), ); }); } /** * 处理错误回调函数 * @param errCb 错误回调 * @param opt 选项 * @returns 处理后的错误回调 */ export function handleErrCb(errCb?: (error: any) => Promise, opt?: HandleErrCbOptions) { const { replaceErrCb, logPrefix } = opt || {}; const isFn = typeof errCb === "function"; if (replaceErrCb && isFn) { return errCb; } return (error: any) => { console.error(logPrefix, error); return isFn ? errCb(error) : Promise.reject(error); }; } /** * 设置 axios.defaults * @param key 配置键 * @param val 配置值 */ export function setAxDefaults(key: string, val: any) { temp.axiosList?.forEach((_ax) => { (_ax.defaults as any)[key] = val; }); } /** * 设置 headers * @param key header 键 * @param val header 值 */ export function setAxHeaders(key: string, val: any) { temp.axiosList?.forEach((_ax) => { (_ax.defaults.headers as any)[key] = val; }); } /** * 设置 timeout * @param timeout 超时时间 */ export function setAxTimeout(timeout = 0) { setAxDefaults("timeout", timeout); } /** * 设置 Authorization token * @param token token 值 */ export function setAxAuthorization(token: string) { setAxHeaders("Authorization", token); } /** * 设置 baseUrl * @param url 基础 URL */ export function setAxBaseUrl(url: string) { setAxDefaults("baseURL", url); } /** * 获取 axios.CancelToken.source,用于取消请求 * @returns CancelToken source */ export function getCancelTokenSource() { return axiosDef.CancelToken.source(); } /** * 检查是否是 cancel 的结果 * @param res * @returns */ export function isCancel(res) { return axiosDef.isCancel(res); } // 接口缓存处理 temp.axiosList?.forEach((_ax) => { _ax.interceptors.response.use( async (response: AxiosResponseCus) => { // 存在缓存时,取缓存数据 if (response.config?.cache) { // 有结果时结束 promise response.config?.cache.resolve(response); const res = await (response.config?.cache.promise as Promise); return res; } return response; }, async (error) => { // 取缓存数据 if (error?.cache && error?.cache.promise) { // 有结果时结束 promise const res = await (error?.cache.promise as Promise); return res; } // 接口取消 if (axiosDef.isCancel(error)) { console.info("axios 请求已取消:", error); return Promise.reject(error); } }, ); }); export { axios, axiosDef }; export default axios;