diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 533a87c3..b4afdd93 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -153,3 +153,20 @@ jobs: username: ${{ secrets.WEB_NAIVE_FTP_ACCOUNT }} password: ${{ secrets.WEB_NAIVE_FTP_PASSWORD }} local-dir: ./apps/web-naive/dist/ + + rerun-on-failure: + name: Rerun on failure + needs: + - deploy-playground-ftp + - deploy-docs-ftp + - deploy-antd-ftp + - deploy-ele-ftp + - deploy-naive-ftp + if: failure() && fromJSON(github.run_attempt) < 10 + runs-on: ubuntu-latest + steps: + - name: Retry ${{ fromJSON(github.run_attempt) }} of 10 + env: + GH_REPO: ${{ github.repository }} + GH_TOKEN: ${{ github.token }} + run: gh workflow run rerun.yml -F run_id=${{ github.run_id }} diff --git a/.github/workflows/rerun.yml b/.github/workflows/rerun.yml new file mode 100644 index 00000000..2b462551 --- /dev/null +++ b/.github/workflows/rerun.yml @@ -0,0 +1,19 @@ +name: Rerun workflow + +on: + workflow_dispatch: + inputs: + run_id: + description: The workflow id to relanch + required: true +jobs: + rerun: + runs-on: ubuntu-latest + steps: + - name: rerun ${{ inputs.run_id }} + env: + GH_REPO: ${{ github.repository }} + GH_TOKEN: ${{ github.token }} + run: | + gh run watch ${{ inputs.run_id }} > /dev/null 2>&1 + gh run rerun ${{ inputs.run_id }} --failed diff --git a/.lintstagedrc.mjs b/.lintstagedrc.mjs index e68d8a3e..94b0192a 100644 --- a/.lintstagedrc.mjs +++ b/.lintstagedrc.mjs @@ -1,4 +1,10 @@ export default { + '*.md': ['prettier --cache --ignore-unknown --write'], + '*.vue': [ + 'prettier --write', + 'eslint --cache --fix', + 'stylelint --fix --allow-empty-input', + ], '*.{js,jsx,ts,tsx}': [ 'prettier --cache --ignore-unknown --write', 'eslint --cache --fix', @@ -7,14 +13,8 @@ export default { 'prettier --cache --ignore-unknown --write', 'stylelint --fix --allow-empty-input', ], - '*.md': ['prettier --cache --ignore-unknown --write'], - '*.vue': [ - 'prettier --write', - 'eslint --cache --fix', - 'stylelint --fix --allow-empty-input', - ], + 'package.json': ['prettier --cache --write'], '{!(package)*.json,*.code-snippets,.!(browserslist)*rc}': [ 'prettier --cache --write--parser json', ], - 'package.json': ['prettier --cache --write'], }; diff --git a/.vscode/launch.json b/.vscode/launch.json index 7740e810..e9673304 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,7 @@ "url": "http://localhost:5555", "env": { "NODE_ENV": "development" }, "sourceMaps": true, - "webRoot": "${workspaceFolder}" + "webRoot": "${workspaceFolder}/playground" }, { "type": "chrome", @@ -18,7 +18,7 @@ "url": "http://localhost:5666", "env": { "NODE_ENV": "development" }, "sourceMaps": true, - "webRoot": "${workspaceFolder}" + "webRoot": "${workspaceFolder}/apps/web-antd" }, { "type": "chrome", @@ -27,7 +27,7 @@ "url": "http://localhost:5777", "env": { "NODE_ENV": "development" }, "sourceMaps": true, - "webRoot": "${workspaceFolder}" + "webRoot": "${workspaceFolder}/apps/web-ele" }, { "type": "chrome", @@ -36,7 +36,7 @@ "url": "http://localhost:5888", "env": { "NODE_ENV": "development" }, "sourceMaps": true, - "webRoot": "${workspaceFolder}" + "webRoot": "${workspaceFolder}/apps/web-naive" } ] } diff --git a/CHANGELOG.md b/CHANGELOG.md index 834f8252..8575978f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,36 @@ -# 1.1.4 +# 1.2.0 + +**REFACTOR** + +- 菜单选择组件重构为Table形式 +- 字典相关功能重构 采用一个Map储存字典(之前为两个Map) +- 代码生成配置页面重构 去除步骤条 **Features** -- 通用的vxe-table排序事件(排序逻辑改为在排序事件中处理而非在api处理) +- 对接后端工作流 +- ~~通用的vxe-table排序事件(排序逻辑改为在排序事件中处理而非在api处理)~~ +- getDict/getDictOptions 提取公共逻辑 减少冗余代码 +- 字典新增对Number类型的支持 -> `getDictOptions('', true);`即可获取number类型的value +- 文件上传 增加上传进度条 下方上传提示 +- 图片上传 增加上传进度条 下方上传提示 +- oss下载进度提示 **BUG FIXES** -- 字典项为空时getDict方法无限调用接口((无奈兼容 不给字典item本来就是错误用法)) +- 字典项为空时getDict方法无限调用接口(无奈兼容 不给字典item本来就是错误用法) +- 表格排序翻页会丢失排序参数 +- 下载文件时(responseType === 'blob')需要判断下载失败(返回json而非二进制)的情况 +- requestClient缺失i18n内容 + +**OTHERS** + +- 用户管理 新增只获取一次(mounted)默认密码而非每次打开modal都获取 +- `apps/web-antd/src/utils/dict.ts` `getDict`方法将于下个版本删除 使用`getDictOptions`替代 +- VxeTable升级V4.10.0 +- 移除`@deprecated` `apps/web-antd/src/adapter/vxe-table.ts`的`tableCheckboxEvent`方法 +- 移除`由于更新方案弃用的` `apps/web-antd/src/adapter/vxe-table.ts`的`vxeSortEvent`方法 +- 移除apps下的ele和naive目录 # 1.1.3 diff --git a/README.zh-CN.md b/README.zh-CN.md index c4cff382..5853a02d 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -2,21 +2,15 @@ ## 提示 -该仓库使用vben最新版本v5开发, ~~老版本v2地址(不维护)~~ [前往](https://gitee.com/dapppp/ruoyi-plus-vben) +该仓库使用vben最新版本v5开发 v5版本采用分仓(包)目录结构, 具体开发路径为: `根目录/apps/web-antd` -目前对应后端版本: **5.2.3/2.2.3** +目前对应后端版本: **分布式5.3.0-beta/微服务2.2.3** V1.1.0版本已支持离线图标 -## 进度 - -**工作流相关模块等待后端重构后开发** - -基础功能已经开发完毕 - -👉 [更新日志](https://gitee.com/dapppp/ruoyi-plus-vben5/blob/main/CHANGELOG.md) +V1.2.0版本对接warmflow工作流 ## 简介 @@ -24,9 +18,9 @@ V1.1.0版本已支持离线图标 | 组件/框架 | 版本 | | :------------- | :----- | -| vben | 5.4.5 | -| ant-design-vue | 4.2.5 | -| vue | 3.5.11 | +| vben | 5.5.2 | +| ant-design-vue | 4.2.6 | +| vue | 3.5.13 | 对应后端项目: **(分布式 5.X 分支 微服务 2.分支)** @@ -194,4 +188,4 @@ pnpm build:antd 如果项目帮助到您 可以考虑请作者喝杯咖啡 万分感谢您对开源的支持! - + diff --git a/apps/backend-mock/api/table/list.ts b/apps/backend-mock/api/table/list.ts index 4a0db94e..55b88eaa 100644 --- a/apps/backend-mock/api/table/list.ts +++ b/apps/backend-mock/api/table/list.ts @@ -43,6 +43,31 @@ export default eventHandler(async (event) => { await sleep(600); - const { page, pageSize } = getQuery(event); - return usePageResponseSuccess(page as string, pageSize as string, mockData); + const { page, pageSize, sortBy, sortOrder } = getQuery(event); + const listData = structuredClone(mockData); + if (sortBy && Reflect.has(listData[0], sortBy as string)) { + listData.sort((a, b) => { + if (sortOrder === 'asc') { + if (sortBy === 'price') { + return ( + Number.parseFloat(a[sortBy as string]) - + Number.parseFloat(b[sortBy as string]) + ); + } else { + return a[sortBy as string] > b[sortBy as string] ? 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; + } + } + }); + } + + return usePageResponseSuccess(page as string, pageSize as string, listData); }); diff --git a/apps/backend-mock/utils/mock-data.ts b/apps/backend-mock/utils/mock-data.ts index 71970a28..3967b906 100644 --- a/apps/backend-mock/utils/mock-data.ts +++ b/apps/backend-mock/utils/mock-data.ts @@ -4,6 +4,7 @@ export interface UserInfo { realName: string; roles: string[]; username: string; + homePath?: string; } export const MOCK_USERS: UserInfo[] = [ @@ -20,6 +21,7 @@ export const MOCK_USERS: UserInfo[] = [ realName: 'Admin', roles: ['admin'], username: 'admin', + homePath: '/workspace', }, { id: 2, @@ -27,6 +29,7 @@ export const MOCK_USERS: UserInfo[] = [ realName: 'Jack', roles: ['user'], username: 'jack', + homePath: '/analytics', }, ]; @@ -50,7 +53,6 @@ export const MOCK_CODES = [ const dashboardMenus = [ { - component: 'BasicLayout', meta: { order: -1, title: 'page.dashboard.title', @@ -113,7 +115,6 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => { return [ { - component: 'BasicLayout', meta: { icon: 'ic:baseline-view-in-ar', keepAlive: true, diff --git a/apps/web-antd/package.json b/apps/web-antd/package.json index d303cf5e..b759ba10 100644 --- a/apps/web-antd/package.json +++ b/apps/web-antd/package.json @@ -1,6 +1,6 @@ { "name": "@vben/web-antd", - "version": "1.1.3", + "version": "1.2.0", "homepage": "https://vben.pro", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "repository": { diff --git a/apps/web-antd/src/adapter/component/index.ts b/apps/web-antd/src/adapter/component/index.ts index 370bcdf3..95472d96 100644 --- a/apps/web-antd/src/adapter/component/index.ts +++ b/apps/web-antd/src/adapter/component/index.ts @@ -3,9 +3,10 @@ * 可用于 vben-form、vben-modal、vben-drawer 等组件使用, */ +import type { Component, SetupContext } from 'vue'; + import type { BaseFormComponentType } from '@vben/common-ui'; -import type { Component, SetupContext } from 'vue'; import { h } from 'vue'; import { ApiComponent, globalShareState, IconPicker } from '@vben/common-ui'; diff --git a/apps/web-antd/src/adapter/vxe-table.ts b/apps/web-antd/src/adapter/vxe-table.ts index 857751b3..592161af 100644 --- a/apps/web-antd/src/adapter/vxe-table.ts +++ b/apps/web-antd/src/adapter/vxe-table.ts @@ -1,10 +1,8 @@ -import { h, type Ref } from 'vue'; +import type { VxeGridPropTypes } from '@vben/plugins/vxe-table'; -import { - setupVbenVxeTable, - useVbenVxeGrid, - type VxeGridDefines, -} from '@vben/plugins/vxe-table'; +import { h } from 'vue'; + +import { setupVbenVxeTable, useVbenVxeGrid } from '@vben/plugins/vxe-table'; import { Button, Image } from 'ant-design-vue'; @@ -43,7 +41,7 @@ setupVbenVxeTable({ // 鼠标移入行显示 hover 样式 isHover: true, // 点击行高亮 - isCurrent: true, + isCurrent: false, }, columnConfig: { // 可拖拽列宽 @@ -105,21 +103,6 @@ export { useVbenVxeGrid }; export type * from '@vben/plugins/vxe-table'; -/** - * 通用的表格复选框是否选中事件 - * @deprecated 使用vxeCheckboxChecked代替 - * @param checked 是否选中 - * @returns function - */ -export function tableCheckboxEvent(checked: Ref) { - const event: (params: VxeGridDefines.CheckboxChangeEventParams) => void = ( - params, - ) => { - checked.value = params.$table.getCheckboxRecords().length > 0; - }; - return event; -} - /** * 判断vxe-table的复选框是否选中 * @param tableApi api @@ -132,22 +115,21 @@ export function vxeCheckboxChecked( } /** - * 通用的vxe-table排序事件 支持单/多字段排序 - * @param tableApi api - * @param sortParams 排序参数 + * 通用的 排序参数添加到请求参数中 + * @param params 请求参数 + * @param sortList vxe-table的排序参数 */ -export function vxeSortEvent( - tableApi: ReturnType[1], - sortParams: VxeGridDefines.SortChangeEventParams, +export function addSortParams( + params: Record, + sortList: VxeGridPropTypes.ProxyAjaxQuerySortCheckedParams[], ) { - const { sortList } = sortParams; - // 这里是排序取消 length为0 就不传参数了 + // 这里是排序取消 length为0 就不添加参数了 if (sortList.length === 0) { - tableApi.query(); return; } // 支持单/多字段排序 const orderByColumn = sortList.map((item) => item.field).join(','); const isAsc = sortList.map((item) => item.order).join(','); - tableApi.query({ orderByColumn, isAsc }); + params.orderByColumn = orderByColumn; + params.isAsc = isAsc; } diff --git a/apps/web-antd/src/api/common.d.ts b/apps/web-antd/src/api/common.d.ts index 233f2aa6..a6ed2aea 100644 --- a/apps/web-antd/src/api/common.d.ts +++ b/apps/web-antd/src/api/common.d.ts @@ -38,4 +38,5 @@ export interface PageQuery { orderByColumn?: string; pageNum?: number; pageSize?: number; + [key: string]: any; } diff --git a/apps/web-antd/src/api/core/menu.ts b/apps/web-antd/src/api/core/menu.ts index e4bef601..e04c2baf 100644 --- a/apps/web-antd/src/api/core/menu.ts +++ b/apps/web-antd/src/api/core/menu.ts @@ -19,7 +19,7 @@ export interface MenuMeta { * @param name 菜单名 * @param path 菜单路径 * @param hidden 是否隐藏 - * @param component 组件名称 Laout + * @param component 组件名称 Layout * @param alwaysShow 总是显示 * @param query 路由参数(json形式) * @param meta 路由信息 diff --git a/apps/web-antd/src/api/core/upload.ts b/apps/web-antd/src/api/core/upload.ts index 32833b66..a74cb19f 100644 --- a/apps/web-antd/src/api/core/upload.ts +++ b/apps/web-antd/src/api/core/upload.ts @@ -1,12 +1,27 @@ +import type { AxiosRequestConfig } from '@vben/request'; + import { requestClient } from '#/api/request'; +/** + * Axios上传进度事件 + */ +export type AxiosProgressEvent = AxiosRequestConfig['onUploadProgress']; + /** * 通过单文件上传接口 * @param file 上传的文件 + * @param onUploadProgress 上传进度事件 非必传 * @returns 上传结果 */ -export function uploadApi(file: Blob | File) { - return requestClient.upload('/resource/oss/upload', { file }); +export function uploadApi( + file: Blob | File, + onUploadProgress?: AxiosProgressEvent, +) { + return requestClient.upload( + '/resource/oss/upload', + { file }, + { onUploadProgress, timeout: 60_000 }, + ); } /** * 默认上传结果 diff --git a/apps/web-antd/src/api/monitor/online/index.ts b/apps/web-antd/src/api/monitor/online/index.ts index 4d8224a2..8b4d938c 100644 --- a/apps/web-antd/src/api/monitor/online/index.ts +++ b/apps/web-antd/src/api/monitor/online/index.ts @@ -17,6 +17,11 @@ export function onlineDeviceList() { return requestClient.get>(Api.root); } +/** + * 这里的分页参数无效 返回的是全部的分页 + * @param params 请求参数 + * @returns 结果 + */ export function onlineList(params?: PageQuery) { return requestClient.get>(Api.onlineList, { params }); } @@ -36,5 +41,5 @@ export function forceLogout(tokenId: string) { * @returns void */ export function forceLogout2(tokenId: string) { - return requestClient.postWithMsg(`${Api.root}/${tokenId}`); + return requestClient.deleteWithMsg(`${Api.root}/myself/${tokenId}`); } diff --git a/apps/web-antd/src/api/monitor/operlog/index.ts b/apps/web-antd/src/api/monitor/operlog/index.ts index c061b447..44946f10 100644 --- a/apps/web-antd/src/api/monitor/operlog/index.ts +++ b/apps/web-antd/src/api/monitor/operlog/index.ts @@ -12,20 +12,36 @@ enum Api { root = '/monitor/operlog', } +/** + * 操作日志分页 + * @param params 查询参数 + * @returns 分页结果 + */ export function operLogList(params?: PageQuery) { return requestClient.get>(Api.operLogList, { params, }); } +/** + * 删除操作日志 + * @param operIds id/ids + */ export function operLogDelete(operIds: IDS) { return requestClient.deleteWithMsg(`${Api.root}/${operIds}`); } +/** + * 清空全部分页日志 + */ export function operLogClean() { return requestClient.deleteWithMsg(Api.operLogClean); } -export function operLogExport(data: any) { +/** + * 导出操作日志 + * @param data 查询参数 + */ +export function operLogExport(data: Partial) { return commonExport(Api.operLogExport, data); } diff --git a/apps/web-antd/src/api/request.ts b/apps/web-antd/src/api/request.ts index b3d80804..515cb70d 100644 --- a/apps/web-antd/src/api/request.ts +++ b/apps/web-antd/src/api/request.ts @@ -1,6 +1,7 @@ /** * 该文件可自行根据业务逻辑进行调整 */ + import type { HttpResponse } from '@vben/request'; import { useAppConfig } from '@vben/hooks'; @@ -134,14 +135,14 @@ function createRequestClient(baseURL: string) { }); // 通用的错误处理, 如果没有进入上面的错误处理逻辑,就会进入这里 - // 主要处理http状态码不为200的情况 必须放在在下面的响应拦截器之前 + // 主要处理http状态码不为200(如网络异常/离线)的情况 必须放在在下面的响应拦截器之前 client.addResponseInterceptor( errorMessageResponseInterceptor((msg: string) => message.error(msg)), ); client.addResponseInterceptor({ - fulfilled: (response) => { - const encryptKey = (response.headers || {})['encrypt-key']; + fulfilled: async (response) => { + const encryptKey = (response.headers ?? {})['encrypt-key']; if (encryptKey) { /** RSA私钥解密 拿到解密秘钥的base64 */ const base64Str = encryptUtil.decrypt(encryptKey); @@ -164,39 +165,57 @@ function createRequestClient(baseURL: string) { // 不进行任何处理,直接返回 // 用于页面代码可能需要直接获取code,data,message这些信息时开启 if (!isTransformResponse) { - return response.data; + /** + * 需要判断下载二进制的情况 正常是返回二进制 报错会返回json + * 当type为blob且content-type为application/json时 则判断已经下载出错 + */ + if ( + response.config.responseType === 'blob' && + response.headers['content-type']?.includes?.('application/json') + ) { + // 这时候的data为blob类型 + const blob = response.data as unknown as Blob; + // 拿到字符串转json对象 + response.data = JSON.parse(await blob.text()); + // 然后按正常逻辑执行下面的代码(判断业务状态码) + } else { + // 其他情况 直接返回 + return response.data; + } } const axiosResponseData = response.data; if (!axiosResponseData) { - throw new Error($t('fallback.http.apiRequestFailed')); + throw new Error($t('http.apiRequestFailed')); } - // ruoyi-plus没有采用严格的{code, msg, data}模式 + // 后端并没有采用严格的{code, msg, data}模式 const { code, data, msg, ...other } = axiosResponseData; - // 这里逻辑可以根据项目进行修改 + // 业务状态码为200则请求成功 const hasSuccess = Reflect.has(axiosResponseData, 'code') && code === 200; if (hasSuccess) { let successMsg = msg; if (isNull(successMsg) || isEmpty(successMsg)) { - successMsg = $t(`fallback.http.operationSuccess`); + successMsg = $t(`http.operationSuccess`); } if (response.config.successMessageMode === 'modal') { Modal.success({ content: successMsg, - title: $t('fallback.http.successTip'), + title: $t('http.successTip'), }); } else if (response.config.successMessageMode === 'message') { message.success(successMsg); } + // 分页情况下为code msg rows total 并没有data字段 // 如果有data 直接返回data 没有data将剩余参数(...other)封装为data返回 - // 需要考虑data为null的情况(比如查询为空) + // 需要考虑data为null的情况(比如查询为空) 所以这里直接判断undefined if (data !== undefined) { return data; } + // 没有data 将其他参数包装为data return other; } // 在此处根据自己项目的实际情况对不同的code执行不同的操作 @@ -209,7 +228,7 @@ function createRequestClient(baseURL: string) { return; } isLogoutProcessing = true; - const _msg = '登录超时, 请重新登录'; + const _msg = $t('http.loginTimeout'); const userStore = useAuthStore(); userStore.logout().finally(() => { message.error(_msg); @@ -230,13 +249,13 @@ function createRequestClient(baseURL: string) { if (response.config.errorMessageMode === 'modal') { Modal.error({ content: timeoutMsg, - title: $t('fallback.http.errorTip'), + title: $t('http.errorTip'), }); } else if (response.config.errorMessageMode === 'message') { message.error(timeoutMsg); } - throw new Error(timeoutMsg || $t('fallback.http.apiRequestFailed')); + throw new Error(timeoutMsg || $t('http.apiRequestFailed')); }, }); diff --git a/apps/web-antd/src/api/system/client/index.ts b/apps/web-antd/src/api/system/client/index.ts index 354fab95..bb8c4b99 100644 --- a/apps/web-antd/src/api/system/client/index.ts +++ b/apps/web-antd/src/api/system/client/index.ts @@ -12,30 +12,60 @@ enum Api { root = '/system/client', } +/** + * 查询客户端分页列表 + * @param params 请求参数 + * @returns 列表 + */ export function clientList(params?: PageQuery) { return requestClient.get>(Api.clientList, { params }); } -export function clientExport(data: any) { +/** + * 导出客户端excel + * @param data 请求参数 + */ +export function clientExport(data: Partial) { return commonExport(Api.clientExport, data); } +/** + * 客户端详情 + * @param id id + * @returns 详情 + */ export function clientInfo(id: ID) { return requestClient.get(`${Api.root}/${id}`); } -export function clientAdd(data: any) { +/** + * 客户端新增 + * @param data 参数 + */ +export function clientAdd(data: Partial) { return requestClient.postWithMsg(Api.root, data); } -export function clientUpdate(data: any) { +/** + * 客户端修改 + * @param data 参数 + */ +export function clientUpdate(data: Partial) { return requestClient.putWithMsg(Api.root, data); } +/** + * 客户端状态修改 + * @param data 状态 + */ export function clientChangeStatus(data: any) { - return requestClient.putWithMsg(Api.root, data); + return requestClient.putWithMsg(Api.clientChangeStatus, data); } +/** + * 客户端删除 + * @param ids id集合 + */ export function clientRemove(ids: IDS) { - return requestClient.deleteWithMsg(`${Api.root}/${ids}`); + return requestClient.deleteWithMsg(`${Api.root}/${ids}`); } diff --git a/apps/web-antd/src/api/system/config/index.ts b/apps/web-antd/src/api/system/config/index.ts index 588bb2b1..546c95b1 100644 --- a/apps/web-antd/src/api/system/config/index.ts +++ b/apps/web-antd/src/api/system/config/index.ts @@ -1,4 +1,4 @@ -import type { Config } from './model'; +import type { SysConfig } from './model'; import type { ID, IDS, PageQuery, PageResult } from '#/api/common'; @@ -13,15 +13,24 @@ enum Api { root = '/system/config', } +/** + * 系统参数分页列表 + * @param params 请求参数 + * @returns 列表 + */ export function configList(params?: PageQuery) { - return requestClient.get>(Api.configList, { params }); + return requestClient.get>(Api.configList, { params }); } export function configInfo(configId: ID) { - return requestClient.get(`${Api.root}/${configId}`); + return requestClient.get(`${Api.root}/${configId}`); } -export function configExport(data: any) { +/** + * 导出 + * @param data 参数 + */ +export function configExport(data: Partial) { return commonExport(Api.configExport, data); } @@ -33,14 +42,26 @@ export function configRefreshCache() { return requestClient.deleteWithMsg(Api.configRefreshCache); } -export function configUpdate(data: any) { +/** + * 更新系统配置 + * @param data 参数 + */ +export function configUpdate(data: Partial) { return requestClient.putWithMsg(Api.root, data); } -export function configAdd(data: any) { +/** + * 新增系统配置 + * @param data 参数 + */ +export function configAdd(data: Partial) { return requestClient.postWithMsg(Api.root, data); } +/** + * 删除配置 + * @param configIds ids + */ export function configRemove(configIds: IDS) { return requestClient.deleteWithMsg(`${Api.root}/${configIds}`); } diff --git a/apps/web-antd/src/api/system/config/model.d.ts b/apps/web-antd/src/api/system/config/model.d.ts index 85afc559..ee13acf4 100644 --- a/apps/web-antd/src/api/system/config/model.d.ts +++ b/apps/web-antd/src/api/system/config/model.d.ts @@ -1,4 +1,4 @@ -export interface Config { +export interface SysConfig { configId: number; configName: string; configKey: string; diff --git a/apps/web-antd/src/api/system/dept/index.ts b/apps/web-antd/src/api/system/dept/index.ts index 7557537d..bf7b721c 100644 --- a/apps/web-antd/src/api/system/dept/index.ts +++ b/apps/web-antd/src/api/system/dept/index.ts @@ -10,7 +10,11 @@ enum Api { root = '/system/dept', } -export function deptList(params?: any) { +/** + * 部门列表 + * @returns list + */ +export function deptList(params?: { deptName?: string; status?: string }) { return requestClient.get(Api.deptList, { params }); } @@ -23,15 +27,28 @@ export function deptNodeList(deptId: ID) { return requestClient.get(`${Api.deptNodeInfo}/${deptId}`); } +/** + * 部门详情 + * @param deptId 部门id + * @returns 部门信息 + */ export function deptInfo(deptId: ID) { return requestClient.get(`${Api.root}/${deptId}`); } -export function deptAdd(data: any) { +/** + * 部门新增 + * @param data 参数 + */ +export function deptAdd(data: Partial) { return requestClient.postWithMsg(Api.root, data); } -export function deptUpdate(data: any) { +/** + * 部门更新 + * @param data 参数 + */ +export function deptUpdate(data: Partial) { return requestClient.putWithMsg(Api.root, data); } diff --git a/apps/web-antd/src/api/system/dict/dict-data.ts b/apps/web-antd/src/api/system/dict/dict-data.ts index a39ec396..61c7db9f 100644 --- a/apps/web-antd/src/api/system/dict/dict-data.ts +++ b/apps/web-antd/src/api/system/dict/dict-data.ts @@ -34,7 +34,7 @@ export function dictDataList(params?: PageQuery) { * @param data 表单参数 * @returns blob */ -export function dictDataExport(data: any) { +export function dictDataExport(data: Partial) { return commonExport(Api.dictDataExport, data); } @@ -52,7 +52,7 @@ export function dictDataRemove(dictIds: IDS) { * @param data 表单参数 * @returns void */ -export function dictDataAdd(data: any) { +export function dictDataAdd(data: Partial) { return requestClient.postWithMsg(Api.root, data); } @@ -61,7 +61,7 @@ export function dictDataAdd(data: any) { * @param data 表单参数 * @returns void */ -export function dictDataUpdate(data: any) { +export function dictDataUpdate(data: Partial) { return requestClient.putWithMsg(Api.root, data); } diff --git a/apps/web-antd/src/api/system/dict/dict-type.ts b/apps/web-antd/src/api/system/dict/dict-type.ts index e3d4b291..96341606 100644 --- a/apps/web-antd/src/api/system/dict/dict-type.ts +++ b/apps/web-antd/src/api/system/dict/dict-type.ts @@ -27,7 +27,7 @@ export function dictTypeList(params?: PageQuery) { * @param data 表单参数 * @returns blob */ -export function dictTypeExport(data: any) { +export function dictTypeExport(data: Partial) { return commonExport(Api.dictTypeExport, data); } @@ -53,7 +53,7 @@ export function refreshDictTypeCache() { * @param data 表单参数 * @returns void */ -export function dictTypeAdd(data: any) { +export function dictTypeAdd(data: Partial) { return requestClient.postWithMsg(Api.root, data); } @@ -62,7 +62,7 @@ export function dictTypeAdd(data: any) { * @param data 表单参数 * @returns void */ -export function dictTypeUpdate(data: any) { +export function dictTypeUpdate(data: Partial) { return requestClient.putWithMsg(Api.root, data); } @@ -76,6 +76,7 @@ export function dictTypeInfo(dictId: ID) { } /** + * 这个在ele用到 v5用不上 * 下拉框 返回值和list一样 * @returns options */ diff --git a/apps/web-antd/src/api/system/menu/index.ts b/apps/web-antd/src/api/system/menu/index.ts index 65c3b4f9..0381449d 100644 --- a/apps/web-antd/src/api/system/menu/index.ts +++ b/apps/web-antd/src/api/system/menu/index.ts @@ -1,4 +1,4 @@ -import type { Menu, MenuOption, MenuResp } from './model'; +import type { Menu, MenuOption, MenuQuery, MenuResp } from './model'; import type { ID, IDS } from '#/api/common'; @@ -12,22 +12,44 @@ enum Api { tenantPackageMenuTreeselect = '/system/menu/tenantPackageMenuTreeselect', } -export function menuList(params?: any) { +/** + * 菜单列表 + * @param params 参数 + * @returns 列表 + */ +export function menuList(params?: MenuQuery) { return requestClient.get(Api.menuList, { params }); } +/** + * 菜单详情 + * @param menuId 菜单id + * @returns 菜单详情 + */ export function menuInfo(menuId: ID) { return requestClient.get(`${Api.root}/${menuId}`); } -export function menuAdd(data: any) { +/** + * 菜单新增 + * @param data 参数 + */ +export function menuAdd(data: Partial) { return requestClient.postWithMsg(Api.root, data); } -export function menuUpdate(data: any) { +/** + * 菜单更新 + * @param data 参数 + */ +export function menuUpdate(data: Partial) { return requestClient.putWithMsg(Api.root, data); } +/** + * 菜单删除 + * @param menuIds ids + */ export function menuRemove(menuIds: IDS) { return requestClient.deleteWithMsg(`${Api.root}/${menuIds}`); } diff --git a/apps/web-antd/src/api/system/menu/model.d.ts b/apps/web-antd/src/api/system/menu/model.d.ts index 0234425f..5854f5c7 100644 --- a/apps/web-antd/src/api/system/menu/model.d.ts +++ b/apps/web-antd/src/api/system/menu/model.d.ts @@ -33,6 +33,8 @@ export interface MenuOption { weight: number; children: MenuOption[]; key: string; // 实际上不存在 ide报错 + menuType: string; + icon: string; } /** @@ -44,3 +46,12 @@ export interface MenuResp { checkedKeys: number[]; menus: MenuOption[]; } + +/** + * 菜单表单查询 + */ +export interface MenuQuery { + menuName?: string; + visible?: string; + status?: string; +} diff --git a/apps/web-antd/src/api/system/notice/index.ts b/apps/web-antd/src/api/system/notice/index.ts index 82877943..8588930f 100644 --- a/apps/web-antd/src/api/system/notice/index.ts +++ b/apps/web-antd/src/api/system/notice/index.ts @@ -9,22 +9,44 @@ enum Api { root = '/system/notice', } +/** + * 通知公告分页 + * @param params 分页参数 + * @returns 分页结果 + */ export function noticeList(params?: PageQuery) { return requestClient.get(Api.noticeList, { params }); } +/** + * 通知公告详情 + * @param noticeId id + * @returns 详情 + */ export function noticeInfo(noticeId: ID) { return requestClient.get(`${Api.root}/${noticeId}`); } -export function noticeAdd(data: any) { +/** + * 通知公告新增 + * @param data 参数 + */ +export function noticeAdd(data: Partial) { return requestClient.postWithMsg(Api.root, data); } +/** + * 通知公告更新 + * @param data 参数 + */ export function noticeUpdate(data: any) { return requestClient.putWithMsg(Api.root, data); } +/** + * 通知公告删除 + * @param noticeIds ids + */ export function noticeRemove(noticeIds: IDS) { return requestClient.deleteWithMsg(`${Api.root}/${noticeIds}`); } diff --git a/apps/web-antd/src/api/system/oss-config/index.ts b/apps/web-antd/src/api/system/oss-config/index.ts index 3774e8af..ee01800a 100644 --- a/apps/web-antd/src/api/system/oss-config/index.ts +++ b/apps/web-antd/src/api/system/oss-config/index.ts @@ -21,12 +21,12 @@ export function ossConfigInfo(ossConfigId: ID) { } // 添加新的OSS配置 -export function ossConfigAdd(data: any) { +export function ossConfigAdd(data: Partial) { return requestClient.postWithMsg(Api.root, data); } // 更新现有的OSS配置 -export function ossConfigUpdate(data: any) { +export function ossConfigUpdate(data: Partial) { return requestClient.putWithMsg(Api.root, data); } diff --git a/apps/web-antd/src/api/system/oss/index.ts b/apps/web-antd/src/api/system/oss/index.ts index 139b9260..f1772aeb 100644 --- a/apps/web-antd/src/api/system/oss/index.ts +++ b/apps/web-antd/src/api/system/oss/index.ts @@ -1,3 +1,5 @@ +import type { AxiosRequestConfig } from '@vben/request'; + import type { OssFile } from './model'; import type { ID, IDS, PageQuery, PageResult } from '#/api/common'; @@ -13,20 +15,30 @@ enum Api { root = '/resource/oss', } +/** + * 文件list + * @param params 参数 + * @returns 分页 + */ export function ossList(params?: PageQuery) { return requestClient.get>(Api.ossList, { params }); } +/** + * 查询文件信息 返回为数组 + * @param ossIds id数组 + * @returns 信息数组 + */ export function ossInfo(ossIds: IDS) { - return requestClient.get(`${Api.ossInfo}/${ossIds}`); + return requestClient.get(`${Api.ossInfo}/${ossIds}`); } /** - * @deprecated + * @deprecated 使用apps/web-antd/src/api/core/upload.ts uploadApi方法 * @param file 文件 * @returns void */ -export function ossUpload(file: any) { +export function ossUpload(file: Blob | File) { const formData = new FormData(); formData.append('file', file); return requestClient.postWithMsg(Api.ossUpload, formData, { @@ -38,16 +50,26 @@ export function ossUpload(file: any) { /** * 下载文件 返回为二进制 * @param ossId ossId + * @param onDownloadProgress 下载进度(可选) * @returns blob */ -export function ossDownload(ossId: ID) { +export function ossDownload( + ossId: ID, + onDownloadProgress?: AxiosRequestConfig['onDownloadProgress'], +) { return requestClient.get(`${Api.ossDownload}/${ossId}`, { responseType: 'blob', timeout: 30 * 1000, isTransformResponse: false, + onDownloadProgress, }); } +/** + * 删除文件 + * @param ossIds id数组 + * @returns void + */ export function ossRemove(ossIds: IDS) { return requestClient.deleteWithMsg(`${Api.root}/${ossIds}`); } diff --git a/apps/web-antd/src/api/system/post/index.ts b/apps/web-antd/src/api/system/post/index.ts index 61c5c3eb..3e1ef2e2 100644 --- a/apps/web-antd/src/api/system/post/index.ts +++ b/apps/web-antd/src/api/system/post/index.ts @@ -21,26 +21,56 @@ export function postList(params?: PageQuery) { return requestClient.get(Api.postList, { params }); } -export function postExport(data: any) { +/** + * 导出岗位信息 + * @param data 请求参数 + * @returns blob + */ +export function postExport(data: Partial) { return commonExport(Api.postExport, data); } +/** + * 查询岗位信息 + * @param postId id + * @returns 岗位信息 + */ export function postInfo(postId: ID) { return requestClient.get(`${Api.root}/${postId}`); } -export function postAdd(data: any) { +/** + * 岗位新增 + * @param data 参数 + * @returns void + */ +export function postAdd(data: Partial) { return requestClient.postWithMsg(Api.root, data); } -export function postUpdate(data: any) { +/** + * 岗位更新 + * @param data 参数 + * @returns void + */ +export function postUpdate(data: Partial) { return requestClient.putWithMsg(Api.root, data); } +/** + * 岗位删除 + * @param postIds ids + * @returns void + */ export function postRemove(postIds: IDS) { return requestClient.deleteWithMsg(`${Api.root}/${postIds}`); } +/** + * 根据部门id获取岗位下拉列表 + * @param deptId 部门id + * @returns 岗位 + */ export function postOptionSelect(deptId: ID) { return requestClient.get(Api.postSelect, { params: { deptId } }); } diff --git a/apps/web-antd/src/api/system/role/index.ts b/apps/web-antd/src/api/system/role/index.ts index 6382fa03..e9e32891 100644 --- a/apps/web-antd/src/api/system/role/index.ts +++ b/apps/web-antd/src/api/system/role/index.ts @@ -1,3 +1,4 @@ +import type { User } from '../user/model'; import type { DeptResp, Role } from './model'; import type { ID, IDS, PageQuery, PageResult } from '#/api/common'; @@ -20,30 +21,65 @@ enum Api { root = '/system/role', } +/** + * 查询角色分页列表 + * @param params 搜索条件 + * @returns 分页列表 + */ export function roleList(params?: PageQuery) { return requestClient.get>(Api.roleList, { params }); } -export function roleExport(data: any) { +/** + * 导出角色信息 + * @param data 查询参数 + * @returns blob + */ +export function roleExport(data: Partial) { return commonExport(Api.roleExport, data); } +/** + * 查询角色信息 + * @param roleId 角色id + * @returns 角色信息 + */ export function roleInfo(roleId: ID) { return requestClient.get(`${Api.root}/${roleId}`); } -export function roleAdd(data: any) { +/** + * 角色新增 + * @param data 参数 + * @returns void + */ +export function roleAdd(data: Partial) { return requestClient.postWithMsg(Api.root, data); } -export function roleUpdate(data: any) { +/** + * 角色更新 + * @param data 参数 + * @returns void + */ +export function roleUpdate(data: Partial) { return requestClient.putWithMsg(Api.root, data); } -export function roleChangeStatus(data: any) { +/** + * 修改角色状态 + * @param data 参数 + * @returns void + */ +export function roleChangeStatus(data: Partial) { return requestClient.putWithMsg(Api.roleChangeStatus, data); } +/** + * 角色删除 + * @param roleIds ids + * @returns void + */ export function roleRemove(roleIds: IDS) { return requestClient.deleteWithMsg(`${Api.root}/${roleIds}`); } @@ -57,12 +93,20 @@ export function roleDataScope(data: any) { return requestClient.putWithMsg(Api.roleDataScope, data); } +/** + * @deprecated 全局并没有用到这个方法 + */ export function roleOptionSelect(params?: any) { return requestClient.get(Api.roleOptionSelect, { params }); } -export function roleAllocatedList(params: any) { - return requestClient.get(Api.roleAllocatedList, { params }); +/** + * 已分配角色的用户分页 + * @param params 请求参数 + * @returns 分页 + */ +export function roleAllocatedList(params?: PageQuery) { + return requestClient.get>(Api.roleAllocatedList, { params }); } /** @@ -71,28 +115,26 @@ export function roleAllocatedList(params: any) { * @returns void */ export function roleUnallocatedList(params: any) { - return requestClient.get(Api.roleUnallocatedList, { params }); + return requestClient.get>(Api.roleUnallocatedList, { + params, + }); } /** - * 取消授权 - * @param data {userId: 2, roleId: "2"} + * 取消用户角色授权 * @returns void */ -export function roleAuthCancel(data: any) { +export function roleAuthCancel(data: { roleId: ID; userId: ID }) { return requestClient.putWithMsg(Api.roleAuthCancel, data); } /** * 批量取消授权 - * @param roleId - * @param userIds + * @param roleId 角色ID + * @param userIds 用户ID集合 * @returns void */ -export function roleAuthCancelAll( - roleId: number | string, - userIds: number[] | string[], -) { +export function roleAuthCancelAll(roleId: ID, userIds: IDS) { return requestClient.putWithMsg( `${Api.roleAuthCancelAll}?roleId=${roleId}&userIds=${userIds.join(',')}`, ); @@ -100,21 +142,18 @@ export function roleAuthCancelAll( /** * 批量授权用户 - * @param roleId - * @param userIds + * @param roleId 角色ID + * @param userIds 用户ID集合 * @returns void */ -export function roleSelectAll( - roleId: number | string, - userIds: number[] | string[], -) { +export function roleSelectAll(roleId: ID, userIds: IDS) { return requestClient.putWithMsg( `${Api.roleAuthSelectAll}?roleId=${roleId}&userIds=${userIds.join(',')}`, ); } /** - * 部门树 + * 根据角色id获取部门树 * @param roleId 角色id * @returns DeptResp */ diff --git a/apps/web-antd/src/api/system/social/index.ts b/apps/web-antd/src/api/system/social/index.ts index 7c50ed3a..91aef896 100644 --- a/apps/web-antd/src/api/system/social/index.ts +++ b/apps/web-antd/src/api/system/social/index.ts @@ -1,5 +1,7 @@ import type { SocialInfo } from './model'; +import type { ID } from '#/api/common'; + import { requestClient } from '#/api/request'; enum Api { @@ -15,6 +17,9 @@ export function socialList() { return requestClient.get(Api.socialList); } -export function socialInfo(id: number | string) { +/** + * @deprecated 并没有用到这个方法 + */ +export function socialInfo(id: ID) { return requestClient.get(`${Api.root}/${id}`); } diff --git a/apps/web-antd/src/api/system/tenant-package/index.ts b/apps/web-antd/src/api/system/tenant-package/index.ts index 1851a7bc..3cb83a2b 100644 --- a/apps/web-antd/src/api/system/tenant-package/index.ts +++ b/apps/web-antd/src/api/system/tenant-package/index.ts @@ -13,37 +13,75 @@ enum Api { root = '/system/tenant/package', } +/** + * 租户套餐分页列表 + * @param params 请求参数 + * @returns 分页列表 + */ export function packageList(params?: PageQuery) { return requestClient.get>(Api.packageList, { params, }); } -// 下拉框 +/** + * 租户套餐下拉框 + * @returns 下拉框 + */ export function packageSelectList() { return requestClient.get(Api.packageSelectList); } -export function packageExport(data: any) { +/** + * 租户套餐导出 + * @param data 参数 + * @returns blob + */ +export function packageExport(data: Partial) { return commonExport(Api.packageExport, data); } +/** + * 租户套餐信息 + * @param id id + * @returns 信息 + */ export function packageInfo(id: ID) { return requestClient.get(`${Api.root}/${id}`); } -export function packageAdd(data: any) { +/** + * 租户套餐新增 + * @param data data + * @returns void + */ +export function packageAdd(data: Partial) { return requestClient.postWithMsg(Api.root, data); } -export function packageUpdate(data: any) { +/** + * 租户套餐更新 + * @param data data + * @returns void + */ +export function packageUpdate(data: Partial) { return requestClient.putWithMsg(Api.root, data); } -export function packageChangeStatus(data: any) { - return requestClient.putWithMsg(Api.packageChangeStatus, data); +/** + * 租户套餐状态变更 + * @param data data + * @returns void + */ +export function packageChangeStatus(data: Partial) { + return requestClient.putWithMsg(Api.packageChangeStatus, data); } +/** + * 租户套餐移除 + * @param ids ids + * @returns void + */ export function packageRemove(ids: IDS) { - return requestClient.deleteWithMsg(`${Api.root}/${ids}`); + return requestClient.deleteWithMsg(`${Api.root}/${ids}`); } diff --git a/apps/web-antd/src/api/system/tenant/index.ts b/apps/web-antd/src/api/system/tenant/index.ts index f5c6505e..e0269b23 100644 --- a/apps/web-antd/src/api/system/tenant/index.ts +++ b/apps/web-antd/src/api/system/tenant/index.ts @@ -16,14 +16,29 @@ enum Api { tenantSyncPackage = '/system/tenant/syncTenantPackage', } +/** + * 查询租户分页列表 + * @param params 参数 + * @returns 分页 + */ export function tenantList(params?: PageQuery) { return requestClient.get(Api.tenantList, { params }); } -export function tenantExport(data: any) { +/** + * 租户导出 + * @param data data + * @returns void + */ +export function tenantExport(data: Partial) { return commonExport(Api.tenantExport, data); } +/** + * 查询租户信息 + * @param id id + * @returns 租户信息 + */ export function tenantInfo(id: ID) { return requestClient.get(`${Api.root}/${id}`); } @@ -33,18 +48,33 @@ export function tenantInfo(id: ID) { * @param data data * @returns void */ -export function tenantAdd(data: any) { +export function tenantAdd(data: Partial) { return requestClient.postWithMsg(Api.root, data, { encrypt: true }); } -export function tenantUpdate(data: any) { +/** + * 租户更新 + * @param data data + * @returns void + */ +export function tenantUpdate(data: Partial) { return requestClient.putWithMsg(Api.root, data); } -export function tenantStatusChange(data: any) { +/** + * 租户状态更新 + * @param data data + * @returns void + */ +export function tenantStatusChange(data: Partial) { return requestClient.putWithMsg(Api.tenantStatus, data); } +/** + * 租户删除 + * @param ids ids + * @returns void + */ export function tenantRemove(ids: IDS) { return requestClient.deleteWithMsg(`${Api.root}/${ids}`); } @@ -70,17 +100,12 @@ export function tenantDynamicClear() { * 租户套餐同步 * @param tenantId 租户id * @param packageId 套餐id - * @param showMsg 是否显示成功信息 * @returns void */ -export function tenantSyncPackage( - tenantId: string, - packageId: string, - showMsg = true, -) { +export function tenantSyncPackage(tenantId: string, packageId: string) { return requestClient.get(Api.tenantSyncPackage, { params: { packageId, tenantId }, - successMessageMode: showMsg ? 'message' : 'none', + successMessageMode: 'message', }); } diff --git a/apps/web-antd/src/api/system/tenant/model.d.ts b/apps/web-antd/src/api/system/tenant/model.d.ts index 309ad587..db6121c3 100644 --- a/apps/web-antd/src/api/system/tenant/model.d.ts +++ b/apps/web-antd/src/api/system/tenant/model.d.ts @@ -9,7 +9,7 @@ export interface Tenant { id: number; intro: string; licenseNumber?: any; - packageId?: string; + packageId: string; remark?: string; status: string; tenantId: string; diff --git a/apps/web-antd/src/api/system/user/index.ts b/apps/web-antd/src/api/system/user/index.ts index ff2a3d52..ed1b20c4 100644 --- a/apps/web-antd/src/api/system/user/index.ts +++ b/apps/web-antd/src/api/system/user/index.ts @@ -38,7 +38,7 @@ export function userList(params?: PageQuery) { * @param data data * @returns blob */ -export function userExport(data: any) { +export function userExport(data: Partial) { return commonExport(Api.userExport, data); } @@ -91,7 +91,7 @@ export function findUserInfo(userId?: ID) { * @param data data * @returns void */ -export function userAdd(data: any) { +export function userAdd(data: Partial) { return requestClient.postWithMsg(Api.root, data); } @@ -100,7 +100,7 @@ export function userAdd(data: any) { * @param data data * @returns void */ -export function userUpdate(data: any) { +export function userUpdate(data: Partial) { return requestClient.putWithMsg(Api.root, data); } @@ -109,7 +109,7 @@ export function userUpdate(data: any) { * @param data data * @returns void */ -export function userStatusChange(data: any) { +export function userStatusChange(data: Partial) { return requestClient.putWithMsg(Api.userStatusChange, data); } diff --git a/apps/web-antd/src/api/tool/gen/index.ts b/apps/web-antd/src/api/tool/gen/index.ts index 0514d3e0..127b962c 100644 --- a/apps/web-antd/src/api/tool/gen/index.ts +++ b/apps/web-antd/src/api/tool/gen/index.ts @@ -1,6 +1,6 @@ import type { GenInfo } from './model'; -import type { ID, IDS } from '#/api/common'; +import type { ID, IDS, PageQuery } from '#/api/common'; import { ContentTypeEnum } from '#/api/helper'; import { requestClient } from '#/api/request'; @@ -19,7 +19,7 @@ enum Api { syncDb = '/tool/gen/synchDb', } // 查询代码生成列表 -export function generatedList(params: any) { +export function generatedList(params?: PageQuery) { return requestClient.get(Api.generatedList, { params }); } @@ -29,7 +29,7 @@ export function genInfo(tableId: ID) { } // 查询数据库列表 -export function readyToGenList(params: any) { +export function readyToGenList(params?: PageQuery) { return requestClient.get(Api.readyToGenList, { params }); } @@ -63,6 +63,7 @@ export function editSave(data: any) { export function genRemove(tableIds: IDS) { return requestClient.deleteWithMsg(`${Api.root}/${tableIds}`); } + // 预览代码 export function previewCode(tableId: ID) { return requestClient.get<{ [key: string]: string }>( diff --git a/apps/web-antd/src/api/workflow/category/index.ts b/apps/web-antd/src/api/workflow/category/index.ts index 3497c749..8126effd 100644 --- a/apps/web-antd/src/api/workflow/category/index.ts +++ b/apps/web-antd/src/api/workflow/category/index.ts @@ -1,9 +1,22 @@ -import type { CategoryForm, CategoryQuery, CategoryVO } from './model'; +import type { + CategoryForm, + CategoryQuery, + CategoryTree, + CategoryVO, +} from './model'; import type { ID, IDS } from '#/api/common'; import { requestClient } from '#/api/request'; +/** + * 获取流程分类树列表 + * @returns tree + */ +export function categoryTree() { + return requestClient.get('/workflow/category/categoryTree'); +} + /** * 查询流程分类列表 * @param params diff --git a/apps/web-antd/src/api/workflow/category/model.d.ts b/apps/web-antd/src/api/workflow/category/model.d.ts index 87937ec8..7d28c1a6 100644 --- a/apps/web-antd/src/api/workflow/category/model.d.ts +++ b/apps/web-antd/src/api/workflow/category/model.d.ts @@ -30,6 +30,7 @@ export interface CategoryVO { * 子对象 */ children: CategoryVO[]; + key: string; } export interface CategoryForm extends BaseEntity { @@ -85,3 +86,12 @@ export interface CategoryQuery { */ params?: any; } + +export interface CategoryTree { + id: number; + parentId: number; + label: string; + weight: number; + children: CategoryTree[]; + key: string; +} diff --git a/apps/web-antd/src/api/workflow/definition/index.ts b/apps/web-antd/src/api/workflow/definition/index.ts new file mode 100644 index 00000000..e21255c3 --- /dev/null +++ b/apps/web-antd/src/api/workflow/definition/index.ts @@ -0,0 +1,155 @@ +import type { ProcessDefinition } from './model'; + +import type { ID, IDS, PageQuery, PageResult } from '#/api/common'; + +import { requestClient } from '#/api/request'; + +/** + * 全部的流程定义 + * @param params 查询参数 + * @returns 分页 + */ +export function workflowDefinitionList(params?: PageQuery) { + return requestClient.get>( + '/workflow/definition/list', + { params }, + ); +} + +/** + * 未发布的流程定义 + * @param params 查询参数 + * @returns 分页 + */ +export function unPublishList(params?: PageQuery) { + return requestClient.get>( + '/workflow/definition/unPublishList', + { params }, + ); +} + +/** + * 获取历史流程定义列表 + * @param flowCode + * @returns ProcessDefinition[] + */ +export function getHisListByKey(flowCode: string) { + return requestClient.get( + `/workflow/definition/getHisListByKey/${flowCode}`, + ); +} + +/** + * 获取流程定义详细信息 + * @param id id + * @returns ProcessDefinition + */ +export function workflowDefinitionInfo(id: ID) { + return requestClient.get(`/workflow/definition/${id}`); +} + +/** + * 新增流程定义 + * @param data + */ +export function workflowDefinitionAdd(data: any) { + return requestClient.postWithMsg('/workflow/definition', data); +} + +/** + * 更新流程定义 + * @param data + */ +export function workflowDefinitionUpdate(data: any) { + return requestClient.putWithMsg('/workflow/definition', data); +} + +/** + * 发布流程定义 + * @param id id + * @returns boolean + */ +export function workflowDefinitionPublish(id: ID) { + return requestClient.putWithMsg( + `/workflow/definition/publish/${id}`, + ); +} + +/** + * 取消发布流程定义 + * @param id id + * @returns boolean + */ +export function workflowDefinitionUnPublish(id: ID) { + return requestClient.putWithMsg( + `/workflow/definition/unPublish/${id}`, + ); +} + +/** + * 删除流程定义 + * @param ids idList + */ +export function workflowDefinitionDelete(ids: IDS) { + return requestClient.deleteWithMsg(`/workflow/definition/${ids}`); +} + +/** + * 复制流程定义 + * @param id id + */ +export function workflowDefinitionCopy(id: ID) { + return requestClient.postWithMsg(`/workflow/definition/copy/${id}`); +} + +/** + * 导入流程定义 + * @returns boolean + */ +export function workflowDefinitionImport(data: { + category: ID; + file: Blob | File; +}) { + return requestClient.postWithMsg( + '/workflow/definition/importDef', + data, + { headers: { 'Content-Type': 'multipart/form-data' } }, + ); +} + +/** + * 导出流程定义 + * @param id id + * @returns blob + */ +export function workflowDefinitionExport(id: ID) { + return requestClient.postWithMsg( + `/workflow/definition/exportDef/${id}`, + {}, + { + responseType: 'blob', + isTransformResponse: false, + }, + ); +} + +/** + * 获取流程定义xml字符串 + * @param id id + * @returns xml + */ +export function workflowDefinitionXml(id: ID) { + return requestClient.get(`/workflow/definition/xmlString/${id}`); +} + +/** + * 激活/挂起流程定义 + * @param id 流程定义id + * @param active 激活/挂起 + * @returns boolean + */ +export function workflowDefinitionActive(id: ID, active: boolean) { + return requestClient.putWithMsg( + `/workflow/definition/active/${id}?active=${active}`, + ); +} diff --git a/apps/web-antd/src/api/workflow/definition/model.d.ts b/apps/web-antd/src/api/workflow/definition/model.d.ts new file mode 100644 index 00000000..b5eeed9c --- /dev/null +++ b/apps/web-antd/src/api/workflow/definition/model.d.ts @@ -0,0 +1,19 @@ +export interface ProcessDefinition { + id: string; + createTime: string; + updateTime: string; + tenantId: string; + delFlag: string; + flowCode: string; + flowName: string; + category: string; + categoryName: string; + version: string; + isPublish: number; + formCustom: string; + formPath: string; + activityStatus: number; + listenerType?: any; + listenerPath?: any; + ext?: any; +} diff --git a/apps/web-antd/src/api/workflow/instance/index.ts b/apps/web-antd/src/api/workflow/instance/index.ts new file mode 100644 index 00000000..774f3999 --- /dev/null +++ b/apps/web-antd/src/api/workflow/instance/index.ts @@ -0,0 +1,120 @@ +import type { TaskInfo } from '../task/model'; +import type { FlowInfoResponse } from './model'; + +import type { ID, IDS, PageQuery, PageResult } from '#/api/common'; + +import { requestClient } from '#/api/request'; + +/** + * @param businessId 业务ID + * @returns TaskInfo + */ +export function getTaskByBusinessId(businessId: string) { + return requestClient.get( + `/workflow/instance/getInfo/${businessId}`, + ); +} + +/** + * 分页查询正在运行的流程实例 + * @param params + * @returns + */ +export function pageByRunning(params?: PageQuery) { + return requestClient.get('/workflow/instance/pageByRunning', { params }); +} + +/** + * pageByFinish + * @param params + * @returns + */ +export function pageByFinish(params?: PageQuery) { + return requestClient.get('/workflow/instance/pageByFinish', { params }); +} + +/** + * 按照业务id删除流程实例 + * @param businessIds 业务id + */ +export function deleteByBusinessIds(businessIds: IDS) { + return requestClient.deleteWithMsg( + `/workflow/instance/deleteByBusinessIds${businessIds}`, + ); +} + +/** + * 按照实例id删除流程实例 + * @param instanceIds 实例id + */ +export function deleteByInstanceIds(instanceIds: IDS) { + return requestClient.deleteWithMsg( + `/workflow/instance/deleteByInstanceIds/${instanceIds}`, + ); +} + +/** + * 撤销流程 + * @param data + */ +export function cancelProcessApply(data: { businessId: ID; message?: string }) { + return requestClient.putWithMsg( + '/workflow/instance/cancelProcessApply', + data, + ); +} + +/** + * 激活/挂起流程实例 + * @param instanceId + * @param active + */ +export function workflowInstanceActive(instanceId: ID, active: boolean) { + return requestClient.putWithMsg( + `/workflow/instance/active/${instanceId}?active=${active}`, + ); +} + +/** + * 获取当前登录人发起的流程实例 + * @param params + * @returns PageResult + */ +export function pageByCurrent(params?: PageQuery) { + return requestClient.get>( + '/workflow/instance/pageByCurrent', + { params }, + ); +} + +/** + * 获取流程图,流程记录 + * @param businessId 业务标识 + * @returns 流程图,流程记录 + */ +export function flowInfo(businessId: string) { + return requestClient.get( + `/workflow/instance/flowImage/${businessId}`, + ); +} + +/** + * 获取流程变量 + * @param instanceId + * @returns Map + */ +export function instanceVariable(instanceId: string) { + return requestClient.get>( + `/workflow/instance/variable/${instanceId}`, + ); +} + +/** + * 作废流程 + */ +export function workflowInstanceInvalid(data: { + comment?: string; + id: string; +}) { + return requestClient.postWithMsg('/workflow/instance/invalid', data); +} diff --git a/apps/web-antd/src/api/workflow/instance/model.d.ts b/apps/web-antd/src/api/workflow/instance/model.d.ts new file mode 100644 index 00000000..8ef4b22e --- /dev/null +++ b/apps/web-antd/src/api/workflow/instance/model.d.ts @@ -0,0 +1,41 @@ +export interface Flow { + id: string; + createTime: string; + updateTime: string; + tenantId: string; + delFlag: string; + definitionId: string; + flowName?: any; + instanceId: string; + taskId: string; + cooperateType: number; + cooperateTypeName: string; + businessId?: any; + nodeCode: string; + nodeName: string; + nodeType: number; + targetNodeCode: string; + targetNodeName: string; + approver: string; + approveName: string; + collaborator?: any; + permissionList?: any; + skipType: string; + flowStatus: string; + flowTaskStatus?: any; + flowStatusName?: any; + message: string; + ext: null | string; + createBy?: any; + formCustom: string; + formPath: string; + flowCode?: any; + version?: any; + runDuration: string; + nickName?: any; +} + +export interface FlowInfoResponse { + image: string; + list: Flow[]; +} diff --git a/apps/web-antd/src/api/workflow/task/index.ts b/apps/web-antd/src/api/workflow/task/index.ts new file mode 100644 index 00000000..ae4cc407 --- /dev/null +++ b/apps/web-antd/src/api/workflow/task/index.ts @@ -0,0 +1,158 @@ +import type { + CompleteTaskReqData, + StartWorkFlowReqData, + TaskInfo, + TaskOperationData, + TaskOperationType, +} from './model'; + +import type { ID, IDS, PageQuery, PageResult } from '#/api/common'; + +import { requestClient } from '#/api/request'; + +/** + * 启动任务 + * @param data + */ +export function startWorkFlow(data: StartWorkFlowReqData) { + return requestClient.post<{ + processInstanceId: string; + taskId: string; + }>('/workflow/task/startWorkFlow', data); +} + +/** + * 办理任务 + * @param data + */ +export function completeTask(data: CompleteTaskReqData) { + return requestClient.postWithMsg('/workflow/task/completeTask', data); +} + +/** + * 查询当前用户的待办任务 + * @param params + */ +export function pageByTaskWait(params?: PageQuery) { + return requestClient.get>( + '/workflow/task/pageByTaskWait', + { params }, + ); +} + +/** + * 查询当前用户的已办任务 + * @param params + */ +export function pageByTaskFinish(params?: PageQuery) { + return requestClient.get>( + '/workflow/task/pageByTaskFinish', + { params }, + ); +} + +/** + * 查询所有待办任务 + * @param params + */ +export function pageByAllTaskWait(params?: PageQuery) { + return requestClient.get>( + '/workflow/task/pageByAllTaskWait', + { params }, + ); +} + +/** + * 查询已办任务 + * @param params + */ +export function pageByAllTaskFinish(params?: PageQuery) { + return requestClient.get>( + '/workflow/task/pageByAllTaskFinish', + { params }, + ); +} + +/** + * 查询当前用户的抄送 + * @param params + */ +export function pageByTaskCopy(params?: PageQuery) { + return requestClient.get>( + '/workflow/task/pageByTaskCopy', + { params }, + ); +} + +/** + * 根据taskId查询代表任务 + * @param taskId 任务id + * @returns info + */ +export function getTaskByTaskId(taskId: string) { + return requestClient.get(`/workflow/task/getTask/${taskId}`); +} + +/** + * 终止任务 + */ +export function terminationTask(data: { comment?: string; taskId: string }) { + return requestClient.postWithMsg( + '/workflow/task/terminationTask', + data, + ); +} + +/** + * 任务操作 + * @param taskOperationData 参数 + * @param taskOperation 操作类型,委派 delegateTask、转办 transferTask、加签 addSignature、减签 reductionSignature + */ +export function taskOperation( + taskOperationData: TaskOperationData, + taskOperation: TaskOperationType, +) { + return requestClient.postWithMsg( + `/workflow/task/taskOperation/${taskOperation}`, + taskOperationData, + ); +} + +/** + * 修改任务办理人 + * @param taskIdList 任务id + * @param userId 办理人id + */ +export function updateAssignee(taskIdList: IDS, userId: ID) { + return requestClient.putWithMsg( + `/workflow/task/updateAssignee/${userId}`, + taskIdList, + ); +} + +/** + * 驳回审批 + * @param data 参数 + */ +export function backProcess(data: any) { + return requestClient.postWithMsg('/workflow/task/backProcess', data); +} + +/** + * 获取可驳回节点 + * @param definitionId 流程定义ID + * @param nodeCode 当前节点编码 + */ +export function getBackTaskNode(definitionId: string, nodeCode: string) { + return requestClient.get<{ nodeCode: string; nodeName: string }[]>( + `/workflow/task/getBackTaskNode/${definitionId}/${nodeCode}`, + ); +} + +/** + * 获取当前任务的所有办理人 + * @param taskId 任务id + */ +export function currentTaskAllUser(taskId: ID) { + return requestClient.get(`/workflow/task/currentTaskAllUser/${taskId}`); +} diff --git a/apps/web-antd/src/api/workflow/task/model.d.ts b/apps/web-antd/src/api/workflow/task/model.d.ts new file mode 100644 index 00000000..8322e2b7 --- /dev/null +++ b/apps/web-antd/src/api/workflow/task/model.d.ts @@ -0,0 +1,74 @@ +export interface TaskInfo { + id: string; + categoryName: string; + createTime: string; + updateTime: string; + tenantId: string; + delFlag?: any; + definitionId: string; + instanceId: string; + flowName: string; + businessId: string; + nodeCode: string; + nodeName: string; + nodeType: number; + permissionList?: any; + userList?: any; + formCustom: string; + formPath?: any; + flowCode: string; + version: string; + flowStatus: string; + flowStatusName: string; + assigneeIds: string; + assigneeNames: string; + processedBy: string; + type: string; + nodeRatio?: string; + createBy: string; + createByName: string; + targetNodeName?: string; +} + +export interface CompleteTaskReqData { + messageType: string[]; + flowCopyList: { userId: string; userName: string }[]; + taskId: ID; + taskVariables: Record; + variables: any; + // 附件ID 1,2,3,4形式 + fileId?: string; +} + +export interface StartWorkFlowReqData { + /** + * 业务ID + */ + businessId: ID; + /** + * flowCode + */ + flowCode: string; + /** + * 流程变量 + */ + variables: Record; +} + +export interface TaskOperationData { + message?: string; + taskId: ID; + // 单个操作人 + userId?: ID; + // 多个操作人 + userIds?: IDS; +} + +/** + * 操作类型,委派 delegateTask、转办 transferTask、加签 addSignature、减签 reductionSignature + */ +export type TaskOperationType = + | 'addSignature' + | 'delegateTask' + | 'reductionSignature' + | 'transferTask'; diff --git a/apps/web-antd/src/bootstrap.ts b/apps/web-antd/src/bootstrap.ts index f842021c..b2d106e8 100644 --- a/apps/web-antd/src/bootstrap.ts +++ b/apps/web-antd/src/bootstrap.ts @@ -1,6 +1,7 @@ import { createApp, watchEffect } from 'vue'; import { registerAccessDirective } from '@vben/access'; +import { initTippy } from '@vben/common-ui'; import { preferences } from '@vben/preferences'; import { initStores } from '@vben/stores'; import '@vben/styles'; @@ -19,6 +20,15 @@ async function bootstrap(namespace: string) { // 初始化组件适配器 await initComponentAdapter(); + // // 设置弹窗的默认配置 + // setDefaultModalProps({ + // fullscreenButton: false, + // }); + // // 设置抽屉的默认配置 + // setDefaultDrawerProps({ + // zIndex: 1020, + // }); + const app = createApp(App); // 全局组件 @@ -33,6 +43,9 @@ async function bootstrap(namespace: string) { // 安装权限指令 registerAccessDirective(app); + // 初始化 tippy + initTippy(app); + // 配置路由及路由守卫 app.use(router); diff --git a/apps/web-antd/src/components/cropper/index.ts b/apps/web-antd/src/components/cropper/index.ts index b74a1272..6afad017 100644 --- a/apps/web-antd/src/components/cropper/index.ts +++ b/apps/web-antd/src/components/cropper/index.ts @@ -1,4 +1,3 @@ -export { default as CropperImage } from './src/cropper.vue'; - export { default as CropperAvatar } from './src/cropper-avatar.vue'; +export { default as CropperImage } from './src/cropper.vue'; export type { Cropper } from './src/typing'; diff --git a/apps/web-antd/src/components/cropper/src/cropper-avatar.vue b/apps/web-antd/src/components/cropper/src/cropper-avatar.vue index 5e17d2f2..a29aab2e 100644 --- a/apps/web-antd/src/components/cropper/src/cropper-avatar.vue +++ b/apps/web-antd/src/components/cropper/src/cropper-avatar.vue @@ -1,18 +1,14 @@ + + diff --git a/apps/web-antd/src/components/tinymce/src/editor.vue b/apps/web-antd/src/components/tinymce/src/editor.vue index 05950305..d3511d10 100644 --- a/apps/web-antd/src/components/tinymce/src/editor.vue +++ b/apps/web-antd/src/components/tinymce/src/editor.vue @@ -2,6 +2,10 @@ import type { IPropTypes } from '@tinymce/tinymce-vue/lib/cjs/main/ts/components/EditorPropTypes'; import type { Editor as EditorType } from 'tinymce/tinymce'; +import type { PropType } from 'vue'; + +import type { UploadResult } from '#/api/core/upload'; + import { computed, nextTick, @@ -9,7 +13,6 @@ import { onBeforeUnmount, onDeactivated, onMounted, - type PropType, ref, unref, useAttrs, @@ -22,7 +25,7 @@ import { buildShortUUID } from '@vben/utils'; import Editor from '@tinymce/tinymce-vue'; import { isNumber } from 'lodash-es'; -import { uploadApi, type UploadResult } from '#/api/core/upload'; +import { uploadApi } from '#/api/core/upload'; import { bindHandlers } from './helper'; import ImgUpload from './img-upload.vue'; diff --git a/apps/web-antd/src/components/tinymce/src/helper.ts b/apps/web-antd/src/components/tinymce/src/helper.ts index e169f501..1f98dda4 100644 --- a/apps/web-antd/src/components/tinymce/src/helper.ts +++ b/apps/web-antd/src/components/tinymce/src/helper.ts @@ -4,9 +4,9 @@ const validEvents = new Set([ 'onBeforeAddUndo', 'onBeforeExecCommand', 'onBeforeGetContent', + 'onBeforePaste', 'onBeforeRenderUI', 'onBeforeSetContent', - 'onBeforePaste', 'onBlur', 'onChange', 'onClearUndos', @@ -42,8 +42,8 @@ const validEvents = new Set([ 'onMouseOver', 'onMouseUp', 'onNodeChange', - 'onObjectResizeStart', 'onObjectResized', + 'onObjectResizeStart', 'onObjectSelected', 'onPaste', 'onPostProcess', diff --git a/apps/web-antd/src/components/tree/index.ts b/apps/web-antd/src/components/tree/index.ts index 3dfcd39a..f142790a 100644 --- a/apps/web-antd/src/components/tree/index.ts +++ b/apps/web-antd/src/components/tree/index.ts @@ -1 +1,2 @@ +export { default as MenuSelectTable } from './src/menu-select-table.vue'; export { default as TreeSelectPanel } from './src/tree-select-panel.vue'; diff --git a/apps/web-antd/src/components/tree/src/data.tsx b/apps/web-antd/src/components/tree/src/data.tsx new file mode 100644 index 00000000..2bbaf317 --- /dev/null +++ b/apps/web-antd/src/components/tree/src/data.tsx @@ -0,0 +1,85 @@ +import type { VxeGridProps } from '#/adapter/vxe-table'; +import type { ID } from '#/api/common'; +import type { MenuOption } from '#/api/system/menu/model'; + +import { h, markRaw } from 'vue'; + +import { FolderIcon, MenuIcon, OkButtonIcon, VbenIcon } from '@vben/icons'; + +export interface Permission { + checked: boolean; + id: ID; + label: string; +} + +export interface MenuPermissionOption extends MenuOption { + permissions: Permission[]; +} + +const menuTypes = { + C: { icon: markRaw(MenuIcon), value: '菜单' }, + F: { icon: markRaw(OkButtonIcon), value: '按钮' }, + M: { icon: markRaw(FolderIcon), value: '目录' }, +}; + +export const nodeOptions = [ + { label: '节点关联', value: true }, + { label: '节点独立', value: false }, +]; + +export const columns: VxeGridProps['columns'] = [ + { + type: 'checkbox', + title: '菜单名称', + field: 'label', + treeNode: true, + headerAlign: 'left', + align: 'left', + width: 230, + }, + { + title: '图标', + field: 'icon', + width: 80, + slots: { + default: ({ row }) => { + if (row?.icon === '#') { + return ''; + } + return ( + + + + ); + }, + }, + }, + { + title: '类型', + field: 'menuType', + width: 80, + slots: { + default: ({ row }) => { + const current = menuTypes[row.menuType as 'C' | 'F' | 'M']; + if (!current) { + return '未知'; + } + return ( + + {h(current.icon, { class: 'size-[18px]' })} + {current.value} + + ); + }, + }, + }, + { + title: '权限标识', + field: 'permissions', + headerAlign: 'left', + align: 'left', + slots: { + default: 'permissions', + }, + }, +]; diff --git a/apps/web-antd/src/components/tree/src/helper.tsx b/apps/web-antd/src/components/tree/src/helper.tsx new file mode 100644 index 00000000..9ce64a21 --- /dev/null +++ b/apps/web-antd/src/components/tree/src/helper.tsx @@ -0,0 +1,133 @@ +import type { MenuPermissionOption } from './data'; + +import type { useVbenVxeGrid } from '#/adapter/vxe-table'; +import type { MenuOption } from '#/api/system/menu/model'; + +import { eachTree, treeToList } from '@vben/utils'; + +import { difference, isEmpty, isUndefined } from 'lodash-es'; + +/** + * 权限列设置是否全选 + * @param record 行记录 + * @param checked 是否选中 + */ +export function setPermissionsChecked( + record: MenuPermissionOption, + checked: boolean, +) { + if (record?.permissions?.length > 0) { + // 全部设置为选中 + record.permissions.forEach((permission) => { + permission.checked = checked; + }); + } +} + +/** + * 设置当前行 & 所有子节点选中状态 + * @param record 行 + * @param checked 是否选中 + */ +export function rowAndChildrenChecked( + record: MenuPermissionOption, + checked: boolean, +) { + // 当前行选中 + setPermissionsChecked(record, checked); + // 所有子节点选中 + record?.children?.forEach?.((permission) => { + rowAndChildrenChecked(permission as MenuPermissionOption, checked); + }); +} + +/** + * void方法 会直接修改原始数据 + * 将树结构转为 tree+permissions结构 + * @param menus 后台返回的menu + */ +export function menusWithPermissions(menus: MenuOption[]) { + eachTree(menus, (item: MenuPermissionOption) => { + if (item.children && item.children.length > 0) { + /** + * 所有为按钮的节点提取出来 + * 需要注意 这里需要过滤目录下直接是按钮的情况item.menuType !== 'M' + * 将按钮往children添加而非加到permissions + */ + const permissions = item.children.filter( + (child: MenuOption) => child.menuType === 'F' && item.menuType !== 'M', + ); + // 取差集 + const diffCollection = difference(item.children, permissions); + // 更新后的children 即去除按钮 + item.children = diffCollection; + + // permissions作为字段添加到item + const permissionsArr = permissions.map((permission) => { + return { + id: permission.id, + label: permission.label, + checked: false, + }; + }); + item.permissions = permissionsArr; + } + }); +} + +/** + * 设置表格选中 + * @param checkedKeys 选中的keys + * @param menus 菜单 转换后的菜单 + * @param tableApi api + * @param association 是否节点关联 + */ +export function setTableChecked( + checkedKeys: (number | string)[], + menus: MenuPermissionOption[], + tableApi: ReturnType['1'], + association: boolean, +) { + // tree转list + const menuList: MenuPermissionOption[] = treeToList(menus); + // 拿到勾选的行数据 + let checkedRows = menuList.filter((item) => checkedKeys.includes(item.id)); + + /** + * 节点独立切换到节点关联 只需要最末尾的数据 即children为空 + */ + if (!association) { + checkedRows = checkedRows.filter( + (item) => isUndefined(item.children) || isEmpty(item.children), + ); + } + + // 设置行选中 & permissions选中 + checkedRows.forEach((item) => { + tableApi.grid.setCheckboxRow(item, true); + if (item?.permissions?.length > 0) { + item.permissions.forEach((permission) => { + if (checkedKeys.includes(permission.id)) { + permission.checked = true; + } + }); + } + }); + + /** + * 节点独立切换到节点关联 + * 勾选后还需要过滤权限没有任何勾选的情况 这时候取消行的勾选 + */ + if (!association) { + const emptyRows = checkedRows.filter((item) => { + if (isUndefined(item.permissions) || isEmpty(item.permissions)) { + return false; + } + return item.permissions.every( + (permission) => permission.checked === false, + ); + }); + // 设置为不选中 + tableApi.grid.setCheckboxRow(emptyRows, false); + } +} diff --git a/apps/web-antd/src/components/tree/src/hook.tsx b/apps/web-antd/src/components/tree/src/hook.tsx new file mode 100644 index 00000000..b7a70ad8 --- /dev/null +++ b/apps/web-antd/src/components/tree/src/hook.tsx @@ -0,0 +1,62 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import type { TourProps } from 'ant-design-vue'; + +import { defineComponent, ref } from 'vue'; + +import { useLocalStorage } from '@vueuse/core'; +import { Tour } from 'ant-design-vue'; + +/** + * 全屏引导 + * @returns value + */ +export function useFullScreenGuide() { + const open = ref(false); + /** + * 是否已读 只显示一次 + */ + const read = useLocalStorage('menu_select_fullscreen_read', false); + + function openGuide() { + if (!read.value) { + open.value = true; + } + } + + function closeGuide() { + open.value = false; + read.value = true; + } + + const steps: TourProps['steps'] = [ + { + title: '提示', + description: '点击这里可以全屏', + target: () => + document.querySelector( + 'div#menu-select-table .vxe-tools--operate > button[title="全屏"]', + )!, + }, + ]; + + const FullScreenGuide = defineComponent({ + name: 'FullScreenGuide', + inheritAttrs: false, + setup() { + return () => ( + + ); + }, + }); + + return { + FullScreenGuide, + openGuide, + closeGuide, + }; +} diff --git a/apps/web-antd/src/components/tree/src/menu-select-table.vue b/apps/web-antd/src/components/tree/src/menu-select-table.vue new file mode 100644 index 00000000..a8a570b6 --- /dev/null +++ b/apps/web-antd/src/components/tree/src/menu-select-table.vue @@ -0,0 +1,412 @@ + + + + + + diff --git a/apps/web-antd/src/components/tree/src/tree-select-panel.vue b/apps/web-antd/src/components/tree/src/tree-select-panel.vue index 9a2a7744..e7597f91 100644 --- a/apps/web-antd/src/components/tree/src/tree-select-panel.vue +++ b/apps/web-antd/src/components/tree/src/tree-select-panel.vue @@ -3,7 +3,9 @@ import type { CheckboxChangeEvent } from 'ant-design-vue/es/checkbox/interface'; import type { DataNode } from 'ant-design-vue/es/tree'; import type { CheckInfo } from 'ant-design-vue/es/vc-tree/props'; -import { computed, nextTick, onMounted, type PropType, ref, watch } from 'vue'; +import type { PropType, SetupContext } from 'vue'; + +import { computed, nextTick, onMounted, ref, useSlots, watch } from 'vue'; import { findGroupParentIds, treeToList } from '@vben/utils'; @@ -108,8 +110,8 @@ const stop = watch([checkedKeys, () => props.treeData], () => { * @param info info.halfCheckedKeys为父节点的ID */ type CheckedState = - | { checked: T[]; halfChecked: T[] } - | T[]; + | T[] + | { checked: T[]; halfChecked: T[] }; function handleChecked(checkedStateKeys: CheckedState, info: CheckInfo) { // 数组的话为节点关联 if (Array.isArray(checkedStateKeys)) { @@ -157,6 +159,8 @@ onMounted(async () => { expandedKeys.value = allKeys.value; } }); + +const slots = useSlots() as SetupContext['slots'];