diff --git a/.gitignore b/.gitignore index c2a8a771f..3399f39c0 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,4 @@ vite.config.ts.* *.sln *.sw? .history +.cursor diff --git a/apps/backend-mock/api/auth/codes.ts b/apps/backend-mock/api/auth/codes.ts index 7ba012705..e610b3381 100644 --- a/apps/backend-mock/api/auth/codes.ts +++ b/apps/backend-mock/api/auth/codes.ts @@ -1,5 +1,7 @@ +import { eventHandler } from 'h3'; import { verifyAccessToken } from '~/utils/jwt-utils'; -import { unAuthorizedResponse } from '~/utils/response'; +import { MOCK_CODES } from '~/utils/mock-data'; +import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response'; export default eventHandler((event) => { const userinfo = verifyAccessToken(event); diff --git a/apps/backend-mock/api/auth/login.post.ts b/apps/backend-mock/api/auth/login.post.ts index df5737a25..e23942c46 100644 --- a/apps/backend-mock/api/auth/login.post.ts +++ b/apps/backend-mock/api/auth/login.post.ts @@ -1,9 +1,15 @@ +import { defineEventHandler, readBody, setResponseStatus } from 'h3'; import { clearRefreshTokenCookie, setRefreshTokenCookie, } from '~/utils/cookie-utils'; import { generateAccessToken, generateRefreshToken } from '~/utils/jwt-utils'; -import { forbiddenResponse } from '~/utils/response'; +import { MOCK_USERS } from '~/utils/mock-data'; +import { + forbiddenResponse, + useResponseError, + useResponseSuccess, +} from '~/utils/response'; export default defineEventHandler(async (event) => { const { password, username } = await readBody(event); diff --git a/apps/backend-mock/api/auth/logout.post.ts b/apps/backend-mock/api/auth/logout.post.ts index ac6afe940..74c8d3151 100644 --- a/apps/backend-mock/api/auth/logout.post.ts +++ b/apps/backend-mock/api/auth/logout.post.ts @@ -1,7 +1,9 @@ +import { defineEventHandler } from 'h3'; import { clearRefreshTokenCookie, getRefreshTokenFromCookie, } from '~/utils/cookie-utils'; +import { useResponseSuccess } from '~/utils/response'; export default defineEventHandler(async (event) => { const refreshToken = getRefreshTokenFromCookie(event); diff --git a/apps/backend-mock/api/auth/refresh.post.ts b/apps/backend-mock/api/auth/refresh.post.ts index 7df4d34f5..7d8d3a51e 100644 --- a/apps/backend-mock/api/auth/refresh.post.ts +++ b/apps/backend-mock/api/auth/refresh.post.ts @@ -1,9 +1,11 @@ +import { defineEventHandler } from 'h3'; import { clearRefreshTokenCookie, getRefreshTokenFromCookie, setRefreshTokenCookie, } from '~/utils/cookie-utils'; -import { verifyRefreshToken } from '~/utils/jwt-utils'; +import { generateAccessToken, verifyRefreshToken } from '~/utils/jwt-utils'; +import { MOCK_USERS } from '~/utils/mock-data'; import { forbiddenResponse } from '~/utils/response'; export default defineEventHandler(async (event) => { diff --git a/apps/backend-mock/api/demo/bigint.ts b/apps/backend-mock/api/demo/bigint.ts index 880cc5ea8..00d6c28c5 100644 --- a/apps/backend-mock/api/demo/bigint.ts +++ b/apps/backend-mock/api/demo/bigint.ts @@ -1,3 +1,7 @@ +import { eventHandler, setHeader } from 'h3'; +import { verifyAccessToken } from '~/utils/jwt-utils'; +import { unAuthorizedResponse } from '~/utils/response'; + export default eventHandler(async (event) => { const userinfo = verifyAccessToken(event); if (!userinfo) { diff --git a/apps/backend-mock/api/menu/all.ts b/apps/backend-mock/api/menu/all.ts index 580cee4f9..7923f7ca5 100644 --- a/apps/backend-mock/api/menu/all.ts +++ b/apps/backend-mock/api/menu/all.ts @@ -1,5 +1,7 @@ +import { eventHandler } from 'h3'; import { verifyAccessToken } from '~/utils/jwt-utils'; -import { unAuthorizedResponse } from '~/utils/response'; +import { MOCK_MENUS } from '~/utils/mock-data'; +import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response'; export default eventHandler(async (event) => { const userinfo = verifyAccessToken(event); diff --git a/apps/backend-mock/api/status.ts b/apps/backend-mock/api/status.ts index 41773e1d5..43782095d 100644 --- a/apps/backend-mock/api/status.ts +++ b/apps/backend-mock/api/status.ts @@ -1,3 +1,6 @@ +import { eventHandler, getQuery, setResponseStatus } from 'h3'; +import { useResponseError } from '~/utils/response'; + export default eventHandler((event) => { const { status } = getQuery(event); setResponseStatus(event, Number(status)); diff --git a/apps/backend-mock/api/system/dept/.post.ts b/apps/backend-mock/api/system/dept/.post.ts index c529ea1bd..9a4896afa 100644 --- a/apps/backend-mock/api/system/dept/.post.ts +++ b/apps/backend-mock/api/system/dept/.post.ts @@ -1,3 +1,4 @@ +import { eventHandler } from 'h3'; import { verifyAccessToken } from '~/utils/jwt-utils'; import { sleep, diff --git a/apps/backend-mock/api/system/dept/[id].delete.ts b/apps/backend-mock/api/system/dept/[id].delete.ts index e48f051cc..eac0f5846 100644 --- a/apps/backend-mock/api/system/dept/[id].delete.ts +++ b/apps/backend-mock/api/system/dept/[id].delete.ts @@ -1,3 +1,4 @@ +import { eventHandler } from 'h3'; import { verifyAccessToken } from '~/utils/jwt-utils'; import { sleep, diff --git a/apps/backend-mock/api/system/dept/[id].put.ts b/apps/backend-mock/api/system/dept/[id].put.ts index aa55c0857..6805e1395 100644 --- a/apps/backend-mock/api/system/dept/[id].put.ts +++ b/apps/backend-mock/api/system/dept/[id].put.ts @@ -1,3 +1,4 @@ +import { eventHandler } from 'h3'; import { verifyAccessToken } from '~/utils/jwt-utils'; import { sleep, diff --git a/apps/backend-mock/api/system/dept/list.ts b/apps/backend-mock/api/system/dept/list.ts index ae819b622..a649a0d2f 100644 --- a/apps/backend-mock/api/system/dept/list.ts +++ b/apps/backend-mock/api/system/dept/list.ts @@ -1,4 +1,5 @@ import { faker } from '@faker-js/faker'; +import { eventHandler } from 'h3'; import { verifyAccessToken } from '~/utils/jwt-utils'; import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response'; diff --git a/apps/backend-mock/api/system/menu/list.ts b/apps/backend-mock/api/system/menu/list.ts index 5328b2fdf..ce96bb14e 100644 --- a/apps/backend-mock/api/system/menu/list.ts +++ b/apps/backend-mock/api/system/menu/list.ts @@ -1,3 +1,4 @@ +import { eventHandler } from 'h3'; import { verifyAccessToken } from '~/utils/jwt-utils'; import { MOCK_MENU_LIST } from '~/utils/mock-data'; import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response'; diff --git a/apps/backend-mock/api/system/menu/name-exists.ts b/apps/backend-mock/api/system/menu/name-exists.ts index 5599c22b3..7d5551b3b 100644 --- a/apps/backend-mock/api/system/menu/name-exists.ts +++ b/apps/backend-mock/api/system/menu/name-exists.ts @@ -1,6 +1,7 @@ +import { eventHandler, getQuery } from 'h3'; import { verifyAccessToken } from '~/utils/jwt-utils'; import { MOCK_MENU_LIST } from '~/utils/mock-data'; -import { unAuthorizedResponse } from '~/utils/response'; +import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response'; const namesMap: Record = {}; diff --git a/apps/backend-mock/api/system/menu/path-exists.ts b/apps/backend-mock/api/system/menu/path-exists.ts index 64774f790..f3c3be997 100644 --- a/apps/backend-mock/api/system/menu/path-exists.ts +++ b/apps/backend-mock/api/system/menu/path-exists.ts @@ -1,6 +1,7 @@ +import { eventHandler, getQuery } from 'h3'; import { verifyAccessToken } from '~/utils/jwt-utils'; import { MOCK_MENU_LIST } from '~/utils/mock-data'; -import { unAuthorizedResponse } from '~/utils/response'; +import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response'; const pathMap: Record = { '/': 0 }; diff --git a/apps/backend-mock/api/system/role/list.ts b/apps/backend-mock/api/system/role/list.ts index 4d5f923e9..bad29a513 100644 --- a/apps/backend-mock/api/system/role/list.ts +++ b/apps/backend-mock/api/system/role/list.ts @@ -1,4 +1,5 @@ import { faker } from '@faker-js/faker'; +import { eventHandler, getQuery } from 'h3'; import { verifyAccessToken } from '~/utils/jwt-utils'; import { getMenuIds, MOCK_MENU_LIST } from '~/utils/mock-data'; import { unAuthorizedResponse, usePageResponseSuccess } from '~/utils/response'; diff --git a/apps/backend-mock/api/table/list.ts b/apps/backend-mock/api/table/list.ts index 3e6f705b8..6664b583e 100644 --- a/apps/backend-mock/api/table/list.ts +++ b/apps/backend-mock/api/table/list.ts @@ -1,6 +1,11 @@ import { faker } from '@faker-js/faker'; +import { eventHandler, getQuery } from 'h3'; import { verifyAccessToken } from '~/utils/jwt-utils'; -import { unAuthorizedResponse, usePageResponseSuccess } from '~/utils/response'; +import { + sleep, + unAuthorizedResponse, + usePageResponseSuccess, +} from '~/utils/response'; function generateMockDataList(count: number) { const dataList = []; @@ -44,30 +49,69 @@ export default eventHandler(async (event) => { await sleep(600); const { page, pageSize, sortBy, sortOrder } = getQuery(event); + // 规范化分页参数,处理 string[] + const pageRaw = Array.isArray(page) ? page[0] : page; + const pageSizeRaw = Array.isArray(pageSize) ? pageSize[0] : pageSize; + const pageNumber = Math.max( + 1, + Number.parseInt(String(pageRaw ?? '1'), 10) || 1, + ); + const pageSizeNumber = Math.min( + 100, + Math.max(1, Number.parseInt(String(pageSizeRaw ?? '10'), 10) || 10), + ); const listData = structuredClone(mockData); - if (sortBy && Reflect.has(listData[0], sortBy as string)) { + + // 规范化 query 入参,兼容 string[] + const sortKeyRaw = Array.isArray(sortBy) ? sortBy[0] : sortBy; + const sortOrderRaw = Array.isArray(sortOrder) ? sortOrder[0] : sortOrder; + // 检查 sortBy 是否是 listData 元素的合法属性键 + if ( + typeof sortKeyRaw === 'string' && + listData[0] && + Object.prototype.hasOwnProperty.call(listData[0], sortKeyRaw) + ) { + // 定义数组元素的类型 + type ItemType = (typeof listData)[0]; + const sortKey = sortKeyRaw as keyof ItemType; // 将 sortBy 断言为合法键 + const isDesc = sortOrderRaw === 'desc'; listData.sort((a, b) => { - if (sortOrder === 'asc') { - if (sortBy === 'price') { - return ( - Number.parseFloat(a[sortBy as string]) - - Number.parseFloat(b[sortBy as string]) - ); + const aValue = a[sortKey] as unknown; + const bValue = b[sortKey] as unknown; + + let result = 0; + + if (typeof aValue === 'number' && typeof bValue === 'number') { + result = aValue - bValue; + } else if (aValue instanceof Date && bValue instanceof Date) { + result = aValue.getTime() - bValue.getTime(); + } else if (typeof aValue === 'boolean' && typeof bValue === 'boolean') { + if (aValue === bValue) { + result = 0; } else { - return a[sortBy as string] > b[sortBy as string] ? 1 : -1; + result = aValue ? 1 : -1; } } else { - if (sortBy === 'price') { - return ( - Number.parseFloat(b[sortBy as string]) - - Number.parseFloat(a[sortBy as string]) - ); - } else { - return a[sortBy as string] < b[sortBy as string] ? 1 : -1; - } + const aStr = String(aValue); + const bStr = String(bValue); + const aNum = Number(aStr); + const bNum = Number(bStr); + result = + Number.isFinite(aNum) && Number.isFinite(bNum) + ? aNum - bNum + : aStr.localeCompare(bStr, undefined, { + numeric: true, + sensitivity: 'base', + }); } + + return isDesc ? -result : result; }); } - return usePageResponseSuccess(page as string, pageSize as string, listData); + return usePageResponseSuccess( + String(pageNumber), + String(pageSizeNumber), + listData, + ); }); diff --git a/apps/backend-mock/api/test.get.ts b/apps/backend-mock/api/test.get.ts index ca4a500be..dc2ceef79 100644 --- a/apps/backend-mock/api/test.get.ts +++ b/apps/backend-mock/api/test.get.ts @@ -1 +1,3 @@ +import { defineEventHandler } from 'h3'; + export default defineEventHandler(() => 'Test get handler'); diff --git a/apps/backend-mock/api/test.post.ts b/apps/backend-mock/api/test.post.ts index 698cf211e..0e9e337a8 100644 --- a/apps/backend-mock/api/test.post.ts +++ b/apps/backend-mock/api/test.post.ts @@ -1 +1,3 @@ +import { defineEventHandler } from 'h3'; + export default defineEventHandler(() => 'Test post handler'); diff --git a/apps/backend-mock/api/upload.ts b/apps/backend-mock/api/upload.ts index 1bb9e602d..436b63cbf 100644 --- a/apps/backend-mock/api/upload.ts +++ b/apps/backend-mock/api/upload.ts @@ -1,5 +1,6 @@ +import { eventHandler } from 'h3'; import { verifyAccessToken } from '~/utils/jwt-utils'; -import { unAuthorizedResponse } from '~/utils/response'; +import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response'; export default eventHandler((event) => { const userinfo = verifyAccessToken(event); diff --git a/apps/backend-mock/api/user/info.ts b/apps/backend-mock/api/user/info.ts index cfa2346cb..138cb4331 100644 --- a/apps/backend-mock/api/user/info.ts +++ b/apps/backend-mock/api/user/info.ts @@ -1,5 +1,6 @@ +import { eventHandler } from 'h3'; import { verifyAccessToken } from '~/utils/jwt-utils'; -import { unAuthorizedResponse } from '~/utils/response'; +import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response'; export default eventHandler((event) => { const userinfo = verifyAccessToken(event); diff --git a/apps/backend-mock/middleware/1.api.ts b/apps/backend-mock/middleware/1.api.ts index bad9a41a1..339cda4db 100644 --- a/apps/backend-mock/middleware/1.api.ts +++ b/apps/backend-mock/middleware/1.api.ts @@ -1,3 +1,4 @@ +import { defineEventHandler } from 'h3'; import { forbiddenResponse, sleep } from '~/utils/response'; export default defineEventHandler(async (event) => { diff --git a/apps/backend-mock/routes/[...].ts b/apps/backend-mock/routes/[...].ts index 99f544b66..5a22563dc 100644 --- a/apps/backend-mock/routes/[...].ts +++ b/apps/backend-mock/routes/[...].ts @@ -1,3 +1,5 @@ +import { defineEventHandler } from 'h3'; + export default defineEventHandler(() => { return `

Hello Vben Admin

diff --git a/apps/backend-mock/utils/cookie-utils.ts b/apps/backend-mock/utils/cookie-utils.ts index 78f3aab73..187ce2f00 100644 --- a/apps/backend-mock/utils/cookie-utils.ts +++ b/apps/backend-mock/utils/cookie-utils.ts @@ -1,5 +1,7 @@ import type { EventHandlerRequest, H3Event } from 'h3'; +import { deleteCookie, getCookie, setCookie } from 'h3'; + export function clearRefreshTokenCookie(event: H3Event) { deleteCookie(event, 'jwt', { httpOnly: true, diff --git a/apps/backend-mock/utils/jwt-utils.ts b/apps/backend-mock/utils/jwt-utils.ts index 8cfc68436..718583070 100644 --- a/apps/backend-mock/utils/jwt-utils.ts +++ b/apps/backend-mock/utils/jwt-utils.ts @@ -1,8 +1,11 @@ import type { EventHandlerRequest, H3Event } from 'h3'; +import type { UserInfo } from './mock-data'; + +import { getHeader } from 'h3'; import jwt from 'jsonwebtoken'; -import { UserInfo } from './mock-data'; +import { MOCK_USERS } from './mock-data'; // TODO: Replace with your own secret key const ACCESS_TOKEN_SECRET = 'access_token_secret'; @@ -31,12 +34,22 @@ export function verifyAccessToken( return null; } - const token = authHeader.split(' ')[1]; + const tokenParts = authHeader.split(' '); + if (tokenParts.length !== 2) { + return null; + } + const token = tokenParts[1] as string; try { - const decoded = jwt.verify(token, ACCESS_TOKEN_SECRET) as UserPayload; + const decoded = jwt.verify( + token, + ACCESS_TOKEN_SECRET, + ) as unknown as UserPayload; const username = decoded.username; const user = MOCK_USERS.find((item) => item.username === username); + if (!user) { + return null; + } const { password: _pwd, ...userinfo } = user; return userinfo; } catch { @@ -50,7 +63,12 @@ export function verifyRefreshToken( try { const decoded = jwt.verify(token, REFRESH_TOKEN_SECRET) as UserPayload; const username = decoded.username; - const user = MOCK_USERS.find((item) => item.username === username); + const user = MOCK_USERS.find( + (item) => item.username === username, + ) as UserInfo; + if (!user) { + return null; + } const { password: _pwd, ...userinfo } = user; return userinfo; } catch { diff --git a/apps/backend-mock/utils/response.ts b/apps/backend-mock/utils/response.ts index 2a5a908f2..2d4242e98 100644 --- a/apps/backend-mock/utils/response.ts +++ b/apps/backend-mock/utils/response.ts @@ -1,5 +1,7 @@ import type { EventHandlerRequest, H3Event } from 'h3'; +import { setResponseStatus } from 'h3'; + export function useResponseSuccess(data: T) { return { code: 0, diff --git a/apps/web-antd/.env b/apps/web-antd/.env index 6b960186e..778a9fa1f 100644 --- a/apps/web-antd/.env +++ b/apps/web-antd/.env @@ -24,3 +24,12 @@ VITE_APP_BAIDU_CODE = e98f2eab6ceb8688bc6d8fc5332ff093 # GoView域名 VITE_GOVIEW_URL='http://127.0.0.1:3000' + +# API 加解密 +VITE_APP_API_ENCRYPT_ENABLE = true +VITE_APP_API_ENCRYPT_HEADER = X-Api-Encrypt +VITE_APP_API_ENCRYPT_ALGORITHM = AES +VITE_APP_API_ENCRYPT_REQUEST_KEY = 52549111389893486934626385991395 +VITE_APP_API_ENCRYPT_RESPONSE_KEY = 96103715984234343991809655248883 +# VITE_APP_API_ENCRYPT_REQUEST_KEY = MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCls2rIpnGdYnLFgz1XU13GbNQ5DloyPpvW00FPGjqn5Z6JpK+kDtVlnkhwR87iRrE5Vf2WNqRX6vzbLSgveIQY8e8oqGCb829myjf1MuI+ZzN4ghf/7tEYhZJGPI9AbfxFqBUzm+kR3/HByAI22GLT96WM26QiMK8n3tIP/yiLswIDAQAB +# VITE_APP_API_ENCRYPT_RESPONSE_KEY = MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAOH8IfIFxL/MR9XIg1UDv5z1fGXQI93E8wrU4iPFovL/sEt9uSgSkjyidC2O7N+m7EKtoN6b1u7cEwXSkwf3kfK0jdWLSQaNpX5YshqXCBzbDfugDaxuyYrNA4/tIMs7mzZAk0APuRXB35Dmupou7Yw7TFW/7QhQmGfzeEKULQvnAgMBAAECgYAw8LqlQGyQoPv5p3gRxEMOCfgL0JzD3XBJKztiRd35RDh40Nx1ejgjW4dPioFwGiVWd2W8cAGHLzALdcQT2KDJh+T/tsd4SPmI6uSBBK6Ff2DkO+kFFcuYvfclQQKqxma5CaZOSqhgenacmgTMFeg2eKlY3symV6JlFNu/IKU42QJBAOhxAK/Eq3e61aYQV2JSguhMR3b8NXJJRroRs/QHEanksJtl+M+2qhkC9nQVXBmBkndnkU/l2tYcHfSBlAyFySMCQQD445tgm/J2b6qMQmuUGQAYDN8FIkHjeKmha+l/fv0igWm8NDlBAem91lNDIPBUzHL1X1+pcts5bjmq99YdOnhtAkAg2J8dN3B3idpZDiQbC8fd5bGPmdI/pSUudAP27uzLEjr2qrE/QPPGdwm2m7IZFJtK7kK1hKio6u48t/bg0iL7AkEAuUUs94h+v702Fnym+jJ2CHEkXvz2US8UDs52nWrZYiM1o1y4tfSHm8H8bv8JCAa9GHyriEawfBraILOmllFdLQJAQSRZy4wmlaG48MhVXodB85X+VZ9krGXZ2TLhz7kz9iuToy53l9jTkESt6L5BfBDCVdIwcXLYgK+8KFdHN5W7HQ== diff --git a/apps/web-antd/package.json b/apps/web-antd/package.json index a74ce8d0a..2c1694a97 100644 --- a/apps/web-antd/package.json +++ b/apps/web-antd/package.json @@ -1,6 +1,6 @@ { "name": "@vben/web-antd", - "version": "5.5.8", + "version": "5.5.9", "homepage": "https://vben.pro", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "repository": { @@ -47,7 +47,6 @@ "@vueuse/integrations": "catalog:", "ant-design-vue": "catalog:", "cropperjs": "catalog:", - "crypto-js": "catalog:", "dayjs": "catalog:", "highlight.js": "catalog:", "pinia": "catalog:", diff --git a/apps/web-antd/src/api/core/auth.ts b/apps/web-antd/src/api/core/auth.ts index ccb6da340..604644a1f 100644 --- a/apps/web-antd/src/api/core/auth.ts +++ b/apps/web-antd/src/api/core/auth.ts @@ -64,7 +64,11 @@ export namespace AuthApi { /** 登录 */ export async function loginApi(data: AuthApi.LoginParams) { - return requestClient.post('/system/auth/login', data); + return requestClient.post('/system/auth/login', data, { + headers: { + isEncrypt: false, + }, + }); } /** 刷新 accessToken */ diff --git a/apps/web-antd/src/api/request.ts b/apps/web-antd/src/api/request.ts index 7f78987b4..2568def87 100644 --- a/apps/web-antd/src/api/request.ts +++ b/apps/web-antd/src/api/request.ts @@ -12,6 +12,7 @@ import { RequestClient, } from '@vben/request'; import { useAccessStore } from '@vben/stores'; +import { createApiEncrypt } from '@vben/utils'; import { message } from 'ant-design-vue'; @@ -21,6 +22,7 @@ import { refreshTokenApi } from './core'; const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD); const tenantEnable = isTenantEnable(); +const apiEncrypt = createApiEncrypt(import.meta.env); function createRequestClient(baseURL: string, options?: RequestClientOptions) { const client = new RequestClient({ @@ -84,10 +86,46 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) { config.headers['visit-tenant-id'] = tenantEnable ? accessStore.visitTenantId : undefined; + + // 是否 API 加密 + if ((config.headers || {}).isEncrypt) { + try { + // 加密请求数据 + if (config.data) { + config.data = apiEncrypt.encryptRequest(config.data); + // 设置加密标识头 + config.headers[apiEncrypt.getEncryptHeader()] = 'true'; + } + } catch (error) { + console.error('请求数据加密失败:', error); + throw error; + } + } return config; }, }); + // API 解密响应拦截器 + client.addResponseInterceptor({ + fulfilled: (response) => { + // 检查是否需要解密响应数据 + const encryptHeader = apiEncrypt.getEncryptHeader(); + const isEncryptResponse = + response.headers[encryptHeader] === 'true' || + response.headers[encryptHeader.toLowerCase()] === 'true'; + if (isEncryptResponse && typeof response.data === 'string') { + try { + // 解密响应数据 + response.data = apiEncrypt.decryptResponse(response.data); + } catch (error) { + console.error('响应数据解密失败:', error); + throw new Error(`响应数据解密失败: ${(error as Error).message}`); + } + } + return response; + }, + }); + // 处理返回的响应数据格式 client.addResponseInterceptor( defaultResponseInterceptor({ diff --git a/apps/web-antd/src/api/system/mail/log/index.ts b/apps/web-antd/src/api/system/mail/log/index.ts index 52c9947c5..c32b790e1 100644 --- a/apps/web-antd/src/api/system/mail/log/index.ts +++ b/apps/web-antd/src/api/system/mail/log/index.ts @@ -8,7 +8,9 @@ export namespace SystemMailLogApi { id: number; userId: number; userType: number; - toMail: string; + toMails: string[]; + ccMails?: string[]; + bccMails?: string[]; accountId: number; fromMail: string; templateId: number; @@ -32,15 +34,3 @@ export function getMailLogPage(params: PageParam) { { params }, ); } - -/** 查询邮件日志详情 */ -export function getMailLog(id: number) { - return requestClient.get( - `/system/mail-log/get?id=${id}`, - ); -} - -/** 重新发送邮件 */ -export function resendMail(id: number) { - return requestClient.put(`/system/mail-log/resend?id=${id}`); -} diff --git a/apps/web-antd/src/api/system/mail/template/index.ts b/apps/web-antd/src/api/system/mail/template/index.ts index d1bd075f1..5f7e7525a 100644 --- a/apps/web-antd/src/api/system/mail/template/index.ts +++ b/apps/web-antd/src/api/system/mail/template/index.ts @@ -20,7 +20,9 @@ export namespace SystemMailTemplateApi { /** 邮件发送信息 */ export interface MailSendReq { - mail: string; + toMails: string[]; + ccMails?: string[]; + bccMails?: string[]; templateCode: string; templateParams: Record; } diff --git a/apps/web-antd/src/components/simple-process-design/components/nodes-config/user-task-node-config.vue b/apps/web-antd/src/components/simple-process-design/components/nodes-config/user-task-node-config.vue index 66e6ed5ad..5dc75bacb 100644 --- a/apps/web-antd/src/components/simple-process-design/components/nodes-config/user-task-node-config.vue +++ b/apps/web-antd/src/components/simple-process-design/components/nodes-config/user-task-node-config.vue @@ -359,7 +359,8 @@ async function saveConfig() { currentNode.value.signEnable = configForm.value.signEnable; // 审批意见 currentNode.value.reasonRequire = configForm.value.reasonRequire; - + // 跳过表达式 + currentNode.value.skipExpression = configForm.value.skipExpression; currentNode.value.showText = getShowText(); drawerApi.close(); return true; @@ -443,7 +444,8 @@ function showUserTaskNodeConfig(node: SimpleFlowNode) { configForm.value.signEnable = node?.signEnable ?? false; // 7. 审批意见 configForm.value.reasonRequire = node?.reasonRequire ?? false; - + // 8. 跳过表达式 + configForm.value.skipExpression = node?.skipExpression ?? ''; drawerApi.open(); } @@ -850,7 +852,7 @@ onMounted(() => { label="流程表达式" name="expression" > -