/*
* @description: 请求封装
* @Author: Jay
* @Date: 2023-04-11 13:24:41
* @LastEditors: Jay
* @LastEditTime: 2023-09-04 15:04:28
*/
// 导入axios
import axios, { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
// 使用element-ui ElMessage做消息提醒 ElLoading加载
import { ElMessage, ElLoading } from "element-plus";
//请求头
import operate from "@/utils/operate"
//加载配置
let loadingInstance: { close: () => void };
let requestNum = 0;
//加载动画
const addLoading = () => {
// 防止重复弹出
requestNum++;
if (requestNum === 1) {
loadingInstance = ElLoading.service({ fullscreen: true });
}
}
// 关闭 加载动画
const cancelLoading = () => {
requestNum--;
if (requestNum === 0) loadingInstance?.close();
}
//格式化请求链接
function getRequestKey(config: AxiosRequestConfig) {
const { url, method, data, params } = config,
//字符串化参数
dataStr = JSON.stringify(data) || '',
paramsStr = JSON.stringify(params) || '',
//记得这里一定要处理 每次请求都掉会变化的参数(比如每个请求都携带了时间戳),否则二个请求的key不一样
key = [method, url, dataStr, paramsStr].join("&");
return key;
}
//创建存储 key 的 集合
const pendingMap = new Map()
//是否重复请求key
function setPendingMap(config: AxiosRequestConfig) {
// 手动取消请求
const controller = new AbortController()
config.signal = controller.signal
const key = getRequestKey(config)
//判断是否存在key 存在取消请求 不存在添加
if (pendingMap.has(key)) {
// abort取消请求
pendingMap.get(key).abort()
//删除key
pendingMap.delete(key)
} else {
pendingMap.set(key, controller)
}
}
//增加新的请求参数类型
declare module 'axios' {
//请求自定义参数
interface AxiosRequestConfig {
// 是否显示加载框
ifLoading?: boolean
// 是否允许重复请求
repeatRequest?: boolean
// 登录 token
isToken?: any;
}
// 解決 类型“AxiosResponse<any, any>”上不存在属性“code”
// interface AxiosResponse<T = any> {
// // 请求 data 里的一级参数
// code: number;
// time: string;
// msg: string;
// data: T;
// }
}
//创建axios的一个实例
const axiosInstance: AxiosInstance = axios.create({
//接口统一域名
baseURL: operate.baseUrl(),
//设置超时
timeout: 1000 * 30,
//跨域携带cookie
withCredentials: true,
})
// 添加请求拦截器
axiosInstance.interceptors.request.use(
(config) => {
//加载动画
if (config?.ifLoading) addLoading();
//是否判断重复请求
if (!config.repeatRequest) {
setPendingMap(config)
}
//判断是否有token 根据自己的需求判断
const token = config.isToken
// console.log("判断是否有token", token)
if (token != undefined) {
//如果要求携带在参数中
// config.params = Object.assign({}, config.params, operate.uploadParameters())
// 如果要求携带在请求头中
// config.headers = Object.assign({}, config.headers, operate.uploadParameters())
}
return config
},
(error: AxiosError) => {
return Promise.reject(error)
}
)
// 添加响应拦截器
axiosInstance.interceptors.response.use((response: AxiosResponse) => {
const config = response.config
// 关闭加载 动画
if (config?.ifLoading) cancelLoading();
//是否登录过期
if (response.data.code == 400 || response.data.code == 401) {
ElMessage.error("登录过期,请重新登录")
// //清除登录缓存
// store.commit("LOGOUT")
// //返回首页
// setTimeout(() => {
// router.push("/");
// }, 500);
return
}
// 返回参数
return response.data
})
// 错误处理
axiosInstance.interceptors.response.use(undefined, (error) => {
const config = error.config
// 关闭加载 动画
if (config?.ifLoading) cancelLoading();
//拦截掉重复请求的错误,中断promise执行
if (axios.isCancel(error)) return []
/***** 接收到异常响应的处理开始 *****/
if (error && error.response) {
// 1.公共错误处理
// 2.根据响应码具体处理
switch (error.response.status) {
case 400:
error.message = '错误请求'
break;
case 401:
error.message = '未授权,请重新登录'
break;
case 403:
error.message = '拒绝访问'
break;
case 404:
error.message = '请求错误,未找到该资源'
// window.location.href = "/NotFound"
break;
case 405:
error.message = '请求方法未允许'
break;
case 408:
error.message = '请求超时'
break;
case 500:
error.message = '服务器端出错'
break;
case 501:
error.message = '网络未实现'
break;
case 502:
error.message = '网络错误'
break;
case 503:
error.message = '服务不可用'
break;
case 504:
error.message = '网络超时'
break;
case 505:
error.message = 'http版本不支持该请求'
break;
default:
error.message = `连接错误${error.response.status}`
}
} else {
// 超时处理
if (JSON.stringify(error).includes('timeout')) {
error.message = '服务器响应超时,请刷新当前页'
} else {
error.message = '连接服务器失败'
}
}
//提示
ElMessage.error(error.message)
/***** 处理结束 *****/
return Promise.resolve(error)
})
export default axiosInstance