目录结构:
import type { AxiosRequestConfig, AxiosResponse } from 'axios'// 封装的拦截器export interface BMYRequestInterceptors {requestInterceptor?: (config: AxiosRequestConfig) => AxiosRequestConfigrequestInterceptorCatch?: (error: any) => any// responseInterceptor?: (res: T) => TresponseInterceptor?: <T = AxiosResponse>(config: T) => TresponseInterceptorCatch?: (error: any) => any}export interface BMYRequestConfig extends AxiosRequestConfig {interceptors?: BMYRequestInterceptorsnoCancel?: boolean// showLoading?: boolean}// type.ts 取消请求的数组,请求前判断同一url是否存在,存在就取消export interface CancelRequestSource {[index: string]: () => void}
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'import Modal from '@/components/Modal'import router from '@/router'import i18n from '@/locale'import Message from '@/components/Message'import type { AxiosInstance } from 'axios'import type { BMYRequestInterceptors, BMYRequestConfig, CancelRequestSource } from './type'class Request {instance: AxiosInstanceinterceptors?: BMYRequestInterceptors/*** 存放取消方法的集合* 在创建请求后将取消请求方法 push 到该集合中* 封装一个方法,可以取消请求,传入 url: string|string[]* 在请求之前判断同一URL是否存在,如果存在就取消请求*/cancelRequestSourceList: CancelRequestSource[] = []/*** 请求之前需要将url push到该集合中* 请求完毕后将url从集合中删除* 添加在发送请求之前完成,删除在响应之后删除*/requestUrlList: string[] = []constructor(config: BMYRequestConfig) {// 创建 axios 实例this.instance = axios.create(config)// 保存基本信息this.interceptors = config.interceptors// 全局请求拦截this.instance.interceptors.request.use((res: AxiosRequestConfig) => {return res},(err: any) => err)// 拦截器this.instance.interceptors.request.use(this.interceptors?.requestInterceptor,this.interceptors?.requestInterceptorCatch)this.instance.interceptors.response.use(this.interceptors?.responseInterceptor,this.interceptors?.responseInterceptorCatch)// 全局响应拦截器保证最后执行this.instance.interceptors.response.use(// 因为我们接口的数据都在res.data下,所以我们直接返回res.data(res: AxiosResponse) => {if (res?.data?.code == 200) {return res.data} else if ((res as any).code === 'ERR_CANCELED') {return Promise.reject('ERR_CANCELED')} else if ((res as any)?.response?.data?.status === 401) {Modal({type: 'error',titleAlign: 'start',title: i18n.global.t('service.resuest.warning'),simple: false,content: i18n.global.t('service.resuest.warningTipInfo'),onOk: () => {router.push({ name: 'login' })}})return Promise.reject('需要重新登录')} else if (res.status == 200) {return res.data} else {Message({content: (res as any)?.response?.data?.message,duration: 2 * 1000,type: 'warning'})return Promise.reject('错误')}},(err: any) => {return err})}// index.ts/*** @description: 获取指定 url 在 cancelRequestSourceList 中的索引* @param {string} url* @returns {number} 索引位置*/private getSourceIndex(url: string): number {return this.cancelRequestSourceList?.findIndex((item: CancelRequestSource) => {return Object.keys(item)[0] === url}) as number}/*** @description: 删除 requestUrlList 和 cancelRequestSourceList* @param {string} url* @returns {*}*/private delUrl(url: string) {const urlIndex = this.requestUrlList?.findIndex((u) => u === url)const sourceIndex = this.getSourceIndex(url)// 删除url和cancel方法urlIndex !== -1 && this.requestUrlList?.splice(urlIndex as number, 1)sourceIndex !== -1 && this.cancelRequestSourceList?.splice(sourceIndex as number, 1)}// 取消全部请求cancelAllRequest() {this.cancelRequestSourceList?.forEach((source) => {const key = Object.keys(source)[0]source[key]()})}// 取消请求cancelRequest(url: string[] | string) {if (typeof url === 'string') {// 取消单个请求const sourceIndex = this.getSourceIndex(url)sourceIndex >= 0 && this.cancelRequestSourceList?.[sourceIndex][url]()} else {// 存在多个需要取消请求的地址url.forEach((u) => {const sourceIndex = this.getSourceIndex(u)sourceIndex >= 0 && this.cancelRequestSourceList?.[sourceIndex][u]()})}}request<T>(config: BMYRequestConfig): Promise<T> {return new Promise((resolve, reject) => {// 如果我们为单个请求设置拦截器,这里使用单个请求的拦截器if (config.interceptors?.requestInterceptor) {config = config.interceptors.requestInterceptor(config)}const { url, noCancel } = configif (url) {// 取消多余的请求!noCancel && this.cancelRequest(url)this.requestUrlList?.push(url)config.cancelToken = new axios.CancelToken((c) => {this.cancelRequestSourceList?.push({[url]: () => {// console.log(c, c())c()return Promise.reject('')}})})}this.instance.request<any, T>(config).then((res) => {// 如果我们为单个响应设置拦截器,这里使用单个响应的拦截器if (config.interceptors?.responseInterceptor) {res = config.interceptors.responseInterceptor<T>(res)}resolve(res)}).catch((err: any) => {reject(err)// return err}).finally(() => {url && this.delUrl(url)})})}get<T = any>(config: BMYRequestConfig): Promise<T> {return this.request<T>({ ...config, method: 'GET' })}post<T = any>(config: BMYRequestConfig): Promise<T> {return this.request<T>({ ...config, method: 'POST' })}delete<T = any>(config: BMYRequestConfig): Promise<T> {return this.request<T>({ ...config, method: 'DELETE' })}put<T = any>(config: BMYRequestConfig): Promise<T> {return this.request<T>({ ...config, method: 'PUT' })}}export default Request
import Request from './request'import { localCache } from '@/util'import type { BMYRequestInterceptors } from './request/type'const interceptors: BMYRequestInterceptors = {requestInterceptor: (config) => {const token = localCache.getCache('token')if (token) {config!.headers!.Authorization = `Bearer ${token}`}return config},requestInterceptorCatch: (err) => {return err},responseInterceptor: (res) => {return res},responseInterceptorCatch: (err) => {return err}}const baseConfig = {timeout: 1000 * 60 * 5,interceptors}export const sysRequest = new Request({...baseConfig,baseURL: '/systemcenter/api/banmayu'})export const webRequest = new Request({...baseConfig,baseURL: '/elabnoteWeb/api/banmayu'})export const materialRequest = new Request({...baseConfig,baseURL: '/material/api/banmayu'})export const request = new Request({...baseConfig})type CancelRequest = {url: string[] | stringrequest: InstanceType<typeof Request>}// 取消请求export const cancelRequest = ({ url, request }: CancelRequest) => {return request.cancelRequest(url)}// 取消全部请求export const cancelAllRequest = (request: InstanceType<typeof Request>) => {return request.cancelAllRequest()}
