import Axios from 'axios' import { ResCode } from './constant' import { setOfflineAxios } from './offline' import type { AxiosResponse, AxiosRequestConfig } from 'axios' export type ResErrorHandler = >(response: AxiosResponse, data?: T) => void export type ReqErrorHandler = (err: Error, response: AxiosRequestConfig) => void export type ResData = { code: ResCode, message: string, data: T, msg: string } export type Hook = { before?: (config: AxiosRequestConfig) => void after?: (config: AxiosRequestConfig) => void } export const axiosFactory = () => { const axiosRaw = Axios.create() const axiosConfig = { token: localStorage.getItem('token'), unTokenSet: [] as string[], unReqErrorSet: [] as string[], unResErrorSet: [] as string[], resErrorHandler: [] as ResErrorHandler[], reqErrorHandler: [] as ReqErrorHandler[], unLoadingSet: [] as string[], hook: [] as Hook[] } type AxiosConfig = typeof axiosConfig type ExponseApi = { set: (val: AxiosConfig[K]) => void } & (AxiosConfig[K] extends Array ? { add: (...val: AxiosConfig[K]) => void, del: (...val: AxiosConfig[K]) => void } : { del: () => void }) const getExponseApi = (key: K): ExponseApi => { let axiosObj = axiosConfig[key] as any[] const apis: any = { set (val: AxiosConfig[K]) { console.error('set', key, val) axiosObj = axiosConfig[key] = val as any } } if (Array.isArray(axiosObj)) { apis.add = (...val: any[]) => { axiosObj.push(...val) } apis.del = (...val: any[]) => { if (val) { apis.set(axiosObj.filter((item: any) => !val?.includes(item)) as any) } else { axiosObj.length = 0 } } } else { apis.del = () => { axiosConfig[key] = undefined as any } } return apis } const getToken = () => axiosConfig.token const setToken = (token: string) => { localStorage.setItem('token', token) axiosConfig.token = token } const delToken = () => { localStorage.removeItem('token') axiosConfig.token = null } const { set: setUnsetTokenURLS, add: addUnsetTokenURLS, del: delUnsetTokenURLS } = getExponseApi('unTokenSet') const { set: setResErrorHandler, add: addResErrorHandler, del: delResErrorHandler } = getExponseApi('resErrorHandler') const { set: setUnsetReqErrorURLS, add: addUnsetReqErrorURLS, del: delUnsetReqErrorURLS } = getExponseApi('unReqErrorSet') const { set: setReqErrorHandler, add: addReqErrorHandler, del: delReqErrorHandler } = getExponseApi('reqErrorHandler') const { set: setUnsetResErrorURLS, add: addUnsetResErrorURLS, del: delUnsetResErrorURLS } = getExponseApi('unResErrorSet') const { set: setHook, add: addHook, del: delHook } = getExponseApi('hook') const setDefaultURI = (url: string) => { axiosRaw.defaults.baseURL = url } const matchURL = (urls: string[], config: AxiosRequestConfig) => config && config.url && urls.includes(config.url) const callErrorHandler = (key: 'req' | 'res', ...args: any[]) => { Promise.resolve() .then(() => { const api = `${key}ErrorHandler` ;(axiosConfig as any)[api].forEach((handler: any) => handler(...args)) }) } axiosRaw.interceptors.request.use( config => { for (const hook of axiosConfig.hook) { hook.before && hook.before(config) } // console.log(axiosConfig.unTokenSet) if (!matchURL(axiosConfig.unTokenSet, config)) { if (!axiosConfig.token) { if (!offline && !matchURL(axiosConfig.unReqErrorSet, config)) { console.log(config) const error = new Error('缺少token') callErrorHandler('req', error, config) throw error } } else { config.headers = { ...config.headers, token: axiosConfig.token } } } return config } ) offline && setOfflineAxios(axiosRaw) axiosRaw.interceptors.response.use( (response: AxiosResponse>) => { for (const hook of axiosConfig.hook) { hook.after && hook.after(response.config) } if (matchURL(axiosConfig.unResErrorSet, response.config)) { return response } if (response.status !== 200) { callErrorHandler('res', response) throw new Error(response.statusText) } else if (response.data.code !== ResCode.SUCCESS) { callErrorHandler('res', response, response.data) if (response.data.code === ResCode.TOKEN_INVALID) { delToken() } throw new Error(response?.data?.message) } else { return response.data.data } }, (err) => { for (const hook of axiosConfig.hook) { hook.after && hook.after(err.config) } if (!matchURL(axiosConfig.unResErrorSet, err.config)) { callErrorHandler('res', err.response) } throw new Error(err.response && err.response.statusText) } ) type AxiosProcess = { getUri(config?: AxiosRequestConfig): string; request(config: AxiosRequestConfig): Promise; get(url: string, config?: AxiosRequestConfig): Promise; delete(url: string, config?: AxiosRequestConfig): Promise; head(url: string, config?: AxiosRequestConfig): Promise; options(url: string, config?: AxiosRequestConfig): Promise; post(url: string, data?: D, config?: AxiosRequestConfig): Promise; put(url: string, data?: D, config?: AxiosRequestConfig): Promise; patch(url: string, data?: D, config?: AxiosRequestConfig): Promise; postForm(url: string, data?: D, config?: AxiosRequestConfig): Promise; putForm(url: string, data?: D, config?: AxiosRequestConfig): Promise; patchForm(url: string, data?: D, config?: AxiosRequestConfig): Promise; } interface AxiosInstanceProcess extends AxiosProcess { (config: AxiosRequestConfig): Promise; (url: string, config?: AxiosRequestConfig): Promise; } const axios: AxiosInstanceProcess = axiosRaw as any return { axios, getToken, setToken, delToken, setUnsetTokenURLS, addUnsetTokenURLS, delUnsetTokenURLS, setResErrorHandler, addResErrorHandler, delResErrorHandler, setUnsetReqErrorURLS, addUnsetReqErrorURLS, delUnsetReqErrorURLS, setReqErrorHandler, addReqErrorHandler, delReqErrorHandler, setUnsetResErrorURLS, addUnsetResErrorURLS, delUnsetResErrorURLS, setDefaultURI, setHook, addHook, delHook, } } export default axiosFactory