mirror of
https://gitee.com/dapppp/ruoyi-plus-vben5.git
synced 2026-03-11 08:41:09 +08:00
feat(http): 实现基于alova的HTTP请求模块
添加HTTP请求核心模块,包括异常处理、状态码检查、消息提示等功能 - 新增alova实例配置,支持请求加密、token添加等特性 - 实现401状态码自动处理及登出逻辑 - 添加多种消息提示方式(message/modal/notification) - 支持请求成功/失败的统一处理 - 添加WithMessage方法简化成功提示 - 更新相关依赖配置
This commit is contained in:
@@ -27,6 +27,7 @@
|
||||
"#/*": "./src/*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@alova/adapter-axios": "^2.0.17",
|
||||
"@ant-design/icons-vue": "^7.0.1",
|
||||
"@tinymce/tinymce-vue": "^6.0.1",
|
||||
"@vben/access": "workspace:*",
|
||||
@@ -44,7 +45,9 @@
|
||||
"@vben/types": "workspace:*",
|
||||
"@vben/utils": "workspace:*",
|
||||
"@vueuse/core": "catalog:",
|
||||
"alova": "^3.4.1",
|
||||
"antdv-next": "catalog:",
|
||||
"axios": "catalog:",
|
||||
"cropperjs": "^1.6.2",
|
||||
"dayjs": "catalog:",
|
||||
"echarts": "^5.5.1",
|
||||
|
||||
51
apps/web-antd/src/utils/http/checkStatus.ts
Normal file
51
apps/web-antd/src/utils/http/checkStatus.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import type { AlovaMeta } from '#/../types/alova';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { showAntdMessage } from './popup';
|
||||
|
||||
export function checkStatus(
|
||||
status: number,
|
||||
msg: string,
|
||||
meta: AlovaMeta | undefined,
|
||||
): void {
|
||||
let errorMessage = msg;
|
||||
|
||||
switch (status) {
|
||||
case 400: {
|
||||
errorMessage = $t('ui.fallback.http.badRequest');
|
||||
break;
|
||||
}
|
||||
case 401: {
|
||||
errorMessage = $t('ui.fallback.http.unauthorized');
|
||||
break;
|
||||
}
|
||||
case 403: {
|
||||
errorMessage = $t('ui.fallback.http.forbidden');
|
||||
break;
|
||||
}
|
||||
case 404: {
|
||||
errorMessage = $t('ui.fallback.http.notFound');
|
||||
break;
|
||||
}
|
||||
case 408: {
|
||||
errorMessage = $t('ui.fallback.http.requestTimeout');
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
errorMessage = $t('ui.fallback.http.internalServerError');
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
errorMessage &&
|
||||
meta &&
|
||||
!['none', undefined].includes(meta.showErrorMessage)
|
||||
) {
|
||||
showAntdMessage({
|
||||
meta,
|
||||
message: errorMessage,
|
||||
type: 'error',
|
||||
});
|
||||
}
|
||||
}
|
||||
21
apps/web-antd/src/utils/http/exception.ts
Normal file
21
apps/web-antd/src/utils/http/exception.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* 业务异常抛出 即业务状态码不为200时抛出
|
||||
*/
|
||||
export class BusinessException extends Error {
|
||||
code: number;
|
||||
|
||||
constructor(code: number, message: string) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 定义一个401专用异常 用于可能会用到的区分场景?
|
||||
*/
|
||||
export class UnauthorizedException extends Error {}
|
||||
|
||||
/**
|
||||
* logout这种接口都返回401 抛出这个异常
|
||||
*/
|
||||
export class ImpossibleReturn401Exception extends Error {}
|
||||
77
apps/web-antd/src/utils/http/helper.ts
Normal file
77
apps/web-antd/src/utils/http/helper.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { useAuthStore } from '#/store';
|
||||
|
||||
import {
|
||||
ImpossibleReturn401Exception,
|
||||
UnauthorizedException,
|
||||
} from './exception';
|
||||
|
||||
/**
|
||||
* @description: contentType
|
||||
*/
|
||||
export const ContentTypeEnum = {
|
||||
// form-data upload
|
||||
FORM_DATA: 'multipart/form-data;charset=UTF-8',
|
||||
// form-data qs
|
||||
FORM_URLENCODED: 'application/x-www-form-urlencoded;charset=UTF-8',
|
||||
// json
|
||||
JSON: 'application/json;charset=UTF-8',
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* 是否已经处在登出过程中了 一个标志位
|
||||
* 主要是防止一个页面会请求多个api 都401 会导致登出执行多次
|
||||
*/
|
||||
let isLogoutProcessing = false;
|
||||
/**
|
||||
* 防止 调用logout接口 logout又返回401 然后又走到Logout逻辑死循环
|
||||
*/
|
||||
let lockLogoutRequest = false;
|
||||
|
||||
/**
|
||||
* 登出逻辑 两个地方用到 提取出来
|
||||
* @throws UnauthorizedException 抛出特定的异常
|
||||
*/
|
||||
export function handleUnauthorizedLogout() {
|
||||
const timeoutMsg = $t('http.loginTimeout');
|
||||
/**
|
||||
* lock 不再请求logout接口
|
||||
* 这里已经算异常情况了
|
||||
*/
|
||||
if (lockLogoutRequest) {
|
||||
throw new UnauthorizedException(timeoutMsg);
|
||||
}
|
||||
// 已经在登出过程中 不再执行
|
||||
if (isLogoutProcessing) {
|
||||
throw new UnauthorizedException(timeoutMsg);
|
||||
}
|
||||
isLogoutProcessing = true;
|
||||
const userStore = useAuthStore();
|
||||
userStore
|
||||
.logout()
|
||||
.catch((error) => {
|
||||
/**
|
||||
* logout接口返回了401
|
||||
* 做Lock处理 且 该标志位不会复位(因为这种场景出现 系统已经算故障了)
|
||||
* 因为这已经不符合正常的逻辑了
|
||||
*/
|
||||
if (error instanceof ImpossibleReturn401Exception) {
|
||||
lockLogoutRequest = true;
|
||||
if (import.meta.env.DEV) {
|
||||
window.modal.error({
|
||||
title: '提示',
|
||||
centered: true,
|
||||
content:
|
||||
'检测到你的logout接口返回了401, 去检查你的后端配置 这已经不符合正常逻辑(该提示不会在非dev环境弹出)',
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
window.message.error(timeoutMsg);
|
||||
isLogoutProcessing = false;
|
||||
});
|
||||
// 不再执行下面逻辑
|
||||
throw new UnauthorizedException(timeoutMsg);
|
||||
}
|
||||
375
apps/web-antd/src/utils/http/index.ts
Normal file
375
apps/web-antd/src/utils/http/index.ts
Normal file
@@ -0,0 +1,375 @@
|
||||
import type { AxiosError } from 'axios';
|
||||
|
||||
import type { HttpResponse } from '@vben/request';
|
||||
import type {
|
||||
BaseAsymmetricEncryption,
|
||||
BaseSymmetricEncryption,
|
||||
} from '@vben/utils';
|
||||
|
||||
import type { AlovaMeta } from '#/../types/alova';
|
||||
|
||||
import { BUSINESS_SUCCESS_CODE, UNAUTHORIZED_CODE } from '@vben/constants';
|
||||
import { useAppConfig } from '@vben/hooks';
|
||||
import { preferences } from '@vben/preferences';
|
||||
import { stringify } from '@vben/request';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
import {
|
||||
AesEncryption,
|
||||
decodeBase64,
|
||||
encodeBase64,
|
||||
randomStr,
|
||||
RsaEncryption,
|
||||
} from '@vben/utils';
|
||||
|
||||
import { axiosRequestAdapter } from '@alova/adapter-axios';
|
||||
import { createAlova } from 'alova';
|
||||
import VueHook from 'alova/vue';
|
||||
import axios from 'axios';
|
||||
import { isEmpty, isNull, merge } from 'lodash-es';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { checkStatus } from './checkStatus';
|
||||
import { BusinessException } from './exception';
|
||||
import { ContentTypeEnum, handleUnauthorizedLogout } from './helper';
|
||||
import { showAntdMessage } from './popup';
|
||||
|
||||
const { apiURL, clientId, enableEncrypt, rsaPublicKey, rsaPrivateKey } =
|
||||
useAppConfig(import.meta.env, import.meta.env.PROD);
|
||||
|
||||
/**
|
||||
* 使用非对称加密的实现 前端已经实现RSA/SM2
|
||||
*
|
||||
* 你可以使用Sm2Encryption来替换 后端也需要同步替换公私钥对
|
||||
*
|
||||
* 后端文件位置: ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/DecryptRequestBodyWrapper.java
|
||||
*
|
||||
* 注意前端sm-crypto库只能支持04开头的公钥! 否则加密会有问题 你可以使用前端的import { logSm2KeyPair } from '@vben/utils';方法来生成
|
||||
* 如果你生成的公钥开头不是04 那么不能正常加密
|
||||
* 或者使用这个网站来生成: https://tool.hiofd.com/sm2-key-gen/
|
||||
*/
|
||||
const asymmetricEncryption: BaseAsymmetricEncryption = new RsaEncryption({
|
||||
publicKey: rsaPublicKey,
|
||||
privateKey: rsaPrivateKey,
|
||||
});
|
||||
|
||||
/**
|
||||
* 对称加密的实现 AES/SM4
|
||||
*/
|
||||
const symmetricEncryption: BaseSymmetricEncryption = new AesEncryption();
|
||||
|
||||
const defaultMeta: AlovaMeta = {
|
||||
showSuccessMessage: 'none',
|
||||
showErrorMessage: 'message',
|
||||
withToken: true,
|
||||
isReturnNativeResponse: false,
|
||||
isTransformResponse: true,
|
||||
encrypt: false,
|
||||
};
|
||||
|
||||
const alovaInstance = createAlova({
|
||||
baseURL: apiURL,
|
||||
/**
|
||||
* 请求超时时间 单位毫秒
|
||||
* @default 10s
|
||||
*/
|
||||
timeout: 10_000,
|
||||
statesHook: VueHook,
|
||||
requestAdapter: axiosRequestAdapter(),
|
||||
// 响应缓存让你可以更好地多次利用服务端数据,而不需要每次请求时都发送请求获取数据。GET 请求将默认设置 5 分钟的内存缓存时间,如果你不需要可以通过以下方式关闭当前请求的缓存。
|
||||
// https://alova.js.org/zh-CN/tutorial/getting-started/basic/method#%E5%93%8D%E5%BA%94%E7%BC%93%E5%AD%98
|
||||
cacheFor: null,
|
||||
shareRequest: false,
|
||||
beforeRequest: (request) => {
|
||||
// 合并默认meta
|
||||
const { meta = {}, config } = request;
|
||||
const mergeMeta = merge({}, defaultMeta, meta);
|
||||
request.meta = mergeMeta;
|
||||
|
||||
// 全局开启token功能 && token存在
|
||||
const accessStore = useAccessStore();
|
||||
const token = accessStore.accessToken;
|
||||
// 添加token
|
||||
if (request.meta.withToken && token) {
|
||||
config.headers.Authorization = token ? `Bearer ${token}` : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* locale跟后台不一致 需要转换
|
||||
*/
|
||||
const language = preferences.app.locale.replace('-', '_');
|
||||
config.headers['Accept-Language'] = language;
|
||||
config.headers['Content-Language'] = language;
|
||||
/**
|
||||
* 添加全局clientId
|
||||
* 关于header的clientId被错误绑定到实体类
|
||||
* https://gitee.com/dapppp/ruoyi-plus-vben5/issues/IC0BDS
|
||||
*/
|
||||
config.headers.ClientID = clientId;
|
||||
/**
|
||||
* 格式化get/delete参数
|
||||
* 如果包含自定义的paramsSerializer则不走此逻辑
|
||||
*/
|
||||
if (
|
||||
['DELETE', 'GET'].includes(request.type?.toUpperCase() || '') &&
|
||||
config.params &&
|
||||
!config.paramsSerializer
|
||||
) {
|
||||
/**
|
||||
* 1. 格式化参数 微服务在传递区间时间选择(后端的params Map类型参数)需要格式化key 否则接收不到
|
||||
* 2. 数组参数需要格式化 后端才能正常接收 会变成arr=1&arr=2&arr=3的格式来接收
|
||||
*/
|
||||
config.paramsSerializer = (params) =>
|
||||
stringify(params, { arrayFormat: 'repeat' });
|
||||
}
|
||||
|
||||
const { encrypt } = request.meta;
|
||||
// 全局开启请求加密功能 && 该请求开启 && 是post/put请求
|
||||
if (
|
||||
enableEncrypt &&
|
||||
encrypt &&
|
||||
['POST', 'PUT'].includes(request.type?.toUpperCase() || '')
|
||||
) {
|
||||
// sm4这里改为randomStr(16)
|
||||
const key = randomStr(32);
|
||||
const keyWithBase64 = encodeBase64(key);
|
||||
config.headers['encrypt-key'] =
|
||||
asymmetricEncryption.encrypt(keyWithBase64);
|
||||
/**
|
||||
* axios会默认给字符串前后加上引号 RSA可以正常解密(加不加都能解密) 但是SM2不行(大坑!!!)
|
||||
* 这里通过transformRequest强制返回原始内容
|
||||
*/
|
||||
config.transformRequest = (data) => data;
|
||||
|
||||
const data = request.data ?? '';
|
||||
switch (typeof request.data) {
|
||||
case 'string': {
|
||||
request.data = symmetricEncryption.encrypt(data as string, key);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
request.data = symmetricEncryption.encrypt(JSON.stringify(data), key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// alova会默认使用www-form-urlencoded 这里需要手动设置为json
|
||||
// 不要提什么为啥明明body是text而要用json 因为用text压根进不了controller 除非你的body用string来接收A
|
||||
config.headers['Content-Type'] = ContentTypeEnum.JSON;
|
||||
}
|
||||
},
|
||||
responded: {
|
||||
onSuccess: async (response, instance) => {
|
||||
// 解密响应数据
|
||||
const encryptKey = (response.headers ?? {})['encrypt-key'];
|
||||
if (encryptKey) {
|
||||
/** RSA私钥解密 拿到解密秘钥的base64 */
|
||||
const base64Str = asymmetricEncryption.decrypt(encryptKey);
|
||||
/** base64 解码 得到请求头的 AES 秘钥 */
|
||||
const secret = decodeBase64(base64Str);
|
||||
/** 使用aesKey解密 responseData */
|
||||
const decryptData = symmetricEncryption.decrypt(
|
||||
response.data as unknown as string,
|
||||
secret,
|
||||
);
|
||||
/** 赋值 需要转为对象 */
|
||||
response.data = JSON.parse(decryptData);
|
||||
}
|
||||
|
||||
const { isReturnNativeResponse, isTransformResponse } =
|
||||
instance.meta as AlovaMeta;
|
||||
// 是否返回原生响应 比如:需要获取响应时使用该属性
|
||||
if (isReturnNativeResponse) {
|
||||
return response;
|
||||
}
|
||||
// 不进行任何处理,直接返回
|
||||
// 用于页面代码可能需要直接获取code,data,message这些信息时开启
|
||||
if (!isTransformResponse) {
|
||||
/**
|
||||
* @warning 注意 微服务版本在401(网关)会返回text/plain的头 所以这里代码会无效
|
||||
* 我建议你改后端而不是前端来做兼容
|
||||
*/
|
||||
// json数据的判断
|
||||
if (response.headers['content-type']?.includes?.('application/json')) {
|
||||
/**
|
||||
* 需要判断是否登录超时/401
|
||||
* 执行登出操作
|
||||
*/
|
||||
const resp = response.data as unknown as HttpResponse;
|
||||
// 抛出异常 不再执行
|
||||
if (
|
||||
typeof resp === 'object' &&
|
||||
Reflect.has(resp, 'code') &&
|
||||
resp.code === UNAUTHORIZED_CODE
|
||||
) {
|
||||
handleUnauthorizedLogout();
|
||||
}
|
||||
|
||||
/**
|
||||
* 需要判断下载二进制的情况 正常是返回二进制 报错会返回json
|
||||
* 当type为blob且content-type为application/json时 则判断已经下载出错
|
||||
*/
|
||||
if (response.config.responseType === 'blob') {
|
||||
// 这时候的data为blob类型
|
||||
const blob = response.data as unknown as Blob;
|
||||
// 拿到字符串转json对象
|
||||
response.data = JSON.parse(await blob.text());
|
||||
// 然后按正常逻辑执行下面的代码(判断业务状态码)
|
||||
} else {
|
||||
// 其他类型数据 直接返回
|
||||
return response.data;
|
||||
}
|
||||
} else {
|
||||
// 非json数据 直接返回 不做校验
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
||||
const axiosResponseData = response.data;
|
||||
if (!axiosResponseData) {
|
||||
throw new Error($t('http.apiRequestFailed'));
|
||||
}
|
||||
|
||||
// 后端并没有采用严格的{code, msg, data}模式
|
||||
const { code, data, msg, ...other } = axiosResponseData;
|
||||
|
||||
// 业务状态码为200 则请求成功
|
||||
const hasSuccess =
|
||||
Reflect.has(axiosResponseData, 'code') &&
|
||||
code === BUSINESS_SUCCESS_CODE;
|
||||
if (hasSuccess) {
|
||||
let successMsg = msg;
|
||||
|
||||
if (isNull(successMsg) || isEmpty(successMsg)) {
|
||||
successMsg = $t(`http.operationSuccess`);
|
||||
}
|
||||
|
||||
if (!['none', undefined].includes(instance.meta?.showSuccessMessage)) {
|
||||
showAntdMessage({
|
||||
meta: instance.meta,
|
||||
message: successMsg,
|
||||
type: 'success',
|
||||
});
|
||||
}
|
||||
// 分页情况下为code msg rows total 并没有data字段
|
||||
// 如果有data 直接返回data 没有data将剩余参数(...other)封装为data返回
|
||||
// 需要考虑data为null的情况(比如查询为空) 所以这里直接判断undefined
|
||||
if (data !== undefined) {
|
||||
return data;
|
||||
}
|
||||
// 没有data 将其他参数包装为data
|
||||
return other;
|
||||
}
|
||||
// 在此处根据自己项目的实际情况对不同的code执行不同的操作
|
||||
// 如果不希望中断当前请求,请return数据,否则直接抛出异常即可
|
||||
let timeoutMsg = '';
|
||||
switch (code) {
|
||||
// 登录超时
|
||||
case UNAUTHORIZED_CODE: {
|
||||
handleUnauthorizedLogout();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (msg) {
|
||||
timeoutMsg = msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// errorMessageMode='modal'的时候会显示modal错误弹窗,而不是消息提示,用于一些比较重要的错误
|
||||
// errorMessageMode='none' 一般是调用时明确表示不希望自动弹出错误提示
|
||||
if (!['none', undefined].includes(instance.meta?.showErrorMessage)) {
|
||||
showAntdMessage({
|
||||
meta: instance.meta,
|
||||
message: timeoutMsg,
|
||||
type: 'error',
|
||||
});
|
||||
}
|
||||
// 抛出业务异常
|
||||
throw new BusinessException(code, timeoutMsg);
|
||||
},
|
||||
onError: (error, method) => {
|
||||
if (axios.isCancel(error)) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
const axiosError = error as AxiosError;
|
||||
const { code, message, response } = axiosError;
|
||||
let errMessage = '';
|
||||
|
||||
try {
|
||||
if (code === 'ECONNABORTED' && message.includes('timeout')) {
|
||||
errMessage = $t('ui.fallback.http.requestTimeout');
|
||||
}
|
||||
if (axiosError?.toString()?.includes('Network Error')) {
|
||||
errMessage = $t('ui.fallback.http.networkError');
|
||||
}
|
||||
|
||||
if (errMessage) {
|
||||
if (!['none', undefined].includes(method.meta?.showErrorMessage)) {
|
||||
showAntdMessage({
|
||||
meta: method.meta,
|
||||
message: errMessage,
|
||||
type: 'error',
|
||||
});
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(error as unknown as string);
|
||||
}
|
||||
|
||||
const msg: string = (response?.data as any)?.msg ?? '';
|
||||
// 根据httpStatus判断错误 弹窗提示
|
||||
checkStatus(error?.response?.status, msg, method.meta);
|
||||
|
||||
return Promise.reject(error);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
提供xxWithMessage方法,用于请求成功后弹出提示
|
||||
*/
|
||||
|
||||
alovaInstance.GetWithMessage = function (url, options) {
|
||||
return this.Get(url, {
|
||||
...options,
|
||||
meta: {
|
||||
...options?.meta,
|
||||
showSuccessMessage: 'message',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
alovaInstance.PostWithMessage = function (url, data, config) {
|
||||
return this.Post(url, data, {
|
||||
...config,
|
||||
meta: {
|
||||
...config?.meta,
|
||||
showSuccessMessage: 'message',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
alovaInstance.PutWithMessage = function (url, data, config) {
|
||||
return this.Put(url, data, {
|
||||
...config,
|
||||
meta: {
|
||||
...config?.meta,
|
||||
showSuccessMessage: 'message',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
alovaInstance.DeleteWithMessage = function (url, data, config) {
|
||||
return this.Delete(url, data, {
|
||||
...config,
|
||||
meta: {
|
||||
...config?.meta,
|
||||
showSuccessMessage: 'message',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export type AlovaInstanceType = typeof alovaInstance;
|
||||
|
||||
export { alovaInstance };
|
||||
49
apps/web-antd/src/utils/http/popup.ts
Normal file
49
apps/web-antd/src/utils/http/popup.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import type { AlovaMeta } from '#/../types/alova';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
|
||||
interface ShowMessageOptions {
|
||||
meta?: AlovaMeta;
|
||||
message: string;
|
||||
type: 'error' | 'success';
|
||||
}
|
||||
|
||||
export function showAntdMessage(options: ShowMessageOptions) {
|
||||
const { meta = {}, message, type } = options;
|
||||
|
||||
if (meta.showErrorMessage === 'message' && type === 'error') {
|
||||
window.message[type](message);
|
||||
}
|
||||
if (meta.showSuccessMessage === 'message' && type === 'success') {
|
||||
window.message[type](message);
|
||||
}
|
||||
|
||||
if (meta.showErrorMessage === 'modal' && type === 'error') {
|
||||
window.modal.error({
|
||||
content: message,
|
||||
title: $t('http.errorTip'),
|
||||
centered: true,
|
||||
okButtonProps: { danger: true },
|
||||
});
|
||||
}
|
||||
if (meta.showSuccessMessage === 'modal' && type === 'success') {
|
||||
window.modal.success({
|
||||
content: message,
|
||||
title: $t('http.successTip'),
|
||||
centered: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (meta.showErrorMessage === 'notification' && type === 'error') {
|
||||
window.notification.error({
|
||||
description: message,
|
||||
title: $t('http.errorTip'),
|
||||
});
|
||||
}
|
||||
if (meta.showSuccessMessage === 'notification' && type === 'success') {
|
||||
window.notification.success({
|
||||
description: message,
|
||||
title: $t('http.successTip'),
|
||||
});
|
||||
}
|
||||
}
|
||||
59
apps/web-antd/types/alova.d.ts
vendored
Normal file
59
apps/web-antd/types/alova.d.ts
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
/* eslint-disable unicorn/require-module-specifiers */
|
||||
import type { AlovaInstanceType } from '#/utils/http';
|
||||
|
||||
import 'alova';
|
||||
|
||||
/**
|
||||
* 接口请求message提示方式
|
||||
*/
|
||||
export type MessageType = 'message' | 'modal' | 'none' | 'notification';
|
||||
|
||||
type GetType = AlovaInstanceType['Get'];
|
||||
type PostType = AlovaInstanceType['Post'];
|
||||
type PutType = AlovaInstanceType['Put'];
|
||||
type DeleteType = AlovaInstanceType['Delete'];
|
||||
|
||||
export type AlovaMeta = {
|
||||
/**
|
||||
* 是否需要对请求体进行加密
|
||||
*/
|
||||
encrypt?: boolean;
|
||||
/**
|
||||
* 是否返回原生axios响应
|
||||
*/
|
||||
isReturnNativeResponse?: boolean;
|
||||
/**
|
||||
* 是否需要转换响应 即只获取{code, msg, data}中的data
|
||||
*/
|
||||
isTransformResponse?: boolean;
|
||||
/**
|
||||
* 接口请求失败时的提示方式
|
||||
*/
|
||||
showErrorMessage?: MessageType;
|
||||
/**
|
||||
* 接口请求成功时的提示方式
|
||||
*/
|
||||
showSuccessMessage?: MessageType;
|
||||
/**
|
||||
* 是否需要在请求头中添加 token
|
||||
*/
|
||||
withToken?: boolean;
|
||||
};
|
||||
|
||||
declare module 'alova' {
|
||||
export interface AlovaCustomTypes {
|
||||
meta: AlovaMeta;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加withMessage方法 用于success弹窗
|
||||
*/
|
||||
interface Alova {
|
||||
GetWithMessage: GetType;
|
||||
PostWithMessage: PostType;
|
||||
PutWithMessage: PutType;
|
||||
DeleteWithMessage: DeleteType;
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
@@ -5,6 +5,7 @@
|
||||
"allowCompoundWords": true,
|
||||
"words": [
|
||||
"acmr",
|
||||
"Alova",
|
||||
"antd",
|
||||
"antdv",
|
||||
"astro",
|
||||
@@ -44,8 +45,8 @@
|
||||
"publint",
|
||||
"Qqchat",
|
||||
"qrcode",
|
||||
"ruoyi",
|
||||
"reka",
|
||||
"ruoyi",
|
||||
"shadcn",
|
||||
"sonner",
|
||||
"sortablejs",
|
||||
|
||||
@@ -69,7 +69,7 @@ catalog:
|
||||
antdv-next: latest
|
||||
archiver: ^7.0.1
|
||||
autoprefixer: ^10.4.22
|
||||
axios: ^1.10.0
|
||||
axios: 1.13.2
|
||||
axios-mock-adapter: ^2.1.0
|
||||
cac: ^6.7.14
|
||||
chalk: ^5.4.1
|
||||
@@ -90,6 +90,7 @@ catalog:
|
||||
dotenv: ^16.6.1
|
||||
echarts: ^6.0.0
|
||||
element-plus: ^2.10.2
|
||||
es-toolkit: ^1.41.0
|
||||
eslint: ^9.39.1
|
||||
eslint-config-turbo: ^2.6.1
|
||||
eslint-plugin-command: ^3.3.1
|
||||
@@ -194,4 +195,3 @@ catalog:
|
||||
watermark-js-plus: ^1.6.2
|
||||
zod: ^3.25.67
|
||||
zod-defaults: 0.1.3
|
||||
es-toolkit: ^1.41.0
|
||||
|
||||
Reference in New Issue
Block a user