123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- import axios, {AxiosInstance, AxiosResponse, AxiosError, AxiosRequestConfig} from 'axios';
- import {ElMessage, ElMessageBox, ElLoading, LoadingOptionsResolved} from 'element-plus';
- // 重复请求队列
- const pendingMap = new Map();
- // 全局loading
- const LoadingInstance = {
- _target: null as any,
- _count: 0,
- };
- type customOptionsType = {
- repeat_request_cancel?: boolean; // 是否开启取消重复请求, 默认为 true
- loading?: boolean; // 是否开启全屏loading层效果, 默认为false
- reduct_data_format?: boolean; // 是否开启简洁的数据结构响应 减少一层data, 默认为true
- error_message_show?: boolean; // 是否开启接口错误信息展示,默认为true
- code_message_show?: boolean; // 是否开启code不为0时的信息提示, 默认为false
- };
- export default function myAxios(axiosConfig: any, customOptions?: customOptionsType, loadingOptions?: LoadingOptionsResolved) {
- // 配置新建一个 axios 实例
- const service: AxiosInstance = axios.create({
- baseURL: import.meta.env.VITE_API_URL,
- timeout: 50000,
- headers: {'Content-Type': 'application/json'},
- });
- // 自定义配置
- let custom_options = Object.assign(
- {
- repeat_request_cancel: false, // 是否开启取消重复请求, 默认为 true
- loading: false, // 是否开启全屏loading层效果, 默认为false
- reduct_data_format: true, // 是否开启简洁的数据结构响应 减少一层data, 默认为true
- error_message_show: true, // 是否开启接口错误信息展示,默认为true
- code_message_show: false, // 是否开启code不为0时的信息提示, 默认为false
- },
- customOptions
- );
- // 添加请求拦截器
- service.interceptors.request.use(
- async (config: any) => {
- removePending(config);
- custom_options.repeat_request_cancel && addPending(config);
- // 创建loading实例
- if (custom_options.loading) {
- LoadingInstance._count++;
- if (LoadingInstance._count === 1) {
- LoadingInstance._target = ElLoading.service(loadingOptions);
- }
- }
- // 在发送请求之前做些什么 token
- if (sessionStorage.getItem('token')) {
- (<any>config.headers)['Authorization'] = `Bearer ${sessionStorage.getItem('token')}`;
- }
- return config;
- },
- async (error: AxiosError) => {
- return Promise.reject(error);
- }
- );
- // 添加响应拦截器
- service.interceptors.response.use(
- (response: AxiosResponse): any => {
- removePending(response.config);
- custom_options.loading && closeLoading(custom_options); // 关闭loading
- // 对响应数据做点什么
- if (custom_options.code_message_show && response.data && response.data.code !== 0) {
- ElMessage({
- type: 'error',
- message: response.data.message,
- });
- return Promise.reject(response.data); // code不等于0, 页面具体逻辑就不执行了
- }
- return custom_options.reduct_data_format ? response.data : response;
- },
- async (error: AxiosError): Promise<never> => {
- error.config && removePending(error.config);
- custom_options.loading && closeLoading(custom_options); // 关闭loading
- custom_options.error_message_show && httpErrorStatusHandle(error); // 处理错误状态码
- return Promise.reject(error); // 错误继续返回给到具体页面
- }
- );
- return service(axiosConfig);
- }
- /**
- * @description 处理异常
- * @param {*} error
- */
- function httpErrorStatusHandle(error: any) {
- // 设置一个变量 处理同一时间多个错误重复弹窗口
- let tokenAbnormal: boolean = false;
- // 处理被取消的请求
- if (axios.isCancel(error)) return;
- let message = '';
- if (error && error.response) {
- switch (error.response.status) {
- case 302:
- message = '接口重定向了!';
- break;
- case 400:
- message = '参数不正确!';
- break;
- case 401:
- if (!tokenAbnormal) {
- tokenAbnormal = true;
- // 弹出框
- ElMessageBox.alert('你已被登出,请重新登录', '提示', {type: 'warning'})
- .then(() => {
- window.localStorage.clear(); // 清除本地存储
- window.sessionStorage.clear(); // 清除临时存储
- location.reload(); //刷新页面
- })
- .catch((): void => {
- });
- // 设置定时器,确保下次异常时弹出框正常弹出
- setTimeout(() => {
- tokenAbnormal = false;
- }, 3000);
- }
- break;
- case 403:
- message = '您没有权限操作!';
- break;
- case 404:
- message = `请求地址出错: ${error.response.config.url}`;
- break; // 在正确域名下
- case 408:
- message = '请求超时!';
- break;
- case 409:
- message = '系统已存在相同数据!';
- break;
- case 500:
- if (error.response?.data.message) message = error.response.data.message;
- else message = '服务器内部错误!';
- break;
- case 501:
- message = '服务未实现!';
- break;
- case 502:
- message = '网关错误!';
- break;
- case 503:
- message = '服务不可用!';
- break;
- case 504:
- message = '服务暂时无法访问,请稍后再试!';
- break;
- case 505:
- message = 'HTTP版本不受支持!';
- break;
- default:
- message = '异常问题,请联系管理员!';
- break;
- }
- }
- if (error.message.includes('timeout')) message = '网络请求超时!';
- if (error.message.includes('Network')) message = window.navigator.onLine ? '服务端异常!' : '您断网了!';
- ElMessage({
- type: 'error',
- message,
- });
- }
- /**
- * @description 关闭Loading层实例
- * @param {*} _options
- */
- function closeLoading(_options: any) {
- if (_options.loading && LoadingInstance._count > 0) LoadingInstance._count--;
- if (LoadingInstance._count === 0) {
- LoadingInstance._target.close();
- LoadingInstance._target = null;
- }
- }
- /**
- * @description 储存每个请求的唯一cancel回调, 以此为标识
- * @param {*} config
- */
- function addPending(config: AxiosRequestConfig) {
- const pendingKey = getPendingKey(config);
- config.cancelToken =
- config.cancelToken ||
- new axios.CancelToken((cancel) => {
- if (!pendingMap.has(pendingKey)) {
- pendingMap.set(pendingKey, cancel);
- }
- });
- }
- /**
- * @description 删除重复的请求
- * @param {*} config
- */
- function removePending(config: AxiosRequestConfig) {
- const pendingKey = getPendingKey(config);
- if (pendingMap.has(pendingKey)) {
- const cancelToken = pendingMap.get(pendingKey);
- // 如你不明白此处为什么需要传递pendingKey可以看文章下方的补丁解释
- cancelToken(pendingKey);
- pendingMap.delete(pendingKey);
- }
- }
- /**
- * @description 生成唯一的每个请求的唯一key
- * @param {*} config
- * @returns
- */
- function getPendingKey(config: AxiosRequestConfig) {
- let {url, method, params, data} = config;
- // if (typeof data === 'string') data = JSON.parse(data); // response里面返回的config.data是个字符串对象
- return [url, method, JSON.stringify(params), JSON.stringify(data)].join('&');
- }
|