import { createAlova, Method, StatesHook } from "alova"; import fetchAdapter from "alova/fetch"; import VueHook from "alova/vue"; import { replaceOriginUrl } from "../bschSsoRedirect"; import config from "@/assets/config/host.config"; import Utils from ".."; /* ============= 类型定义 ============= */ export interface RequestHooks { /** 前置票据/通用头注入 */ injectHeaders?: (method: Method) => void; /** 加载开始 */ onLoadingStart?: () => void; /** 加载结束 */ onLoadingEnd?: () => void; /** 业务错误提示(code !== 0)*/ onBizError?: (msg: string) => void; /** http 错误提示(status !== 200)*/ onHttpError?: (msg: string) => void; } /** 和后端约定的通用响应格式 */ export interface BschResponse { code: number; msg?: string; data: T; urlToRedirectTo?: string; } /* ============= 默认空实现 ============= */ const defHooks: Required = { injectHeaders: () => {}, onLoadingStart: () => {}, onLoadingEnd: () => {}, onBizError: () => {}, onHttpError: () => {}, }; /* ============= 错误处理(原 axios 逻辑平移) ============= */ function errorHandle(res: BschResponse, status: number): T { // http 层错误 if (status !== 200) { let msg = "网络错误"; switch (status) { case 401: case 403: break; case 404: msg = "请求的资源不存在!"; break; default: msg = res?.msg || "内部服务错误!"; } throw new Error(msg); } // 业务层错误 switch (res.code) { case 0: return res.data ?? (null as T); case 302: console.log(res); window.location.href = res.urlToRedirectTo!; // window.location.href = replaceOriginUrl(res.urlToRedirectTo!, config.serviceHost); throw new Error("redirect"); case 4: throw new Error(res.msg || "参数错误!"); default: throw new Error(res.msg || "业务错误"); } } /* ============= 工厂函数 ============= */ export function createBschAlova(baseURL: string, hooks: RequestHooks = {}, statesHook: StatesHook = VueHook) { const { injectHeaders, onLoadingStart, onLoadingEnd, onBizError, onHttpError } = { ...defHooks, ...hooks, }; let reqCount = 0; let timer: ReturnType; /* ---- 请求拦截 ---- */ function beforeRequest(method: Method) { reqCount++; if (reqCount === 1) timer = setTimeout(() => onLoadingStart(), 800); Promise.resolve().then(() => { if (reqCount === 0) { clearTimeout(timer); onLoadingEnd(); } }); // 1. 用户注入票据/角色等头 injectHeaders(method); method.config.credentials = "include"; // 2. 通用头 method.config.headers = Object.assign(method.config.headers || {}, { yschapirequest: "1", timeout: 20000, yschreferer: Utils.strToHexCharCode(window.location.href), }); } /* ---- 响应拦截 ---- */ async function onSuccess(response: Response) { reqCount--; if (reqCount === 0) { clearTimeout(timer); onLoadingEnd(); } const res: BschResponse = await response.json(); try { return errorHandle(res, response.status); } catch (e: any) { console.log(e); onBizError(e.message); return Promise.reject(e); } } async function onError(err: any) { reqCount = 0; clearTimeout(timer); onLoadingEnd(); const msg = err?.message || "网络请求出错"; onHttpError(msg); return Promise.reject(err); } async function onComplete(method: Method) { if (method.fromCache) { reqCount--; if (reqCount === 0) { clearTimeout(timer); onLoadingEnd(); } } } return createAlova({ statesHook, requestAdapter: fetchAdapter(), baseURL, cacheFor: null, beforeRequest, responded: { onSuccess, onError, onComplete }, }); }