import Axios, { type AxiosInstance, type AxiosRequestConfig } from 'axios'; import type { SuHttpError, RequestMethods, SuHttpResponse, SuHttpRequestConfig } from './types.d'; import { NProgress, cookies, errorMessage } from '@utogether/utils'; import { isRef } from 'vue'; import { has } from 'xe-utils'; const kTOKENKEY = 'authorized-token'; const defaultConfig: AxiosRequestConfig = { timeout: 180000, headers: { Accept: 'application/json, text/plain, */*', 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' } }; class SuHttp { constructor() { this.httpInterceptorsRequest(); this.httpInterceptorsResponse(); } router = undefined; baseUrl = 'http: //192.168.101.138.8099'; /** token过期后,暂存待执行的请求 */ private static requests = []; /** 防止重复刷新token */ private static isRefreshing = false; /** 设置router调整 */ setRouter(router) { this.router = router; } setBaseUrl(baseUrl) { this.baseUrl = baseUrl; } // 初始化配置对象 private static initConfig: SuHttpRequestConfig = {}; // 保存当前Axios实例对象 private static axiosInstance: AxiosInstance = Axios.create(defaultConfig); /** 重连原始请求 */ private static retryOriginalRequest(config: SuHttpRequestConfig) { return new Promise(resolve => { // @ts-ignore SuHttp.requests.push((token: string) => { config.headers!['Authorization'] = 'Bearer ' + token; resolve(config); }); }); } // 请求拦截 private httpInterceptorsRequest(): void { SuHttp.axiosInstance.interceptors.request.use( (config: any) => { if (has(config, 'loading') && isRef(config.loading)) { config.loading.value = true; } // 开启进度条动画 NProgress.start(); // 优先判断post/get等方法是否传入回掉,否则执行初始化设置等回掉 if (typeof config.beforeRequestCallback === 'function') { config.beforeRequestCallback(config); return config; } if (SuHttp.initConfig.beforeRequestCallback) { SuHttp.initConfig.beforeRequestCallback(config); return config; } const whiteList = ['/refreshToken', '/login']; if (whiteList.some(v => config.url.indexOf(v) > -1)) return config; return new Promise(resolve => { const token = cookies.get(kTOKENKEY); if (token) { const data = JSON.parse(token); const now = new Date().getTime(); const expired = data.expires - now <= 0; if (expired) { if (!SuHttp.isRefreshing) { SuHttp.isRefreshing = true; // requests.push(() => SuHttp.axiosInstance(config)); this.get(this.baseUrl + '/uath/refreshToken', { refreshToken: data.refreshToken }) .then((res: any) => { if (!res?.access_token) { this.routeToLogin(); } else { // 替换token this.setToken(res); config.headers['Authorization'] = 'Bearer ' + res.access_token; // token 刷新后将数组的方法重新执行 SuHttp.requests.forEach(cb => cb(res.access_token)); SuHttp.requests = []; // 重新请求完清空 } }) .catch(() => { this.routeToLogin(); }) .finally(() => { SuHttp.isRefreshing = false; }); } // @ts-ignore resolve(SuHttp.retryOriginalRequest(config)); } else { config.headers['Authorization'] = 'Bearer ' + data.accessToken; resolve(config); } } else { const sessionKey = cookies.get('kCookies_token') as string; if (sessionKey) { config.headers['X-Token'] = sessionKey; } resolve(config); } }); }, error => { return Promise.reject(error); } ); } private setToken(token) { const { access_token, expires_in, refresh_token } = token; const cache = { accessToken: access_token, refreshToken: refresh_token, expires: Date.now() + expires_in * 1000 }; cookies.set(kTOKENKEY, JSON.stringify(cache)); // cookies.set(kTOKENKEY, JSON.stringify(cache), { expires: expires_in }); } private routeToLogin() { SuHttp.requests = []; // 重新请求完清空 cookies.remove('kCookies_token'); cookies.remove('authorized-token'); cookies.remove('loginer'); this.router.push({ name: 'login' }); } // 响应拦截 private httpInterceptorsResponse(): void { const instance = SuHttp.axiosInstance; instance.interceptors.response.use( (response: SuHttpResponse) => { const $config = response.config; if (has($config, 'loading') && isRef($config.loading)) { $config.loading.value = false; } // 关闭进度条动画 NProgress.done(); // 优先判断post/get等方法是否传入回掉,否则执行初始化设置等回掉 if (typeof $config.beforeResponseCallback === 'function') { $config.beforeResponseCallback(response); return response.data; } if (SuHttp.initConfig.beforeResponseCallback) { SuHttp.initConfig.beforeResponseCallback(response); return response.data; } return response.data; }, (error: SuHttpError) => { const $error = error; // @ts-ignore if (has(error.config, 'loading') && isRef(error?.config?.loading)) { // @ts-ignore error.config.loading.value = false; } $error.isCancelRequest = Axios.isCancel($error); // 关闭进度条动画 NProgress.done(); // 所有的响应异常 区分来源为取消请求/非取消请求 return Promise.reject($error); } ); } private transformConfigByMethod(params: any, config: SuHttpRequestConfig): SuHttpRequestConfig { const { method } = config; const methods = ['get']; const m = method!.toLocaleLowerCase(); const props = methods.includes(m) ? 'params' : 'data'; return { ...config, [props]: params }; } // 通用请求工具函数 public request( method: RequestMethods, url: string, param?: AxiosRequestConfig, axiosConfig?: SuHttpRequestConfig ): Promise { const config = this.transformConfigByMethod(param, { method, url, ...axiosConfig } as SuHttpRequestConfig); // 单独处理自定义请求/响应回掉 return new Promise((resolve, reject) => { SuHttp.axiosInstance .request(config) .then((res: any) => { if (res && res?.code === '0') { resolve(res.data); } else if (res.code === '500') { errorMessage(res.msg, { duration: 8000 }); if (config.method === 'get') { resolve(res); } else { reject(res.msg); } } else if (res?.code !== '-1') { resolve(res); } else { const msg = res?.msg || '服务异常'; errorMessage(msg, { duration: 8000 }); reject(msg); } }) .catch(e => { if (e?.code) { if (e?.response?.status === 401) { // @ts-ignore return this.router?.push({ path: '/login' }); } errorMessage(this.getNetworkError(e?.response?.status) || e?.message, { duration: 8000 }); } if (e.config.method === 'get') { resolve(e); } else { reject(e); } }); }); } getNetworkError = code => { if (!code) return null; const msgMap = { 400: '错误的请求', 401: '服务未授权,请重新登录', 403: '拒绝访问', 404: '请求错误,未找到该资源', 408: '请求超时', 500: '服务器端出差', 502: '网络超时', 503: '服务不可用', 504: '网络超时' }; return msgMap[code]; }; // 单独抽离的post工具函数 public post(url: string, params?: T, config?: SuHttpRequestConfig): Promise

{ // @ts-ignore return this.request

('post', url, params, config); } // 单独抽离的delete工具函数 public delete(url: string, params?: T, config?: SuHttpRequestConfig): Promise

{ // @ts-ignore return this.request

('delete', url, params, config); } // 单独抽离的put工具函数 public put(url: string, params?: T, config?: SuHttpRequestConfig): Promise

{ // @ts-ignore return this.request

('put', url, params, config); } // 单独抽离的get工具函数 public get(url: string, params?: T, config?: SuHttpRequestConfig): Promise

{ for (const key in params) { if (!params[key]) delete params[key]; } // @ts-ignore return this.request

('get', url, params, config); } public postRouter(params?: any): Promise { return this.request('post', 'route/service', params); } } export const http = new SuHttp();