目录结构:
import type { AxiosRequestConfig, AxiosResponse } from 'axios'
// 封装的拦截器
export interface BMYRequestInterceptors {
requestInterceptor?: (config: AxiosRequestConfig) => AxiosRequestConfig
requestInterceptorCatch?: (error: any) => any
// responseInterceptor?: (res: T) => T
responseInterceptor?: <T = AxiosResponse>(config: T) => T
responseInterceptorCatch?: (error: any) => any
}
export interface BMYRequestConfig extends AxiosRequestConfig {
interceptors?: BMYRequestInterceptors
noCancel?: 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: AxiosInstance
interceptors?: 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 } = config
if (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[] | string
request: InstanceType<typeof Request>
}
// 取消请求
export const cancelRequest = ({ url, request }: CancelRequest) => {
return request.cancelRequest(url)
}
// 取消全部请求
export const cancelAllRequest = (request: InstanceType<typeof Request>) => {
return request.cancelAllRequest()
}