// axios配置 可自行根据项目进行更改,只需更改该文件即可,其他文件可以不动 import type { AxiosResponse } from 'axios'; import { clone } from 'lodash-es'; import type { RequestOptions, Result } from '/#/axios'; import type { AxiosTransform, CreateAxiosOptions } from './axiosTransform'; import { BzAxios } from './Axios'; import { checkStatus } from './checkStatus'; import { message, Modal, notification } from 'ant-design-vue'; import { RequestEnum, ResultEnum, ContentTypeEnum } from '/@/enums/httpEnum'; import { isString } from '/@/utils/is'; import { setObjToUrlParams, deepMerge } from '/@/utils'; import { joinTimestamp, formatRequestDate } from './helper'; import { isFunction, isObject } from 'lodash'; /** * @description: 数据处理,方便区分多种处理方式 */ const transform: AxiosTransform = { /** * @description: 处理响应数据。如果数据不是预期格式,可直接抛出错误 */ transformResponseHook: (res: AxiosResponse, options: RequestOptions) => { const { isTransformResponse, isReturnNativeResponse, replaceFields } = options; // 是否返回原生响应头 比如:需要获取响应头时使用该属性 if (isReturnNativeResponse) { return res; } // 如果是字节流直接返回 if (res.data instanceof Blob || res.data instanceof ArrayBuffer) { return res; } // 不进行任何处理,直接返回 // 用于页面代码可能需要直接获取code,data,message这些信息时开启 if (!isTransformResponse) { return res.data; } // 错误的时候返回 // console.log(replaceFields); const statusField = replaceFields?.statusField || 'status'; const messageField = replaceFields?.messageField || 'msg'; const dataField = replaceFields?.dataField || 'data'; const resultTypeField = replaceFields?.resultTypeField || 'resultType'; if (!res.data) { // 请求没有返回值 throw new Error('请求出错,请稍后重试'); } // 这里 status, data, msg, resultType 为 后台统一的字段,需要在 types.ts内修改为项目自己的接口返回格式 const status = res.data[statusField]; const data = res.data[dataField]; const msg = res.data[messageField]; let resultType = res.data[resultTypeField]; if (!resultType) { resultType = 1; } // 这里逻辑可以根据项目进行修改 if (resultType === ResultEnum.SUCCESS) { return data; } //todo resultType 其他情况 // 在此处根据自己项目的实际情况对不同的code执行不同的操作 // 如果不希望中断当前请求,请return数据,否则直接抛出异常即可 let timeoutMsg = ''; let isNeedShowMsg = false; switch (resultType) { case ResultEnum.ERROR: // 801 需要重新登陆 802 无权限 if (status == 801) { timeoutMsg = msg || '登陆超时,请重新登陆'; if (options.needAuthCallBack && isFunction(options.needAuthCallBack)) { options.needAuthCallBack(res.data); } return; } else if (status == 802) { timeoutMsg = msg || '无访问权限'; } else { timeoutMsg = msg; } break; case ResultEnum.PROMPT: timeoutMsg = msg; isNeedShowMsg = true; break; case ResultEnum.FORM_CHECK: timeoutMsg = msg; break; default: if (msg) { timeoutMsg = msg; } } // errorMessageMode=‘modal’的时候会显示modal错误弹窗,而不是消息提示,用于一些比较重要的错误 // errorMessageMode='none' 一般是调用时明确表示不希望自动弹出错误提示 if (options.errorMessageMode === 'modal' || isNeedShowMsg) { Modal[options.errorModalType || 'error']({ content: () => timeoutMsg, }); } else if (options.errorMessageMode === 'message') { message[options.errorModalType || 'error'](timeoutMsg); } else if (options.errorMessageMode === 'notification') { notification[options.errorModalType || 'error']({ message: timeoutMsg, }); } else if (options.errorMessageMode === 'custom') { if (options.customModalCallBack && isFunction(options.customModalCallBack)) { options.customModalCallBack(res.data); } } if (res.data && options.isReturnResult) { return res.data; } throw new Error(timeoutMsg ? `接口错误:${timeoutMsg}` : '请求出错,请稍后再试'); }, // 请求之前处理config beforeRequestHook: (config, options) => { const { apiUrl, joinPrefix, joinParamsToUrl, formatDate, joinTime = true, urlPrefix } = options; if (joinPrefix && urlPrefix) { config.url = `${urlPrefix}${config.url}`; } if (apiUrl && isString(apiUrl)) { config.url = `${apiUrl}${config.url}`; } const params = config.params || {}; const data = config.data || false; formatDate && data && !isString(data) && formatRequestDate(data); if (config.method?.toUpperCase() === RequestEnum.GET) { if (!isString(params)) { // 给 get 请求加上时间戳参数,避免从缓存中拿数据。 config.params = Object.assign(params || {}, joinTimestamp(joinTime, false)); } else { // 兼容restful风格 config.url = config.url + params + `${joinTimestamp(joinTime, true)}`; config.params = undefined; } } else { if (!isString(params)) { formatDate && formatRequestDate(params); if ( Reflect.has(config, 'data') && config.data && (Object.keys(config.data).length > 0 || Object.prototype.toString.call(config.data) === '[object FormData]') ) { config.data = data; config.params = params; } else { // 非GET请求如果没有提供data,则将params视为data config.data = params; config.params = undefined; } if (joinParamsToUrl) { config.url = setObjToUrlParams( config.url as string, Object.assign({}, config.params, config.data), ); } } else { // 兼容restful风格 config.url = config.url + params; config.params = undefined; } } return config; }, /** * @description: 请求拦截器处理 */ requestInterceptors: (config: any, options) => { // 请求之前处理config const token = options.authToken; if (token && config && config.requestOptions.withToken !== false) { // jwt token config.headers.Authorization = options.authenticationScheme ? `${options.authenticationScheme} ${token}` : token; } if (options.setHeaders) { const headers = options.setHeaders() || {}; if (isObject(headers)) { config.headers = deepMerge(config.headers, headers); } } return config; }, /** * @description: 响应拦截器处理 */ responseInterceptors: (res: AxiosResponse) => { return res; }, /** * @description: 响应错误处理 */ responseInterceptorsCatch: (axiosInstance: AxiosResponse, error: any) => { const { response, code, message: errorMsg, config } = error || {}; const errorMessageMode = config?.requestOptions?.errorMessageMode || 'none'; const errorModalType = config?.requestOptions?.errorModalType || 'error'; const needAuthCallBack = config?.requestOptions?.needAuthCallBack; const customModalCallBack = config?.requestOptions?.customModalCallBack; const msg: string = response?.data?.message ?? ''; const err: string = error?.toString?.() ?? ''; let errMessage = ''; if (response.data instanceof Blob || response.data instanceof ArrayBuffer) { if (response.data.type == 'application/json') { const reader = new FileReader(); //创建一个FileReader实例 reader.readAsText(response.data, 'utf-8'); //读取文件,结果用字符串形式表示 reader.onload = function () { //读取完成后,**获取reader.result** const { message: errMsg } = JSON.parse(reader.result); message && message.error(errMsg); errMessage = errMsg; return Promise.reject(errMsg); }; } } try { if (code === 'ECONNABORTED' && errorMsg.indexOf('timeout') !== -1) { errMessage = '接口请求超时,请刷新页面重试!'; } if (err?.includes('Network Error')) { errMessage = '网络异常,请检查您的网络连接是否正常!'; } if (errMessage) { if (errorMessageMode === 'modal') { Modal.error({ title: () => '错误提示', content: () => errMessage, }); } else if (errorMessageMode === 'message') { message.error(errMessage); } else if (errorMessageMode === 'custom') { if (customModalCallBack && isFunction(customModalCallBack)) { customModalCallBack({ msg: errMessage, status: code }); } } if (errorMessageMode === 'modal') { Modal[errorModalType]({ content: () => errMessage, }); } else if (errorMessageMode === 'message') { message[errorModalType](errMessage); } else if (errorMessageMode === 'notification') { notification[errorModalType]({ message: errMessage, }); } return Promise.reject(error); } } catch (error) { throw new Error(String(error)); } checkStatus( error?.response?.status, msg, errorMessageMode, errorModalType, needAuthCallBack, customModalCallBack, ); return Promise.reject(error); }, }; function createAxios(opt?: CreateAxiosOptions) { // if (!opt?.tenantId) { // throw new Error("BzAxios配置错误:tenantId不能为空!"); // } return new BzAxios( deepMerge( { // authenticationScheme: "Bearer", timeout: 60 * 1000, // 基础接口地址 // baseURL: globSetting.apiUrl, headers: { 'Content-Type': ContentTypeEnum.JSON }, // 如果是form-data格式 // headers: { 'Content-Type': ContentTypeEnum.FORM_URLENCODED }, // 数据处理方式 transform: clone(transform), // 配置项,下面的选项都可以在独立的接口请求中覆盖 requestOptions: { // 默认将prefix 添加到url joinPrefix: true, // 是否返回原生响应头 比如:需要获取响应头时使用该属性 isReturnNativeResponse: false, // 需要对返回数据进行处理 isTransformResponse: true, // 错误情况 依然返回结果 isReturnResult: false, // post请求的时候添加参数到url joinParamsToUrl: false, // 格式化提交参数时间 formatDate: true, // 消息提示框类型 errorMessageMode: 'message', // 消息提示类型 errorModalType: 'error', // 是否加入时间戳 joinTime: true, // 忽略重复请求 ignoreCancelToken: true, // 是否携带token withToken: true, // 配置返回结构体 replaceFields: { statusField: 'status', messageField: 'msg', dataField: 'data', resultTypeField: 'resultType', }, }, }, opt || {}, ), ); } export const useBzAxios = () => { return { createAxios, }; };