Sanakey
5 天以前 2af71bcf522c485ea005184c977986374a7dcc4a
提交 | 用户 | age
2f6253 1 // axios配置  可自行根据项目进行更改,只需更改该文件即可,其他文件可以不动
2 // The axios configuration can be changed according to the project, just change the file, other files can be left unchanged
3
c118e8 4 import type { AxiosInstance, AxiosResponse } from 'axios';
4f35b9 5 import { clone } from 'lodash-es';
4d2fb0 6 import type { RequestOptions, Result } from '#/axios';
b7ce74 7 import type { AxiosTransform, CreateAxiosOptions } from './axiosTransform';
V 8 import { VAxios } from './Axios';
2f6253 9 import { checkStatus } from './checkStatus';
4d2fb0 10 import { useGlobSetting } from '@/hooks/setting';
X 11 import { useMessage } from '@/hooks/web/useMessage';
12 import { RequestEnum, ResultEnum, ContentTypeEnum } from '@/enums/httpEnum';
13 import { isString, isUndefined, isNull, isEmpty } from '@/utils/is';
14 import { getToken } from '@/utils/auth';
15 import { setObjToUrlParams, deepMerge } from '@/utils';
16 import { useErrorLogStoreWithOut } from '@/store/modules/errorLog';
17 import { useI18n } from '@/hooks/web/useI18n';
50f94b 18 import { joinTimestamp, formatRequestDate } from './helper';
4d2fb0 19 import { useUserStoreWithOut } from '@/store/modules/user';
X 20 import { AxiosRetry } from '@/utils/http/axios/axiosRetry';
b1f78c 21 import axios from 'axios';
faf761 22 import DragBar from '@/layouts/default/sider/DragBar.vue';
2f6253 23
737b1b 24 const globSetting = useGlobSetting();
50f94b 25 const urlPrefix = globSetting.urlPrefix;
17d16a 26 const { createMessage, createErrorModal, createSuccessModal } = useMessage();
2f6253 27
28 /**
29  * @description: 数据处理,方便区分多种处理方式
30  */
31 const transform: AxiosTransform = {
32   /**
c0e40f 33    * @description: 处理响应数据。如果数据不是预期格式,可直接抛出错误
2f6253 34    */
c0e40f 35   transformResponseHook: (res: AxiosResponse<Result>, options: RequestOptions) => {
962f90 36     const { t } = useI18n();
b6d5b0 37     const { isTransformResponse, isReturnNativeResponse } = options;
56d8af 38     // 是否返回原生响应头 比如:需要获取响应头时使用该属性
Z 39     if (isReturnNativeResponse) {
40       return res;
41     }
2f6253 42     // 不进行任何处理,直接返回
43     // 用于页面代码可能需要直接获取code,data,message这些信息时开启
b6d5b0 44     if (!isTransformResponse) {
2f6253 45       return res.data;
46     }
47     // 错误的时候返回
48     const { data } = res;
49     if (!data) {
50       // return '[HTTP] Request has no return value';
b218f1 51       throw new Error(t('sys.api.apiRequestFailed'));
2f6253 52     }
53     //  这里 code,result,message为 后台统一的字段,需要在 types.ts内修改为项目自己的接口返回格式
12f730 54     const { code, result, state } = data;
H 55     const { msg: message } = data;
56
2f6253 57     // 这里逻辑可以根据项目进行修改
12f730 58     const isPass = code === ResultEnum.SUCCESS || state === ResultEnum.SUCCESS;
H 59     const isReflect = Reflect.has(data, 'code') || Reflect.has(data, 'state');
60     const hasSuccess = data && isReflect && isPass;
b69dcd 61     if (hasSuccess) {
17d16a 62       let successMsg = message;
bc0990 63
f91d77 64       if (isNull(successMsg) || isUndefined(successMsg) || isEmpty(successMsg)) {
bc0990 65         successMsg = t(`sys.api.operationSuccess`);
17d16a 66       }
bc0990 67
17d16a 68       if (options.successMessageMode === 'modal') {
M 69         createSuccessModal({ title: t('sys.api.successTip'), content: successMsg });
70       } else if (options.successMessageMode === 'message') {
71         createMessage.success(successMsg);
72       }
3bace1 73       return result || data;
2f6253 74     }
b69dcd 75
76     // 在此处根据自己项目的实际情况对不同的code执行不同的操作
77     // 如果不希望中断当前请求,请return数据,否则直接抛出异常即可
49b66e 78     let timeoutMsg = '';
b69dcd 79     switch (code) {
80       case ResultEnum.TIMEOUT:
49b66e 81         timeoutMsg = t('sys.api.timeoutMessage');
6544f8 82         const userStore = useUserStoreWithOut();
236ddf 83         // 被动登出,带redirect地址
X 84         userStore.logout(false);
49f39d 85         break;
b69dcd 86       default:
87         if (message) {
49b66e 88           timeoutMsg = message;
b69dcd 89         }
2f6253 90     }
49b66e 91
9ba157 92     // errorMessageMode='modal'的时候会显示modal错误弹窗,而不是消息提示,用于一些比较重要的错误
49b66e 93     // errorMessageMode='none' 一般是调用时明确表示不希望自动弹出错误提示
X 94     if (options.errorMessageMode === 'modal') {
95       createErrorModal({ title: t('sys.api.errorTip'), content: timeoutMsg });
96     } else if (options.errorMessageMode === 'message') {
97       createMessage.error(timeoutMsg);
98     }
99
100     throw new Error(timeoutMsg || t('sys.api.apiRequestFailed'));
2f6253 101   },
102
103   // 请求之前处理config
104   beforeRequestHook: (config, options) => {
996f2f 105     const { apiUrl, joinPrefix, joinParamsToUrl, formatDate, joinTime = true, urlPrefix } = options;
2f6253 106
107     if (joinPrefix) {
50f94b 108       config.url = `${urlPrefix}${config.url}`;
2f6253 109     }
110
111     if (apiUrl && isString(apiUrl)) {
112       config.url = `${apiUrl}${config.url}`;
113     }
ac1a36 114     const params = config.params || {};
49f39d 115     const data = config.data || false;
116     formatDate && data && !isString(data) && formatRequestDate(data);
6b3195 117     if (config.method?.toUpperCase() === RequestEnum.GET) {
ac1a36 118       if (!isString(params)) {
c96002 119         // 给 get 请求加上时间戳参数,避免从缓存中拿数据。
50f94b 120         config.params = Object.assign(params || {}, joinTimestamp(joinTime, false));
2f6253 121       } else {
122         // 兼容restful风格
50f94b 123         config.url = config.url + params + `${joinTimestamp(joinTime, true)}`;
34c09f 124         config.params = undefined;
2f6253 125       }
126     } else {
ac1a36 127       if (!isString(params)) {
V 128         formatDate && formatRequestDate(params);
b1f78c 129         if (
L 130           Reflect.has(config, 'data') &&
131           config.data &&
132           (Object.keys(config.data).length > 0 || config.data instanceof FormData)
133         ) {
49f39d 134           config.data = data;
135           config.params = params;
136         } else {
137           // 非GET请求如果没有提供data,则将params视为data
138           config.data = params;
139           config.params = undefined;
140         }
2f6253 141         if (joinParamsToUrl) {
49f39d 142           config.url = setObjToUrlParams(
143             config.url as string,
56a966 144             Object.assign({}, config.params, config.data),
49f39d 145           );
2f6253 146         }
147       } else {
148         // 兼容restful风格
ac1a36 149         config.url = config.url + params;
34c09f 150         config.params = undefined;
2f6253 151       }
152     }
153     return config;
154   },
155
156   /**
157    * @description: 请求拦截器处理
158    */
b6d5b0 159   requestInterceptors: (config, options) => {
2f6253 160     // 请求之前处理config
b7ce74 161     const token = getToken();
d509e8 162     if (token && (config as Recordable)?.requestOptions?.withToken !== false) {
2f6253 163       // jwt token
590288 164       (config as Recordable).headers.Authorization = options.authenticationScheme
b6d5b0 165         ? `${options.authenticationScheme} ${token}`
V 166         : token;
2f6253 167     }
168     return config;
169   },
170
171   /**
49b66e 172    * @description: 响应拦截器处理
X 173    */
174   responseInterceptors: (res: AxiosResponse<any>) => {
175     return res;
176   },
177
178   /**
2f6253 179    * @description: 响应错误处理
180    */
c118e8 181   responseInterceptorsCatch: (axiosInstance: AxiosInstance, error: any) => {
962f90 182     const { t } = useI18n();
215d8b 183     const errorLogStore = useErrorLogStoreWithOut();
V 184     errorLogStore.addAjaxErrorInfo(error);
49b66e 185     const { response, code, message, config } = error || {};
X 186     const errorMessageMode = config?.requestOptions?.errorMessageMode || 'none';
de5bf7 187     const msg: string = response?.data?.error?.message ?? '';
V 188     const err: string = error?.toString?.() ?? '';
49b66e 189     let errMessage = '';
X 190
b1f78c 191     if (axios.isCancel(error)) {
L 192       return Promise.reject(error);
193     }
194
2f6253 195     try {
196       if (code === 'ECONNABORTED' && message.indexOf('timeout') !== -1) {
49b66e 197         errMessage = t('sys.api.apiTimeoutMessage');
2f6253 198       }
6b3195 199       if (err?.includes('Network Error')) {
49b66e 200         errMessage = t('sys.api.networkExceptionMsg');
X 201       }
202
203       if (errMessage) {
204         if (errorMessageMode === 'modal') {
205           createErrorModal({ title: t('sys.api.errorTip'), content: errMessage });
206         } else if (errorMessageMode === 'message') {
207           createMessage.error(errMessage);
208         }
209         return Promise.reject(error);
2f6253 210       }
211     } catch (error) {
590288 212       throw new Error(error as unknown as string);
2f6253 213     }
49b66e 214
X 215     checkStatus(error?.response?.status, msg, errorMessageMode);
136cbb 216
C 217     // 添加自动重试机制 保险起见 只针对GET请求
218     const retryRequest = new AxiosRetry();
219     const { isOpenRetry } = config.requestOptions.retryRequest;
220     config.method?.toUpperCase() === RequestEnum.GET &&
221       isOpenRetry &&
362740 222       error?.response?.status !== 401 &&
136cbb 223       // @ts-ignore
C 224       retryRequest.retry(axiosInstance, error);
661db0 225     return Promise.reject(error);
2f6253 226   },
227 };
228
229 function createAxios(opt?: Partial<CreateAxiosOptions>) {
230   return new VAxios(
19dc88 231     // 深度合并
2f6253 232     deepMerge(
233       {
b6d5b0 234         // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#authentication_schemes
V 235         // authentication schemes,e.g: Bearer
236         // authenticationScheme: 'Bearer',
237         authenticationScheme: '',
89d7b1 238         timeout: 60 * 1000,
2f6253 239         // 基础接口地址
61d4ef 240         // baseURL: globSetting.apiUrl,
996f2f 241
2f6253 242         headers: { 'Content-Type': ContentTypeEnum.JSON },
f646e3 243         // 如果是form-data格式
V 244         // headers: { 'Content-Type': ContentTypeEnum.FORM_URLENCODED },
2f6253 245         // 数据处理方式
4f35b9 246         transform: clone(transform),
128809 247         withCredentials: true,
2f6253 248         // 配置项,下面的选项都可以在独立的接口请求中覆盖
249         requestOptions: {
250           // 默认将prefix 添加到url
251           joinPrefix: true,
56d8af 252           // 是否返回原生响应头 比如:需要获取响应头时使用该属性
Z 253           isReturnNativeResponse: false,
2f6253 254           // 需要对返回数据进行处理
b6d5b0 255           isTransformResponse: true,
2f6253 256           // post请求的时候添加参数到url
257           joinParamsToUrl: false,
258           // 格式化提交参数时间
259           formatDate: true,
260           // 消息提示类型
4ce1d5 261           errorMessageMode: 'message',
2f6253 262           // 接口地址
263           apiUrl: globSetting.apiUrl,
7df9b5 264           // 接口拼接地址
L 265           urlPrefix: urlPrefix,
f646e3 266           //  是否加入时间戳
V 267           joinTime: true,
3b8ca4 268           // 忽略重复请求
V 269           ignoreCancelToken: true,
c99cf5 270           // 是否携带token
271           withToken: true,
136cbb 272           retryRequest: {
C 273             isOpenRetry: true,
274             count: 5,
275             waitTime: 100,
276           },
2f6253 277         },
278       },
56a966 279       opt || {},
V 280     ),
2f6253 281   );
282 }
283 export const defHttp = createAxios();
284
285 // other api url
286 // export const otherHttp = createAxios({
287 //   requestOptions: {
288 //     apiUrl: 'xxx',
7df9b5 289 //     urlPrefix: 'xxx',
2f6253 290 //   },
291 // });