mirror of
https://github.com/m-xlsea/ruoyi-plus-soybean.git
synced 2025-09-24 07:49:47 +08:00
Compare commits
8 Commits
flow
...
8aeb73627a
Author | SHA1 | Date | |
---|---|---|---|
8aeb73627a | |||
3f148a4e62 | |||
34ab7d5da2 | |||
513dc31eaa | |||
dc2fbbd556 | |||
56fd5434ca | |||
ad207255bb | |||
3146c039f0 |
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -31,7 +31,8 @@
|
|||||||
"vue.server.hybridMode": true,
|
"vue.server.hybridMode": true,
|
||||||
"files.exclude": { "/docs": true },
|
"files.exclude": { "/docs": true },
|
||||||
"search.exclude": {
|
"search.exclude": {
|
||||||
"/docs": true
|
"/docs": true,
|
||||||
|
"**/dist/**": true
|
||||||
},
|
},
|
||||||
"cSpell.words": ["Axios", "tinymce"]
|
"cSpell.words": ["Axios", "tinymce"]
|
||||||
}
|
}
|
||||||
|
70
CHANGELOG.md
70
CHANGELOG.md
@ -2,55 +2,15 @@
|
|||||||
|
|
||||||
## [v1.1.3](https://gitee.com/xlsea/ruoyi-plus-soybean/compare/v1.1.2...v1.1.3) (2025-08-16)
|
## [v1.1.3](https://gitee.com/xlsea/ruoyi-plus-soybean/compare/v1.1.2...v1.1.3) (2025-08-16)
|
||||||
|
|
||||||
### 🚀 新功能
|
|
||||||
|
|
||||||
- 对接工作流分类模块 - by **AN** [<samp>(25790)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/25790d4b)
|
|
||||||
- 新增流程定义页面 - by @m-xlsea [<samp>(11aba)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/11aba9e2)
|
|
||||||
- 添加流程实例功能 - by **AN** [<samp>(f9d57)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/f9d57f1b)
|
|
||||||
- 增加流程作废功能并优化按钮组件 - by **AN** [<samp>(350de)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/350de08f)
|
|
||||||
- 流程实例,查看变量功能 - by **AN** [<samp>(92b9c)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/92b9c213)
|
|
||||||
- 更新工作流分类选择组件,修复值回显问题并优化添加数据操作 - by **AN** [<samp>(14a29)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/14a29070)
|
|
||||||
- 添加请假申请功能 - by **AN** [<samp>(d7e05)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/d7e0516c)
|
|
||||||
- 更新请假申请功能,添加日期范围选择和请假天数自动计算 - by **AN** [<samp>(89a2a)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/89a2a6cb)
|
|
||||||
- 添加请假申请详情接口,优化请假操作表单样式 - by **AN** [<samp>(ab1d3)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/ab1d3a23)
|
|
||||||
- 更新请假申请表单,添加流程类型选择和流程启动功能 - by **AN** [<samp>(49521)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/49521b66)
|
|
||||||
- 更新工作流任务申请模态框样式,添加消息类型选择禁用功能 - by **AN** [<samp>(4614b)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/4614b977)
|
|
||||||
- **components**:
|
|
||||||
- 增强审批信息面板,优化附件处理 - by **AN** [<samp>(49224)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/49224afe)
|
|
||||||
- 面板添加流程图 - by **AN** [<samp>(ae5c7)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/ae5c7e83)
|
|
||||||
- **projects**:
|
|
||||||
- 优化组件,完成流程实例-流程预览 - by **AN** [<samp>(50e7b)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/50e7b515)
|
|
||||||
- 新增group-tag组件,待办任务查看功能 - by **AN** [<samp>(1c322)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/1c322e28)
|
|
||||||
- 新增用户选择器组件,添加流程干预按钮 - by **AN** [<samp>(b8c77)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/b8c771cd)
|
|
||||||
- 新增转办和终止功能 - by **AN** [<samp>(80faf)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/80faf4b4)
|
|
||||||
- 新增加签功能 - by **AN** [<samp>(55dce)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/55dceca2)
|
|
||||||
- 新增减签功能 - by **AN** [<samp>(f1d7b)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/f1d7b973)
|
|
||||||
- 新增 '我发起的' 功能 - by **AN** [<samp>(a77ed)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/a77edc2e)
|
|
||||||
- 新增抄送、下一审批人提交功能,优化组件通用性 - by **AN** [<samp>(523ac)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/523aca6b)
|
|
||||||
- 新增我的待办功能,新增审批,驳回组件 - by **AN** [<samp>(130ee)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/130ee1dc)
|
|
||||||
- 新增我的已办,我的抄送功能 - by **AN** [<samp>(01d42)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/01d42722)
|
|
||||||
- 补充搜索条件 - by **AN** [<samp>(b6c7b)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/b6c7b1b3)
|
|
||||||
- 新增流程表达式功能 - by **AN** [<samp>(d562f)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/d562f8c1)
|
|
||||||
- **types**:
|
|
||||||
- 补充类型定义 - by **AN** [<samp>(bd6b5)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/bd6b575a)
|
|
||||||
|
|
||||||
### 🐞 Bug 修复
|
### 🐞 Bug 修复
|
||||||
|
|
||||||
- 修复控制台报错:Message compilation error,i18n的@为特殊符号 - by **AN** [<samp>(1c646)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/1c646937)
|
|
||||||
- 修复运行状态变化时数据加载顺序问题 - by **AN** [<samp>(ed118)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/ed118069)
|
|
||||||
- 修复添加行时的操作顺序问题 - by **AN** [<samp>(f004e)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/f004e75c)
|
|
||||||
- **hooks**:
|
- **hooks**:
|
||||||
- 非安全环境下不使用流式下载 - by @m-xlsea [<samp>(f8983)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/f8983557)
|
- 非安全环境下不使用流式下载 - by @m-xlsea [<samp>(f8983)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/f8983557)
|
||||||
- 修复oss下载时未转码问题 - by **AN** [<samp>(2d31d)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/2d31d7dc)
|
- 修复oss下载时未转码问题 - by **AN** [<samp>(2d31d)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/2d31d7dc)
|
||||||
- **other**:
|
|
||||||
- 修复代码生成字典相关问题 - by @m-xlsea [<samp>(9aa65)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/9aa6597d)
|
|
||||||
- **project**:
|
- **project**:
|
||||||
- 关闭多租户功能后仍然遍历租户列表导致控制台报错的问题 - by **wang_rui** [<samp>(b96c4)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/b96c46ba)
|
- 关闭多租户功能后仍然遍历租户列表导致控制台报错的问题 - by **wang_rui** [<samp>(b96c4)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/b96c46ba)
|
||||||
- 关闭多租户功能后仍然遍历租户列表导致控制台报错的问题 Merge pull request !25 from littleghost2016/dev - by **不寻俗** [<samp>(90276)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/9027632b)
|
- 关闭多租户功能后仍然遍历租户列表导致控制台报错的问题 Merge pull request !25 from littleghost2016/dev - by **不寻俗** [<samp>(90276)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/9027632b)
|
||||||
- **projects**:
|
- **projects**:
|
||||||
- 修复更新后产生问题 - by **AN** [<samp>(400ea)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/400eaf89)
|
|
||||||
- 修复动态组件弹窗动画问题 - by @m-xlsea [<samp>(2e029)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/2e029929)
|
|
||||||
- 修复抽屉问题 - by **AN** [<samp>(a9c58)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/a9c58b25)
|
|
||||||
- 修复一级菜单隐藏失效问题 - by **AN** [<samp>(8fcc7)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/8fcc70d7)
|
- 修复一级菜单隐藏失效问题 - by **AN** [<samp>(8fcc7)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/8fcc70d7)
|
||||||
- 修复日期搜索条件清除问题 - by **AN** [<samp>(52318)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/52318c10)
|
- 修复日期搜索条件清除问题 - by **AN** [<samp>(52318)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/52318c10)
|
||||||
- 修复登录过期事件监听未被重置 - by @m-xlsea [<samp>(71037)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/71037439)
|
- 修复登录过期事件监听未被重置 - by @m-xlsea [<samp>(71037)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/71037439)
|
||||||
@ -61,43 +21,13 @@
|
|||||||
- **readme**:
|
- **readme**:
|
||||||
- update GitHub stars and forks links for gitee - by @soybeanjs [<samp>(923eb)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/923eb98a)
|
- update GitHub stars and forks links for gitee - by @soybeanjs [<samp>(923eb)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/923eb98a)
|
||||||
|
|
||||||
### 🛠 优化
|
|
||||||
|
|
||||||
- **projects**:
|
|
||||||
- 统一button的添加方式 - by **AN** [<samp>(b3f81)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/b3f81ba3)
|
|
||||||
- 动态加载组件方法抽取为公共函数 - by **AN** [<samp>(2f8a6)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/2f8a6b4b)
|
|
||||||
- 优化代码 - by **AN** [<samp>(59a69)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/59a69dd9)
|
|
||||||
- 优化代码 - by **AN** [<samp>(6c608)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/6c6086f8)
|
|
||||||
- 优化搜索FormItem展示 - by **AN** [<samp>(94faf)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/94fafe39)
|
|
||||||
- **types**:
|
|
||||||
- 补充标签类型 - by **AN** [<samp>(30316)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/30316d7e)
|
|
||||||
- 补充标签类型 - by **AN** [<samp>(71297)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/71297cf2)
|
|
||||||
|
|
||||||
### 💅 重构
|
### 💅 重构
|
||||||
|
|
||||||
- remove WorkflowLeaveForm type from Vue typings - by **AN** [<samp>(394db)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/394db6fe)
|
|
||||||
- 优化代码 - by **AN** [<samp>(56d6d)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/56d6d77d)
|
|
||||||
- **menu**:
|
- **menu**:
|
||||||
- 菜单管理中隐藏的菜单显示灰色 - by **NicholasLD** [<samp>(adca2)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/adca2e26)
|
- 菜单管理中隐藏的菜单显示灰色 - by **NicholasLD** [<samp>(adca2)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/adca2e26)
|
||||||
- 菜单管理中隐藏的菜单显示灰色 Merge pull request !24 from NicholasLD/N/A - by **不寻俗** [<samp>(4eb77)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/4eb77eac)
|
- 菜单管理中隐藏的菜单显示灰色 Merge pull request !24 from NicholasLD/N/A - by **不寻俗** [<samp>(4eb77)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/4eb77eac)
|
||||||
- **projects**:
|
- **projects**:
|
||||||
- 调整为批量上传文件 - by **AN** [<samp>(ffd62)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/ffd6211e)
|
|
||||||
- 重构流程设计菜单层级 - by **AN** [<samp>(b6f4f)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/b6f4fb5a)
|
|
||||||
- 修改流程实例动态引入组件 - by **AN** [<samp>(a3dce)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/a3dcee4a)
|
|
||||||
- 文件命名修正 - by **AN** [<samp>(5e496)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/5e49622e)
|
|
||||||
- 移动工作流组件位置 - by @m-xlsea [<samp>(7ffaa)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/7ffaac58)
|
|
||||||
- 菜单列表新增禁用菜单样式 - by @m-xlsea [<samp>(e5383)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/e538355f)
|
- 菜单列表新增禁用菜单样式 - by @m-xlsea [<samp>(e5383)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/e538355f)
|
||||||
- **types**:
|
|
||||||
- 移除无用type - by **AN** [<samp>(e86a6)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/e86a6d1b)
|
|
||||||
- **utils**:
|
|
||||||
- 简化加载组件方法 - by **AN** [<samp>(22710)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/22710ecb)
|
|
||||||
|
|
||||||
### 📖 文档
|
|
||||||
|
|
||||||
- **projects**:
|
|
||||||
- 流程定义菜单更新 - by **AN** [<samp>(1af4e)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/1af4e963)
|
|
||||||
- 新增工作流更新sql - by **AN** [<samp>(89e7e)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/89e7edb3)
|
|
||||||
- 流程表达式菜单sql更新 - by **AN** [<samp>(7484f)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/7484f79b)
|
|
||||||
|
|
||||||
### 🏡 杂项
|
### 🏡 杂项
|
||||||
|
|
||||||
|
@ -35,23 +35,4 @@ UPDATE `sys_menu` SET `component` = 'FrameView', `query_param` = 'https://previe
|
|||||||
UPDATE `sys_menu` SET `path` = 'https://gitee.com/xlsea/ruoyi-plus-soybean', `component` = 'FrameView', `icon` = 'local-icon-gitee', `menu_name` = 'RuoYi-Plus-Soybean' WHERE `menu_id` = 4;
|
UPDATE `sys_menu` SET `path` = 'https://gitee.com/xlsea/ruoyi-plus-soybean', `component` = 'FrameView', `icon` = 'local-icon-gitee', `menu_name` = 'RuoYi-Plus-Soybean' WHERE `menu_id` = 4;
|
||||||
|
|
||||||
-- plus-ui 需要禁用的页面
|
-- plus-ui 需要禁用的页面
|
||||||
UPDATE `sys_menu` SET `status` = '1' WHERE `menu_id` IN ( '116', '130', '131', '132' );
|
UPDATE `sys_menu` SET `status` = '1' WHERE `menu_id` IN ( '116', '130', '131', '132', '11700', '11701' );
|
||||||
-- 工作流要启用的页面
|
|
||||||
UPDATE `sys_menu` SET `status` = '0' WHERE `menu_id` IN ( '11616', '11618', '11700', '11701' );
|
|
||||||
|
|
||||||
-- 工作流菜单
|
|
||||||
UPDATE `sys_menu` SET `component` = 'Layout', `icon` = 'hugeicons:flow-square' WHERE `menu_id` = 11616;
|
|
||||||
UPDATE `sys_menu` SET `component` = 'Layout', `icon` = 'fluent:notepad-person-16-regular' WHERE `menu_id` = 11618;
|
|
||||||
UPDATE `sys_menu` SET `component` = 'workflow/task/taskWaiting/index', `icon` = 'ri:todo-line' WHERE `menu_id` = 11619;
|
|
||||||
UPDATE `sys_menu` SET `icon` = 'weui:setting-outlined' WHERE `menu_id` = 11620;
|
|
||||||
UPDATE `sys_menu` SET `icon` = 'ri:instance-line' WHERE `menu_id` = 11621;
|
|
||||||
UPDATE `sys_menu` SET `icon` = 'carbon:category' WHERE `menu_id` = 11622;
|
|
||||||
UPDATE `sys_menu` SET `component` = 'workflow/task/myDocument/index', `icon` = 'hugeicons:start-up-02' WHERE `menu_id` = 11629;
|
|
||||||
UPDATE `sys_menu` SET `component` = 'Layout', `icon` = 'lucide:monitor-cog' WHERE `menu_id` = 11630;
|
|
||||||
UPDATE `sys_menu` SET `component` = 'workflow/task/allTaskWaiting/index', `icon` = 'ri:todo-line' WHERE `menu_id` = 11631;
|
|
||||||
UPDATE `sys_menu` SET `component` = 'workflow/task/taskFinish/index', `icon` = 'hugeicons:task-done-01' WHERE `menu_id` = 11632;
|
|
||||||
UPDATE `sys_menu` SET `path` = 'taskCopy', `component` = 'workflow/task/taskCopy/index', `icon` = 'mynaui:copy' WHERE `menu_id` = 11633;
|
|
||||||
UPDATE `sys_menu` SET `icon` = 'ic:twotone-time-to-leave' WHERE `menu_id` = 11638;
|
|
||||||
UPDATE `sys_menu` SET `icon` = 'material-symbols:design-services-outline', `path` = 'design', `component` = 'workflow/design/index' WHERE `menu_id` = 11700;
|
|
||||||
UPDATE `sys_menu` SET `icon` = 'ic:twotone-time-to-leave' WHERE `menu_id` = 11701;
|
|
||||||
UPDATE `sys_menu` SET `icon` = 'material-symbols:regular-expression-rounded' WHERE `menu_id` = 11801;
|
|
||||||
|
@ -64,10 +64,7 @@ export default function useHookTable<A extends ApiFn, T, C>(config: TableConfig<
|
|||||||
const { loading, startLoading, endLoading } = useLoading();
|
const { loading, startLoading, endLoading } = useLoading();
|
||||||
const { bool: empty, setBool: setEmpty } = useBoolean();
|
const { bool: empty, setBool: setEmpty } = useBoolean();
|
||||||
|
|
||||||
const { transformer, immediate = true, getColumnChecks, getColumns } = config;
|
const { apiFn, apiParams, transformer, immediate = true, getColumnChecks, getColumns } = config;
|
||||||
|
|
||||||
let currentApiFn = config.apiFn;
|
|
||||||
const apiParams = config.apiParams;
|
|
||||||
|
|
||||||
const searchParams: NonNullable<Parameters<A>[0]> = reactive(jsonClone({ ...apiParams }));
|
const searchParams: NonNullable<Parameters<A>[0]> = reactive(jsonClone({ ...apiParams }));
|
||||||
|
|
||||||
@ -97,7 +94,7 @@ export default function useHookTable<A extends ApiFn, T, C>(config: TableConfig<
|
|||||||
|
|
||||||
const formattedParams = formatSearchParams(searchParams);
|
const formattedParams = formatSearchParams(searchParams);
|
||||||
|
|
||||||
const response = await currentApiFn(formattedParams);
|
const response = await apiFn(formattedParams);
|
||||||
|
|
||||||
const transformed = transformer(response as Awaited<ReturnType<A>>);
|
const transformed = transformer(response as Awaited<ReturnType<A>>);
|
||||||
|
|
||||||
@ -122,10 +119,6 @@ export default function useHookTable<A extends ApiFn, T, C>(config: TableConfig<
|
|||||||
return formattedParams;
|
return formattedParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateApiFn(newApiFn: A) {
|
|
||||||
currentApiFn = newApiFn;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* update search params
|
* update search params
|
||||||
*
|
*
|
||||||
@ -155,7 +148,6 @@ export default function useHookTable<A extends ApiFn, T, C>(config: TableConfig<
|
|||||||
getData,
|
getData,
|
||||||
searchParams,
|
searchParams,
|
||||||
updateSearchParams,
|
updateSearchParams,
|
||||||
resetSearchParams,
|
resetSearchParams
|
||||||
updateApiFn
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
681
pnpm-lock.yaml
generated
681
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -1,79 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import { computed } from 'vue';
|
|
||||||
import { NPopover, NSpace, NTag } from 'naive-ui';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
value: string | any[];
|
|
||||||
type?: NaiveUI.ThemeColor;
|
|
||||||
size?: 'small' | 'medium' | 'large';
|
|
||||||
placeholder?: string;
|
|
||||||
closable?: boolean;
|
|
||||||
threadshold?: number; // 超过该数量显示popover
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
|
||||||
type: 'info',
|
|
||||||
size: 'small',
|
|
||||||
placeholder: '无',
|
|
||||||
closable: false,
|
|
||||||
threadshold: 1 // 默认超过1个就显示popover
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Emits {
|
|
||||||
(e: 'close', index?: number): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
|
||||||
|
|
||||||
// 统一解析 value 成数组
|
|
||||||
const tags = computed(() => {
|
|
||||||
if (!props.value) return [];
|
|
||||||
return Array.isArray(props.value) ? props.value : props.value.split(',');
|
|
||||||
});
|
|
||||||
|
|
||||||
function handleClose(index?: number) {
|
|
||||||
emit('close', index);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<template v-if="tags.length === 0">
|
|
||||||
<NTag :size="size">
|
|
||||||
{{ placeholder }}
|
|
||||||
</NTag>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-else-if="tags.length <= threadshold">
|
|
||||||
<NTag
|
|
||||||
v-for="(tag, index) in tags"
|
|
||||||
:key="index"
|
|
||||||
:type="type"
|
|
||||||
class="m-1"
|
|
||||||
:size="size"
|
|
||||||
:closable="closable"
|
|
||||||
@close="handleClose(index)"
|
|
||||||
>
|
|
||||||
{{ tag }}
|
|
||||||
</NTag>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-else>
|
|
||||||
<NPopover trigger="hover" placement="bottom">
|
|
||||||
<template #trigger>
|
|
||||||
<NTag :type="type" :size="size" class="cursor-pointer">{{ tags[0] }}...({{ tags.length }})</NTag>
|
|
||||||
</template>
|
|
||||||
<NSpace vertical size="small">
|
|
||||||
<NTag
|
|
||||||
v-for="(tag, index) in tags"
|
|
||||||
:key="index"
|
|
||||||
:type="type"
|
|
||||||
:size="size"
|
|
||||||
:closable="closable"
|
|
||||||
@close="handleClose(index)"
|
|
||||||
>
|
|
||||||
{{ tag }}
|
|
||||||
</NTag>
|
|
||||||
</NSpace>
|
|
||||||
</NPopover>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
@ -1,370 +0,0 @@
|
|||||||
<script setup lang="tsx">
|
|
||||||
import { computed, ref, watch } from 'vue';
|
|
||||||
import { NButton } from 'naive-ui';
|
|
||||||
import { useLoading } from '@sa/hooks';
|
|
||||||
import { fetchGetDeptTree, fetchGetUserList } from '@/service/api/system';
|
|
||||||
import { useAppStore } from '@/store/modules/app';
|
|
||||||
import { useTable, useTableOperate } from '@/hooks/common/table';
|
|
||||||
import { useDict } from '@/hooks/business/dict';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
import UserSearch from '@/views/system/user/modules/user-search.vue';
|
|
||||||
import DictTag from './dict-tag.vue';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'UserSelectModal'
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
title?: string;
|
|
||||||
multiple?: boolean;
|
|
||||||
/** 禁选用户ID */
|
|
||||||
disabledIds?: CommonType.IdType[];
|
|
||||||
rowKeys?: CommonType.IdType[];
|
|
||||||
searchUserIds?: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
|
||||||
title: '用户选择',
|
|
||||||
multiple: false,
|
|
||||||
disabledIds: () => [],
|
|
||||||
rowKeys: () => [],
|
|
||||||
searchUserIds: null
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Emits {
|
|
||||||
(e: 'confirm', value: CommonType.IdType[], rows?: Api.System.User[]): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
|
||||||
|
|
||||||
const visible = defineModel<boolean>('visible', {
|
|
||||||
default: false
|
|
||||||
});
|
|
||||||
|
|
||||||
useDict('sys_normal_disable');
|
|
||||||
|
|
||||||
const appStore = useAppStore();
|
|
||||||
|
|
||||||
const {
|
|
||||||
columns,
|
|
||||||
columnChecks,
|
|
||||||
data,
|
|
||||||
getData,
|
|
||||||
getDataByPage,
|
|
||||||
loading,
|
|
||||||
mobilePagination,
|
|
||||||
searchParams,
|
|
||||||
resetSearchParams
|
|
||||||
} = useTable({
|
|
||||||
apiFn: fetchGetUserList,
|
|
||||||
apiParams: {
|
|
||||||
pageNum: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
// if you want to use the searchParams in Form, you need to define the following properties, and the value is null
|
|
||||||
// the value can not be undefined, otherwise the property in Form will not be reactive
|
|
||||||
deptId: null,
|
|
||||||
userName: null,
|
|
||||||
nickName: null,
|
|
||||||
phonenumber: null,
|
|
||||||
status: null,
|
|
||||||
params: {}
|
|
||||||
},
|
|
||||||
immediate: false,
|
|
||||||
columns: () => [
|
|
||||||
{
|
|
||||||
type: 'selection',
|
|
||||||
multiple: props.multiple,
|
|
||||||
align: 'center',
|
|
||||||
width: 48,
|
|
||||||
disabled: row => props.disabledIds.includes(row.userId.toString())
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'index',
|
|
||||||
title: $t('common.index'),
|
|
||||||
align: 'center',
|
|
||||||
width: 64
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'userName',
|
|
||||||
title: $t('page.system.user.userName'),
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 120,
|
|
||||||
ellipsis: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'nickName',
|
|
||||||
title: $t('page.system.user.nickName'),
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 120,
|
|
||||||
ellipsis: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'deptName',
|
|
||||||
title: $t('page.system.user.deptName'),
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 120,
|
|
||||||
ellipsis: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'phonenumber',
|
|
||||||
title: $t('page.system.user.phonenumber'),
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 120,
|
|
||||||
ellipsis: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'status',
|
|
||||||
title: $t('page.system.user.status'),
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 80,
|
|
||||||
render(row) {
|
|
||||||
return <DictTag dict-code="sys_normal_disable" value={row.status} />;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'createTime',
|
|
||||||
title: $t('page.system.user.createTime'),
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 120
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
const { checkedRowKeys } = useTableOperate(data, getData);
|
|
||||||
|
|
||||||
// 存储所有页面的用户数据,用于跨页选择
|
|
||||||
const allPagesData = ref<Api.System.User[]>([]);
|
|
||||||
|
|
||||||
// 更新allPagesData,保存当前页数据
|
|
||||||
function updateAllPagesData() {
|
|
||||||
// 将当前页数据添加到allPagesData中,避免重复
|
|
||||||
data.value.forEach(user => {
|
|
||||||
const existIndex = allPagesData.value.findIndex(item => item.userId === user.userId);
|
|
||||||
if (existIndex === -1) {
|
|
||||||
allPagesData.value.push(user);
|
|
||||||
} else {
|
|
||||||
// 更新已存在的数据
|
|
||||||
allPagesData.value[existIndex] = user;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const { loading: treeLoading, startLoading: startTreeLoading, endLoading: endTreeLoading } = useLoading();
|
|
||||||
const deptPattern = ref<string>();
|
|
||||||
const deptData = ref<Api.Common.CommonTreeRecord>([]);
|
|
||||||
const selectedKeys = ref<string[]>([]);
|
|
||||||
|
|
||||||
async function getTreeData() {
|
|
||||||
startTreeLoading();
|
|
||||||
const { data: tree, error } = await fetchGetDeptTree();
|
|
||||||
if (!error) {
|
|
||||||
deptData.value = tree;
|
|
||||||
}
|
|
||||||
endTreeLoading();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleClickTree(keys: string[]) {
|
|
||||||
searchParams.deptId = keys.length ? keys[0] : null;
|
|
||||||
checkedRowKeys.value = [];
|
|
||||||
getDataByPage();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleResetTreeData() {
|
|
||||||
deptPattern.value = undefined;
|
|
||||||
getTreeData();
|
|
||||||
}
|
|
||||||
|
|
||||||
const expandedKeys = ref<CommonType.IdType[]>([100]);
|
|
||||||
|
|
||||||
const selectable = computed(() => {
|
|
||||||
return !loading.value;
|
|
||||||
});
|
|
||||||
|
|
||||||
function handleResetSearch() {
|
|
||||||
resetSearchParams();
|
|
||||||
selectedKeys.value = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeModal() {
|
|
||||||
checkedRowKeys.value = [];
|
|
||||||
allPagesData.value = [];
|
|
||||||
visible.value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleConfirm() {
|
|
||||||
if (checkedRowKeys.value.length === 0) {
|
|
||||||
window.$message?.error('请选择用户');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 获取选中行对应的用户对象(从所有页面数据中筛选)
|
|
||||||
const selectedUsers = allPagesData.value.filter(item => checkedRowKeys.value.includes(item.userId.toString()));
|
|
||||||
emit('confirm', checkedRowKeys.value, selectedUsers);
|
|
||||||
closeModal();
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRowProps(row: Api.System.User) {
|
|
||||||
return {
|
|
||||||
onClick: (e: MouseEvent) => {
|
|
||||||
const target = e.target as HTMLElement;
|
|
||||||
if (target.closest('.n-data-table-td--selection')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (props.disabledIds.includes(row.userId.toString())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将userId转为字符串
|
|
||||||
const userId = row.userId.toString();
|
|
||||||
|
|
||||||
if (props.multiple) {
|
|
||||||
const index = checkedRowKeys.value.findIndex(key => key === userId);
|
|
||||||
if (index > -1) {
|
|
||||||
checkedRowKeys.value.splice(index, 1);
|
|
||||||
} else {
|
|
||||||
checkedRowKeys.value.push(userId);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
checkedRowKeys.value = [userId];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听数据变化(页面切换时)
|
|
||||||
watch(
|
|
||||||
data,
|
|
||||||
() => {
|
|
||||||
updateAllPagesData();
|
|
||||||
},
|
|
||||||
{ deep: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
watch(visible, () => {
|
|
||||||
if (visible.value) {
|
|
||||||
getTreeData();
|
|
||||||
if (props.searchUserIds) {
|
|
||||||
searchParams.userIds = props.searchUserIds;
|
|
||||||
}
|
|
||||||
allPagesData.value = [];
|
|
||||||
getDataByPage();
|
|
||||||
checkedRowKeys.value = [...props.rowKeys];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NModal
|
|
||||||
v-model:show="visible"
|
|
||||||
class="user-select-modal max-h-800px max-w-90% w-1400px"
|
|
||||||
preset="card"
|
|
||||||
size="medium"
|
|
||||||
:title="props.title"
|
|
||||||
>
|
|
||||||
<TableSiderLayout class="bg-gray-50 p-2" :sider-title="$t('page.system.dept.title')">
|
|
||||||
<template #header-extra>
|
|
||||||
<NButton size="small" text class="h-18px" @click.stop="() => handleResetTreeData()">
|
|
||||||
<template #icon>
|
|
||||||
<SvgIcon icon="ic:round-refresh" />
|
|
||||||
</template>
|
|
||||||
</NButton>
|
|
||||||
</template>
|
|
||||||
<template #sider>
|
|
||||||
<NInput v-model:value="deptPattern" clearable :placeholder="$t('common.keywordSearch')" />
|
|
||||||
<NSpin class="dept-tree" :show="treeLoading">
|
|
||||||
<NTree
|
|
||||||
v-model:expanded-keys="expandedKeys"
|
|
||||||
v-model:selected-keys="selectedKeys"
|
|
||||||
block-node
|
|
||||||
show-line
|
|
||||||
:data="deptData as []"
|
|
||||||
:show-irrelevant-nodes="false"
|
|
||||||
:pattern="deptPattern"
|
|
||||||
class="infinite-scroll h-full min-h-200px py-3"
|
|
||||||
key-field="id"
|
|
||||||
label-field="label"
|
|
||||||
virtual-scroll
|
|
||||||
:selectable="selectable"
|
|
||||||
@update:selected-keys="handleClickTree"
|
|
||||||
>
|
|
||||||
<template #empty>
|
|
||||||
<NEmpty :description="$t('page.system.dept.empty')" class="h-full min-h-200px justify-center" />
|
|
||||||
</template>
|
|
||||||
</NTree>
|
|
||||||
</NSpin>
|
|
||||||
</template>
|
|
||||||
<div class="h-full flex-col-stretch gap-12px overflow-hidden lt-sm:max-h-500px lt-sm:overflow-auto">
|
|
||||||
<UserSearch v-model:model="searchParams" @reset="handleResetSearch" @search="getDataByPage" />
|
|
||||||
<TableRowCheckAlert v-model:checked-row-keys="checkedRowKeys" />
|
|
||||||
<NAlert v-if="props.disabledIds.length > 0" type="warning">
|
|
||||||
<span>已存在的用户无法被选择</span>
|
|
||||||
</NAlert>
|
|
||||||
<NCard
|
|
||||||
:title="$t('page.system.user.title')"
|
|
||||||
:bordered="false"
|
|
||||||
size="small"
|
|
||||||
class="card-wrapper sm:flex-1-hidden lt-sm:overflow-auto"
|
|
||||||
>
|
|
||||||
<template #header-extra>
|
|
||||||
<TableHeaderOperation
|
|
||||||
v-model:columns="columnChecks"
|
|
||||||
:loading="loading"
|
|
||||||
:show-add="false"
|
|
||||||
:show-delete="false"
|
|
||||||
:show-export="false"
|
|
||||||
@refresh="getData"
|
|
||||||
></TableHeaderOperation>
|
|
||||||
</template>
|
|
||||||
<NDataTable
|
|
||||||
v-model:checked-row-keys="checkedRowKeys"
|
|
||||||
:columns="columns"
|
|
||||||
:data="data"
|
|
||||||
size="small"
|
|
||||||
:flex-height="!appStore.isMobile"
|
|
||||||
:scroll-x="962"
|
|
||||||
:loading="loading"
|
|
||||||
:row-props="getRowProps"
|
|
||||||
remote
|
|
||||||
:row-key="row => row.userId.toString()"
|
|
||||||
:pagination="mobilePagination"
|
|
||||||
class="h-full lt-sm:max-h-300px"
|
|
||||||
/>
|
|
||||||
</NCard>
|
|
||||||
</div>
|
|
||||||
</TableSiderLayout>
|
|
||||||
<template #footer>
|
|
||||||
<NSpace justify="end" :size="16">
|
|
||||||
<NButton @click="closeModal">{{ $t('common.cancel') }}</NButton>
|
|
||||||
<NButton type="primary" @click="handleConfirm">{{ $t('common.confirm') }}</NButton>
|
|
||||||
</NSpace>
|
|
||||||
</template>
|
|
||||||
</NModal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
:deep(.n-layout) {
|
|
||||||
height: 600px;
|
|
||||||
|
|
||||||
@media (max-width: 639px) {
|
|
||||||
height: auto;
|
|
||||||
max-height: 500px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-select-modal {
|
|
||||||
@media (max-width: 639px) {
|
|
||||||
:deep(.n-card-content) {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.n-data-table) {
|
|
||||||
max-height: 300px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.n-alert {
|
|
||||||
--n-padding: 5px 13px !important;
|
|
||||||
--n-icon-margin: 6px 8px 0 12px !important;
|
|
||||||
--n-icon-size: 20px !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,178 +0,0 @@
|
|||||||
<script setup lang="tsx">
|
|
||||||
import { ref } from 'vue';
|
|
||||||
import { NPopover, NSpace, NTag } from 'naive-ui';
|
|
||||||
import { useLoading } from '@sa/hooks';
|
|
||||||
import { fetchGetFlowHisTaskList } from '@/service/api/workflow/instance';
|
|
||||||
import { fetchGetOssListByIds } from '@/service/api/system/oss';
|
|
||||||
import { useDict } from '@/hooks/business/dict';
|
|
||||||
import { useDownload } from '@/hooks/business/download';
|
|
||||||
import DictTag from '@/components/custom/dict-tag.vue';
|
|
||||||
import TagGroup from '@/components/custom/tag-group.vue';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'ApprovalInfoPanel'
|
|
||||||
});
|
|
||||||
interface Props {
|
|
||||||
/** 业务id */
|
|
||||||
businessId: CommonType.IdType;
|
|
||||||
}
|
|
||||||
const props = defineProps<Props>();
|
|
||||||
|
|
||||||
useDict('wf_task_status');
|
|
||||||
|
|
||||||
const activeTab = ref('image');
|
|
||||||
|
|
||||||
const { loading, startLoading, endLoading } = useLoading();
|
|
||||||
|
|
||||||
const { oss } = useDownload();
|
|
||||||
|
|
||||||
const columns = ref<NaiveUI.TableColumn<Api.Workflow.HisTask>[]>([
|
|
||||||
{
|
|
||||||
title: '任务名称',
|
|
||||||
key: 'nodeName',
|
|
||||||
align: 'center',
|
|
||||||
width: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '办理人',
|
|
||||||
key: 'approveName',
|
|
||||||
align: 'center',
|
|
||||||
width: 100,
|
|
||||||
render: row => {
|
|
||||||
return <TagGroup value={row.approveName} />;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '任务状态',
|
|
||||||
key: 'flowStatus',
|
|
||||||
align: 'center',
|
|
||||||
width: 100,
|
|
||||||
render: row => {
|
|
||||||
return <DictTag size="small" value={row.flowStatus} dict-code="wf_task_status" />;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '审批意见',
|
|
||||||
key: 'message',
|
|
||||||
align: 'center',
|
|
||||||
width: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '开始时间',
|
|
||||||
key: 'createTime',
|
|
||||||
align: 'center',
|
|
||||||
width: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '结束时间',
|
|
||||||
key: 'updateTime',
|
|
||||||
align: 'center',
|
|
||||||
width: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '运行时间',
|
|
||||||
key: 'runDuration',
|
|
||||||
align: 'center',
|
|
||||||
width: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '附件',
|
|
||||||
key: 'attachmentList',
|
|
||||||
align: 'center',
|
|
||||||
width: 120,
|
|
||||||
render: row => {
|
|
||||||
if (!row.attachmentList || row.attachmentList.length === 0) return null;
|
|
||||||
|
|
||||||
if (row.attachmentList.length === 1) {
|
|
||||||
return (
|
|
||||||
<NTag size="small" type="info" class="cursor-pointer">
|
|
||||||
<div class="flex items-center gap-2" onClick={() => oss(row.attachmentList[0].ossId)}>
|
|
||||||
{row.attachmentList[0].originalName}
|
|
||||||
</div>
|
|
||||||
</NTag>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<NPopover trigger="hover" placement="bottom">
|
|
||||||
{{
|
|
||||||
trigger: () => (
|
|
||||||
<NTag size="small" type="info" class="cursor-pointer">
|
|
||||||
{row.attachmentList[0].originalName}...({row.attachmentList.length})
|
|
||||||
</NTag>
|
|
||||||
),
|
|
||||||
default: () => (
|
|
||||||
<NSpace vertical size="small">
|
|
||||||
{row.attachmentList.map(item => (
|
|
||||||
<NTag key={item.ossId} size="small" type="info" class="cursor-pointer">
|
|
||||||
<div class="flex items-center gap-2" onClick={() => oss(item.ossId)}>
|
|
||||||
{item.originalName}
|
|
||||||
</div>
|
|
||||||
</NTag>
|
|
||||||
))}
|
|
||||||
</NSpace>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</NPopover>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
const instanceId = ref<CommonType.IdType>();
|
|
||||||
|
|
||||||
const hisTask = ref<Api.Workflow.HisTask[]>([]);
|
|
||||||
|
|
||||||
/** 初始化数据 */
|
|
||||||
async function initData() {
|
|
||||||
activeTab.value = 'image';
|
|
||||||
instanceId.value = undefined;
|
|
||||||
hisTask.value = [];
|
|
||||||
await getData();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getData() {
|
|
||||||
startLoading();
|
|
||||||
const { error, data } = await fetchGetFlowHisTaskList(props.businessId);
|
|
||||||
if (error) {
|
|
||||||
window.$message?.error(error.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
instanceId.value = data?.instanceId || '';
|
|
||||||
|
|
||||||
const rawList = data?.list || [];
|
|
||||||
if (rawList.length === 0) {
|
|
||||||
hisTask.value = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const promises = rawList.map(async (item: Api.Workflow.HisTask) => {
|
|
||||||
if (item.ext) {
|
|
||||||
const { error: err, data: ossList } = await fetchGetOssListByIds(item.ext.split(','));
|
|
||||||
if (!err) {
|
|
||||||
item.attachmentList = ossList;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await Promise.all(promises);
|
|
||||||
hisTask.value = rawList;
|
|
||||||
endLoading();
|
|
||||||
}
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
initData
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NDivider />
|
|
||||||
<div class="h-full">
|
|
||||||
<NTabs v-model:value="activeTab" type="segment" animated>
|
|
||||||
<NTabPane display-directive="show" bar-width="100px" name="image" tab="流程图">
|
|
||||||
<FlowPreview v-if="instanceId" :instance-id="instanceId" />
|
|
||||||
</NTabPane>
|
|
||||||
<NTabPane bar-width="100px" name="info" tab="审批信息">
|
|
||||||
<NDataTable size="small" :scroll-x="760" :columns="columns" :data="hisTask" :loading="loading" />
|
|
||||||
</NTabPane>
|
|
||||||
</NTabs>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
@ -1,142 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import { reactive, ref, watch } from 'vue';
|
|
||||||
import type { UploadFileInfo } from 'naive-ui';
|
|
||||||
import { useLoading } from '@sa/hooks';
|
|
||||||
import { messageTypeOptions } from '@/constants/workflow';
|
|
||||||
import { fetchBackTask, fetchGetBackNode } from '@/service/api/workflow/task';
|
|
||||||
defineOptions({
|
|
||||||
name: 'BackTaskModal'
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
task: Api.Workflow.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = defineProps<Props>();
|
|
||||||
|
|
||||||
interface Emits {
|
|
||||||
(e: 'submit'): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
|
||||||
|
|
||||||
const visible = defineModel<boolean>('visible', {
|
|
||||||
default: false
|
|
||||||
});
|
|
||||||
const title = defineModel<string>('title', {
|
|
||||||
default: '驳回'
|
|
||||||
});
|
|
||||||
const { loading: backFormLoading, startLoading: startBackFormLoading, endLoading: endBackFormLoading } = useLoading();
|
|
||||||
const { loading: backBtnLoading, startLoading: startBackBtnLoading, endLoading: endBackBtnLoading } = useLoading();
|
|
||||||
|
|
||||||
const accept = ref<string>('.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt,.pdf,.jpg,.jpeg,.png,.gif,.bmp,.webp');
|
|
||||||
|
|
||||||
type Model = Api.Workflow.BackOperateParams;
|
|
||||||
|
|
||||||
const backModel = reactive(createBackModel());
|
|
||||||
|
|
||||||
function createBackModel(): Model {
|
|
||||||
return {
|
|
||||||
taskId: null,
|
|
||||||
fileId: null,
|
|
||||||
messageType: ['1'],
|
|
||||||
nodeCode: null,
|
|
||||||
message: null,
|
|
||||||
notice: null,
|
|
||||||
variables: null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const fileList = ref<UploadFileInfo[]>([]);
|
|
||||||
|
|
||||||
const backTaskNodeOptions = ref<CommonType.Option<string, string>[]>([]);
|
|
||||||
|
|
||||||
async function initDefault() {
|
|
||||||
startBackFormLoading();
|
|
||||||
startBackBtnLoading();
|
|
||||||
Object.assign(backModel, createBackModel());
|
|
||||||
const { error, data } = await fetchGetBackNode(props.task.definitionId, props.task.nodeCode);
|
|
||||||
endBackFormLoading();
|
|
||||||
endBackBtnLoading();
|
|
||||||
if (error) return;
|
|
||||||
backTaskNodeOptions.value = data.map(item => ({
|
|
||||||
label: item.nodeName,
|
|
||||||
value: item.nodeCode
|
|
||||||
}));
|
|
||||||
backModel.nodeCode = data[0].nodeCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleSubmit() {
|
|
||||||
backModel.taskId = props.task.id;
|
|
||||||
if (fileList.value?.length) {
|
|
||||||
const fileIds = fileList.value.map(item => item.id);
|
|
||||||
backModel.fileId = fileIds.join(',');
|
|
||||||
}
|
|
||||||
window.$dialog?.warning({
|
|
||||||
title: '提示',
|
|
||||||
content: `是否确认驳回?`,
|
|
||||||
positiveText: `确认驳回`,
|
|
||||||
positiveButtonProps: {
|
|
||||||
type: 'primary'
|
|
||||||
},
|
|
||||||
negativeText: '取消',
|
|
||||||
onPositiveClick: async () => {
|
|
||||||
startBackBtnLoading();
|
|
||||||
startBackFormLoading();
|
|
||||||
const { error } = await fetchBackTask(backModel);
|
|
||||||
endBackBtnLoading();
|
|
||||||
endBackFormLoading();
|
|
||||||
if (error) return;
|
|
||||||
window.$message?.success('驳回成功');
|
|
||||||
closeDrawer();
|
|
||||||
emit('submit');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeDrawer() {
|
|
||||||
visible.value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(visible, () => {
|
|
||||||
if (visible.value) {
|
|
||||||
initDefault();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NModal v-model:show="visible" preset="card" class="w-800px" :title="title" :native-scrollbar="false" closable>
|
|
||||||
<NSpin :show="backFormLoading">
|
|
||||||
<NForm v-if="task.flowStatus === 'waiting'" :model="backModel">
|
|
||||||
<NFormItem label="驳回节点" path="nodeCode">
|
|
||||||
<NSelect v-model:value="backModel.nodeCode" :options="backTaskNodeOptions" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="通知方式" path="messageType">
|
|
||||||
<NCheckboxGroup v-model:value="backModel.messageType">
|
|
||||||
<NSpace item-style="display: flex;">
|
|
||||||
<NCheckbox
|
|
||||||
v-for="item in messageTypeOptions"
|
|
||||||
:key="item.value"
|
|
||||||
:disabled="item.value === '1'"
|
|
||||||
:value="item.value"
|
|
||||||
:label="item.label"
|
|
||||||
/>
|
|
||||||
</NSpace>
|
|
||||||
</NCheckboxGroup>
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="附件" path="fileId">
|
|
||||||
<FileUpload v-model:file-list="fileList" :file-size="20" :max="20" upload-type="file" :accept="accept" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="审批意见" path="message">
|
|
||||||
<NInput v-model:value="backModel.message" type="textarea" />
|
|
||||||
</NFormItem>
|
|
||||||
</NForm>
|
|
||||||
</NSpin>
|
|
||||||
<template #footer>
|
|
||||||
<NSpace justify="end" :size="16">
|
|
||||||
<NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
|
|
||||||
<NButton :loading="backBtnLoading" type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
|
|
||||||
</NSpace>
|
|
||||||
</template>
|
|
||||||
</NModal>
|
|
||||||
</template>
|
|
@ -1,62 +0,0 @@
|
|||||||
<script setup lang="tsx">
|
|
||||||
import { computed, useAttrs } from 'vue';
|
|
||||||
import type { TreeSelectProps } from 'naive-ui';
|
|
||||||
import { useLoading } from '@sa/hooks';
|
|
||||||
import { fetchGetCategoryTree } from '@/service/api/workflow';
|
|
||||||
import { isNull } from '@/utils/common';
|
|
||||||
|
|
||||||
defineOptions({ name: 'FlowCategorySelect' });
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
defineProps<Props>();
|
|
||||||
|
|
||||||
const rawValue = defineModel<CommonType.IdType | null>('value', { required: false });
|
|
||||||
const options = defineModel<Api.Common.CommonTreeRecord>('options', { required: false, default: [] });
|
|
||||||
const expandedKeys = defineModel<CommonType.IdType[]>('expandedKeys', { required: false, default: [] });
|
|
||||||
|
|
||||||
const attrs: TreeSelectProps = useAttrs();
|
|
||||||
const { loading, startLoading, endLoading } = useLoading();
|
|
||||||
|
|
||||||
/** 转换为str,id可能是number类型或者String类型,导致回显失败 */
|
|
||||||
const strValue = computed({
|
|
||||||
get() {
|
|
||||||
return isNull(rawValue.value) ? null : rawValue.value?.toString();
|
|
||||||
},
|
|
||||||
set(val) {
|
|
||||||
rawValue.value = val;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
async function getCategoryList() {
|
|
||||||
startLoading();
|
|
||||||
const { error, data } = await fetchGetCategoryTree();
|
|
||||||
if (error) return;
|
|
||||||
options.value = data;
|
|
||||||
// 设置默认展开的节点
|
|
||||||
if (data?.length && !expandedKeys.value.length) {
|
|
||||||
expandedKeys.value = [data[0].id];
|
|
||||||
}
|
|
||||||
endLoading();
|
|
||||||
}
|
|
||||||
|
|
||||||
getCategoryList();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NTreeSelect
|
|
||||||
v-model:value="strValue"
|
|
||||||
v-model:expanded-keys="expandedKeys"
|
|
||||||
filterable
|
|
||||||
class="h-full"
|
|
||||||
:loading="loading"
|
|
||||||
key-field="id"
|
|
||||||
label-field="label"
|
|
||||||
:options="options as []"
|
|
||||||
v-bind="attrs"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
@ -1,96 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { computed } from 'vue';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
/** 抽屉是否可见 */
|
|
||||||
visible?: boolean;
|
|
||||||
/** 抽屉标题 */
|
|
||||||
title: string;
|
|
||||||
/** 是否显示加载状态 */
|
|
||||||
loading?: boolean;
|
|
||||||
/** 抽屉宽度 */
|
|
||||||
width?: number;
|
|
||||||
operateType: CommonType.WorkflowTableOperateType;
|
|
||||||
status?: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
|
||||||
visible: false,
|
|
||||||
loading: false,
|
|
||||||
width: 1200,
|
|
||||||
status: null
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Emits {
|
|
||||||
(e: 'update:visible', visible: boolean): void;
|
|
||||||
(e: 'close'): void;
|
|
||||||
(e: 'saveDraft'): void;
|
|
||||||
(e: 'submit'): void;
|
|
||||||
(e: 'approval'): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
|
||||||
|
|
||||||
const visibleValue = computed({
|
|
||||||
get: () => props.visible,
|
|
||||||
set: value => {
|
|
||||||
emit('update:visible', value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const showSubmit = computed(
|
|
||||||
() =>
|
|
||||||
props.operateType === 'add' ||
|
|
||||||
(props.operateType === 'edit' &&
|
|
||||||
props.status &&
|
|
||||||
(props.status === 'draft' || props.status === 'cancel' || props.status === 'back'))
|
|
||||||
);
|
|
||||||
const showApproval = computed(() => props.operateType === 'approval' && props.status && props.status === 'waiting');
|
|
||||||
|
|
||||||
function handleClose() {
|
|
||||||
emit('close');
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleSaveDraft() {
|
|
||||||
emit('saveDraft');
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleSubmit() {
|
|
||||||
emit('submit');
|
|
||||||
}
|
|
||||||
function handleApproval() {
|
|
||||||
emit('approval');
|
|
||||||
}
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
handleClose,
|
|
||||||
handleSaveDraft,
|
|
||||||
handleSubmit,
|
|
||||||
handleApproval
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NDrawer v-model:show="visibleValue" :title="title" display-directive="show" :width="width" class="max-w-90%">
|
|
||||||
<NDrawerContent :title="title" :native-scrollbar="false" closable @close="handleClose">
|
|
||||||
<NSpin :show="loading">
|
|
||||||
<slot></slot>
|
|
||||||
</NSpin>
|
|
||||||
<template #footer>
|
|
||||||
<slot name="footer">
|
|
||||||
<div>
|
|
||||||
<NSpace :size="16">
|
|
||||||
<NButton v-if="showSubmit || showApproval" @click="handleClose">{{ $t('common.cancel') }}</NButton>
|
|
||||||
<NButton v-if="showSubmit" type="warning" @click="handleSaveDraft">暂存</NButton>
|
|
||||||
<NButton v-if="showSubmit" type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
|
|
||||||
<NButton v-if="showApproval" type="primary" @click="handleApproval">办理</NButton>
|
|
||||||
</NSpace>
|
|
||||||
</div>
|
|
||||||
</slot>
|
|
||||||
</template>
|
|
||||||
</NDrawerContent>
|
|
||||||
</NDrawer>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
@ -1,222 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import { computed, reactive, ref, watch } from 'vue';
|
|
||||||
import { useBoolean, useLoading } from '@sa/hooks';
|
|
||||||
import { fetchGetTask, fetchTaskOperate, fetchTerminateTask } from '@/service/api/workflow/task';
|
|
||||||
import ReduceSignatureModal from './reduce-signature-modal.vue';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'FlowInterveneModal'
|
|
||||||
});
|
|
||||||
|
|
||||||
const { loading, startLoading, endLoading } = useLoading();
|
|
||||||
const { bool: addSignatureVisible, setTrue: openAddSignatureModal } = useBoolean();
|
|
||||||
const { bool: transferVisible, setTrue: openTransferModal } = useBoolean();
|
|
||||||
const { bool: reduceSignatureVisible, setTrue: openReduceSignatureModal } = useBoolean();
|
|
||||||
interface Props {
|
|
||||||
taskId: CommonType.IdType;
|
|
||||||
assigneeIds: CommonType.IdType[];
|
|
||||||
assigneeNames: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = defineProps<Props>();
|
|
||||||
|
|
||||||
interface Emits {
|
|
||||||
(e: 'refresh'): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
|
||||||
|
|
||||||
const taskInfo = ref<Api.Workflow.Task>();
|
|
||||||
|
|
||||||
const isWaiting = computed(() => taskInfo.value?.flowStatus === 'waiting');
|
|
||||||
|
|
||||||
// 流程签署比例值 大于0为票签,会签
|
|
||||||
const isTicketOrSignInstance = computed(() => Number(taskInfo.value?.nodeRatio) > 0);
|
|
||||||
|
|
||||||
const visible = defineModel<boolean>('visible', {
|
|
||||||
default: false
|
|
||||||
});
|
|
||||||
|
|
||||||
type Model = Api.Workflow.TaskOperateParams;
|
|
||||||
const model: Model = reactive(createDefaultModel());
|
|
||||||
|
|
||||||
function createDefaultModel(): Model {
|
|
||||||
return {
|
|
||||||
taskId: null,
|
|
||||||
userId: undefined,
|
|
||||||
userIds: undefined,
|
|
||||||
message: ''
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
type TerminateModel = Api.Workflow.TerminateTaskOperateParams;
|
|
||||||
const terminateModel: TerminateModel = reactive(createDefaultTerminateModel());
|
|
||||||
|
|
||||||
function createDefaultTerminateModel(): TerminateModel {
|
|
||||||
return {
|
|
||||||
taskId: null,
|
|
||||||
comment: ''
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleTransferConfirm(ids: CommonType.IdType[]) {
|
|
||||||
model.userId = ids[0];
|
|
||||||
model.taskId = props.taskId;
|
|
||||||
window.$dialog?.warning({
|
|
||||||
title: '提示',
|
|
||||||
content: '是否确认转办?',
|
|
||||||
positiveText: '确认转办',
|
|
||||||
positiveButtonProps: {
|
|
||||||
type: 'primary'
|
|
||||||
},
|
|
||||||
negativeText: '取消',
|
|
||||||
onPositiveClick: async () => {
|
|
||||||
const { error } = await fetchTaskOperate(model, 'transferTask');
|
|
||||||
if (error) return;
|
|
||||||
window.$message?.success('转办成功');
|
|
||||||
visible.value = false;
|
|
||||||
emit('refresh');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleAddSignatureConfirm(ids: CommonType.IdType[]) {
|
|
||||||
model.userIds = ids;
|
|
||||||
window.$dialog?.warning({
|
|
||||||
title: '提示',
|
|
||||||
content: '是否确认加签?',
|
|
||||||
positiveText: '确认加签',
|
|
||||||
positiveButtonProps: {
|
|
||||||
type: 'primary'
|
|
||||||
},
|
|
||||||
negativeText: '取消',
|
|
||||||
onPositiveClick: async () => {
|
|
||||||
const { error } = await fetchTaskOperate(model, 'addSignature');
|
|
||||||
if (error) return;
|
|
||||||
window.$message?.success('加签成功');
|
|
||||||
visible.value = false;
|
|
||||||
emit('refresh');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleTerminate() {
|
|
||||||
terminateModel.taskId = props.taskId;
|
|
||||||
window.$dialog?.warning({
|
|
||||||
title: '提示',
|
|
||||||
content: '是否确认终止?',
|
|
||||||
positiveText: '确认',
|
|
||||||
positiveButtonProps: {
|
|
||||||
type: 'primary'
|
|
||||||
},
|
|
||||||
negativeText: '取消',
|
|
||||||
onPositiveClick: async () => {
|
|
||||||
const { error } = await fetchTerminateTask(terminateModel);
|
|
||||||
if (error) return;
|
|
||||||
window.$message?.success('终止成功');
|
|
||||||
visible.value = false;
|
|
||||||
emit('refresh');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleReduceSubmit() {
|
|
||||||
visible.value = false;
|
|
||||||
emit('refresh');
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getTaskInfo() {
|
|
||||||
startLoading();
|
|
||||||
const { error, data } = await fetchGetTask(props.taskId);
|
|
||||||
if (error) return;
|
|
||||||
taskInfo.value = data;
|
|
||||||
endLoading();
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(visible, () => {
|
|
||||||
if (visible.value) {
|
|
||||||
getTaskInfo();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NModal
|
|
||||||
v-model:show="visible"
|
|
||||||
class="max-h-520px max-w-90% w-700px"
|
|
||||||
title="流程干预"
|
|
||||||
preset="card"
|
|
||||||
size="medium"
|
|
||||||
:native-scrollbar="false"
|
|
||||||
>
|
|
||||||
<NSpin :show="loading">
|
|
||||||
<NDescriptions
|
|
||||||
:title="`${taskInfo?.flowName} (${taskInfo?.flowCode})`"
|
|
||||||
label-placement="left"
|
|
||||||
:column="2"
|
|
||||||
size="small"
|
|
||||||
bordered
|
|
||||||
>
|
|
||||||
<NDescriptionsItem label="任务名称">
|
|
||||||
{{ taskInfo?.nodeName }}
|
|
||||||
</NDescriptionsItem>
|
|
||||||
<NDescriptionsItem label="节点编码">
|
|
||||||
{{ taskInfo?.nodeCode }}
|
|
||||||
</NDescriptionsItem>
|
|
||||||
<NDescriptionsItem label="开始时间">
|
|
||||||
{{ taskInfo?.createTime }}
|
|
||||||
</NDescriptionsItem>
|
|
||||||
<NDescriptionsItem label="流程实例ID">
|
|
||||||
{{ taskInfo?.instanceId }}
|
|
||||||
</NDescriptionsItem>
|
|
||||||
<NDescriptionsItem label="办理人">
|
|
||||||
<TagGroup :value="assigneeNames" />
|
|
||||||
</NDescriptionsItem>
|
|
||||||
<NDescriptionsItem label="版本号">
|
|
||||||
<NTag type="info" size="small">v{{ taskInfo?.version }}.0</NTag>
|
|
||||||
</NDescriptionsItem>
|
|
||||||
<NDescriptionsItem label="业务ID">
|
|
||||||
{{ taskInfo?.businessId }}
|
|
||||||
</NDescriptionsItem>
|
|
||||||
</NDescriptions>
|
|
||||||
</NSpin>
|
|
||||||
|
|
||||||
<template #footer>
|
|
||||||
<NSpace justify="end" :size="16">
|
|
||||||
<NButton v-if="isWaiting" :loading="loading" type="primary" @click="openTransferModal">转办</NButton>
|
|
||||||
<NButton
|
|
||||||
v-if="isWaiting && isTicketOrSignInstance"
|
|
||||||
:loading="loading"
|
|
||||||
type="primary"
|
|
||||||
@click="openAddSignatureModal"
|
|
||||||
>
|
|
||||||
加签
|
|
||||||
</NButton>
|
|
||||||
<NButton
|
|
||||||
v-if="isWaiting && isTicketOrSignInstance"
|
|
||||||
:loading="loading"
|
|
||||||
type="primary"
|
|
||||||
@click="openReduceSignatureModal"
|
|
||||||
>
|
|
||||||
减签
|
|
||||||
</NButton>
|
|
||||||
<NButton v-if="isWaiting" :loading="loading" type="error" @click="handleTerminate">终止</NButton>
|
|
||||||
</NSpace>
|
|
||||||
</template>
|
|
||||||
<!-- 转办用户选择器 -->
|
|
||||||
<UserSelectModal v-model:visible="transferVisible" :disabled-ids="assigneeIds" @confirm="handleTransferConfirm" />
|
|
||||||
<!-- 加签用户选择器 -->
|
|
||||||
<UserSelectModal
|
|
||||||
v-model:visible="addSignatureVisible"
|
|
||||||
multiple
|
|
||||||
:disabled-ids="assigneeIds"
|
|
||||||
@confirm="handleAddSignatureConfirm"
|
|
||||||
/>
|
|
||||||
<!-- 减签用户 -->
|
|
||||||
<ReduceSignatureModal
|
|
||||||
v-model:visible="reduceSignatureVisible"
|
|
||||||
:task="taskInfo!"
|
|
||||||
@reduce-submit="handleReduceSubmit"
|
|
||||||
/>
|
|
||||||
</NModal>
|
|
||||||
</template>
|
|
@ -1,30 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { stringify } from 'qs';
|
|
||||||
import { getToken } from '@/store/modules/auth/shared';
|
|
||||||
import { getServiceBaseURL } from '@/utils/service';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'FlowPreview'
|
|
||||||
});
|
|
||||||
interface Props {
|
|
||||||
instanceId: CommonType.IdType;
|
|
||||||
}
|
|
||||||
const props = defineProps<Props>();
|
|
||||||
|
|
||||||
const isHttpProxy = import.meta.env.DEV && import.meta.env.VITE_HTTP_PROXY === 'Y';
|
|
||||||
const { baseURL } = getServiceBaseURL(import.meta.env, isHttpProxy);
|
|
||||||
const urlParams = {
|
|
||||||
type: 'FlowChart',
|
|
||||||
id: props.instanceId,
|
|
||||||
Authorization: `Bearer ${getToken()}`,
|
|
||||||
clientid: import.meta.env.VITE_APP_CLIENT_ID || ''
|
|
||||||
};
|
|
||||||
|
|
||||||
const iframeUrl = `${baseURL}/warm-flow-ui/index.html?${stringify(urlParams)}`;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<iframe :src="iframeUrl" class="h-[450px] w-full" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
@ -1,496 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { computed, reactive, ref, watch } from 'vue';
|
|
||||||
import type { UploadFileInfo } from 'naive-ui';
|
|
||||||
import { useBoolean, useLoading } from '@sa/hooks';
|
|
||||||
import { messageTypeOptions } from '@/constants/workflow';
|
|
||||||
import {
|
|
||||||
fetchCompleteTask,
|
|
||||||
fetchGetTask,
|
|
||||||
fetchGetkNextNode,
|
|
||||||
fetchTaskOperate,
|
|
||||||
fetchTerminateTask
|
|
||||||
} from '@/service/api/workflow';
|
|
||||||
import FileUpload from '@/components/custom/file-upload.vue';
|
|
||||||
import ReduceSignatureModal from './reduce-signature-modal.vue';
|
|
||||||
import BackTaskModal from './back-task-modal.vue';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'FlowTaskApprovalModal'
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
/** 任务id */
|
|
||||||
taskId: CommonType.IdType;
|
|
||||||
/** 任务变量 */
|
|
||||||
taskVariables: { [key: string]: any };
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = defineProps<Props>();
|
|
||||||
|
|
||||||
interface Emits {
|
|
||||||
(e: 'finished'): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
|
||||||
|
|
||||||
const visible = defineModel<boolean>('visible', {
|
|
||||||
default: false
|
|
||||||
});
|
|
||||||
const { loading: baseFormLoading, startLoading: startBaseFormLoading, endLoading: endBaseFormLoading } = useLoading();
|
|
||||||
const { loading: btnLoading, startLoading: startBtnLoading, endLoading: endBtnLoading } = useLoading();
|
|
||||||
const { bool: copyVisible, setTrue: openCopyModal } = useBoolean();
|
|
||||||
const { bool: assigneeVisible, setTrue: openAssigneeModal } = useBoolean();
|
|
||||||
const { bool: delegateVisible, setTrue: openDelegateModal, setFalse: closeDelegateModal } = useBoolean();
|
|
||||||
const { bool: transferVisible, setTrue: openTransferModal, setFalse: closeTransferModal } = useBoolean();
|
|
||||||
const { bool: addSignatureVisible, setTrue: openAddSignatureModal, setFalse: closeAddSignatureModal } = useBoolean();
|
|
||||||
const { bool: reduceSignatureVisible, setTrue: openReduceSignatureModal } = useBoolean();
|
|
||||||
const { bool: backVisible, setTrue: openBackModal } = useBoolean();
|
|
||||||
const title = defineModel<string>('title', {
|
|
||||||
default: '流程发起'
|
|
||||||
});
|
|
||||||
|
|
||||||
const accept = ref<string>('.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt,.pdf,.jpg,.jpeg,.png,.gif,.bmp,.webp');
|
|
||||||
|
|
||||||
type Model = Api.Workflow.CompleteTaskOperateParams;
|
|
||||||
|
|
||||||
const task = ref<Api.Workflow.Task>();
|
|
||||||
|
|
||||||
const isWaiting = computed(() => task.value?.flowStatus === 'waiting');
|
|
||||||
|
|
||||||
const model: Model = reactive(createDefaultModel());
|
|
||||||
|
|
||||||
function createDefaultModel(): Model {
|
|
||||||
return {
|
|
||||||
taskId: null,
|
|
||||||
fileId: null,
|
|
||||||
flowCopyList: [],
|
|
||||||
messageType: ['1'],
|
|
||||||
taskVariables: null,
|
|
||||||
variables: null,
|
|
||||||
assigneeMap: null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const fileList = ref<UploadFileInfo[]>([]);
|
|
||||||
// 抄送人
|
|
||||||
const selectCopyUserList = ref<Api.System.User[]>([]);
|
|
||||||
// 抄送人id
|
|
||||||
const selectCopyUserIds = ref<CommonType.IdType[]>([]);
|
|
||||||
// 下一节点列表
|
|
||||||
const nestNodeList = ref<Api.Workflow.FlowNode[]>([]);
|
|
||||||
|
|
||||||
const nickNameMap = ref<{ [key: string]: string }>({});
|
|
||||||
|
|
||||||
const assigneeSearchUserIds = ref<string | null>(null);
|
|
||||||
|
|
||||||
const selectAssigneeIds = ref<string[]>([]);
|
|
||||||
|
|
||||||
// 节点编码
|
|
||||||
const nodeCode = ref<string>('');
|
|
||||||
// 按钮权限
|
|
||||||
interface ButtonPerm {
|
|
||||||
pop: boolean;
|
|
||||||
trust: boolean;
|
|
||||||
transfer: boolean;
|
|
||||||
addSign: boolean;
|
|
||||||
subSign: boolean;
|
|
||||||
termination: boolean;
|
|
||||||
back: boolean;
|
|
||||||
copy: boolean;
|
|
||||||
}
|
|
||||||
const buttonPerm = reactive<ButtonPerm>(createDefaultButtonPerm());
|
|
||||||
|
|
||||||
function createDefaultButtonPerm(): ButtonPerm {
|
|
||||||
return {
|
|
||||||
pop: false,
|
|
||||||
trust: false,
|
|
||||||
transfer: false,
|
|
||||||
addSign: false,
|
|
||||||
subSign: false,
|
|
||||||
termination: false,
|
|
||||||
back: false,
|
|
||||||
copy: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function initDefault() {
|
|
||||||
selectCopyUserList.value = [];
|
|
||||||
selectCopyUserIds.value = [];
|
|
||||||
nickNameMap.value = {};
|
|
||||||
assigneeSearchUserIds.value = null;
|
|
||||||
selectAssigneeIds.value = [];
|
|
||||||
nodeCode.value = '';
|
|
||||||
Object.assign(model, createDefaultModel());
|
|
||||||
Object.assign(buttonPerm, createDefaultButtonPerm());
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getTask() {
|
|
||||||
startBtnLoading();
|
|
||||||
startBaseFormLoading();
|
|
||||||
const { error, data } = await fetchGetTask(props.taskId);
|
|
||||||
if (error) {
|
|
||||||
endBtnLoading();
|
|
||||||
endBaseFormLoading();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
task.value = data;
|
|
||||||
task.value.buttonList?.forEach(item => {
|
|
||||||
buttonPerm[item.code as keyof ButtonPerm] = item.show!;
|
|
||||||
});
|
|
||||||
endBtnLoading();
|
|
||||||
const { error: nextNodeError, data: nextNodeData } = await fetchGetkNextNode({
|
|
||||||
taskId: props.taskId,
|
|
||||||
taskVariables: props.taskVariables
|
|
||||||
});
|
|
||||||
endBaseFormLoading();
|
|
||||||
if (nextNodeError) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
nestNodeList.value = nextNodeData;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleSubmit() {
|
|
||||||
if (buttonPerm.pop && nestNodeList.value?.length) {
|
|
||||||
const hasEmptyAssignee = nestNodeList.value.some(e => !model.assigneeMap || !model.assigneeMap[e.nodeCode]);
|
|
||||||
if (hasEmptyAssignee) {
|
|
||||||
window.$message?.error('请选择审批人!');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
model.assigneeMap = {};
|
|
||||||
}
|
|
||||||
if (selectCopyUserList.value?.length) {
|
|
||||||
model.flowCopyList = selectCopyUserList.value.map(e => ({
|
|
||||||
userId: e.userId,
|
|
||||||
userName: e.nickName
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
if (fileList.value?.length) {
|
|
||||||
const fileIds = fileList.value.map(item => item.id);
|
|
||||||
model.fileId = fileIds.join(',');
|
|
||||||
}
|
|
||||||
model.taskId = props.taskId;
|
|
||||||
model.variables = props.taskVariables;
|
|
||||||
startBtnLoading();
|
|
||||||
startBaseFormLoading();
|
|
||||||
try {
|
|
||||||
const { error } = await fetchCompleteTask(model);
|
|
||||||
if (error) return;
|
|
||||||
window.$message?.success('提交成功');
|
|
||||||
visible.value = false;
|
|
||||||
emit('finished');
|
|
||||||
} catch (error) {
|
|
||||||
window.$message?.error(`提交失败,请稍后重试,${error}`);
|
|
||||||
} finally {
|
|
||||||
endBtnLoading();
|
|
||||||
endBaseFormLoading();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleCopyConfirm(userIds: CommonType.IdType[], users?: Api.System.User[]) {
|
|
||||||
selectCopyUserList.value = users || [];
|
|
||||||
selectCopyUserIds.value = userIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleAssigneeOpen(item: Api.Workflow.FlowNode) {
|
|
||||||
if (!item.permissionFlag) {
|
|
||||||
window.$message?.error('没有可选人员,请联系管理员!');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
assigneeSearchUserIds.value = item.permissionFlag;
|
|
||||||
nodeCode.value = item.nodeCode;
|
|
||||||
selectAssigneeIds.value = model.assigneeMap?.[item.nodeCode]?.split(',') || [];
|
|
||||||
openAssigneeModal();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleAssigneeConfirm(userIds: CommonType.IdType[], users?: Api.System.User[]) {
|
|
||||||
// 更新当前节点的审批人
|
|
||||||
if (!model.assigneeMap) model.assigneeMap = {};
|
|
||||||
model.assigneeMap[nodeCode.value] = userIds.join(',');
|
|
||||||
nickNameMap.value[nodeCode.value] = users?.map(item => item.nickName).join(',') || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleCopyTagClose(index?: number) {
|
|
||||||
if (index !== undefined) {
|
|
||||||
// 删除指定索引的用户
|
|
||||||
selectCopyUserIds.value = selectCopyUserIds.value.filter((_, i) => i !== index);
|
|
||||||
selectCopyUserList.value = selectCopyUserList.value.filter((_, i) => i !== index);
|
|
||||||
} else {
|
|
||||||
// 清空所有用户
|
|
||||||
selectCopyUserList.value = [];
|
|
||||||
selectCopyUserIds.value = [];
|
|
||||||
model.flowCopyList = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleAssigneeTagClose(code: string, index?: number) {
|
|
||||||
if (!model.assigneeMap?.[code]) return;
|
|
||||||
|
|
||||||
// 获取当前节点的用户ID列表和名称列表
|
|
||||||
const userIds = model.assigneeMap[code].split(',');
|
|
||||||
const nickNames = nickNameMap.value[code]?.split(',') || [];
|
|
||||||
|
|
||||||
if (index !== undefined) {
|
|
||||||
// 删除指定索引的用户
|
|
||||||
// 使用filter方式移除指定索引的元素
|
|
||||||
const newUserIds = userIds.filter((_, i) => i !== index);
|
|
||||||
const newNickNames = nickNames.filter((_, i) => i !== index);
|
|
||||||
|
|
||||||
// 更新数据
|
|
||||||
model.assigneeMap[code] = newUserIds.join(',');
|
|
||||||
nickNameMap.value[code] = newNickNames.join(',');
|
|
||||||
} else {
|
|
||||||
// 清空所有用户
|
|
||||||
model.assigneeMap[code] = '';
|
|
||||||
nickNameMap.value[code] = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TaskOperationOptions {
|
|
||||||
userIds: CommonType.IdType[];
|
|
||||||
operation: 'delegateTask' | 'transferTask' | 'addSignature';
|
|
||||||
confirmText: string;
|
|
||||||
successMessage: string;
|
|
||||||
closeModal: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleTaskOperationConfirm(options: TaskOperationOptions) {
|
|
||||||
const { userIds, operation, confirmText, successMessage, closeModal } = options;
|
|
||||||
|
|
||||||
const taskModel = {
|
|
||||||
taskId: props.taskId,
|
|
||||||
userId: userIds[0],
|
|
||||||
message: model.message
|
|
||||||
};
|
|
||||||
|
|
||||||
window.$dialog?.warning({
|
|
||||||
title: '提示',
|
|
||||||
content: `是否确认${confirmText}?`,
|
|
||||||
positiveText: `确认${confirmText}`,
|
|
||||||
positiveButtonProps: {
|
|
||||||
type: 'primary'
|
|
||||||
},
|
|
||||||
negativeText: '取消',
|
|
||||||
onPositiveClick: async () => {
|
|
||||||
startBtnLoading();
|
|
||||||
startBaseFormLoading();
|
|
||||||
const { error } = await fetchTaskOperate(taskModel, operation);
|
|
||||||
endBtnLoading();
|
|
||||||
endBaseFormLoading();
|
|
||||||
if (error) return;
|
|
||||||
window.$message?.success(successMessage);
|
|
||||||
closeModal();
|
|
||||||
visible.value = false;
|
|
||||||
emit('finished');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 委托
|
|
||||||
function handleDelegateConfirm(userIds: CommonType.IdType[]) {
|
|
||||||
handleTaskOperationConfirm({
|
|
||||||
userIds,
|
|
||||||
operation: 'delegateTask',
|
|
||||||
confirmText: '委托',
|
|
||||||
successMessage: '委托成功',
|
|
||||||
closeModal: closeDelegateModal
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 转办
|
|
||||||
function handleTransferConfirm(userIds: CommonType.IdType[]) {
|
|
||||||
handleTaskOperationConfirm({
|
|
||||||
userIds,
|
|
||||||
operation: 'transferTask',
|
|
||||||
confirmText: '转办',
|
|
||||||
successMessage: '转办成功',
|
|
||||||
closeModal: closeTransferModal
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 加签
|
|
||||||
function handleAddSignatureConfirm(userIds: CommonType.IdType[]) {
|
|
||||||
handleTaskOperationConfirm({
|
|
||||||
userIds,
|
|
||||||
operation: 'addSignature',
|
|
||||||
confirmText: '加签',
|
|
||||||
successMessage: '加签成功',
|
|
||||||
closeModal: closeAddSignatureModal
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 减签
|
|
||||||
function handleReduceSubmit() {
|
|
||||||
visible.value = false;
|
|
||||||
emit('finished');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 终止
|
|
||||||
function handleTerminate() {
|
|
||||||
const terminateModel = {
|
|
||||||
taskId: props.taskId,
|
|
||||||
comment: model.message
|
|
||||||
};
|
|
||||||
window.$dialog?.warning({
|
|
||||||
title: '提示',
|
|
||||||
content: '是否确认终止?',
|
|
||||||
positiveText: '确认',
|
|
||||||
positiveButtonProps: {
|
|
||||||
type: 'primary'
|
|
||||||
},
|
|
||||||
negativeText: '取消',
|
|
||||||
onPositiveClick: async () => {
|
|
||||||
startBtnLoading();
|
|
||||||
startBaseFormLoading();
|
|
||||||
const { error } = await fetchTerminateTask(terminateModel);
|
|
||||||
endBtnLoading();
|
|
||||||
endBaseFormLoading();
|
|
||||||
if (error) return;
|
|
||||||
window.$message?.success('终止成功');
|
|
||||||
visible.value = false;
|
|
||||||
emit('finished');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleBackSubmit() {
|
|
||||||
visible.value = false;
|
|
||||||
emit('finished');
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(visible, () => {
|
|
||||||
if (visible.value) {
|
|
||||||
initDefault();
|
|
||||||
getTask();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NModal v-model:show="visible" preset="card" class="w-800px" :title="title" :native-scrollbar="false" closable>
|
|
||||||
<NSpin :show="baseFormLoading">
|
|
||||||
<NForm :model="model" label-placement="left" :label-width="100">
|
|
||||||
<NFormItem label="通知方式" path="messageType">
|
|
||||||
<NCheckboxGroup v-model:value="model.messageType">
|
|
||||||
<NSpace item-style="display: flex;">
|
|
||||||
<NCheckbox
|
|
||||||
v-for="item in messageTypeOptions"
|
|
||||||
:key="item.value"
|
|
||||||
:disabled="item.value === '1'"
|
|
||||||
:value="item.value"
|
|
||||||
:label="item.label"
|
|
||||||
/>
|
|
||||||
</NSpace>
|
|
||||||
</NCheckboxGroup>
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="附件" path="fileId">
|
|
||||||
<FileUpload v-model:file-list="fileList" :file-size="20" :max="20" upload-type="file" :accept="accept" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem v-if="buttonPerm.copy" label="抄送人员">
|
|
||||||
<NSpace>
|
|
||||||
<NButton ghost type="primary" @click="openCopyModal">选择抄送人员</NButton>
|
|
||||||
<TagGroup
|
|
||||||
size="large"
|
|
||||||
:value="selectCopyUserList.map(item => item.nickName)"
|
|
||||||
:closable="true"
|
|
||||||
@close="handleCopyTagClose"
|
|
||||||
/>
|
|
||||||
</NSpace>
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem
|
|
||||||
v-if="buttonPerm.pop && nestNodeList && nestNodeList.length > 0"
|
|
||||||
label="下一步审批人"
|
|
||||||
path="assigneeMap"
|
|
||||||
>
|
|
||||||
<NSpace>
|
|
||||||
<div v-for="(item, index) in nestNodeList" :key="index">
|
|
||||||
<span>【{{ item.nodeName }}】:</span>
|
|
||||||
<NSpace>
|
|
||||||
<NButton ghost type="primary" @click="handleAssigneeOpen(item)">选择审批人员</NButton>
|
|
||||||
<NInput v-if="false" v-model:value="model.assigneeMap![item.nodeCode]" />
|
|
||||||
<TagGroup
|
|
||||||
size="large"
|
|
||||||
:value="nickNameMap[item.nodeCode]"
|
|
||||||
:closable="true"
|
|
||||||
@close="index => handleAssigneeTagClose(item.nodeCode, index)"
|
|
||||||
/>
|
|
||||||
</NSpace>
|
|
||||||
</div>
|
|
||||||
</NSpace>
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem v-if="isWaiting" label="审批意见" path="message">
|
|
||||||
<NInput v-model:value="model.message" type="textarea" />
|
|
||||||
</NFormItem>
|
|
||||||
</NForm>
|
|
||||||
</NSpin>
|
|
||||||
<template #footer>
|
|
||||||
<NSpace justify="end" :size="16">
|
|
||||||
<NButton @click="visible = false">{{ $t('common.cancel') }}</NButton>
|
|
||||||
<!-- 委托 -->
|
|
||||||
<NButton v-if="isWaiting && buttonPerm.trust" :loading="btnLoading" type="warning" @click="openDelegateModal">
|
|
||||||
委托
|
|
||||||
</NButton>
|
|
||||||
<!-- 转办 -->
|
|
||||||
<NButton
|
|
||||||
v-if="isWaiting && buttonPerm.transfer"
|
|
||||||
:loading="btnLoading"
|
|
||||||
type="warning"
|
|
||||||
@click="openTransferModal"
|
|
||||||
>
|
|
||||||
转办
|
|
||||||
</NButton>
|
|
||||||
<!-- 加签 -->
|
|
||||||
<NButton
|
|
||||||
v-if="isWaiting && buttonPerm.addSign && Number(task?.nodeRatio) > 0"
|
|
||||||
:loading="btnLoading"
|
|
||||||
type="warning"
|
|
||||||
@click="openAddSignatureModal"
|
|
||||||
>
|
|
||||||
加签
|
|
||||||
</NButton>
|
|
||||||
<!-- 减签 -->
|
|
||||||
<NButton
|
|
||||||
v-if="isWaiting && buttonPerm.subSign && Number(task?.nodeRatio) > 0"
|
|
||||||
:loading="btnLoading"
|
|
||||||
type="warning"
|
|
||||||
@click="openReduceSignatureModal"
|
|
||||||
>
|
|
||||||
减签
|
|
||||||
</NButton>
|
|
||||||
<!-- 终止 -->
|
|
||||||
<NButton v-if="isWaiting && buttonPerm.termination" :loading="btnLoading" type="error" @click="handleTerminate">
|
|
||||||
终止
|
|
||||||
</NButton>
|
|
||||||
<!-- 驳回 -->
|
|
||||||
<NButton v-if="isWaiting && buttonPerm.back" :loading="btnLoading" type="error" @click="openBackModal">
|
|
||||||
驳回
|
|
||||||
</NButton>
|
|
||||||
<NButton :loading="btnLoading" type="primary" @click="handleSubmit">提交</NButton>
|
|
||||||
</NSpace>
|
|
||||||
</template>
|
|
||||||
<!-- 抄送人员选择 -->
|
|
||||||
<UserSelectModal
|
|
||||||
v-model:visible="copyVisible"
|
|
||||||
:row-keys="selectCopyUserIds"
|
|
||||||
multiple
|
|
||||||
@confirm="handleCopyConfirm"
|
|
||||||
/>
|
|
||||||
<!-- 下一步审批人员选择 -->
|
|
||||||
<UserSelectModal
|
|
||||||
v-model:visible="assigneeVisible"
|
|
||||||
:row-keys="selectAssigneeIds"
|
|
||||||
:search-user-ids="assigneeSearchUserIds"
|
|
||||||
multiple
|
|
||||||
@confirm="handleAssigneeConfirm"
|
|
||||||
/>
|
|
||||||
<!-- 转办 -->
|
|
||||||
<UserSelectModal v-model:visible="transferVisible" @confirm="handleTransferConfirm" />
|
|
||||||
<!-- 委托 -->
|
|
||||||
<UserSelectModal v-model:visible="delegateVisible" @confirm="handleDelegateConfirm" />
|
|
||||||
<!-- 加签 -->
|
|
||||||
<UserSelectModal v-model:visible="addSignatureVisible" @confirm="handleAddSignatureConfirm" />
|
|
||||||
<!-- 减签 -->
|
|
||||||
<ReduceSignatureModal v-model:visible="reduceSignatureVisible" :task="task!" @reduce-submit="handleReduceSubmit" />
|
|
||||||
<!-- 驳回 -->
|
|
||||||
<BackTaskModal v-model:visible="backVisible" :task="task!" @submit="handleBackSubmit" />
|
|
||||||
</NModal>
|
|
||||||
</template>
|
|
@ -1,95 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import { reactive, watch } from 'vue';
|
|
||||||
import { messageTypeOptions } from '@/constants/workflow';
|
|
||||||
import { fetchTaskUrge } from '@/service/api/workflow';
|
|
||||||
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'FlowUrgeModal'
|
|
||||||
});
|
|
||||||
|
|
||||||
const { createRequiredRule } = useFormRules();
|
|
||||||
const { formRef, validate } = useNaiveForm();
|
|
||||||
interface Props {
|
|
||||||
taskIds: CommonType.IdType[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = defineProps<Props>();
|
|
||||||
|
|
||||||
interface Emits {
|
|
||||||
(e: 'submit'): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
|
||||||
|
|
||||||
const visible = defineModel<boolean>('visible', {
|
|
||||||
default: false
|
|
||||||
});
|
|
||||||
|
|
||||||
type Model = Api.Workflow.TaskUrgeOperateParams;
|
|
||||||
const model = reactive(createDefaultModel());
|
|
||||||
|
|
||||||
function createDefaultModel(): Model {
|
|
||||||
return {
|
|
||||||
taskIdList: props.taskIds,
|
|
||||||
messageType: ['1'],
|
|
||||||
message: ''
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
type RuleKey = Extract<keyof Model, 'taskIdList' | 'messageType' | 'message'>;
|
|
||||||
|
|
||||||
const rules: Record<RuleKey, App.Global.FormRule> = {
|
|
||||||
taskIdList: createRequiredRule('任务ID不能为空'),
|
|
||||||
messageType: createRequiredRule('消息提醒不能为空'),
|
|
||||||
message: createRequiredRule('消息内容不能为空')
|
|
||||||
};
|
|
||||||
|
|
||||||
function closeDrawer() {
|
|
||||||
visible.value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(visible, () => {
|
|
||||||
if (visible.value) {
|
|
||||||
Object.assign(model, createDefaultModel());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
async function handleSubmit() {
|
|
||||||
await validate();
|
|
||||||
const { error } = await fetchTaskUrge(model);
|
|
||||||
if (error) return;
|
|
||||||
window.$message?.success('催办成功');
|
|
||||||
closeDrawer();
|
|
||||||
emit('submit');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NModal v-model:show="visible" preset="card" class="w-800px" title="催办" :native-scrollbar="false" closable>
|
|
||||||
<NForm ref="formRef" :model="model" :rules="rules">
|
|
||||||
<NFormItem label="消息提醒" path="messageType">
|
|
||||||
<NCheckboxGroup v-model:value="model.messageType">
|
|
||||||
<NSpace item-style="display: flex;">
|
|
||||||
<NCheckbox
|
|
||||||
v-for="item in messageTypeOptions"
|
|
||||||
:key="item.value"
|
|
||||||
:disabled="item.value === '1'"
|
|
||||||
:value="item.value"
|
|
||||||
:label="item.label"
|
|
||||||
/>
|
|
||||||
</NSpace>
|
|
||||||
</NCheckboxGroup>
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="消息呢用" path="message">
|
|
||||||
<NInput v-model:value="model.message" type="textarea" />
|
|
||||||
</NFormItem>
|
|
||||||
</NForm>
|
|
||||||
<template #footer>
|
|
||||||
<NSpace justify="end" :size="16">
|
|
||||||
<NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
|
|
||||||
<NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
|
|
||||||
</NSpace>
|
|
||||||
</template>
|
|
||||||
</NModal>
|
|
||||||
</template>
|
|
@ -1,317 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { computed, reactive, ref, watch } from 'vue';
|
|
||||||
import dayjs from 'dayjs';
|
|
||||||
import { useBoolean, useLoading } from '@sa/hooks';
|
|
||||||
import { flowCodeTypeOptions, flowCodeTypeRecord, leaveTypeOptions, leaveTypeRecord } from '@/constants/workflow';
|
|
||||||
import { fetchCreateLeave, fetchGetLeaveDetail, fetchStartWorkflow, fetchUpdateLeave } from '@/service/api/workflow';
|
|
||||||
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
|
||||||
import { useDict } from '@/hooks/business/dict';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
import ApprovalInfoPanel from '@/components/workflow/approval-info-panel.vue';
|
|
||||||
import FlowTaskApprovalModal from '@/components/workflow/flow-task-approval-modal.vue';
|
|
||||||
import FlowDrawer from '@/components/workflow/flow-drawer.vue';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'LeaveEdit'
|
|
||||||
});
|
|
||||||
|
|
||||||
useDict('wf_task_status');
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
operateType: CommonType.WorkflowTableOperateType;
|
|
||||||
/** 业务ID */
|
|
||||||
businessId?: CommonType.IdType;
|
|
||||||
taskId?: CommonType.IdType;
|
|
||||||
/** the edit row data */
|
|
||||||
rowData?: Api.Workflow.Leave | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
|
||||||
rowData: null,
|
|
||||||
businessId: undefined,
|
|
||||||
taskId: undefined
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Emits {
|
|
||||||
(e: 'submitted'): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
|
||||||
|
|
||||||
const visible = defineModel<boolean>('visible', {
|
|
||||||
default: false
|
|
||||||
});
|
|
||||||
const approvalInfoPanelRef = ref<InstanceType<typeof ApprovalInfoPanel>>();
|
|
||||||
|
|
||||||
const { bool: taskApplyVisible, setTrue: setTaskApplyVisible } = useBoolean();
|
|
||||||
const { loading, startLoading, endLoading } = useLoading();
|
|
||||||
|
|
||||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
|
||||||
const { createRequiredRule } = useFormRules();
|
|
||||||
const title = computed(() => {
|
|
||||||
const titles: Record<CommonType.WorkflowTableOperateType, string> = {
|
|
||||||
add: '新增请假申请',
|
|
||||||
edit: '编辑请假申请',
|
|
||||||
detail: '查看请假申请',
|
|
||||||
approval: '审批请假申请'
|
|
||||||
};
|
|
||||||
return titles[props.operateType];
|
|
||||||
});
|
|
||||||
const readonly = computed(() => {
|
|
||||||
return props.operateType === 'detail' || props.operateType === 'approval';
|
|
||||||
});
|
|
||||||
|
|
||||||
const taskId = ref<CommonType.IdType>(props.taskId!);
|
|
||||||
|
|
||||||
const respLeave = ref<Api.Workflow.Leave>();
|
|
||||||
const startWorkflowResult = ref<Api.Workflow.StartWorkflowResult>();
|
|
||||||
|
|
||||||
type Model = Api.Workflow.LeaveOperateParams & {
|
|
||||||
flowCode: Api.Workflow.FlowCodeType;
|
|
||||||
};
|
|
||||||
|
|
||||||
const model: Model = reactive(createDefaultModel());
|
|
||||||
|
|
||||||
function createDefaultModel(): Model {
|
|
||||||
return {
|
|
||||||
flowCode: 'leave1',
|
|
||||||
leaveType: null,
|
|
||||||
startDate: null,
|
|
||||||
endDate: null,
|
|
||||||
leaveDays: null,
|
|
||||||
remark: ''
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
type ModelDetail = Api.Workflow.LeaveDetail & {
|
|
||||||
flowCode: Api.Workflow.FlowCodeType;
|
|
||||||
};
|
|
||||||
|
|
||||||
const modelDetail: ModelDetail = reactive(createDefaultModelDetail());
|
|
||||||
|
|
||||||
function createDefaultModelDetail(): ModelDetail {
|
|
||||||
return {
|
|
||||||
flowCode: 'leave1',
|
|
||||||
status: null,
|
|
||||||
id: null,
|
|
||||||
leaveType: null,
|
|
||||||
startDate: null,
|
|
||||||
endDate: null,
|
|
||||||
leaveDays: null,
|
|
||||||
remark: ''
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const showApprovalInfoPanel = computed(() => {
|
|
||||||
return modelDetail.status !== 'draft';
|
|
||||||
});
|
|
||||||
type StartWorkflowModel = Api.Workflow.StartWorkflowOperateParams;
|
|
||||||
|
|
||||||
const startWorkflowModel: StartWorkflowModel = reactive(createDefaultStartWorkflowModel());
|
|
||||||
|
|
||||||
function createDefaultStartWorkflowModel(): StartWorkflowModel {
|
|
||||||
return {
|
|
||||||
flowCode: null,
|
|
||||||
businessId: null,
|
|
||||||
flowInstanceBizExtBo: null,
|
|
||||||
variables: {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const dateRange = computed<[string, string] | null>({
|
|
||||||
get: () => {
|
|
||||||
if (!model.startDate || !model.endDate) return null;
|
|
||||||
return [model.startDate, model.endDate] as [string, string];
|
|
||||||
},
|
|
||||||
set: (value: [string, string] | null) => {
|
|
||||||
if (value) {
|
|
||||||
model.startDate = value[0];
|
|
||||||
model.endDate = value[1];
|
|
||||||
// 计算请假天数
|
|
||||||
const start = dayjs(value[0]);
|
|
||||||
const end = dayjs(value[1]);
|
|
||||||
model.leaveDays = end.diff(start, 'day') + 1;
|
|
||||||
} else {
|
|
||||||
model.startDate = null;
|
|
||||||
model.endDate = null;
|
|
||||||
model.leaveDays = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
type RuleKey = Extract<keyof Model, 'id' | 'leaveType' | 'leaveDays' | 'startDate' | 'endDate'> | 'flowCode';
|
|
||||||
|
|
||||||
const rules: Record<RuleKey, App.Global.FormRule> = {
|
|
||||||
id: createRequiredRule('id不能为空'),
|
|
||||||
flowCode: createRequiredRule('流程类型不能为空'),
|
|
||||||
leaveType: createRequiredRule('请假类型不能为空'),
|
|
||||||
startDate: createRequiredRule('请假时间不能为空'),
|
|
||||||
endDate: createRequiredRule('结束时间不能为空'),
|
|
||||||
leaveDays: createRequiredRule('请假天数不能为空')
|
|
||||||
};
|
|
||||||
|
|
||||||
async function handleUpdateModelWhenEdit() {
|
|
||||||
if (props.operateType === 'add') {
|
|
||||||
Object.assign(model, createDefaultModel());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.rowData) {
|
|
||||||
Object.assign(model, props.rowData);
|
|
||||||
Object.assign(modelDetail, props.rowData);
|
|
||||||
} else {
|
|
||||||
const { error, data } = await fetchGetLeaveDetail(props.businessId!);
|
|
||||||
if (error) {
|
|
||||||
window.$message?.error(error.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Object.assign(model, data);
|
|
||||||
Object.assign(modelDetail, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeDrawer() {
|
|
||||||
visible.value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleOperate() {
|
|
||||||
await validate();
|
|
||||||
// request
|
|
||||||
if (props.operateType === 'add') {
|
|
||||||
const { leaveType, startDate, endDate, leaveDays, remark } = model;
|
|
||||||
const { error, data } = await fetchCreateLeave({ leaveType, startDate, endDate, leaveDays, remark });
|
|
||||||
if (error) return;
|
|
||||||
respLeave.value = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.operateType === 'edit') {
|
|
||||||
const { id, leaveType, startDate, endDate, leaveDays, remark } = model;
|
|
||||||
const { error, data } = await fetchUpdateLeave({ id, leaveType, startDate, endDate, leaveDays, remark });
|
|
||||||
if (error) return;
|
|
||||||
respLeave.value = data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleSaveDraft() {
|
|
||||||
await handleOperate();
|
|
||||||
window.$message?.success($t('common.updateSuccess'));
|
|
||||||
closeDrawer();
|
|
||||||
emit('submitted');
|
|
||||||
}
|
|
||||||
|
|
||||||
const taskVariables = ref<{ [key: string]: any }>({});
|
|
||||||
|
|
||||||
async function handleSubmit() {
|
|
||||||
await handleOperate();
|
|
||||||
window.$message?.success($t('common.updateSuccess'));
|
|
||||||
// 提交流程
|
|
||||||
startWorkflowModel.businessId = respLeave.value?.id;
|
|
||||||
startWorkflowModel.flowCode = model.flowCode;
|
|
||||||
startWorkflowModel.flowInstanceBizExtBo = {
|
|
||||||
businessCode: respLeave.value?.applyCode,
|
|
||||||
businessTitle: '请假申请'
|
|
||||||
};
|
|
||||||
taskVariables.value = {
|
|
||||||
leaveDays: respLeave.value?.leaveDays,
|
|
||||||
userList: ['1', '3', '4']
|
|
||||||
};
|
|
||||||
startWorkflowModel.variables = taskVariables.value;
|
|
||||||
const { error, data } = await fetchStartWorkflow(startWorkflowModel);
|
|
||||||
if (error) return;
|
|
||||||
startWorkflowResult.value = data;
|
|
||||||
taskId.value = data.taskId!;
|
|
||||||
setTaskApplyVisible();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleTaskFinished() {
|
|
||||||
closeDrawer();
|
|
||||||
emit('submitted');
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleApproval() {
|
|
||||||
setTaskApplyVisible();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function initializeData() {
|
|
||||||
if (visible.value) {
|
|
||||||
startLoading();
|
|
||||||
await handleUpdateModelWhenEdit();
|
|
||||||
restoreValidation();
|
|
||||||
|
|
||||||
if (showApprovalInfoPanel.value) {
|
|
||||||
approvalInfoPanelRef.value?.initData();
|
|
||||||
}
|
|
||||||
endLoading();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(visible, initializeData, { immediate: true });
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<FlowDrawer
|
|
||||||
v-model:visible="visible"
|
|
||||||
:title="title"
|
|
||||||
:loading="loading"
|
|
||||||
:operate-type="operateType"
|
|
||||||
:status="modelDetail.status"
|
|
||||||
@close="closeDrawer"
|
|
||||||
@save-draft="handleSaveDraft"
|
|
||||||
@submit="handleSubmit"
|
|
||||||
@approval="handleApproval"
|
|
||||||
>
|
|
||||||
<div :class="loading ? 'hidden' : ''">
|
|
||||||
<div v-if="!readonly" class="h-full">
|
|
||||||
<NForm ref="formRef" :model="model" :rules="rules">
|
|
||||||
<NFormItem label="流程类型" path="flowCode">
|
|
||||||
<NSelect v-model:value="model.flowCode" placeholder="请输入流程类型" :options="flowCodeTypeOptions" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="请假类型" path="leaveType">
|
|
||||||
<NSelect v-model:value="model.leaveType" placeholder="请输入请假类型" :options="leaveTypeOptions" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="请假时间" path="startDate">
|
|
||||||
<NDatePicker
|
|
||||||
v-model:formatted-value="dateRange"
|
|
||||||
class="w-full"
|
|
||||||
type="datetimerange"
|
|
||||||
format="yyyy-MM-dd HH:mm:ss"
|
|
||||||
clearable
|
|
||||||
/>
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="请假天数" path="leaveDays">
|
|
||||||
<NInputNumber v-model:value="model.leaveDays" class="w-full" disabled placeholder="请输入请假天数" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="请假原因" path="remark">
|
|
||||||
<NInput v-model:value="model.remark" placeholder="请输入请假原因" />
|
|
||||||
</NFormItem>
|
|
||||||
</NForm>
|
|
||||||
</div>
|
|
||||||
<div v-else>
|
|
||||||
<NDescriptions size="small" bordered :column="2" label-placement="left">
|
|
||||||
<NDescriptionsItem label="流程类型">
|
|
||||||
{{ flowCodeTypeRecord[modelDetail.flowCode] }}
|
|
||||||
</NDescriptionsItem>
|
|
||||||
<NDescriptionsItem label="请假类型">
|
|
||||||
<NTag type="info">{{ leaveTypeRecord[modelDetail.leaveType!] }}</NTag>
|
|
||||||
</NDescriptionsItem>
|
|
||||||
<NDescriptionsItem label="请假时间">
|
|
||||||
{{ `${modelDetail.startDate} 至 ${modelDetail.endDate}` }}
|
|
||||||
</NDescriptionsItem>
|
|
||||||
<NDescriptionsItem label="请假天数">{{ modelDetail.leaveDays }} 天</NDescriptionsItem>
|
|
||||||
<NDescriptionsItem label="请假原因">
|
|
||||||
{{ modelDetail.remark || '-' }}
|
|
||||||
</NDescriptionsItem>
|
|
||||||
</NDescriptions>
|
|
||||||
<!-- 审批信息 -->
|
|
||||||
<ApprovalInfoPanel v-if="showApprovalInfoPanel" ref="approvalInfoPanelRef" :business-id="modelDetail.id!" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</FlowDrawer>
|
|
||||||
<FlowTaskApprovalModal
|
|
||||||
v-model:visible="taskApplyVisible"
|
|
||||||
:task-id="taskId"
|
|
||||||
:task-variables="taskVariables"
|
|
||||||
@finished="handleTaskFinished"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
@ -1,154 +0,0 @@
|
|||||||
<script lang="tsx" setup>
|
|
||||||
import { reactive, ref, watch } from 'vue';
|
|
||||||
import { useLoading } from '@sa/hooks';
|
|
||||||
import { fetchGetCurrentTaskAllUser, fetchTaskOperate } from '@/service/api/workflow/task';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
import ButtonIcon from '@/components/custom/button-icon.vue';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'ReduceSignatureModal'
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
task: Api.Workflow.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = defineProps<Props>();
|
|
||||||
|
|
||||||
const visible = defineModel<boolean>('visible', {
|
|
||||||
default: false
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Emits {
|
|
||||||
(e: 'reduceSubmit'): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
|
||||||
|
|
||||||
const { loading, startLoading, endLoading } = useLoading();
|
|
||||||
type UserTaskModel = Api.System.User & { nodeName: string };
|
|
||||||
|
|
||||||
const userData = ref<UserTaskModel[]>([]);
|
|
||||||
|
|
||||||
type Model = Api.Workflow.TaskOperateParams;
|
|
||||||
const model: Model = reactive(createDefaultModel());
|
|
||||||
|
|
||||||
const checkedRowKeys = ref<CommonType.IdType[]>([]);
|
|
||||||
|
|
||||||
function createDefaultModel(): Model {
|
|
||||||
return {
|
|
||||||
taskId: null,
|
|
||||||
userId: undefined,
|
|
||||||
userIds: undefined,
|
|
||||||
message: ''
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const columns = ref<NaiveUI.TableColumn<UserTaskModel>[]>([
|
|
||||||
{
|
|
||||||
type: 'selection',
|
|
||||||
align: 'center',
|
|
||||||
width: 50
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '节点名称',
|
|
||||||
key: 'nodeName',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '办理人员',
|
|
||||||
key: 'nickName',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'operate',
|
|
||||||
title: $t('common.operate'),
|
|
||||||
align: 'center',
|
|
||||||
width: 130,
|
|
||||||
render(row) {
|
|
||||||
return (
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="error"
|
|
||||||
icon="material-symbols:delete-outline"
|
|
||||||
tooltipContent={'减签'}
|
|
||||||
popconfirmContent={'是否确认减签?'}
|
|
||||||
onPositiveClick={() => handleReduceSignature([row.userId])}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
async function handleReduceSignature(userIds: CommonType.IdType[]) {
|
|
||||||
model.taskId = props.task.id;
|
|
||||||
model.userIds = userIds;
|
|
||||||
const { error } = await fetchTaskOperate(model, 'reductionSignature');
|
|
||||||
if (error) return;
|
|
||||||
window.$message?.success('减签成功');
|
|
||||||
handleCloseDrawer();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getTaskAllUser() {
|
|
||||||
startLoading();
|
|
||||||
const { error, data } = await fetchGetCurrentTaskAllUser(props.task.id);
|
|
||||||
if (error) return;
|
|
||||||
userData.value = data.map(item => ({
|
|
||||||
...item,
|
|
||||||
nodeName: props.task.nodeName
|
|
||||||
}));
|
|
||||||
endLoading();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleCloseDrawer() {
|
|
||||||
visible.value = false;
|
|
||||||
emit('reduceSubmit');
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(visible, async () => {
|
|
||||||
if (visible.value) {
|
|
||||||
await getTaskAllUser();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NModal v-model:show="visible" class="w-700px" preset="card" title="待减签人员">
|
|
||||||
<NCard class="h-full card-wrapper">
|
|
||||||
<NSpace wrap justify="space-between" class="mb-16px lt-sm:w-200px">
|
|
||||||
<TableRowCheckAlert v-model:checked-row-keys="checkedRowKeys" />
|
|
||||||
<NButton
|
|
||||||
size="small"
|
|
||||||
ghost
|
|
||||||
type="error"
|
|
||||||
:disabled="checkedRowKeys.length === 0"
|
|
||||||
@click="handleReduceSignature(checkedRowKeys)"
|
|
||||||
>
|
|
||||||
<template #icon>
|
|
||||||
<icon-material-symbols:delete-outline class="text-icon" />
|
|
||||||
</template>
|
|
||||||
批量减签
|
|
||||||
</NButton>
|
|
||||||
</NSpace>
|
|
||||||
<NDataTable
|
|
||||||
v-model:checked-row-keys="checkedRowKeys"
|
|
||||||
class="h-400px"
|
|
||||||
flex-height
|
|
||||||
:row-key="row => row.userId"
|
|
||||||
size="small"
|
|
||||||
:columns="columns"
|
|
||||||
:data="userData"
|
|
||||||
:loading="loading"
|
|
||||||
/>
|
|
||||||
</NCard>
|
|
||||||
</NModal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.n-alert {
|
|
||||||
--n-padding: 5px 13px !important;
|
|
||||||
--n-icon-margin: 6px 8px 0 12px !important;
|
|
||||||
--n-icon-size: 20px !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,90 +0,0 @@
|
|||||||
import { transformRecordToOption } from '@/utils/common';
|
|
||||||
|
|
||||||
export const cooperateTypeRecord: Record<Api.Workflow.CooperateType, string> = {
|
|
||||||
1: '审批',
|
|
||||||
2: '转办',
|
|
||||||
3: '委派',
|
|
||||||
4: '会签',
|
|
||||||
5: '票签',
|
|
||||||
6: '加签',
|
|
||||||
7: '减签'
|
|
||||||
};
|
|
||||||
|
|
||||||
export const cooperateTypeOptions = transformRecordToOption(cooperateTypeRecord);
|
|
||||||
|
|
||||||
export const businessStatusRecord: Record<Api.Workflow.BusinessStatus, string> = {
|
|
||||||
cancel: '已撤销',
|
|
||||||
draft: '草稿',
|
|
||||||
waiting: '待审批',
|
|
||||||
finish: '已完成',
|
|
||||||
invalid: '已作废',
|
|
||||||
back: '已退回',
|
|
||||||
termination: '已终止'
|
|
||||||
};
|
|
||||||
|
|
||||||
export const businessStatusOptions = transformRecordToOption(businessStatusRecord);
|
|
||||||
|
|
||||||
export const messageTypeRecord: Record<Api.Workflow.MessageType, string> = {
|
|
||||||
'1': '站内信',
|
|
||||||
'2': '邮件',
|
|
||||||
'3': '短信'
|
|
||||||
};
|
|
||||||
|
|
||||||
export const messageTypeOptions = transformRecordToOption(messageTypeRecord);
|
|
||||||
|
|
||||||
export const flowCodeTypeRecord: Record<Api.Workflow.FlowCodeType, string> = {
|
|
||||||
leave1: '请假申请-普通',
|
|
||||||
leave2: '请假申请-排他网关',
|
|
||||||
leave3: '请假申请-并行网关',
|
|
||||||
leave4: '请假申请-会签',
|
|
||||||
leave5: '请假申请-并行会签网关',
|
|
||||||
leave6: '请假申请-排他并行会签'
|
|
||||||
};
|
|
||||||
|
|
||||||
export const flowCodeTypeOptions = transformRecordToOption(flowCodeTypeRecord);
|
|
||||||
|
|
||||||
/** leave type */
|
|
||||||
export const leaveTypeRecord: Record<Api.Workflow.LeaveType, string> = {
|
|
||||||
'1': '事假',
|
|
||||||
'2': '调休',
|
|
||||||
'3': '病假',
|
|
||||||
'4': '婚假'
|
|
||||||
};
|
|
||||||
|
|
||||||
export const leaveTypeOptions = transformRecordToOption(leaveTypeRecord);
|
|
||||||
|
|
||||||
/** workflow publish status */
|
|
||||||
export const workflowPublishStatusRecord: Record<Api.Workflow.WorkflowPublishStatus, string> = {
|
|
||||||
'0': '未发布',
|
|
||||||
'1': '已发布',
|
|
||||||
'9': '失效'
|
|
||||||
};
|
|
||||||
|
|
||||||
export const workflowPublishStatusOptions = transformRecordToOption(workflowPublishStatusRecord);
|
|
||||||
|
|
||||||
/** node type */
|
|
||||||
export const workflowNodeTypeRecord: Record<Api.Workflow.WorkflowNodeType, string> = {
|
|
||||||
0: '开始节点',
|
|
||||||
1: '中间节点',
|
|
||||||
2: '结束节点',
|
|
||||||
3: '互斥网关',
|
|
||||||
4: '并行网关'
|
|
||||||
};
|
|
||||||
|
|
||||||
export const workflowNodeTypeOptions = transformRecordToOption(workflowNodeTypeRecord);
|
|
||||||
|
|
||||||
/** definition designer mode */
|
|
||||||
export const definitionDesignerModeRecord: Record<Api.Workflow.DefinitionDesignerMode, string> = {
|
|
||||||
CLASSICS: '经典模式',
|
|
||||||
MIMIC: '仿钉钉模式'
|
|
||||||
};
|
|
||||||
|
|
||||||
export const definitionDesignerModeOptions = transformRecordToOption(definitionDesignerModeRecord);
|
|
||||||
|
|
||||||
/** activity status */
|
|
||||||
export const workflowActivityStatusRecord: Record<Api.Workflow.WorkflowActivityStatus, string> = {
|
|
||||||
0: '挂起',
|
|
||||||
1: '激活'
|
|
||||||
};
|
|
||||||
|
|
||||||
export const workflowActivityStatusOptions = transformRecordToOption(workflowActivityStatusRecord);
|
|
@ -4,5 +4,6 @@ export enum SetupStoreId {
|
|||||||
Auth = 'auth-store',
|
Auth = 'auth-store',
|
||||||
Route = 'route-store',
|
Route = 'route-store',
|
||||||
Tab = 'tab-store',
|
Tab = 'tab-store',
|
||||||
Notice = 'notice-store'
|
Notice = 'notice-store',
|
||||||
|
Dict = 'dict-store'
|
||||||
}
|
}
|
||||||
|
@ -32,8 +32,7 @@ export function useTable<A extends NaiveUI.TableApiFn>(config: NaiveUI.NaiveTabl
|
|||||||
getData,
|
getData,
|
||||||
searchParams,
|
searchParams,
|
||||||
updateSearchParams,
|
updateSearchParams,
|
||||||
resetSearchParams,
|
resetSearchParams
|
||||||
updateApiFn
|
|
||||||
} = useHookTable<A, GetTableData<A>, TableColumn<NaiveUI.TableDataWithIndex<GetTableData<A>>>>({
|
} = useHookTable<A, GetTableData<A>, TableColumn<NaiveUI.TableDataWithIndex<GetTableData<A>>>>({
|
||||||
apiFn,
|
apiFn,
|
||||||
apiParams,
|
apiParams,
|
||||||
@ -213,8 +212,7 @@ export function useTable<A extends NaiveUI.TableApiFn>(config: NaiveUI.NaiveTabl
|
|||||||
getDataByPage,
|
getDataByPage,
|
||||||
searchParams,
|
searchParams,
|
||||||
updateSearchParams,
|
updateSearchParams,
|
||||||
resetSearchParams,
|
resetSearchParams
|
||||||
updateApiFn
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,23 +231,10 @@ const local: App.I18n.Schema = {
|
|||||||
demo: 'Demo',
|
demo: 'Demo',
|
||||||
demo_demo: 'Demo Table',
|
demo_demo: 'Demo Table',
|
||||||
demo_tree: 'Demo Tree',
|
demo_tree: 'Demo Tree',
|
||||||
workflow: 'Workflow',
|
|
||||||
workflow_category: 'Workflow Category',
|
|
||||||
exception: 'Exception',
|
exception: 'Exception',
|
||||||
exception_403: '403',
|
exception_403: '403',
|
||||||
exception_404: '404',
|
exception_404: '404',
|
||||||
exception_500: '500',
|
exception_500: '500'
|
||||||
workflow_design: 'Process Design',
|
|
||||||
workflow_spel: 'Process Spel',
|
|
||||||
'workflow_process-definition': 'Process Definition',
|
|
||||||
'workflow_process-instance': 'Process Instance',
|
|
||||||
workflow_task: 'Task',
|
|
||||||
'workflow_task_all-task-waiting': 'All Task Waiting',
|
|
||||||
workflow_leave: 'Leave Apply',
|
|
||||||
'workflow_task_my-document': 'My Document',
|
|
||||||
'workflow_task_task-waiting': 'My Task Waiting',
|
|
||||||
'workflow_task_task-finish': 'My Task Finish',
|
|
||||||
'workflow_task_task-copy': 'My Task Copy'
|
|
||||||
},
|
},
|
||||||
menu: {
|
menu: {
|
||||||
system_tenant: 'Tenant Management',
|
system_tenant: 'Tenant Management',
|
||||||
|
@ -231,23 +231,10 @@ const local: App.I18n.Schema = {
|
|||||||
demo: '测试',
|
demo: '测试',
|
||||||
demo_demo: '测试单表',
|
demo_demo: '测试单表',
|
||||||
demo_tree: '测试树表',
|
demo_tree: '测试树表',
|
||||||
workflow: '流程管理',
|
|
||||||
workflow_category: '流程分类',
|
|
||||||
exception: '异常页',
|
exception: '异常页',
|
||||||
exception_403: '403',
|
exception_403: '403',
|
||||||
exception_404: '404',
|
exception_404: '404',
|
||||||
exception_500: '500',
|
exception_500: '500'
|
||||||
workflow_design: '流程设计',
|
|
||||||
workflow_spel: '流程表达式',
|
|
||||||
'workflow_process-definition': '流程定义',
|
|
||||||
'workflow_process-instance': '流程实例',
|
|
||||||
workflow_task: '任务',
|
|
||||||
'workflow_task_all-task-waiting': '待办任务',
|
|
||||||
workflow_leave: '请假申请',
|
|
||||||
'workflow_task_my-document': '我发起的',
|
|
||||||
'workflow_task_task-waiting': '我的待办',
|
|
||||||
'workflow_task_task-finish': '我的已办',
|
|
||||||
'workflow_task_task-copy': '我的抄送'
|
|
||||||
},
|
},
|
||||||
menu: {
|
menu: {
|
||||||
system_tenant: '租户管理',
|
system_tenant: '租户管理',
|
||||||
|
@ -43,15 +43,4 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
|
|||||||
system_tenant: () => import("@/views/system/tenant/index.vue"),
|
system_tenant: () => import("@/views/system/tenant/index.vue"),
|
||||||
system_user: () => import("@/views/system/user/index.vue"),
|
system_user: () => import("@/views/system/user/index.vue"),
|
||||||
tool_gen: () => import("@/views/tool/gen/index.vue"),
|
tool_gen: () => import("@/views/tool/gen/index.vue"),
|
||||||
workflow_category: () => import("@/views/workflow/category/index.vue"),
|
|
||||||
workflow_design: () => import("@/views/workflow/design/index.vue"),
|
|
||||||
workflow_leave: () => import("@/views/workflow/leave/index.vue"),
|
|
||||||
"workflow_process-definition": () => import("@/views/workflow/process-definition/index.vue"),
|
|
||||||
"workflow_process-instance": () => import("@/views/workflow/process-instance/index.vue"),
|
|
||||||
workflow_spel: () => import("@/views/workflow/spel/index.vue"),
|
|
||||||
"workflow_task_all-task-waiting": () => import("@/views/workflow/task/all-task-waiting/index.vue"),
|
|
||||||
"workflow_task_my-document": () => import("@/views/workflow/task/my-document/index.vue"),
|
|
||||||
"workflow_task_task-copy": () => import("@/views/workflow/task/task-copy/index.vue"),
|
|
||||||
"workflow_task_task-finish": () => import("@/views/workflow/task/task-finish/index.vue"),
|
|
||||||
"workflow_task_task-waiting": () => import("@/views/workflow/task/task-waiting/index.vue"),
|
|
||||||
};
|
};
|
||||||
|
@ -331,125 +331,5 @@ export const generatedRoutes: GeneratedRoute[] = [
|
|||||||
icon: 'material-symbols:account-circle-full',
|
icon: 'material-symbols:account-circle-full',
|
||||||
hideInMenu: true
|
hideInMenu: true
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'workflow',
|
|
||||||
path: '/workflow',
|
|
||||||
component: 'layout.base',
|
|
||||||
meta: {
|
|
||||||
title: 'workflow',
|
|
||||||
i18nKey: 'route.workflow'
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
name: 'workflow_category',
|
|
||||||
path: '/workflow/category',
|
|
||||||
component: 'view.workflow_category',
|
|
||||||
meta: {
|
|
||||||
title: 'workflow_category',
|
|
||||||
i18nKey: 'route.workflow_category'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'workflow_design',
|
|
||||||
path: '/workflow/design',
|
|
||||||
component: 'view.workflow_design',
|
|
||||||
meta: {
|
|
||||||
title: 'workflow_design',
|
|
||||||
i18nKey: 'route.workflow_design'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'workflow_leave',
|
|
||||||
path: '/workflow/leave',
|
|
||||||
component: 'view.workflow_leave',
|
|
||||||
meta: {
|
|
||||||
title: 'workflow_leave',
|
|
||||||
i18nKey: 'route.workflow_leave'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'workflow_process-definition',
|
|
||||||
path: '/workflow/process-definition',
|
|
||||||
component: 'view.workflow_process-definition',
|
|
||||||
meta: {
|
|
||||||
title: 'workflow_process-definition',
|
|
||||||
i18nKey: 'route.workflow_process-definition'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'workflow_process-instance',
|
|
||||||
path: '/workflow/process-instance',
|
|
||||||
component: 'view.workflow_process-instance',
|
|
||||||
meta: {
|
|
||||||
title: 'workflow_process-instance',
|
|
||||||
i18nKey: 'route.workflow_process-instance'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'workflow_spel',
|
|
||||||
path: '/workflow/spel',
|
|
||||||
component: 'view.workflow_spel',
|
|
||||||
meta: {
|
|
||||||
title: 'workflow_spel',
|
|
||||||
i18nKey: 'route.workflow_spel'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'workflow_task',
|
|
||||||
path: '/workflow/task',
|
|
||||||
meta: {
|
|
||||||
title: 'workflow_task',
|
|
||||||
i18nKey: 'route.workflow_task'
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
name: 'workflow_task_all-task-waiting',
|
|
||||||
path: '/workflow/task/all-task-waiting',
|
|
||||||
component: 'view.workflow_task_all-task-waiting',
|
|
||||||
meta: {
|
|
||||||
title: 'workflow_task_all-task-waiting',
|
|
||||||
i18nKey: 'route.workflow_task_all-task-waiting'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'workflow_task_my-document',
|
|
||||||
path: '/workflow/task/my-document',
|
|
||||||
component: 'view.workflow_task_my-document',
|
|
||||||
meta: {
|
|
||||||
title: 'workflow_task_my-document',
|
|
||||||
i18nKey: 'route.workflow_task_my-document'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'workflow_task_task-copy',
|
|
||||||
path: '/workflow/task/task-copy',
|
|
||||||
component: 'view.workflow_task_task-copy',
|
|
||||||
meta: {
|
|
||||||
title: 'workflow_task_task-copy',
|
|
||||||
i18nKey: 'route.workflow_task_task-copy'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'workflow_task_task-finish',
|
|
||||||
path: '/workflow/task/task-finish',
|
|
||||||
component: 'view.workflow_task_task-finish',
|
|
||||||
meta: {
|
|
||||||
title: 'workflow_task_task-finish',
|
|
||||||
i18nKey: 'route.workflow_task_task-finish'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'workflow_task_task-waiting',
|
|
||||||
path: '/workflow/task/task-waiting',
|
|
||||||
component: 'view.workflow_task_task-waiting',
|
|
||||||
meta: {
|
|
||||||
title: 'workflow_task_task-waiting',
|
|
||||||
i18nKey: 'route.workflow_task_task-waiting'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@ -198,20 +198,7 @@ const routeMap: RouteMap = {
|
|||||||
"system_user": "/system/user",
|
"system_user": "/system/user",
|
||||||
"tool": "/tool",
|
"tool": "/tool",
|
||||||
"tool_gen": "/tool/gen",
|
"tool_gen": "/tool/gen",
|
||||||
"user-center": "/user-center",
|
"user-center": "/user-center"
|
||||||
"workflow": "/workflow",
|
|
||||||
"workflow_category": "/workflow/category",
|
|
||||||
"workflow_design": "/workflow/design",
|
|
||||||
"workflow_leave": "/workflow/leave",
|
|
||||||
"workflow_process-definition": "/workflow/process-definition",
|
|
||||||
"workflow_process-instance": "/workflow/process-instance",
|
|
||||||
"workflow_spel": "/workflow/spel",
|
|
||||||
"workflow_task": "/workflow/task",
|
|
||||||
"workflow_task_all-task-waiting": "/workflow/task/all-task-waiting",
|
|
||||||
"workflow_task_my-document": "/workflow/task/my-document",
|
|
||||||
"workflow_task_task-copy": "/workflow/task/task-copy",
|
|
||||||
"workflow_task_task-finish": "/workflow/task/task-finish",
|
|
||||||
"workflow_task_task-waiting": "/workflow/task/task-waiting"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
import { request } from '@/service/request';
|
|
||||||
|
|
||||||
/** 获取测试树列表 */
|
|
||||||
export function fetchGetCategoryList(params?: Api.Workflow.WorkflowCategorySearchParams) {
|
|
||||||
return request<Api.Workflow.WorkflowCategoryList>({
|
|
||||||
url: '/workflow/category/list',
|
|
||||||
method: 'get',
|
|
||||||
params
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 新增测试树 */
|
|
||||||
export function fetchCreateCategory(data: Api.Workflow.WorkflowCategoryOperateParams) {
|
|
||||||
return request<boolean>({
|
|
||||||
url: '/workflow/category',
|
|
||||||
method: 'post',
|
|
||||||
data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 修改测试树 */
|
|
||||||
export function fetchUpdateCategory(data: Api.Workflow.WorkflowCategoryOperateParams) {
|
|
||||||
return request<boolean>({
|
|
||||||
url: '/workflow/category',
|
|
||||||
method: 'put',
|
|
||||||
data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 删除分类 */
|
|
||||||
export function fetchDeleteCategory(id: CommonType.IdType) {
|
|
||||||
return request<boolean>({
|
|
||||||
url: `/workflow/category/${id}`,
|
|
||||||
method: 'delete'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 导出工作流分类 */
|
|
||||||
export function fetchExportCategory(params?: Api.Workflow.WorkflowCategorySearchParams) {
|
|
||||||
return request<boolean>({
|
|
||||||
url: '/workflow/category/export',
|
|
||||||
method: 'post',
|
|
||||||
params
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获取分类树 */
|
|
||||||
export function fetchGetCategoryTree() {
|
|
||||||
return request<Api.Common.CommonTreeRecord>({
|
|
||||||
url: '/workflow/category/categoryTree',
|
|
||||||
method: 'get'
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
import { request } from '@/service/request';
|
|
||||||
|
|
||||||
/** 获取流程定义列表 */
|
|
||||||
export function fetchGetDefinitionList(params?: Api.Workflow.DefinitionSearchParams) {
|
|
||||||
return request<Api.Workflow.DefinitionList>({
|
|
||||||
url: '/workflow/definition/list',
|
|
||||||
method: 'get',
|
|
||||||
params
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获取未发布流程定义列表 */
|
|
||||||
export function fetchGetUnPublishDefinitionList(params?: Api.Workflow.DefinitionSearchParams) {
|
|
||||||
return request<Api.Workflow.DefinitionList>({
|
|
||||||
url: '/workflow/definition/unPublishList',
|
|
||||||
method: 'get',
|
|
||||||
params
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 新增流程定义 */
|
|
||||||
export function fetchCreateDefinition(data: Api.Workflow.DefinitionOperateParams) {
|
|
||||||
return request<boolean>({
|
|
||||||
url: '/workflow/definition',
|
|
||||||
method: 'post',
|
|
||||||
data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 修改流程定义 */
|
|
||||||
export function fetchUpdateDefinition(data: Api.Workflow.DefinitionOperateParams) {
|
|
||||||
return request<boolean>({
|
|
||||||
url: '/workflow/definition',
|
|
||||||
method: 'put',
|
|
||||||
data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 批量删除流程定义 */
|
|
||||||
export function fetchBatchDeleteDefinition(ids: CommonType.IdType[]) {
|
|
||||||
return request<boolean>({
|
|
||||||
url: `/workflow/definition/${ids.join(',')}`,
|
|
||||||
method: 'delete'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 激活/挂起流程定义 */
|
|
||||||
export function fetchActiveDefinition(id: CommonType.IdType, active: boolean) {
|
|
||||||
return request<boolean>({
|
|
||||||
url: `/workflow/definition/active/${id}`,
|
|
||||||
method: 'put',
|
|
||||||
params: {
|
|
||||||
active
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 发布流程定义 */
|
|
||||||
export function fetchPublishDefinition(id: CommonType.IdType) {
|
|
||||||
return request<boolean>({
|
|
||||||
url: `/workflow/definition/publish/${id}`,
|
|
||||||
method: 'put'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 复制流程定义 */
|
|
||||||
export function fetchCopyDefinition(id: CommonType.IdType) {
|
|
||||||
return request<boolean>({
|
|
||||||
url: `/workflow/definition/copy/${id}`,
|
|
||||||
method: 'post'
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
export * from './category';
|
|
||||||
export * from './leave';
|
|
||||||
export * from './instance';
|
|
||||||
export * from './definition';
|
|
||||||
export * from './task';
|
|
@ -1,78 +0,0 @@
|
|||||||
import { request } from '@/service/request';
|
|
||||||
|
|
||||||
/** 查询正在运行的流程实例列表 */
|
|
||||||
export function fetchGetRunningInstanceList(params: Api.Workflow.InstanceSearchParams) {
|
|
||||||
return request<Api.Workflow.InstanceList>({
|
|
||||||
url: '/workflow/instance/pageByRunning',
|
|
||||||
method: 'get',
|
|
||||||
params
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 查询已结束的流程实例列表 */
|
|
||||||
export function fetchGetFinishedInstanceList(params: Api.Workflow.InstanceSearchParams) {
|
|
||||||
return request<Api.Workflow.InstanceList>({
|
|
||||||
url: '/workflow/instance/pageByFinish',
|
|
||||||
method: 'get',
|
|
||||||
params
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 按照实例id删除流程实例 */
|
|
||||||
export function fetchBatchDeleteInstance(instanceIds: CommonType.IdType[]) {
|
|
||||||
return request<boolean>({
|
|
||||||
url: `/workflow/instance/deleteByInstanceIds/${instanceIds.join(',')}`,
|
|
||||||
method: 'delete'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 流程作废操作 */
|
|
||||||
export function fetchFlowInvalidOperate(data: Api.Workflow.FlowInvalidOperateParams) {
|
|
||||||
return request<boolean>({
|
|
||||||
url: '/workflow/instance/invalid',
|
|
||||||
method: 'post',
|
|
||||||
data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获取流程记录 */
|
|
||||||
export function fetchGetFlowHisTaskList(businessId: CommonType.IdType) {
|
|
||||||
return request<Api.Workflow.InstanceIdWithHisTask>({
|
|
||||||
url: `/workflow/instance/flowHisTaskList/${businessId}`,
|
|
||||||
method: 'get'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
/** 流程作废操作 */
|
|
||||||
export function fetchCancelProcessApply(data: Api.Workflow.CancelProcessApplyParams) {
|
|
||||||
return request<boolean>({
|
|
||||||
url: '/workflow/instance/cancelProcessApply',
|
|
||||||
method: 'put',
|
|
||||||
data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获取我的待办 */
|
|
||||||
export function fetchGetMyDocument(data: Api.Workflow.InstanceSearchParams) {
|
|
||||||
return request<Api.Workflow.InstanceList>({
|
|
||||||
url: '/workflow/instance/pageByCurrent',
|
|
||||||
method: 'get',
|
|
||||||
params: data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 更新流程变量信息 */
|
|
||||||
export function fetchUpdateInstanceVariable(data: Api.Workflow.InstanceVariableOperateParams) {
|
|
||||||
return request<boolean>({
|
|
||||||
url: '/workflow/instance/updateVariable',
|
|
||||||
method: 'put',
|
|
||||||
data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获取流程变量信息 */
|
|
||||||
export function fetchGetInstanceVariable(instanceId: CommonType.IdType) {
|
|
||||||
return request<Api.Workflow.InstanceVariableInfo>({
|
|
||||||
url: `/workflow/instance/instanceVariable/${instanceId}`,
|
|
||||||
method: 'get'
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
import { request } from '@/service/request';
|
|
||||||
|
|
||||||
/** 获取请假申请列表 */
|
|
||||||
export function fetchGetLeaveList(params?: Api.Workflow.LeaveSearchParams) {
|
|
||||||
return request<Api.Workflow.LeaveList>({
|
|
||||||
url: '/workflow/leave/list',
|
|
||||||
method: 'get',
|
|
||||||
params
|
|
||||||
});
|
|
||||||
}
|
|
||||||
/** 获取请假申请详情 */
|
|
||||||
export function fetchGetLeaveDetail(id: CommonType.IdType) {
|
|
||||||
return request<Api.Workflow.LeaveDetail>({
|
|
||||||
url: `/workflow/leave/${id}`,
|
|
||||||
method: 'get'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 新增请假申请 */
|
|
||||||
export function fetchCreateLeave(data: Api.Workflow.LeaveOperateParams) {
|
|
||||||
return request<Api.Workflow.Leave>({
|
|
||||||
url: '/workflow/leave',
|
|
||||||
method: 'post',
|
|
||||||
data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 修改请假申请 */
|
|
||||||
export function fetchUpdateLeave(data: Api.Workflow.LeaveOperateParams) {
|
|
||||||
return request<Api.Workflow.Leave>({
|
|
||||||
url: '/workflow/leave',
|
|
||||||
method: 'put',
|
|
||||||
data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 批量删除请假申请 */
|
|
||||||
export function fetchBatchDeleteLeave(ids: CommonType.IdType[]) {
|
|
||||||
return request<boolean>({
|
|
||||||
url: `/workflow/leave/${ids.join(',')}`,
|
|
||||||
method: 'delete'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 撤销请假申请 */
|
|
||||||
export function fetchCancelLeave(id: CommonType.IdType) {
|
|
||||||
return request<boolean>({
|
|
||||||
url: `/workflow/leave/cancel/${id}`,
|
|
||||||
method: 'put'
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
import { request } from '@/service/request';
|
|
||||||
|
|
||||||
/** 获取流程spel达式定义列表 */
|
|
||||||
export function fetchGetSpelList(params?: Api.Workflow.SpelSearchParams) {
|
|
||||||
return request<Api.Workflow.SpelList>({
|
|
||||||
url: '/workflow/spel/list',
|
|
||||||
method: 'get',
|
|
||||||
params
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 新增流程spel达式定义 */
|
|
||||||
export function fetchCreateSpel(data: Api.Workflow.SpelOperateParams) {
|
|
||||||
return request<boolean>({
|
|
||||||
url: '/workflow/spel',
|
|
||||||
method: 'post',
|
|
||||||
data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 修改流程spel达式定义 */
|
|
||||||
export function fetchUpdateSpel(data: Api.Workflow.SpelOperateParams) {
|
|
||||||
return request<boolean>({
|
|
||||||
url: '/workflow/spel',
|
|
||||||
method: 'put',
|
|
||||||
data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 批量删除流程spel达式定义 */
|
|
||||||
export function fetchBatchDeleteSpel(ids: CommonType.IdType[]) {
|
|
||||||
return request<boolean>({
|
|
||||||
url: `/workflow/spel/${ids.join(',')}`,
|
|
||||||
method: 'delete'
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,142 +0,0 @@
|
|||||||
import { request } from '@/service/request';
|
|
||||||
|
|
||||||
/** 启动任务 */
|
|
||||||
export function fetchStartWorkflow(data: Api.Workflow.StartWorkflowOperateParams) {
|
|
||||||
return request<Api.Workflow.StartWorkflowResult>({
|
|
||||||
url: '/workflow/task/startWorkFlow',
|
|
||||||
method: 'post',
|
|
||||||
data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获取任务 */
|
|
||||||
export function fetchGetTask(taskId: CommonType.IdType) {
|
|
||||||
return request<Api.Workflow.Task>({
|
|
||||||
url: `/workflow/task/getTask/${taskId}`,
|
|
||||||
method: 'get'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获取任务下一个节点 */
|
|
||||||
export function fetchGetkNextNode(data: Api.Workflow.TaskNextNodeSearchParams) {
|
|
||||||
return request<Api.Workflow.FlowNodeList>({
|
|
||||||
url: '/workflow/task/getNextNodeList',
|
|
||||||
method: 'post',
|
|
||||||
data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 完成任务 */
|
|
||||||
export function fetchCompleteTask(data: Api.Workflow.CompleteTaskOperateParams) {
|
|
||||||
return request<boolean>({
|
|
||||||
url: '/workflow/task/completeTask',
|
|
||||||
method: 'post',
|
|
||||||
data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获取所有待办任务 */
|
|
||||||
export function fetchGetAllWaitingTask(data: Api.Workflow.TaskSearchParams) {
|
|
||||||
return request<Api.Workflow.TaskList>({
|
|
||||||
url: '/workflow/task/pageByAllTaskWait',
|
|
||||||
method: 'get',
|
|
||||||
params: data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获取所有已办任务 */
|
|
||||||
export function fetchGetAllFinishedTask(data: Api.Workflow.TaskSearchParams) {
|
|
||||||
return request<Api.Workflow.HisTaskList>({
|
|
||||||
url: '/workflow/task/pageByAllTaskFinish',
|
|
||||||
method: 'get',
|
|
||||||
params: data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 任务操作 */
|
|
||||||
export function fetchTaskOperate(data: Api.Workflow.TaskOperateParams, operateType: Api.Workflow.TaskOperateType) {
|
|
||||||
return request<boolean>({
|
|
||||||
url: `/workflow/task/taskOperation/${operateType}`,
|
|
||||||
method: 'post',
|
|
||||||
data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 终止任务 */
|
|
||||||
export function fetchTerminateTask(data: Api.Workflow.TerminateTaskOperateParams) {
|
|
||||||
return request<boolean>({
|
|
||||||
url: '/workflow/task/terminationTask',
|
|
||||||
method: 'post',
|
|
||||||
data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获取当前任务所有人员 */
|
|
||||||
export function fetchGetCurrentTaskAllUser(taskId: CommonType.IdType) {
|
|
||||||
return request<Api.System.User[]>({
|
|
||||||
url: `/workflow/task/currentTaskAllUser/${taskId}`,
|
|
||||||
method: 'get'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获取我的待办 */
|
|
||||||
export function fetchGetTaskWaitList(data: Api.Workflow.TaskSearchParams) {
|
|
||||||
return request<Api.Workflow.TaskList>({
|
|
||||||
url: '/workflow/task/pageByTaskWait',
|
|
||||||
method: 'get',
|
|
||||||
params: data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获取可驳回节点 */
|
|
||||||
export function fetchGetBackNode(definitionId: CommonType.IdType, nodeCode: string) {
|
|
||||||
return request<Api.Workflow.FlowNodeList>({
|
|
||||||
url: `/workflow/task/getBackTaskNode/${definitionId}/${nodeCode}`,
|
|
||||||
method: 'get'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 驳回任务 */
|
|
||||||
export function fetchBackTask(data: Api.Workflow.BackOperateParams) {
|
|
||||||
return request<boolean>({
|
|
||||||
url: '/workflow/task/backProcess',
|
|
||||||
method: 'post',
|
|
||||||
data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获取我的已办任务 */
|
|
||||||
export function fetchGetFinishedTask(data: Api.Workflow.TaskSearchParams) {
|
|
||||||
return request<Api.Workflow.HisTaskList>({
|
|
||||||
url: '/workflow/task/pageByTaskFinish',
|
|
||||||
method: 'get',
|
|
||||||
params: data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获取我的抄送任务 */
|
|
||||||
export function fetchGetCopyTask(data: Api.Workflow.TaskSearchParams) {
|
|
||||||
return request<Api.Workflow.TaskList>({
|
|
||||||
url: '/workflow/task/pageByTaskCopy',
|
|
||||||
method: 'get',
|
|
||||||
params: data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 修改办理人 */
|
|
||||||
export function fetchTaskAssignee(taskIds: CommonType.IdType[], userId: CommonType.IdType) {
|
|
||||||
return request<boolean>({
|
|
||||||
url: `/workflow/task/updateAssignee/${userId}`,
|
|
||||||
method: 'put',
|
|
||||||
data: taskIds
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 任务催办 */
|
|
||||||
export function fetchTaskUrge(data: Api.Workflow.TaskUrgeOperateParams) {
|
|
||||||
return request<boolean>({
|
|
||||||
url: '/workflow/task/urgeTask',
|
|
||||||
method: 'post',
|
|
||||||
data
|
|
||||||
});
|
|
||||||
}
|
|
@ -8,6 +8,7 @@ import { localStg } from '@/utils/storage';
|
|||||||
import { SetupStoreId } from '@/enum';
|
import { SetupStoreId } from '@/enum';
|
||||||
import { useRouteStore } from '../route';
|
import { useRouteStore } from '../route';
|
||||||
import { useTabStore } from '../tab';
|
import { useTabStore } from '../tab';
|
||||||
|
import useNoticeStore from '../notice';
|
||||||
import { clearAuthStorage, getToken } from './shared';
|
import { clearAuthStorage, getToken } from './shared';
|
||||||
|
|
||||||
export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
|
export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
|
||||||
@ -15,6 +16,7 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
|
|||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
const routeStore = useRouteStore();
|
const routeStore = useRouteStore();
|
||||||
const tabStore = useTabStore();
|
const tabStore = useTabStore();
|
||||||
|
const noticeStore = useNoticeStore();
|
||||||
const { toLogin, redirectFromLogin } = useRouterPush(false);
|
const { toLogin, redirectFromLogin } = useRouterPush(false);
|
||||||
const { loading: loginLoading, startLoading, endLoading } = useLoading();
|
const { loading: loginLoading, startLoading, endLoading } = useLoading();
|
||||||
|
|
||||||
@ -48,6 +50,7 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
|
|||||||
await toLogin();
|
await toLogin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
noticeStore.clearNotice();
|
||||||
tabStore.cacheTabs();
|
tabStore.cacheTabs();
|
||||||
routeStore.resetStore();
|
routeStore.resetStore();
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
|
import { SetupStoreId } from '@/enum';
|
||||||
|
|
||||||
export const useDictStore = defineStore('dict', () => {
|
export const useDictStore = defineStore(SetupStoreId.Dict, () => {
|
||||||
const dictData = ref<{ [key: string]: Api.System.DictData[] }>({});
|
const dictData = ref<{ [key: string]: Api.System.DictData[] }>({});
|
||||||
|
|
||||||
const getDict = (key: string) => {
|
const getDict = (key: string) => {
|
||||||
|
1
src/typings/api/system.api.d.ts
vendored
1
src/typings/api/system.api.d.ts
vendored
@ -128,7 +128,6 @@ declare namespace Api {
|
|||||||
type UserSearchParams = CommonType.RecordNullable<
|
type UserSearchParams = CommonType.RecordNullable<
|
||||||
Pick<User, 'deptId' | 'userName' | 'nickName' | 'phonenumber' | 'status'> & {
|
Pick<User, 'deptId' | 'userName' | 'nickName' | 'phonenumber' | 'status'> & {
|
||||||
roleId: CommonType.IdType;
|
roleId: CommonType.IdType;
|
||||||
userIds: string;
|
|
||||||
} & Common.CommonSearchParams
|
} & Common.CommonSearchParams
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
595
src/typings/api/workflow.api.d.ts
vendored
595
src/typings/api/workflow.api.d.ts
vendored
@ -1,595 +0,0 @@
|
|||||||
/**
|
|
||||||
* Namespace Api
|
|
||||||
*
|
|
||||||
* All backend api type
|
|
||||||
*/
|
|
||||||
declare namespace Api {
|
|
||||||
/**
|
|
||||||
* namespace Workflow
|
|
||||||
*
|
|
||||||
* backend api module: "Workflow"
|
|
||||||
*/
|
|
||||||
namespace Workflow {
|
|
||||||
/** 业务流程状态 */
|
|
||||||
type BusinessStatus = 'cancel' | 'draft' | 'waiting' | 'finish' | 'invalid' | 'back' | 'termination';
|
|
||||||
|
|
||||||
/** 流程类型 */
|
|
||||||
type FlowCodeType = 'leave1' | 'leave2' | 'leave3' | 'leave4' | 'leave5' | 'leave6';
|
|
||||||
/** 请假状态 */
|
|
||||||
type LeaveType = '1' | '2' | '3' | '4';
|
|
||||||
|
|
||||||
/** leave */
|
|
||||||
type Leave = Common.CommonRecord<{
|
|
||||||
/** id */
|
|
||||||
id: CommonType.IdType;
|
|
||||||
/** 申请编码 */
|
|
||||||
applyCode: string;
|
|
||||||
/** 请假类型 */
|
|
||||||
leaveType: LeaveType;
|
|
||||||
/** 开始时间 */
|
|
||||||
startDate: string;
|
|
||||||
/** 结束时间 */
|
|
||||||
endDate: string;
|
|
||||||
/** 请假天数 */
|
|
||||||
leaveDays: number;
|
|
||||||
/** 请假原因 */
|
|
||||||
remark: string;
|
|
||||||
/** 状态 */
|
|
||||||
status: BusinessStatus;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
/** leave search params */
|
|
||||||
type LeaveSearchParams = CommonType.RecordNullable<
|
|
||||||
{ startLeaveDays: number; endLeaveDays: number } & Api.Common.CommonSearchParams
|
|
||||||
>;
|
|
||||||
|
|
||||||
/** leave operate params */
|
|
||||||
type LeaveOperateParams = CommonType.RecordNullable<
|
|
||||||
Pick<Api.Workflow.Leave, 'id' | 'leaveType' | 'startDate' | 'endDate' | 'leaveDays' | 'remark'>
|
|
||||||
>;
|
|
||||||
|
|
||||||
/** leave detail */
|
|
||||||
type LeaveDetail = CommonType.RecordNullable<
|
|
||||||
Pick<Api.Workflow.Leave, 'id' | 'leaveType' | 'startDate' | 'endDate' | 'leaveDays' | 'remark' | 'status'>
|
|
||||||
>;
|
|
||||||
|
|
||||||
/** leave list */
|
|
||||||
type LeaveList = Api.Common.PaginatingQueryRecord<Leave>;
|
|
||||||
/** 工作流分类 */
|
|
||||||
type WorkflowCategory = Common.CommonRecord<{
|
|
||||||
/** 主键 */
|
|
||||||
categoryId: CommonType.IdType;
|
|
||||||
/** 租户编号 */
|
|
||||||
tenantId: CommonType.IdType;
|
|
||||||
/** 分类名称 */
|
|
||||||
categoryName: string;
|
|
||||||
/** 父级ID */
|
|
||||||
parentId: CommonType.IdType;
|
|
||||||
/** 祖级列表 */
|
|
||||||
ancestors: string;
|
|
||||||
/** 排序号 */
|
|
||||||
orderNum: number;
|
|
||||||
/** 删除标志 */
|
|
||||||
delFlag: number;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
/** 工作流分类搜索参数 */
|
|
||||||
type WorkflowCategorySearchParams = CommonType.RecordNullable<
|
|
||||||
Pick<WorkflowCategory, 'categoryName'> & Api.Common.CommonSearchParams
|
|
||||||
>;
|
|
||||||
|
|
||||||
/** 工作流分类操作参数 */
|
|
||||||
type WorkflowCategoryOperateParams = CommonType.RecordNullable<
|
|
||||||
Pick<WorkflowCategory, 'categoryId' | 'categoryName' | 'parentId' | 'orderNum'>
|
|
||||||
>;
|
|
||||||
|
|
||||||
/** 工作流分类列表 */
|
|
||||||
type WorkflowCategoryList = WorkflowCategory[];
|
|
||||||
|
|
||||||
/** spel */
|
|
||||||
type Spel = Common.CommonRecord<{
|
|
||||||
/** 主键id */
|
|
||||||
id: CommonType.IdType;
|
|
||||||
/** 组件名称 */
|
|
||||||
componentName: string;
|
|
||||||
/** 方法名 */
|
|
||||||
methodName: string;
|
|
||||||
/** 参数 */
|
|
||||||
methodParams: string;
|
|
||||||
/** spel表达式 */
|
|
||||||
viewSpel: string;
|
|
||||||
/** 备注 */
|
|
||||||
remark: string;
|
|
||||||
/** 状态 */
|
|
||||||
status: string;
|
|
||||||
/** 删除标志 */
|
|
||||||
delFlag: string;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
/** spel search params */
|
|
||||||
type SpelSearchParams = CommonType.RecordNullable<
|
|
||||||
Pick<Api.Workflow.Spel, 'componentName' | 'methodName' | 'status'> & Api.Common.CommonSearchParams
|
|
||||||
>;
|
|
||||||
|
|
||||||
/** spel operate params */
|
|
||||||
type SpelOperateParams = CommonType.RecordNullable<
|
|
||||||
Pick<Api.Workflow.Spel, 'id' | 'componentName' | 'methodName' | 'methodParams' | 'viewSpel' | 'remark' | 'status'>
|
|
||||||
>;
|
|
||||||
|
|
||||||
/** spel list */
|
|
||||||
type SpelList = Api.Common.PaginatingQueryRecord<Spel>;
|
|
||||||
|
|
||||||
/** 工作流发布状态 */
|
|
||||||
type WorkflowPublishStatus = 0 | 1 | 9;
|
|
||||||
|
|
||||||
/** 设计器模式 */
|
|
||||||
type DefinitionDesignerMode = 'CLASSICS' | 'MIMIC';
|
|
||||||
|
|
||||||
/** definition */
|
|
||||||
type Definition = Common.CommonTenantRecord<{
|
|
||||||
/** 主键id */
|
|
||||||
id: CommonType.IdType;
|
|
||||||
/** 流程编码 */
|
|
||||||
flowCode: string;
|
|
||||||
/** 流程名称 */
|
|
||||||
flowName: string;
|
|
||||||
/** 流程类别 */
|
|
||||||
category: string;
|
|
||||||
/** 流程分类名称 */
|
|
||||||
categoryName: string;
|
|
||||||
/** 流程版本 */
|
|
||||||
version: string;
|
|
||||||
/** 是否发布(0未发布 1已发布 9失效) */
|
|
||||||
isPublish: WorkflowPublishStatus;
|
|
||||||
/** 审批表单是否自定义(Y是 N否) */
|
|
||||||
formCustom: Api.Common.YesOrNoStatus;
|
|
||||||
/** 审批表单路径 */
|
|
||||||
formPath: string;
|
|
||||||
/** 流程激活状态(0挂起 1激活) */
|
|
||||||
activityStatus: WorkflowActivityStatus;
|
|
||||||
/** 监听器类型 */
|
|
||||||
listenerType: string;
|
|
||||||
/** 监听器路径 */
|
|
||||||
listenerPath: string;
|
|
||||||
/** 业务详情 存业务表对象json字符串 */
|
|
||||||
ext: string;
|
|
||||||
/** 设计器模式 */
|
|
||||||
modelValue: DefinitionDesignerMode;
|
|
||||||
/** 删除标志 */
|
|
||||||
delFlag: string;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
/** definition search params */
|
|
||||||
type DefinitionSearchParams = CommonType.RecordNullable<
|
|
||||||
Pick<Api.Workflow.Definition, 'flowCode' | 'flowName' | 'category'> & Api.Common.CommonSearchParams
|
|
||||||
>;
|
|
||||||
|
|
||||||
/** definition operate params */
|
|
||||||
type DefinitionOperateParams = CommonType.RecordNullable<
|
|
||||||
Pick<
|
|
||||||
Api.Workflow.Definition,
|
|
||||||
'id' | 'flowCode' | 'flowName' | 'category' | 'formPath' | 'formCustom' | 'modelValue' | 'ext'
|
|
||||||
>
|
|
||||||
>;
|
|
||||||
|
|
||||||
/** definition list */
|
|
||||||
type DefinitionList = Api.Common.PaginatingQueryRecord<Definition>;
|
|
||||||
|
|
||||||
type InstanceVariable = CommonType.RecordNullable<{
|
|
||||||
key: string;
|
|
||||||
value: string;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
type InstanceVariableOperateParams = CommonType.RecordNullable<{
|
|
||||||
instanceId: CommonType.IdType;
|
|
||||||
}> &
|
|
||||||
InstanceVariable;
|
|
||||||
|
|
||||||
type InstanceVariableInfo = CommonType.RecordNullable<{
|
|
||||||
/** 键 */
|
|
||||||
variable: string;
|
|
||||||
/** 值 */
|
|
||||||
variableList: InstanceVariable[];
|
|
||||||
}>;
|
|
||||||
|
|
||||||
/** 节点类型 */
|
|
||||||
type WorkflowNodeType = 0 | 1 | 2 | 3 | 4;
|
|
||||||
|
|
||||||
/** 流程激活状态 */
|
|
||||||
type WorkflowActivityStatus = 0 | 1;
|
|
||||||
|
|
||||||
/** 流程实例 */
|
|
||||||
type Instance = Common.CommonRecord<{
|
|
||||||
/** 主键 */
|
|
||||||
id: CommonType.IdType;
|
|
||||||
/** 租户编号 */
|
|
||||||
tenantId: CommonType.IdType;
|
|
||||||
/** 分类ID */
|
|
||||||
category: CommonType.IdType;
|
|
||||||
/** 分类名称 */
|
|
||||||
categoryName: string;
|
|
||||||
/** 流程定义ID */
|
|
||||||
definitionId: CommonType.IdType;
|
|
||||||
/** 流程定义名称 */
|
|
||||||
flowName: string;
|
|
||||||
/** 流程定义编码 */
|
|
||||||
flowCode: string;
|
|
||||||
/** 业务ID */
|
|
||||||
businessId: CommonType.IdType;
|
|
||||||
/** 业务编码 */
|
|
||||||
businessCode: string;
|
|
||||||
/** 业务名称 */
|
|
||||||
businessTitle: string;
|
|
||||||
/** 节点类型 */
|
|
||||||
nodeType: WorkflowNodeType;
|
|
||||||
/** 节点编码 */
|
|
||||||
nodeCode: string;
|
|
||||||
/** 节点名称 */
|
|
||||||
nodeName: string;
|
|
||||||
/** 变量 */
|
|
||||||
variable: string;
|
|
||||||
/** 流程状态 */
|
|
||||||
flowStatus: string;
|
|
||||||
/** 流程状态名称 */
|
|
||||||
flowStatusName: string;
|
|
||||||
/** 流程激活状态 */
|
|
||||||
activityStatus: WorkflowActivityStatus;
|
|
||||||
/** 审批表单是否自定义 */
|
|
||||||
formCustom: Api.Common.YesOrNoStatus;
|
|
||||||
/** 审批表单路径 */
|
|
||||||
formPath: string;
|
|
||||||
/** 扩展字段 */
|
|
||||||
ext: string;
|
|
||||||
/** 流程定义版本 */
|
|
||||||
version: string;
|
|
||||||
/** 创建者名称 */
|
|
||||||
createByName: string;
|
|
||||||
/** 删除标志 */
|
|
||||||
delFlag: number;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
/** 流程实例搜索参数 */
|
|
||||||
type InstanceSearchParams = CommonType.RecordNullable<
|
|
||||||
Pick<Instance, 'flowName' | 'flowCode' | 'businessId' | 'category' | 'nodeName'> &
|
|
||||||
Api.Common.CommonSearchParams & {
|
|
||||||
startUserId: CommonType.IdType;
|
|
||||||
createByIds: CommonType.IdType[];
|
|
||||||
}
|
|
||||||
>;
|
|
||||||
/** 流程实例列表 */
|
|
||||||
type InstanceList = Common.PaginatingQueryRecord<Instance>;
|
|
||||||
|
|
||||||
/** 流程作废操作参数 */
|
|
||||||
type FlowInvalidOperateParams = CommonType.RecordNullable<{
|
|
||||||
/** 主键 */
|
|
||||||
id: CommonType.IdType;
|
|
||||||
/** 作废原因 */
|
|
||||||
comment: string;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
/** 流程撤销操作参数 */
|
|
||||||
type CancelProcessApplyParams = CommonType.RecordNullable<{
|
|
||||||
/** 主键 */
|
|
||||||
businessId: CommonType.IdType;
|
|
||||||
/** 撤销原因 */
|
|
||||||
message: string;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
type BusinessInfo = CommonType.RecordNullable<{
|
|
||||||
/** 业务编码 */
|
|
||||||
businessCode: string;
|
|
||||||
/** 业务名称 */
|
|
||||||
businessTitle: string;
|
|
||||||
}>;
|
|
||||||
/** 启动流程操作参数 */
|
|
||||||
type StartWorkflowOperateParams = CommonType.RecordNullable<{
|
|
||||||
/** 流程定义ID */
|
|
||||||
flowCode: string;
|
|
||||||
/** 业务ID */
|
|
||||||
businessId: CommonType.IdType;
|
|
||||||
/** 业务信息 */
|
|
||||||
flowInstanceBizExtBo: BusinessInfo;
|
|
||||||
/** 变量 */
|
|
||||||
variables: { [key: string]: any };
|
|
||||||
}>;
|
|
||||||
|
|
||||||
/** 启动流程结果 */
|
|
||||||
type StartWorkflowResult = CommonType.RecordNullable<{
|
|
||||||
/** 流程实例ID */
|
|
||||||
instanceId: CommonType.IdType;
|
|
||||||
/** 任务ID */
|
|
||||||
taskId: CommonType.IdType;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
/** 抄送人 */
|
|
||||||
type FlowCopy = CommonType.RecordNullable<{
|
|
||||||
/** 用户ID */
|
|
||||||
userId: CommonType.IdType;
|
|
||||||
/** 用户名称 */
|
|
||||||
userName: string;
|
|
||||||
}>;
|
|
||||||
/** 按钮权限 */
|
|
||||||
type ButtonPermission = CommonType.RecordNullable<{
|
|
||||||
/** 唯一编码 */
|
|
||||||
code: CommonType.IdType;
|
|
||||||
/** 选项值 */
|
|
||||||
value: string;
|
|
||||||
/** 是否显示 */
|
|
||||||
show: boolean;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
type TaskOrHisTask = Task | HisTask;
|
|
||||||
|
|
||||||
/** 任务详情 */
|
|
||||||
type Task = Common.CommonTenantRecord<{
|
|
||||||
/** 任务ID */
|
|
||||||
id: CommonType.IdType;
|
|
||||||
/** 删除标志 */
|
|
||||||
delFlag: number;
|
|
||||||
/** 流程定义ID */
|
|
||||||
definitionId: CommonType.IdType;
|
|
||||||
/** 流程实例ID */
|
|
||||||
instanceId: CommonType.IdType;
|
|
||||||
/** 业务ID */
|
|
||||||
businessId: CommonType.IdType;
|
|
||||||
/** 节点编码 */
|
|
||||||
nodeCode: string;
|
|
||||||
/** 节点类型 */
|
|
||||||
nodeType: WorkflowNodeType;
|
|
||||||
/** 权限列表 */
|
|
||||||
permissionList: string[];
|
|
||||||
/** 用户列表 */
|
|
||||||
userList: any[];
|
|
||||||
/** 审批表单是否自定义 */
|
|
||||||
formCustom: Api.Common.YesOrNoStatus;
|
|
||||||
/** 审批表单路径 */
|
|
||||||
formPath: string;
|
|
||||||
/** 流程状态 */
|
|
||||||
flowStatus: string;
|
|
||||||
/** 流程状态名称 */
|
|
||||||
flowStatusName: string;
|
|
||||||
/** 分类ID */
|
|
||||||
category: CommonType.IdType;
|
|
||||||
/** 分类名称 */
|
|
||||||
categoryName: string;
|
|
||||||
/** 办理人类型 */
|
|
||||||
type: string;
|
|
||||||
/** 审批人 */
|
|
||||||
assigneeIds: string;
|
|
||||||
/** 审批人名称 */
|
|
||||||
assigneeNames: string;
|
|
||||||
/** 审批人 */
|
|
||||||
processedBy: string;
|
|
||||||
/** 审批人名称 */
|
|
||||||
processedByName: string;
|
|
||||||
/** 流程签署比例值 大于0为票签,会签 */
|
|
||||||
nodeRatio: string;
|
|
||||||
/** 创建人名称 */
|
|
||||||
createByName: string;
|
|
||||||
/** 是否为申请人节点 */
|
|
||||||
applyNode: string;
|
|
||||||
/** 按钮列表 */
|
|
||||||
buttonList: ButtonPermission[];
|
|
||||||
/** 节点名称 */
|
|
||||||
nodeName: string;
|
|
||||||
/** 流程定义名称 */
|
|
||||||
flowName: string;
|
|
||||||
/** 流程定义编码 */
|
|
||||||
flowCode: string;
|
|
||||||
/** 流程版本号 */
|
|
||||||
version: string;
|
|
||||||
/** 业务编码 */
|
|
||||||
businessCode: string;
|
|
||||||
/** 业务名称 */
|
|
||||||
businessTitle: string;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
/** 任务列表 */
|
|
||||||
type TaskList = Common.PaginatingQueryRecord<Task>;
|
|
||||||
|
|
||||||
/** 任务催办操作参数 */
|
|
||||||
type TaskUrgeOperateParams = CommonType.RecordNullable<{
|
|
||||||
taskIdList: CommonType.IdType[];
|
|
||||||
messageType: MessageType[];
|
|
||||||
message: string;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
/** 任务操作类型 */
|
|
||||||
type TaskOperateType = 'delegateTask' | 'transferTask' | 'addSignature' | 'reductionSignature' | 'stopTask';
|
|
||||||
|
|
||||||
/** 任务操作参数 */
|
|
||||||
type TaskOperateParams = CommonType.RecordNullable<{
|
|
||||||
taskId: CommonType.IdType;
|
|
||||||
userId?: CommonType.IdType;
|
|
||||||
userIds?: CommonType.IdType[];
|
|
||||||
message?: string;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
/** 终止任务 */
|
|
||||||
type TerminateTaskOperateParams = CommonType.RecordNullable<{
|
|
||||||
taskId: CommonType.IdType;
|
|
||||||
comment?: string;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
/** 协作方式 */
|
|
||||||
type CooperateType = 1 | 2 | 3 | 4 | 5 | 6 | 7;
|
|
||||||
|
|
||||||
/** 历史任务 */
|
|
||||||
type HisTask = Common.CommonTenantRecord<{
|
|
||||||
/** 任务ID */
|
|
||||||
id: CommonType.IdType;
|
|
||||||
/** 删除标志 */
|
|
||||||
delFlag: number;
|
|
||||||
/** 流程定义ID */
|
|
||||||
definitionId: CommonType.IdType;
|
|
||||||
/** 流程定义名称 */
|
|
||||||
flowName: string;
|
|
||||||
/** 流程实例ID */
|
|
||||||
instanceId: CommonType.IdType;
|
|
||||||
/** 任务表ID */
|
|
||||||
taskId: CommonType.IdType;
|
|
||||||
/** 协作方式(1审批 2转办 3委派 4会签 5票签 6加签 7减签) */
|
|
||||||
cooperateType: CooperateType;
|
|
||||||
/** 协作方式名称 */
|
|
||||||
cooperateTypeName: string;
|
|
||||||
/** 业务ID */
|
|
||||||
businessId: string;
|
|
||||||
/** 节点编码 */
|
|
||||||
nodeCode: string;
|
|
||||||
/** 节点名称 */
|
|
||||||
nodeName: string;
|
|
||||||
/** 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) */
|
|
||||||
nodeType: WorkflowNodeType;
|
|
||||||
/** 目标节点编码 */
|
|
||||||
targetNodeCode: string;
|
|
||||||
/** 目标节点名称 */
|
|
||||||
targetNodeName: string;
|
|
||||||
/** 审批者 */
|
|
||||||
approver: string;
|
|
||||||
/** 审批者名称 */
|
|
||||||
approveName: string;
|
|
||||||
/** 协作人 */
|
|
||||||
collaborator: string;
|
|
||||||
/** 权限标识 */
|
|
||||||
permissionList: string[];
|
|
||||||
/** 跳转类型(PASS通过 REJECT退回 NONE无动作) */
|
|
||||||
skipType: string;
|
|
||||||
/** 流程状态 */
|
|
||||||
flowStatus: string;
|
|
||||||
/** 任务状态 */
|
|
||||||
flowTaskStatus: string;
|
|
||||||
/** 流程状态名称 */
|
|
||||||
flowStatusName: string;
|
|
||||||
/** 审批意见 */
|
|
||||||
message: string;
|
|
||||||
/** 业务扩展信息(JSON字符串) */
|
|
||||||
ext: string;
|
|
||||||
/** 创建者姓名(申请人名称) */
|
|
||||||
createByName: string;
|
|
||||||
/** 流程分类ID */
|
|
||||||
category: string;
|
|
||||||
/** 流程分类名称 */
|
|
||||||
categoryName: string;
|
|
||||||
/** 审批表单是否自定义(Y是 N否) */
|
|
||||||
formCustom: Api.Common.YesOrNoStatus;
|
|
||||||
/** 表单路径 */
|
|
||||||
formPath: string;
|
|
||||||
/** 流程定义编码 */
|
|
||||||
flowCode: string;
|
|
||||||
/** 流程版本号 */
|
|
||||||
version: string;
|
|
||||||
/** 运行时长 */
|
|
||||||
runDuration: string;
|
|
||||||
/** 附件 */
|
|
||||||
attachmentList: Api.System.Oss[];
|
|
||||||
/** 业务编码 */
|
|
||||||
businessCode: string;
|
|
||||||
/** 业务名称 */
|
|
||||||
businessTitle: string;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
/** 历史任务列表 */
|
|
||||||
type HisTaskList = Common.PaginatingQueryRecord<HisTask>;
|
|
||||||
|
|
||||||
/** 流程实例ID与历史任务 */
|
|
||||||
type InstanceIdWithHisTask = Common.CommonRecord<{
|
|
||||||
/** 流程实例ID */
|
|
||||||
instanceId: CommonType.IdType;
|
|
||||||
/** 历史任务 */
|
|
||||||
list: HisTask[];
|
|
||||||
}>;
|
|
||||||
|
|
||||||
/** 任务搜索参数 */
|
|
||||||
type TaskSearchParams = CommonType.RecordNullable<
|
|
||||||
Pick<Task, 'flowName' | 'flowCode' | 'businessId' | 'category' | 'nodeName' | 'instanceId' | 'permissionList'> &
|
|
||||||
Api.Common.CommonSearchParams & {
|
|
||||||
createByIds: CommonType.IdType[];
|
|
||||||
}
|
|
||||||
>;
|
|
||||||
type TaskNextNodeSearchParams = CommonType.RecordNullable<{
|
|
||||||
taskId: CommonType.IdType;
|
|
||||||
taskVariables: { [key: string]: any };
|
|
||||||
}>;
|
|
||||||
|
|
||||||
/** 消息类型 */
|
|
||||||
type MessageType = '1' | '2' | '3';
|
|
||||||
|
|
||||||
/** 完成任务操作参数 */
|
|
||||||
type CompleteTaskOperateParams = CommonType.RecordNullable<{
|
|
||||||
/** 任务ID */
|
|
||||||
taskId: CommonType.IdType;
|
|
||||||
/** 文件ID */
|
|
||||||
fileId: CommonType.IdType;
|
|
||||||
/** 抄送人 */
|
|
||||||
flowCopyList: FlowCopy[];
|
|
||||||
/** 消息类型 */
|
|
||||||
messageType: string[];
|
|
||||||
/** 消息 */
|
|
||||||
message: string;
|
|
||||||
/** 通知 */
|
|
||||||
notice: string;
|
|
||||||
/** 任务变量 */
|
|
||||||
taskVariables: { [key: string]: any };
|
|
||||||
/** 变量 */
|
|
||||||
variables: { [key: string]: any };
|
|
||||||
/** 审批人 */
|
|
||||||
assigneeMap: { [key: string]: string };
|
|
||||||
/** 扩展字段 */
|
|
||||||
ext: string;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
/** 工作流节点 */
|
|
||||||
type FlowNode = Common.CommonTenantRecord<{
|
|
||||||
/** 节点ID */
|
|
||||||
id: CommonType.IdType;
|
|
||||||
/** 删除标志 */
|
|
||||||
delFlag: string;
|
|
||||||
/** 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) */
|
|
||||||
nodeType: WorkflowNodeType;
|
|
||||||
/** 流程定义ID */
|
|
||||||
definitionId: CommonType.IdType;
|
|
||||||
/** 节点编码 */
|
|
||||||
nodeCode: string;
|
|
||||||
/** 节点名称 */
|
|
||||||
nodeName: string;
|
|
||||||
/** 权限标识 */
|
|
||||||
permissionFlag: string;
|
|
||||||
/** 流程签署比例值 */
|
|
||||||
nodeRatio: string;
|
|
||||||
/** 节点坐标 */
|
|
||||||
coordinate: string;
|
|
||||||
/** 流程版本号 */
|
|
||||||
version: string;
|
|
||||||
/** 是否允许任意节点跳转 */
|
|
||||||
anyNodeSkip: string;
|
|
||||||
/** 监听器类型 */
|
|
||||||
listenerType: string;
|
|
||||||
/** 监听器路径 */
|
|
||||||
listenerPath: string;
|
|
||||||
/** 处理器类型 */
|
|
||||||
handlerType: string;
|
|
||||||
/** 处理器路径 */
|
|
||||||
handlerPath: string;
|
|
||||||
/** 审批表单是否自定义(Y是 N否) */
|
|
||||||
formCustom: Api.Common.YesOrNoStatus;
|
|
||||||
/** 审批表单路径 */
|
|
||||||
formPath: string;
|
|
||||||
/** 扩展字段 */
|
|
||||||
ext: string;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
/** 工作流节点列表 */
|
|
||||||
type FlowNodeList = FlowNode[];
|
|
||||||
|
|
||||||
/** 驳回操作参数 */
|
|
||||||
type BackOperateParams = CommonType.RecordNullable<{
|
|
||||||
taskId: CommonType.IdType;
|
|
||||||
fileId: CommonType.IdType;
|
|
||||||
messageType: string[];
|
|
||||||
nodeCode: string;
|
|
||||||
message: string;
|
|
||||||
notice: string;
|
|
||||||
variables: { [key: string]: any };
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
}
|
|
2
src/typings/common.d.ts
vendored
2
src/typings/common.d.ts
vendored
@ -43,6 +43,4 @@ declare namespace CommonType {
|
|||||||
/** filter function */
|
/** filter function */
|
||||||
filterFn?: (node: any) => boolean;
|
filterFn?: (node: any) => boolean;
|
||||||
};
|
};
|
||||||
/** the type of workflow table operate */
|
|
||||||
type WorkflowTableOperateType = 'add' | 'edit' | 'detail' | 'approval';
|
|
||||||
}
|
}
|
||||||
|
20
src/typings/components.d.ts
vendored
20
src/typings/components.d.ts
vendored
@ -9,8 +9,6 @@ export {}
|
|||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
AppProvider: typeof import('./../components/common/app-provider.vue')['default']
|
AppProvider: typeof import('./../components/common/app-provider.vue')['default']
|
||||||
ApprovalInfoPanel: typeof import('./../components/workflow/approval-info-panel.vue')['default']
|
|
||||||
BackTaskModal: typeof import('./../components/workflow/back-task-modal.vue')['default']
|
|
||||||
BetterScroll: typeof import('./../components/custom/better-scroll.vue')['default']
|
BetterScroll: typeof import('./../components/custom/better-scroll.vue')['default']
|
||||||
BooleanTag: typeof import('./../components/custom/boolean-tag.vue')['default']
|
BooleanTag: typeof import('./../components/custom/boolean-tag.vue')['default']
|
||||||
ButtonIcon: typeof import('./../components/custom/button-icon.vue')['default']
|
ButtonIcon: typeof import('./../components/custom/button-icon.vue')['default']
|
||||||
@ -24,12 +22,6 @@ declare module 'vue' {
|
|||||||
DictTag: typeof import('./../components/custom/dict-tag.vue')['default']
|
DictTag: typeof import('./../components/custom/dict-tag.vue')['default']
|
||||||
ExceptionBase: typeof import('./../components/common/exception-base.vue')['default']
|
ExceptionBase: typeof import('./../components/common/exception-base.vue')['default']
|
||||||
FileUpload: typeof import('./../components/custom/file-upload.vue')['default']
|
FileUpload: typeof import('./../components/custom/file-upload.vue')['default']
|
||||||
FlowCategorySelect: typeof import('./../components/workflow/flow-category-select.vue')['default']
|
|
||||||
FlowDrawer: typeof import('./../components/workflow/flow-drawer.vue')['default']
|
|
||||||
FlowInterveneModal: typeof import('./../components/workflow/flow-intervene-modal.vue')['default']
|
|
||||||
FlowPreview: typeof import('./../components/workflow/flow-preview.vue')['default']
|
|
||||||
FlowTaskApprovalModal: typeof import('./../components/workflow/flow-task-approval-modal.vue')['default']
|
|
||||||
FlowUrgeModal: typeof import('./../components/workflow/flow-urge-modal.vue')['default']
|
|
||||||
FormTip: typeof import('./../components/custom/form-tip.vue')['default']
|
FormTip: typeof import('./../components/custom/form-tip.vue')['default']
|
||||||
FullScreen: typeof import('./../components/common/full-screen.vue')['default']
|
FullScreen: typeof import('./../components/common/full-screen.vue')['default']
|
||||||
IconAntDesignEnterOutlined: typeof import('~icons/ant-design/enter-outlined')['default']
|
IconAntDesignEnterOutlined: typeof import('~icons/ant-design/enter-outlined')['default']
|
||||||
@ -46,11 +38,14 @@ declare module 'vue' {
|
|||||||
'IconMaterialSymbols:add': typeof import('~icons/material-symbols/add')['default']
|
'IconMaterialSymbols:add': typeof import('~icons/material-symbols/add')['default']
|
||||||
'IconMaterialSymbols:deleteOutline': typeof import('~icons/material-symbols/delete-outline')['default']
|
'IconMaterialSymbols:deleteOutline': typeof import('~icons/material-symbols/delete-outline')['default']
|
||||||
'IconMaterialSymbols:downloadRounded': typeof import('~icons/material-symbols/download-rounded')['default']
|
'IconMaterialSymbols:downloadRounded': typeof import('~icons/material-symbols/download-rounded')['default']
|
||||||
'IconMaterialSymbols:driveFileRenameOutlineOutline': typeof import('~icons/material-symbols/drive-file-rename-outline-outline')['default']
|
|
||||||
'IconMaterialSymbols:imageOutline': typeof import('~icons/material-symbols/image-outline')['default']
|
'IconMaterialSymbols:imageOutline': typeof import('~icons/material-symbols/image-outline')['default']
|
||||||
'IconMaterialSymbols:refreshRounded': typeof import('~icons/material-symbols/refresh-rounded')['default']
|
'IconMaterialSymbols:refreshRounded': typeof import('~icons/material-symbols/refresh-rounded')['default']
|
||||||
|
'IconMaterialSymbols:syncOutline': typeof import('~icons/material-symbols/sync-outline')['default']
|
||||||
'IconMaterialSymbols:uploadRounded': typeof import('~icons/material-symbols/upload-rounded')['default']
|
'IconMaterialSymbols:uploadRounded': typeof import('~icons/material-symbols/upload-rounded')['default']
|
||||||
'IconMaterialSymbols:warningOutlineRounded': typeof import('~icons/material-symbols/warning-outline-rounded')['default']
|
'IconMaterialSymbols:warningOutlineRounded': typeof import('~icons/material-symbols/warning-outline-rounded')['default']
|
||||||
|
IconMaterialSymbolsAddRounded: typeof import('~icons/material-symbols/add-rounded')['default']
|
||||||
|
IconMaterialSymbolsDeleteOutline: typeof import('~icons/material-symbols/delete-outline')['default']
|
||||||
|
IconMaterialSymbolsDriveFileRenameOutlineOutline: typeof import('~icons/material-symbols/drive-file-rename-outline-outline')['default']
|
||||||
'IconMdi:github': typeof import('~icons/mdi/github')['default']
|
'IconMdi:github': typeof import('~icons/mdi/github')['default']
|
||||||
IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default']
|
IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default']
|
||||||
IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
|
IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
|
||||||
@ -63,7 +58,6 @@ declare module 'vue' {
|
|||||||
IconUilSearch: typeof import('~icons/uil/search')['default']
|
IconUilSearch: typeof import('~icons/uil/search')['default']
|
||||||
JsonPreview: typeof import('./../components/custom/json-preview.vue')['default']
|
JsonPreview: typeof import('./../components/custom/json-preview.vue')['default']
|
||||||
LangSwitch: typeof import('./../components/common/lang-switch.vue')['default']
|
LangSwitch: typeof import('./../components/common/lang-switch.vue')['default']
|
||||||
LeaveEdit: typeof import('./../components/workflow/form/leave-edit/index.vue')['default']
|
|
||||||
LookForward: typeof import('./../components/custom/look-forward.vue')['default']
|
LookForward: typeof import('./../components/custom/look-forward.vue')['default']
|
||||||
MenuToggler: typeof import('./../components/common/menu-toggler.vue')['default']
|
MenuToggler: typeof import('./../components/common/menu-toggler.vue')['default']
|
||||||
MenuTree: typeof import('./../components/custom/menu-tree.vue')['default']
|
MenuTree: typeof import('./../components/custom/menu-tree.vue')['default']
|
||||||
@ -78,7 +72,6 @@ declare module 'vue' {
|
|||||||
NButton: typeof import('naive-ui')['NButton']
|
NButton: typeof import('naive-ui')['NButton']
|
||||||
NCard: typeof import('naive-ui')['NCard']
|
NCard: typeof import('naive-ui')['NCard']
|
||||||
NCheckbox: typeof import('naive-ui')['NCheckbox']
|
NCheckbox: typeof import('naive-ui')['NCheckbox']
|
||||||
NCheckboxGroup: typeof import('naive-ui')['NCheckboxGroup']
|
|
||||||
NCode: typeof import('naive-ui')['NCode']
|
NCode: typeof import('naive-ui')['NCode']
|
||||||
NCollapse: typeof import('naive-ui')['NCollapse']
|
NCollapse: typeof import('naive-ui')['NCollapse']
|
||||||
NCollapseItem: typeof import('naive-ui')['NCollapseItem']
|
NCollapseItem: typeof import('naive-ui')['NCollapseItem']
|
||||||
@ -92,6 +85,7 @@ declare module 'vue' {
|
|||||||
NDrawer: typeof import('naive-ui')['NDrawer']
|
NDrawer: typeof import('naive-ui')['NDrawer']
|
||||||
NDrawerContent: typeof import('naive-ui')['NDrawerContent']
|
NDrawerContent: typeof import('naive-ui')['NDrawerContent']
|
||||||
NDropdown: typeof import('naive-ui')['NDropdown']
|
NDropdown: typeof import('naive-ui')['NDropdown']
|
||||||
|
NDynamicInput: typeof import('naive-ui')['NDynamicInput']
|
||||||
NEllipsis: typeof import('naive-ui')['NEllipsis']
|
NEllipsis: typeof import('naive-ui')['NEllipsis']
|
||||||
NEmpty: typeof import('naive-ui')['NEmpty']
|
NEmpty: typeof import('naive-ui')['NEmpty']
|
||||||
NForm: typeof import('naive-ui')['NForm']
|
NForm: typeof import('naive-ui')['NForm']
|
||||||
@ -102,6 +96,7 @@ declare module 'vue' {
|
|||||||
NGridItem: typeof import('naive-ui')['NGridItem']
|
NGridItem: typeof import('naive-ui')['NGridItem']
|
||||||
NInput: typeof import('naive-ui')['NInput']
|
NInput: typeof import('naive-ui')['NInput']
|
||||||
NInputGroup: typeof import('naive-ui')['NInputGroup']
|
NInputGroup: typeof import('naive-ui')['NInputGroup']
|
||||||
|
NInputGroupLabel: typeof import('naive-ui')['NInputGroupLabel']
|
||||||
NInputNumber: typeof import('naive-ui')['NInputNumber']
|
NInputNumber: typeof import('naive-ui')['NInputNumber']
|
||||||
NLayout: typeof import('naive-ui')['NLayout']
|
NLayout: typeof import('naive-ui')['NLayout']
|
||||||
NLayoutContent: typeof import('naive-ui')['NLayoutContent']
|
NLayoutContent: typeof import('naive-ui')['NLayoutContent']
|
||||||
@ -140,7 +135,6 @@ declare module 'vue' {
|
|||||||
OssUpload: typeof import('./../components/custom/oss-upload.vue')['default']
|
OssUpload: typeof import('./../components/custom/oss-upload.vue')['default']
|
||||||
PinToggler: typeof import('./../components/common/pin-toggler.vue')['default']
|
PinToggler: typeof import('./../components/common/pin-toggler.vue')['default']
|
||||||
PostSelect: typeof import('./../components/custom/post-select.vue')['default']
|
PostSelect: typeof import('./../components/custom/post-select.vue')['default']
|
||||||
ReduceSignatureModal: typeof import('./../components/workflow/reduce-signature-modal.vue')['default']
|
|
||||||
ReloadButton: typeof import('./../components/common/reload-button.vue')['default']
|
ReloadButton: typeof import('./../components/common/reload-button.vue')['default']
|
||||||
RoleSelect: typeof import('./../components/custom/role-select.vue')['default']
|
RoleSelect: typeof import('./../components/custom/role-select.vue')['default']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
@ -153,12 +147,10 @@ declare module 'vue' {
|
|||||||
TableHeaderOperation: typeof import('./../components/advanced/table-header-operation.vue')['default']
|
TableHeaderOperation: typeof import('./../components/advanced/table-header-operation.vue')['default']
|
||||||
TableRowCheckAlert: typeof import('./../components/advanced/table-row-check-alert.vue')['default']
|
TableRowCheckAlert: typeof import('./../components/advanced/table-row-check-alert.vue')['default']
|
||||||
TableSiderLayout: typeof import('./../components/advanced/table-sider-layout.vue')['default']
|
TableSiderLayout: typeof import('./../components/advanced/table-sider-layout.vue')['default']
|
||||||
TagGroup: typeof import('./../components/custom/tag-group.vue')['default']
|
|
||||||
TenantSelect: typeof import('./../components/custom/tenant-select.vue')['default']
|
TenantSelect: typeof import('./../components/custom/tenant-select.vue')['default']
|
||||||
ThemeSchemaSwitch: typeof import('./../components/common/theme-schema-switch.vue')['default']
|
ThemeSchemaSwitch: typeof import('./../components/common/theme-schema-switch.vue')['default']
|
||||||
TinymceEditor: typeof import('./../components/custom/tinymce-editor.vue')['default']
|
TinymceEditor: typeof import('./../components/custom/tinymce-editor.vue')['default']
|
||||||
UserSelect: typeof import('./../components/custom/user-select.vue')['default']
|
UserSelect: typeof import('./../components/custom/user-select.vue')['default']
|
||||||
UserSelectModal: typeof import('./../components/custom/user-select-modal.vue')['default']
|
|
||||||
WaveBg: typeof import('./../components/custom/wave-bg.vue')['default']
|
WaveBg: typeof import('./../components/custom/wave-bg.vue')['default']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
25
src/typings/elegant-router.d.ts
vendored
25
src/typings/elegant-router.d.ts
vendored
@ -53,19 +53,6 @@ declare module "@elegant-router/types" {
|
|||||||
"tool": "/tool";
|
"tool": "/tool";
|
||||||
"tool_gen": "/tool/gen";
|
"tool_gen": "/tool/gen";
|
||||||
"user-center": "/user-center";
|
"user-center": "/user-center";
|
||||||
"workflow": "/workflow";
|
|
||||||
"workflow_category": "/workflow/category";
|
|
||||||
"workflow_design": "/workflow/design";
|
|
||||||
"workflow_leave": "/workflow/leave";
|
|
||||||
"workflow_process-definition": "/workflow/process-definition";
|
|
||||||
"workflow_process-instance": "/workflow/process-instance";
|
|
||||||
"workflow_spel": "/workflow/spel";
|
|
||||||
"workflow_task": "/workflow/task";
|
|
||||||
"workflow_task_all-task-waiting": "/workflow/task/all-task-waiting";
|
|
||||||
"workflow_task_my-document": "/workflow/task/my-document";
|
|
||||||
"workflow_task_task-copy": "/workflow/task/task-copy";
|
|
||||||
"workflow_task_task-finish": "/workflow/task/task-finish";
|
|
||||||
"workflow_task_task-waiting": "/workflow/task/task-waiting";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -113,7 +100,6 @@ declare module "@elegant-router/types" {
|
|||||||
| "system"
|
| "system"
|
||||||
| "tool"
|
| "tool"
|
||||||
| "user-center"
|
| "user-center"
|
||||||
| "workflow"
|
|
||||||
>;
|
>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -159,17 +145,6 @@ declare module "@elegant-router/types" {
|
|||||||
| "system_tenant"
|
| "system_tenant"
|
||||||
| "system_user"
|
| "system_user"
|
||||||
| "tool_gen"
|
| "tool_gen"
|
||||||
| "workflow_category"
|
|
||||||
| "workflow_design"
|
|
||||||
| "workflow_leave"
|
|
||||||
| "workflow_process-definition"
|
|
||||||
| "workflow_process-instance"
|
|
||||||
| "workflow_spel"
|
|
||||||
| "workflow_task_all-task-waiting"
|
|
||||||
| "workflow_task_my-document"
|
|
||||||
| "workflow_task_task-copy"
|
|
||||||
| "workflow_task_task-finish"
|
|
||||||
| "workflow_task_task-waiting"
|
|
||||||
>;
|
>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import { defineAsyncComponent, markRaw } from 'vue';
|
|
||||||
import { AcceptType } from '@/enum/business';
|
import { AcceptType } from '@/enum/business';
|
||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transform record to option
|
* Transform record to option
|
||||||
*
|
*
|
||||||
@ -78,30 +76,6 @@ export function humpToLine(str: string, line: string = '-') {
|
|||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 动态加载组件 */
|
|
||||||
export async function loadDynamicComponent(
|
|
||||||
modules: Record<string, () => Promise<any>>,
|
|
||||||
formPath: string,
|
|
||||||
{ delay = 2000, timeout = 3000 } = {}
|
|
||||||
) {
|
|
||||||
const suffix = `${humpToLine(formPath)}.vue`;
|
|
||||||
const componentPath = suffix.replace('/workflow', '/workflow/form');
|
|
||||||
const matched = Object.entries(modules).find(([path]) => path.endsWith(componentPath));
|
|
||||||
|
|
||||||
if (!matched) {
|
|
||||||
window.$message?.error(`组件不存在: ${suffix}`);
|
|
||||||
throw new Error(`组件不存在: ${suffix}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return markRaw(
|
|
||||||
defineAsyncComponent({
|
|
||||||
loader: matched[1],
|
|
||||||
delay,
|
|
||||||
timeout
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 判断是否为空 */
|
/** 判断是否为空 */
|
||||||
export function isNotNull(value: any) {
|
export function isNotNull(value: any) {
|
||||||
return value !== undefined && value !== null && value !== '';
|
return value !== undefined && value !== null && value !== '';
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { watch } from 'vue';
|
import { watch } from 'vue';
|
||||||
import { useEventSource } from '@vueuse/core';
|
import { useEventSource } from '@vueuse/core';
|
||||||
import useNoticeStore from '@/store/modules/notice';
|
import useNoticeStore from '@/store/modules/notice';
|
||||||
|
import { $t } from '@/locales';
|
||||||
import { localStg } from './storage';
|
import { localStg } from './storage';
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
@ -34,9 +35,14 @@ export const initSSE = (url: any) => {
|
|||||||
read: false,
|
read: false,
|
||||||
time: new Date().toLocaleString()
|
time: new Date().toLocaleString()
|
||||||
});
|
});
|
||||||
|
let content = data.value;
|
||||||
|
const noticeType = content.match(/\[dict\.(.*?)\]/)?.[1];
|
||||||
|
if (noticeType) {
|
||||||
|
content = content.replace(`dict.${noticeType}`, $t(`dict.${noticeType}` as App.I18n.I18nKey));
|
||||||
|
}
|
||||||
window.$notification?.create({
|
window.$notification?.create({
|
||||||
title: '消息',
|
title: '消息',
|
||||||
content: data.value,
|
content,
|
||||||
type: 'success',
|
type: 'success',
|
||||||
duration: 3000
|
duration: 3000
|
||||||
});
|
});
|
||||||
|
@ -39,8 +39,7 @@ const activeModule = computed(() => moduleMap[props.module || 'pwd-login']);
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<!-- Copyright By https://github.com/Daymychen/art-design-pro/blob/main/src/components/core/views/login/LoginLeftView.vue -->
|
<div class="scroll box-border size-full flex">
|
||||||
<div class="box-border size-full flex">
|
|
||||||
<div class="relative box-border hidden h-full w-65vw overflow-hidden bg-primary-50 xl:block dark:bg-primary-900">
|
<div class="relative box-border hidden h-full w-65vw overflow-hidden bg-primary-50 xl:block dark:bg-primary-900">
|
||||||
<div class="relative z-100 flex items-center pl-30px pt-30px">
|
<div class="relative z-100 flex items-center pl-30px pt-30px">
|
||||||
<SystemLogo class="text-32px text-primary" />
|
<SystemLogo class="text-32px text-primary" />
|
||||||
@ -55,13 +54,13 @@ const activeModule = computed(() => moduleMap[props.module || 'pwd-login']);
|
|||||||
</div>
|
</div>
|
||||||
<WaveBg />
|
<WaveBg />
|
||||||
</div>
|
</div>
|
||||||
<header class="relative h-full flex-1 xl:m-auto sm:!w-full">
|
<div class="relative h-full flex-1 xl:m-auto sm:!w-full">
|
||||||
<div class="relative z-100 block flex items-center pl-30px pt-30px xl:hidden">
|
<header class="flex-y-center justify-between px-30px pt-30px xl:justify-end">
|
||||||
<SystemLogo class="text-32px text-primary" />
|
<div class="relative z-100 block flex items-center xl:hidden">
|
||||||
<h3 class="ml-10px text-20px font-400">{{ $t('system.title') }}</h3>
|
<SystemLogo class="text-32px text-primary" />
|
||||||
</div>
|
<h3 class="ml-10px text-20px font-400">{{ $t('system.title') }}</h3>
|
||||||
<div class="position-fixed right-30px top-24px z-100 flex items-center justify-end">
|
</div>
|
||||||
<div class="ml-15px inline-block flex cursor-pointer select-none p-5px">
|
<div class="flex items-center justify-end">
|
||||||
<ThemeSchemaSwitch
|
<ThemeSchemaSwitch
|
||||||
:theme-schema="themeStore.themeScheme"
|
:theme-schema="themeStore.themeScheme"
|
||||||
:show-tooltip="false"
|
:show-tooltip="false"
|
||||||
@ -77,14 +76,30 @@ const activeModule = computed(() => moduleMap[props.module || 'pwd-login']);
|
|||||||
@change-lang="appStore.changeLocale"
|
@change-lang="appStore.changeLocale"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</header>
|
||||||
<main class="absolute inset-0 m-auto h-630px max-w-450px w-full overflow-hidden rounded-5px bg-cover px-24px">
|
<main class="mt-10% max-w-450px w-full rounded-5px bg-cover px-24px xl:absolute xl:m-auto">
|
||||||
<Transition :name="themeStore.page.animateMode" mode="out-in" appear>
|
<Transition :name="themeStore.page.animateMode" mode="out-in" appear>
|
||||||
<component :is="activeModule.component" />
|
<component :is="activeModule.component" />
|
||||||
</Transition>
|
</Transition>
|
||||||
</main>
|
</main>
|
||||||
</header>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped>
|
||||||
|
.scroll {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll {
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll {
|
||||||
|
scrollbar-width: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -38,6 +38,8 @@ const visible = defineModel<boolean>('visible', {
|
|||||||
default: false
|
default: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const defaultIcon = import.meta.env.VITE_MENU_ICON;
|
||||||
|
|
||||||
const iconType = ref<Api.System.IconType>('1');
|
const iconType = ref<Api.System.IconType>('1');
|
||||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
const { formRef, validate, restoreValidation } = useNaiveForm();
|
||||||
const { createRequiredRule, createNumberRequiredRule } = useFormRules();
|
const { createRequiredRule, createNumberRequiredRule } = useFormRules();
|
||||||
@ -69,7 +71,7 @@ function createDefaultModel(): Model {
|
|||||||
visible: '0',
|
visible: '0',
|
||||||
status: '0',
|
status: '0',
|
||||||
perms: '',
|
perms: '',
|
||||||
icon: undefined,
|
icon: defaultIcon,
|
||||||
remark: ''
|
remark: ''
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -118,6 +120,7 @@ const localIconOptions = localIcons.map<SelectOption>(item => ({
|
|||||||
|
|
||||||
function handleInitModel() {
|
function handleInitModel() {
|
||||||
queryList.value = [];
|
queryList.value = [];
|
||||||
|
iconType.value = '1';
|
||||||
Object.assign(model, createDefaultModel());
|
Object.assign(model, createDefaultModel());
|
||||||
|
|
||||||
if (props.operateType === 'edit' && props.rowData) {
|
if (props.operateType === 'edit' && props.rowData) {
|
||||||
@ -208,7 +211,7 @@ async function handleSubmit() {
|
|||||||
visible: menuVisible,
|
visible: menuVisible,
|
||||||
status,
|
status,
|
||||||
perms,
|
perms,
|
||||||
icon,
|
icon: icon || defaultIcon,
|
||||||
component: processComponent(component),
|
component: processComponent(component),
|
||||||
remark
|
remark
|
||||||
};
|
};
|
||||||
|
@ -323,11 +323,11 @@ function handleResetSearch() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
:deep(.n-tree-node) {
|
:deep(.n-tree-node) {
|
||||||
height: 33px;
|
height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.n-tree-node-switcher) {
|
:deep(.n-tree-node-switcher) {
|
||||||
height: 33px;
|
height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.n-tree-node-switcher__icon) {
|
:deep(.n-tree-node-switcher__icon) {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { NButton, NDivider } from 'naive-ui';
|
import { NAvatar, NButton, NDivider, NEllipsis } from 'naive-ui';
|
||||||
import { useBoolean, useLoading } from '@sa/hooks';
|
import { useBoolean, useLoading } from '@sa/hooks';
|
||||||
import { jsonClone } from '@sa/utils';
|
import { jsonClone } from '@sa/utils';
|
||||||
import { fetchBatchDeleteUser, fetchGetDeptTree, fetchGetUserList, fetchUpdateUserStatus } from '@/service/api/system';
|
import { fetchBatchDeleteUser, fetchGetDeptTree, fetchGetUserList, fetchUpdateUserStatus } from '@/service/api/system';
|
||||||
@ -12,6 +12,7 @@ import { useDownload } from '@/hooks/business/download';
|
|||||||
import ButtonIcon from '@/components/custom/button-icon.vue';
|
import ButtonIcon from '@/components/custom/button-icon.vue';
|
||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
import StatusSwitch from '@/components/custom/status-switch.vue';
|
import StatusSwitch from '@/components/custom/status-switch.vue';
|
||||||
|
import DictTag from '@/components/custom/dict-tag.vue';
|
||||||
import UserOperateDrawer from './modules/user-operate-drawer.vue';
|
import UserOperateDrawer from './modules/user-operate-drawer.vue';
|
||||||
import UserImportModal from './modules/user-import-modal.vue';
|
import UserImportModal from './modules/user-import-modal.vue';
|
||||||
import UserPasswordDrawer from './modules/user-password-drawer.vue';
|
import UserPasswordDrawer from './modules/user-password-drawer.vue';
|
||||||
@ -65,41 +66,64 @@ const {
|
|||||||
key: 'index',
|
key: 'index',
|
||||||
title: $t('common.index'),
|
title: $t('common.index'),
|
||||||
align: 'center',
|
align: 'center',
|
||||||
width: 64
|
width: 48
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'userName',
|
key: 'userName',
|
||||||
title: $t('page.system.user.userName'),
|
title: $t('page.system.user.userName'),
|
||||||
align: 'center',
|
align: 'left',
|
||||||
minWidth: 120,
|
width: 200,
|
||||||
ellipsis: true
|
ellipsis: true,
|
||||||
|
render: row => {
|
||||||
|
return (
|
||||||
|
<div class="flex items-center justify-center gap-2">
|
||||||
|
<NAvatar src={row.avatar} class="bg-primary">
|
||||||
|
{row.avatar ? undefined : row.nickName.charAt(0)}
|
||||||
|
</NAvatar>
|
||||||
|
<div class="max-w-160px flex flex-col">
|
||||||
|
<NEllipsis>{row.userName}</NEllipsis>
|
||||||
|
<NEllipsis>{row.nickName}</NEllipsis>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'nickName',
|
key: 'sex',
|
||||||
title: $t('page.system.user.nickName'),
|
title: $t('page.system.user.sex'),
|
||||||
align: 'center',
|
align: 'center',
|
||||||
minWidth: 120,
|
width: 80,
|
||||||
ellipsis: true
|
ellipsis: true,
|
||||||
|
render(row) {
|
||||||
|
return <DictTag value={row.sex} dictCode="sys_user_sex" />;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'deptName',
|
key: 'deptName',
|
||||||
title: $t('page.system.user.deptName'),
|
title: $t('page.system.user.deptName'),
|
||||||
align: 'center',
|
align: 'center',
|
||||||
minWidth: 120,
|
width: 120,
|
||||||
|
ellipsis: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'email',
|
||||||
|
title: $t('page.system.user.email'),
|
||||||
|
align: 'center',
|
||||||
|
width: 120,
|
||||||
ellipsis: true
|
ellipsis: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'phonenumber',
|
key: 'phonenumber',
|
||||||
title: $t('page.system.user.phonenumber'),
|
title: $t('page.system.user.phonenumber'),
|
||||||
align: 'center',
|
align: 'center',
|
||||||
minWidth: 120,
|
width: 120,
|
||||||
ellipsis: true
|
ellipsis: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'status',
|
key: 'status',
|
||||||
title: $t('page.system.user.status'),
|
title: $t('page.system.user.status'),
|
||||||
align: 'center',
|
align: 'center',
|
||||||
minWidth: 80,
|
width: 80,
|
||||||
render(row) {
|
render(row) {
|
||||||
return (
|
return (
|
||||||
<StatusSwitch
|
<StatusSwitch
|
||||||
@ -115,7 +139,7 @@ const {
|
|||||||
key: 'createTime',
|
key: 'createTime',
|
||||||
title: $t('page.system.user.createTime'),
|
title: $t('page.system.user.createTime'),
|
||||||
align: 'center',
|
align: 'center',
|
||||||
minWidth: 120
|
width: 120
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'operate',
|
key: 'operate',
|
||||||
@ -341,7 +365,7 @@ function handleResetSearch() {
|
|||||||
:data="data"
|
:data="data"
|
||||||
size="small"
|
size="small"
|
||||||
:flex-height="!appStore.isMobile"
|
:flex-height="!appStore.isMobile"
|
||||||
:scroll-x="962"
|
:scroll-x="1200"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
remote
|
remote
|
||||||
:row-key="row => row.userId"
|
:row-key="row => row.userId"
|
||||||
@ -391,11 +415,11 @@ function handleResetSearch() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
:deep(.n-tree-node) {
|
:deep(.n-tree-node) {
|
||||||
height: 25px;
|
height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.n-tree-node-switcher) {
|
:deep(.n-tree-node-switcher) {
|
||||||
height: 25px;
|
height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.n-tree-node-switcher__icon) {
|
:deep(.n-tree-node-switcher__icon) {
|
||||||
|
@ -1,210 +0,0 @@
|
|||||||
<script setup lang="tsx">
|
|
||||||
import { NDivider } from 'naive-ui';
|
|
||||||
import { jsonClone } from '@sa/utils';
|
|
||||||
import { type TableDataWithIndex } from '@sa/hooks';
|
|
||||||
import { fetchDeleteCategory, fetchGetCategoryList } from '@/service/api/workflow';
|
|
||||||
import { useAppStore } from '@/store/modules/app';
|
|
||||||
import { useAuth } from '@/hooks/business/auth';
|
|
||||||
import { useTreeTable, useTreeTableOperate } from '@/hooks/common/tree-table';
|
|
||||||
import { useDownload } from '@/hooks/business/download';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
import ButtonIcon from '@/components/custom/button-icon.vue';
|
|
||||||
import WorkflowCategoryOperateDrawer from './modules/category-operate-drawer.vue';
|
|
||||||
import WorkflowCategorySearch from './modules/category-search.vue';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'WorkflowCategoryList'
|
|
||||||
});
|
|
||||||
|
|
||||||
const appStore = useAppStore();
|
|
||||||
const { download } = useDownload();
|
|
||||||
const { hasAuth } = useAuth();
|
|
||||||
|
|
||||||
const {
|
|
||||||
columns,
|
|
||||||
columnChecks,
|
|
||||||
data,
|
|
||||||
getData,
|
|
||||||
loading,
|
|
||||||
searchParams,
|
|
||||||
resetSearchParams,
|
|
||||||
expandedRowKeys,
|
|
||||||
isCollapse,
|
|
||||||
expandAll,
|
|
||||||
collapseAll
|
|
||||||
} = useTreeTable({
|
|
||||||
apiFn: fetchGetCategoryList,
|
|
||||||
apiParams: {
|
|
||||||
pageNum: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
// if you want to use the searchParams in Form, you need to define the following properties, and the value is null
|
|
||||||
// the value can not be undefined, otherwise the property in Form will not be reactive
|
|
||||||
categoryName: null
|
|
||||||
},
|
|
||||||
idField: 'categoryId',
|
|
||||||
columns: () => [
|
|
||||||
{
|
|
||||||
key: 'categoryName',
|
|
||||||
title: '分类名称',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'orderNum',
|
|
||||||
title: '显示顺序',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'createTime',
|
|
||||||
title: '创建时间',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'operate',
|
|
||||||
title: $t('common.operate'),
|
|
||||||
align: 'center',
|
|
||||||
width: 130,
|
|
||||||
render: row => {
|
|
||||||
const addBtn = () => {
|
|
||||||
return (
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
icon="material-symbols:add-2-rounded"
|
|
||||||
tooltipContent={$t('common.add')}
|
|
||||||
onClick={() => addInRow(row)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const editBtn = () => {
|
|
||||||
return (
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
icon="material-symbols:drive-file-rename-outline-outline"
|
|
||||||
tooltipContent={$t('common.edit')}
|
|
||||||
onClick={() => edit(row)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteBtn = () => {
|
|
||||||
return (
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="error"
|
|
||||||
icon="material-symbols:delete-outline"
|
|
||||||
tooltipContent={$t('common.delete')}
|
|
||||||
popconfirmContent={$t('common.confirmDelete')}
|
|
||||||
onPositiveClick={() => handleDelete(row.categoryId!)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const buttons = [];
|
|
||||||
if (hasAuth('workflow:category:add')) buttons.push(addBtn());
|
|
||||||
if (hasAuth('workflow:category:edit')) buttons.push(editBtn());
|
|
||||||
if (hasAuth('workflow:category:remove')) buttons.push(deleteBtn());
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div class="flex-center gap-4px">
|
|
||||||
{buttons.map((btn, index) => (
|
|
||||||
<>
|
|
||||||
{index !== 0 && <NDivider vertical />}
|
|
||||||
{btn}
|
|
||||||
</>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
const { drawerVisible, operateType, editingData, handleAdd, handleEdit, checkedRowKeys, onDeleted } =
|
|
||||||
useTreeTableOperate(data, getData);
|
|
||||||
|
|
||||||
async function handleDelete(id: CommonType.IdType) {
|
|
||||||
// request
|
|
||||||
const { error } = await fetchDeleteCategory(id);
|
|
||||||
if (error) return;
|
|
||||||
onDeleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function edit(row: TableDataWithIndex<Api.Workflow.WorkflowCategory>) {
|
|
||||||
handleEdit(row);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function addInRow(row: TableDataWithIndex<Api.Workflow.WorkflowCategory>) {
|
|
||||||
editingData.value = jsonClone(row);
|
|
||||||
handleAdd();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleAddOperate() {
|
|
||||||
editingData.value = null;
|
|
||||||
handleAdd();
|
|
||||||
}
|
|
||||||
function handleExport() {
|
|
||||||
download('/workflow/category/export', searchParams, `流程分类_#[[${new Date().getTime()}]]#.xlsx`);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
|
|
||||||
<WorkflowCategorySearch v-model:model="searchParams" @reset="resetSearchParams" @search="getData" />
|
|
||||||
<NCard title="流程分类列表" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
|
|
||||||
<template #header-extra>
|
|
||||||
<TableHeaderOperation
|
|
||||||
v-model:columns="columnChecks"
|
|
||||||
:loading="loading"
|
|
||||||
:show-add="hasAuth('workflow:category:add')"
|
|
||||||
:show-delete="false"
|
|
||||||
:show-export="false"
|
|
||||||
@add="handleAddOperate"
|
|
||||||
@export="handleExport"
|
|
||||||
@refresh="getData"
|
|
||||||
>
|
|
||||||
<template #prefix>
|
|
||||||
<NButton v-if="!isCollapse" :disabled="!data.length" size="small" @click="expandAll">
|
|
||||||
<template #icon>
|
|
||||||
<icon-quill:expand />
|
|
||||||
</template>
|
|
||||||
全部展开
|
|
||||||
</NButton>
|
|
||||||
<NButton v-if="isCollapse" :disabled="!data.length" size="small" @click="collapseAll">
|
|
||||||
<template #icon>
|
|
||||||
<icon-quill:collapse />
|
|
||||||
</template>
|
|
||||||
全部收起
|
|
||||||
</NButton>
|
|
||||||
</template>
|
|
||||||
</TableHeaderOperation>
|
|
||||||
</template>
|
|
||||||
<NDataTable
|
|
||||||
v-model:checked-row-keys="checkedRowKeys"
|
|
||||||
v-model:expanded-row-keys="expandedRowKeys"
|
|
||||||
:columns="columns"
|
|
||||||
:data="data"
|
|
||||||
size="small"
|
|
||||||
:indent="32"
|
|
||||||
:flex-height="!appStore.isMobile"
|
|
||||||
:scroll-x="962"
|
|
||||||
:loading="loading"
|
|
||||||
remote
|
|
||||||
:row-key="row => row.categoryId"
|
|
||||||
class="sm:h-full"
|
|
||||||
/>
|
|
||||||
<WorkflowCategoryOperateDrawer
|
|
||||||
v-model:visible="drawerVisible"
|
|
||||||
:operate-type="operateType"
|
|
||||||
:row-data="editingData"
|
|
||||||
@submitted="getData"
|
|
||||||
/>
|
|
||||||
</NCard>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
@ -1,128 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { computed, reactive, watch } from 'vue';
|
|
||||||
import { fetchCreateCategory, fetchUpdateCategory } from '@/service/api/workflow';
|
|
||||||
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
defineOptions({
|
|
||||||
name: 'WorkflowCategoryOperateDrawer'
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
/** the type of operation */
|
|
||||||
operateType: NaiveUI.TableOperateType;
|
|
||||||
/** the edit row data */
|
|
||||||
rowData?: Api.Workflow.WorkflowCategory | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = defineProps<Props>();
|
|
||||||
|
|
||||||
interface Emits {
|
|
||||||
(e: 'submitted'): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
|
||||||
|
|
||||||
const visible = defineModel<boolean>('visible', {
|
|
||||||
default: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
|
||||||
const { createRequiredRule } = useFormRules();
|
|
||||||
|
|
||||||
const title = computed(() => {
|
|
||||||
const titles: Record<NaiveUI.TableOperateType, string> = {
|
|
||||||
add: '新增测试树',
|
|
||||||
edit: '编辑测试树'
|
|
||||||
};
|
|
||||||
return titles[props.operateType];
|
|
||||||
});
|
|
||||||
|
|
||||||
type Model = Api.Workflow.WorkflowCategoryOperateParams;
|
|
||||||
|
|
||||||
const model: Model = reactive(createDefaultModel());
|
|
||||||
|
|
||||||
function createDefaultModel(): Model {
|
|
||||||
return {
|
|
||||||
parentId: '',
|
|
||||||
categoryName: '',
|
|
||||||
orderNum: 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
type RuleKey = Extract<keyof Model, 'categoryId' | 'parentId' | 'categoryName'>;
|
|
||||||
|
|
||||||
const rules: Record<RuleKey, App.Global.FormRule> = {
|
|
||||||
categoryId: createRequiredRule('主键不能为空'),
|
|
||||||
parentId: createRequiredRule('上级分类不能为空'),
|
|
||||||
categoryName: createRequiredRule('分类名称不能为空')
|
|
||||||
};
|
|
||||||
|
|
||||||
function handleUpdateModelWhenEdit() {
|
|
||||||
if (props.operateType === 'add') {
|
|
||||||
Object.assign(model, createDefaultModel());
|
|
||||||
model.parentId = props.rowData?.categoryId || 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.operateType === 'edit' && props.rowData) {
|
|
||||||
Object.assign(model, props.rowData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeDrawer() {
|
|
||||||
visible.value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleSubmit() {
|
|
||||||
await validate();
|
|
||||||
|
|
||||||
// request
|
|
||||||
if (props.operateType === 'add') {
|
|
||||||
const { parentId, categoryName, orderNum } = model;
|
|
||||||
const { error } = await fetchCreateCategory({ parentId, categoryName, orderNum });
|
|
||||||
if (error) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.operateType === 'edit') {
|
|
||||||
const { categoryId, parentId, categoryName, orderNum } = model;
|
|
||||||
const { error } = await fetchUpdateCategory({ categoryId, parentId, categoryName, orderNum });
|
|
||||||
if (error) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.$message?.success($t('common.updateSuccess'));
|
|
||||||
closeDrawer();
|
|
||||||
emit('submitted');
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(visible, () => {
|
|
||||||
if (visible.value) {
|
|
||||||
handleUpdateModelWhenEdit();
|
|
||||||
restoreValidation();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NDrawer v-model:show="visible" :title="title" display-directive="show" :width="800" class="max-w-90%">
|
|
||||||
<NDrawerContent :title="title" :native-scrollbar="false" closable>
|
|
||||||
<NForm ref="formRef" :model="model" :rules="rules">
|
|
||||||
<NFormItem label="上级分类" path="parentId">
|
|
||||||
<FlowCategorySelect v-model:value="model.parentId" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="分类名称" path="categoryName">
|
|
||||||
<NInput v-model:value="model.categoryName" placeholder="请输入分类名称" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="排序" path="orderNum">
|
|
||||||
<NInputNumber v-model:value="model.orderNum" placeholder="请输入排序" />
|
|
||||||
</NFormItem>
|
|
||||||
</NForm>
|
|
||||||
<template #footer>
|
|
||||||
<NSpace :size="16">
|
|
||||||
<NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
|
|
||||||
<NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
|
|
||||||
</NSpace>
|
|
||||||
</template>
|
|
||||||
</NDrawerContent>
|
|
||||||
</NDrawer>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
@ -1,63 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { useNaiveForm } from '@/hooks/common/form';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'WorkflowCategorySearch'
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Emits {
|
|
||||||
(e: 'reset'): void;
|
|
||||||
(e: 'search'): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
|
||||||
|
|
||||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
|
||||||
|
|
||||||
const model = defineModel<Api.Workflow.WorkflowCategorySearchParams>('model', { required: true });
|
|
||||||
|
|
||||||
async function reset() {
|
|
||||||
await restoreValidation();
|
|
||||||
emit('reset');
|
|
||||||
}
|
|
||||||
|
|
||||||
async function search() {
|
|
||||||
await validate();
|
|
||||||
emit('search');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
|
||||||
<NCollapse>
|
|
||||||
<NCollapseItem :title="$t('common.search')" name="user-search">
|
|
||||||
<NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
|
|
||||||
<NGrid responsive="screen" item-responsive>
|
|
||||||
<NFormItemGi span="24 s:12 m:12" label="分类名称" path="categoryName" class="pr-24px">
|
|
||||||
<NInput v-model:value="model.categoryName" placeholder="请输入分类名称" />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi :show-feedback="false" span="24 s:12 m:12" class="pr-24px">
|
|
||||||
<NSpace class="w-full" justify="end">
|
|
||||||
<NButton @click="reset">
|
|
||||||
<template #icon>
|
|
||||||
<icon-ic-round-refresh class="text-icon" />
|
|
||||||
</template>
|
|
||||||
{{ $t('common.reset') }}
|
|
||||||
</NButton>
|
|
||||||
<NButton type="primary" ghost @click="search">
|
|
||||||
<template #icon>
|
|
||||||
<icon-ic-round-search class="text-icon" />
|
|
||||||
</template>
|
|
||||||
{{ $t('common.search') }}
|
|
||||||
</NButton>
|
|
||||||
</NSpace>
|
|
||||||
</NFormItemGi>
|
|
||||||
</NGrid>
|
|
||||||
</NForm>
|
|
||||||
</NCollapseItem>
|
|
||||||
</NCollapse>
|
|
||||||
</NCard>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
@ -1,48 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { useRoute } from 'vue-router';
|
|
||||||
import { useEventListener } from '@vueuse/core';
|
|
||||||
import { stringify } from 'qs';
|
|
||||||
import { getToken } from '@/store/modules/auth/shared';
|
|
||||||
import { getTabIdByRoute } from '@/store/modules/tab/shared';
|
|
||||||
import { useTabStore } from '@/store/modules/tab';
|
|
||||||
import { useRouterPush } from '@/hooks/common/router';
|
|
||||||
import { getServiceBaseURL } from '@/utils/service';
|
|
||||||
|
|
||||||
const isHttpProxy = import.meta.env.DEV && import.meta.env.VITE_HTTP_PROXY === 'Y';
|
|
||||||
const { baseURL } = getServiceBaseURL(import.meta.env, isHttpProxy);
|
|
||||||
|
|
||||||
const { removeTab } = useTabStore();
|
|
||||||
const { routerPushByKey } = useRouterPush();
|
|
||||||
const route = useRoute();
|
|
||||||
const disabled = route.query.disabled === 'true';
|
|
||||||
|
|
||||||
const urlParams = {
|
|
||||||
Authorization: `Bearer ${getToken()}`,
|
|
||||||
id: route.query.definitionId,
|
|
||||||
clientid: import.meta.env.VITE_APP_CLIENT_ID,
|
|
||||||
disabled
|
|
||||||
};
|
|
||||||
|
|
||||||
const iframeUrl = `${baseURL}/warm-flow-ui/index.html?${stringify(urlParams)}`;
|
|
||||||
|
|
||||||
function messageHandler(event: MessageEvent) {
|
|
||||||
switch (event.data.method) {
|
|
||||||
case 'close': {
|
|
||||||
const tabId = getTabIdByRoute(route);
|
|
||||||
removeTab(tabId);
|
|
||||||
routerPushByKey('workflow_process-definition');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// iframe监听组件内设计器保存事件
|
|
||||||
useEventListener('message', messageHandler);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<iframe :src="iframeUrl" class="size-full"></iframe>
|
|
||||||
</template>
|
|
@ -1,268 +0,0 @@
|
|||||||
<script setup lang="tsx">
|
|
||||||
import { ref } from 'vue';
|
|
||||||
import { NDivider, NTag } from 'naive-ui';
|
|
||||||
import { jsonClone } from '@sa/utils';
|
|
||||||
import { leaveTypeRecord } from '@/constants/workflow';
|
|
||||||
import { fetchBatchDeleteLeave, fetchGetLeaveList } from '@/service/api/workflow';
|
|
||||||
import { useAppStore } from '@/store/modules/app';
|
|
||||||
import { useAuth } from '@/hooks/business/auth';
|
|
||||||
import { useDownload } from '@/hooks/business/download';
|
|
||||||
import { useTable, useTableOperate } from '@/hooks/common/table';
|
|
||||||
import { useDict } from '@/hooks/business/dict';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
import DictTag from '@/components/custom/dict-tag.vue';
|
|
||||||
import ButtonIcon from '@/components/custom/button-icon.vue';
|
|
||||||
import LeaveEdit from '@/components/workflow/form/leave-edit/index.vue';
|
|
||||||
import LeaveSearch from './modules/leave-search.vue';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'LeaveList'
|
|
||||||
});
|
|
||||||
|
|
||||||
const appStore = useAppStore();
|
|
||||||
const { download } = useDownload();
|
|
||||||
const { hasAuth } = useAuth();
|
|
||||||
|
|
||||||
useDict('wf_business_status');
|
|
||||||
|
|
||||||
const workflowTableOperateType = ref<CommonType.WorkflowTableOperateType>('add');
|
|
||||||
const {
|
|
||||||
columns,
|
|
||||||
columnChecks,
|
|
||||||
data,
|
|
||||||
getData,
|
|
||||||
getDataByPage,
|
|
||||||
loading,
|
|
||||||
mobilePagination,
|
|
||||||
searchParams,
|
|
||||||
resetSearchParams
|
|
||||||
} = useTable({
|
|
||||||
apiFn: fetchGetLeaveList,
|
|
||||||
apiParams: {
|
|
||||||
pageNum: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
// if you want to use the searchParams in Form, you need to define the following properties, and the value is null
|
|
||||||
// the value can not be undefined, otherwise the property in Form will not be reactive
|
|
||||||
startLeaveDays: null,
|
|
||||||
endLeaveDays: null,
|
|
||||||
params: {}
|
|
||||||
},
|
|
||||||
columns: () => [
|
|
||||||
{
|
|
||||||
type: 'selection',
|
|
||||||
align: 'center',
|
|
||||||
width: 48
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'leaveType',
|
|
||||||
title: '请假类型',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 100,
|
|
||||||
render: row => {
|
|
||||||
return (
|
|
||||||
<NTag size="small" type="info">
|
|
||||||
{leaveTypeRecord[row.leaveType]}
|
|
||||||
</NTag>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'startDate',
|
|
||||||
title: '开始时间',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'endDate',
|
|
||||||
title: '结束时间',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'leaveDays',
|
|
||||||
title: '请假天数',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 100,
|
|
||||||
render: row => {
|
|
||||||
return `${row.leaveDays} 天`;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'remark',
|
|
||||||
title: '请假原因',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'status',
|
|
||||||
title: '状态',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 100,
|
|
||||||
render: row => {
|
|
||||||
return <DictTag size="small" value={row.status} dictCode="wf_business_status" />;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'operate',
|
|
||||||
title: $t('common.operate'),
|
|
||||||
align: 'center',
|
|
||||||
width: 130,
|
|
||||||
render: row => {
|
|
||||||
const buttons = [];
|
|
||||||
|
|
||||||
const showEdit =
|
|
||||||
hasAuth('workflow:leave:edit') &&
|
|
||||||
(row.status === 'draft' || row.status === 'cancel' || row.status === 'back');
|
|
||||||
|
|
||||||
const showDelete =
|
|
||||||
hasAuth('workflow:leave:remove') &&
|
|
||||||
(row.status === 'draft' || row.status === 'cancel' || row.status === 'back');
|
|
||||||
|
|
||||||
const showCancel = row.status === 'waiting';
|
|
||||||
if (hasAuth('workflow:leave:query')) {
|
|
||||||
buttons.push(
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="info"
|
|
||||||
icon="material-symbols:visibility-outline"
|
|
||||||
tooltipContent="查看"
|
|
||||||
onClick={() => view(row.id!)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showEdit) {
|
|
||||||
buttons.push(
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
icon="material-symbols:drive-file-rename-outline-outline"
|
|
||||||
tooltipContent={$t('common.edit')}
|
|
||||||
onClick={() => edit(row.id!)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showCancel) {
|
|
||||||
buttons.push(
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="warning"
|
|
||||||
icon="material-symbols:cancel-outline"
|
|
||||||
tooltipContent="撤销"
|
|
||||||
popconfirmContent="确定要撤销该申请吗?"
|
|
||||||
onPositiveClick={() => {}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showDelete) {
|
|
||||||
buttons.push(
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="error"
|
|
||||||
icon="material-symbols:delete-outline"
|
|
||||||
tooltipContent={$t('common.delete')}
|
|
||||||
popconfirmContent={$t('common.confirmDelete')}
|
|
||||||
onPositiveClick={() => handleDelete(row.id!)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div class="flex-center gap-1px">
|
|
||||||
{buttons.map((btn, index) => (index > 0 ? [<NDivider vertical />, btn] : btn))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
const { drawerVisible, openDrawer, editingData, checkedRowKeys, onBatchDeleted, onDeleted } = useTableOperate(
|
|
||||||
data,
|
|
||||||
getData
|
|
||||||
);
|
|
||||||
|
|
||||||
async function handleBatchDelete() {
|
|
||||||
// request
|
|
||||||
const { error } = await fetchBatchDeleteLeave(checkedRowKeys.value);
|
|
||||||
if (error) return;
|
|
||||||
onBatchDeleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleDelete(id: CommonType.IdType) {
|
|
||||||
// request
|
|
||||||
const { error } = await fetchBatchDeleteLeave([id]);
|
|
||||||
if (error) return;
|
|
||||||
onDeleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleAdd() {
|
|
||||||
workflowTableOperateType.value = 'add';
|
|
||||||
openDrawer();
|
|
||||||
}
|
|
||||||
|
|
||||||
function cloneAndOpenDrawer(id: CommonType.IdType) {
|
|
||||||
const findItem = data.value.find(item => item.id === id) || null;
|
|
||||||
editingData.value = jsonClone(findItem);
|
|
||||||
openDrawer();
|
|
||||||
}
|
|
||||||
|
|
||||||
function edit(id: CommonType.IdType) {
|
|
||||||
workflowTableOperateType.value = 'edit';
|
|
||||||
cloneAndOpenDrawer(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
function view(id: CommonType.IdType) {
|
|
||||||
workflowTableOperateType.value = 'detail';
|
|
||||||
cloneAndOpenDrawer(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleExport() {
|
|
||||||
download('/workflow/leave/export', searchParams, `请假申请_${new Date().getTime()}.xlsx`);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
|
|
||||||
<LeaveSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getDataByPage" />
|
|
||||||
<NCard title="请假申请列表" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
|
|
||||||
<template #header-extra>
|
|
||||||
<TableHeaderOperation
|
|
||||||
v-model:columns="columnChecks"
|
|
||||||
:disabled-delete="checkedRowKeys.length === 0"
|
|
||||||
:loading="loading"
|
|
||||||
:show-add="hasAuth('workflow:leave:add')"
|
|
||||||
:show-delete="hasAuth('workflow:leave:remove')"
|
|
||||||
:show-export="hasAuth('workflow:leave:export')"
|
|
||||||
@add="handleAdd"
|
|
||||||
@delete="handleBatchDelete"
|
|
||||||
@export="handleExport"
|
|
||||||
@refresh="getData"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<NDataTable
|
|
||||||
v-model:checked-row-keys="checkedRowKeys"
|
|
||||||
:columns="columns"
|
|
||||||
:data="data"
|
|
||||||
size="small"
|
|
||||||
:flex-height="!appStore.isMobile"
|
|
||||||
:scroll-x="778"
|
|
||||||
:loading="loading"
|
|
||||||
remote
|
|
||||||
:row-key="row => row.id"
|
|
||||||
:pagination="mobilePagination"
|
|
||||||
class="sm:h-full"
|
|
||||||
/>
|
|
||||||
<LeaveEdit
|
|
||||||
v-model:visible="drawerVisible"
|
|
||||||
:operate-type="workflowTableOperateType"
|
|
||||||
:row-data="editingData"
|
|
||||||
@submitted="getDataByPage"
|
|
||||||
/>
|
|
||||||
</NCard>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
@ -1,68 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { useNaiveForm } from '@/hooks/common/form';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'LeaveSearch'
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Emits {
|
|
||||||
(e: 'reset'): void;
|
|
||||||
(e: 'search'): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
|
||||||
|
|
||||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
|
||||||
|
|
||||||
const model = defineModel<Api.Workflow.LeaveSearchParams>('model', { required: true });
|
|
||||||
|
|
||||||
async function reset() {
|
|
||||||
Object.assign(model.value.params!, {});
|
|
||||||
await restoreValidation();
|
|
||||||
emit('reset');
|
|
||||||
}
|
|
||||||
|
|
||||||
async function search() {
|
|
||||||
await validate();
|
|
||||||
emit('search');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
|
||||||
<NCollapse>
|
|
||||||
<NCollapseItem :title="$t('common.search')" name="user-search">
|
|
||||||
<NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
|
|
||||||
<NGrid responsive="screen" item-responsive>
|
|
||||||
<NFormItemGi span="24 s:12 m:16" label="请假天数" path="startLeaveDays" class="pr-24px">
|
|
||||||
<NSpace align="center" class="w-full">
|
|
||||||
<NInputNumber v-model:value="model.startLeaveDays" placeholder="开始天数" />
|
|
||||||
<span class="mx-2">至</span>
|
|
||||||
<NInputNumber v-model:value="model.endLeaveDays" placeholder="结束天数" />
|
|
||||||
</NSpace>
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="24 s:12 m:8" class="pr-24px">
|
|
||||||
<NSpace class="w-full" justify="end">
|
|
||||||
<NButton @click="reset">
|
|
||||||
<template #icon>
|
|
||||||
<icon-ic-round-refresh class="text-icon" />
|
|
||||||
</template>
|
|
||||||
{{ $t('common.reset') }}
|
|
||||||
</NButton>
|
|
||||||
<NButton type="primary" ghost @click="search">
|
|
||||||
<template #icon>
|
|
||||||
<icon-ic-round-search class="text-icon" />
|
|
||||||
</template>
|
|
||||||
{{ $t('common.search') }}
|
|
||||||
</NButton>
|
|
||||||
</NSpace>
|
|
||||||
</NFormItemGi>
|
|
||||||
</NGrid>
|
|
||||||
</NForm>
|
|
||||||
</NCollapseItem>
|
|
||||||
</NCollapse>
|
|
||||||
</NCard>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
@ -1,471 +0,0 @@
|
|||||||
<script setup lang="tsx">
|
|
||||||
import { computed, ref, watch } from 'vue';
|
|
||||||
import { NDivider, NSwitch, NTag } from 'naive-ui';
|
|
||||||
import { useBoolean, useLoading } from '@sa/hooks';
|
|
||||||
import { type TableDataWithIndex } from '@sa/hooks';
|
|
||||||
import { workflowPublishStatusRecord } from '@/constants/workflow';
|
|
||||||
import {
|
|
||||||
fetchActiveDefinition,
|
|
||||||
fetchBatchDeleteDefinition,
|
|
||||||
fetchCopyDefinition,
|
|
||||||
fetchGetCategoryTree,
|
|
||||||
fetchGetDefinitionList,
|
|
||||||
fetchGetUnPublishDefinitionList,
|
|
||||||
fetchPublishDefinition
|
|
||||||
} from '@/service/api/workflow';
|
|
||||||
import { useAppStore } from '@/store/modules/app';
|
|
||||||
import { useAuth } from '@/hooks/business/auth';
|
|
||||||
import { useDownload } from '@/hooks/business/download';
|
|
||||||
import { useTable, useTableOperate } from '@/hooks/common/table';
|
|
||||||
import { useRouterPush } from '@/hooks/common/router';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
import ButtonIcon from '@/components/custom/button-icon.vue';
|
|
||||||
import DefinitionOperateDrawer from './modules/definition-operate-drawer.vue';
|
|
||||||
import DefinitionSearch from './modules/definition-search.vue';
|
|
||||||
import DefinitionImportModal from './modules/definition-import-modal.vue';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'DefinitionList'
|
|
||||||
});
|
|
||||||
|
|
||||||
interface IsPublishOption {
|
|
||||||
label: string;
|
|
||||||
value: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const appStore = useAppStore();
|
|
||||||
const { download } = useDownload();
|
|
||||||
const { hasAuth } = useAuth();
|
|
||||||
const { routerPushByKey } = useRouterPush();
|
|
||||||
|
|
||||||
const { bool: importVisible, setTrue: showImportModal } = useBoolean();
|
|
||||||
const isPublish = ref<boolean>(true);
|
|
||||||
const isPublishOptions = ref<IsPublishOption[]>([
|
|
||||||
{
|
|
||||||
label: '已发布',
|
|
||||||
value: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '未发布',
|
|
||||||
value: false
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
const {
|
|
||||||
columns,
|
|
||||||
columnChecks,
|
|
||||||
data,
|
|
||||||
getData,
|
|
||||||
getDataByPage,
|
|
||||||
loading,
|
|
||||||
mobilePagination,
|
|
||||||
searchParams,
|
|
||||||
resetSearchParams,
|
|
||||||
updateApiFn
|
|
||||||
} = useTable({
|
|
||||||
apiFn: fetchGetDefinitionList,
|
|
||||||
apiParams: {
|
|
||||||
pageNum: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
// if you want to use the searchParams in Form, you need to define the following properties, and the value is null
|
|
||||||
// the value can not be undefined, otherwise the property in Form will not be reactive
|
|
||||||
flowCode: null,
|
|
||||||
flowName: null,
|
|
||||||
category: null,
|
|
||||||
params: {}
|
|
||||||
},
|
|
||||||
columns: () => [
|
|
||||||
{
|
|
||||||
type: 'selection',
|
|
||||||
align: 'center',
|
|
||||||
width: 48
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'flowName',
|
|
||||||
title: '流程定义名称',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'flowCode',
|
|
||||||
title: '流程定义编码',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'categoryName',
|
|
||||||
title: '流程分类',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'version',
|
|
||||||
title: '版本号',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 120,
|
|
||||||
render: row => <NTag type="info">v{row.version}.0</NTag>
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'activityStatus',
|
|
||||||
title: '激活状态',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 120,
|
|
||||||
render(row) {
|
|
||||||
const {
|
|
||||||
loading: activityLoading,
|
|
||||||
startLoading: startActivityLoading,
|
|
||||||
endLoading: endActivityLoading
|
|
||||||
} = useLoading();
|
|
||||||
|
|
||||||
/** 处理状态切换 */
|
|
||||||
async function handleStatusChange(value: boolean) {
|
|
||||||
window.$dialog?.warning({
|
|
||||||
title: '系统提示',
|
|
||||||
content: `确定要${value ? '激活' : '挂起'} ${row.flowCode} 吗?`,
|
|
||||||
positiveText: '确定',
|
|
||||||
negativeText: '取消',
|
|
||||||
onPositiveClick: async () => {
|
|
||||||
startActivityLoading();
|
|
||||||
const { error } = await fetchActiveDefinition(row.id, value);
|
|
||||||
if (error) return;
|
|
||||||
|
|
||||||
if (!error) row.activityStatus = value ? 1 : 0;
|
|
||||||
|
|
||||||
window.$message?.success($t('page.system.user.statusChangeSuccess'));
|
|
||||||
getData();
|
|
||||||
endActivityLoading();
|
|
||||||
},
|
|
||||||
onNegativeClick: () => {}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<NSwitch
|
|
||||||
v-model:value={row.activityStatus}
|
|
||||||
loading={activityLoading.value}
|
|
||||||
rubber-band={false}
|
|
||||||
checked-value={1}
|
|
||||||
unchecked-value={0}
|
|
||||||
on-update:value={handleStatusChange}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'isPublish',
|
|
||||||
title: '发布状态',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 120,
|
|
||||||
render: row => {
|
|
||||||
if (row.isPublish === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const tagMap: Record<Api.Workflow.WorkflowPublishStatus, NaiveUI.ThemeColor> = {
|
|
||||||
0: 'warning',
|
|
||||||
1: 'success',
|
|
||||||
9: 'error'
|
|
||||||
};
|
|
||||||
return <NTag type={tagMap[row.isPublish]}>{workflowPublishStatusRecord[row.isPublish]}</NTag>;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'operate',
|
|
||||||
title: $t('common.operate'),
|
|
||||||
align: 'center',
|
|
||||||
width: 150,
|
|
||||||
fixed: 'right',
|
|
||||||
render: row => {
|
|
||||||
const firstRowButtons = [];
|
|
||||||
firstRowButtons.push(
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
icon="material-symbols:drive-file-rename-outline-outline"
|
|
||||||
tooltipContent={$t('common.edit')}
|
|
||||||
onClick={() => edit(row.id)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
firstRowButtons.push(
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="error"
|
|
||||||
icon="material-symbols:delete-outline"
|
|
||||||
tooltipContent={$t('common.delete')}
|
|
||||||
popconfirmContent={$t('common.confirmDelete')}
|
|
||||||
onPositiveClick={() => handleDelete(row.id)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
firstRowButtons.push(
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
icon="material-symbols:content-copy"
|
|
||||||
tooltipContent="复制流程"
|
|
||||||
popconfirmContent={`确定要复制 ${row.flowName} 吗?`}
|
|
||||||
onPositiveClick={() => handleCopy(row.id)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
const firstRowWithDividers = firstRowButtons.map((btn, index) =>
|
|
||||||
index > 0 ? [<NDivider vertical />, btn] : btn
|
|
||||||
);
|
|
||||||
|
|
||||||
const secondRowButtons = [];
|
|
||||||
|
|
||||||
secondRowButtons.push(
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
icon="material-symbols:file-export"
|
|
||||||
tooltipContent="导出流程"
|
|
||||||
onClick={() => handleExport(row)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
secondRowButtons.push(
|
|
||||||
isPublish.value ? (
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
icon="material-symbols:visibility-outline"
|
|
||||||
tooltipContent="查看流程"
|
|
||||||
onClick={() => handlePreview(row.id)}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
icon="material-symbols:design-services"
|
|
||||||
tooltipContent="流程设计"
|
|
||||||
onClick={() => handleDesign(row.id)}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!isPublish.value) {
|
|
||||||
secondRowButtons.push(
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
icon="material-symbols:publish"
|
|
||||||
tooltipContent="发布流程"
|
|
||||||
popconfirmContent={`确定要发布 ${row.flowName} 吗?`}
|
|
||||||
onPositiveClick={() => handlePublish(row.id)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const secondRowWithDividers = secondRowButtons.map((btn, index) =>
|
|
||||||
index > 0 ? [<NDivider vertical />, btn] : btn
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div class="flex-col">
|
|
||||||
<div class="h-[24px] flex-center gap-4px">{firstRowWithDividers}</div>
|
|
||||||
<div class="h-[24px] flex-center gap-4px">{secondRowWithDividers}</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
// 监听运行状态变化
|
|
||||||
watch(isPublish, async () => {
|
|
||||||
const newApiFn = isPublish.value ? fetchGetDefinitionList : fetchGetUnPublishDefinitionList;
|
|
||||||
updateApiFn(newApiFn);
|
|
||||||
await getDataByPage();
|
|
||||||
});
|
|
||||||
|
|
||||||
const { drawerVisible, operateType, editingData, handleAdd, handleEdit, checkedRowKeys, onBatchDeleted, onDeleted } =
|
|
||||||
useTableOperate(data, getData);
|
|
||||||
|
|
||||||
async function handleBatchDelete() {
|
|
||||||
// request
|
|
||||||
const { error } = await fetchBatchDeleteDefinition(checkedRowKeys.value);
|
|
||||||
if (error) return;
|
|
||||||
onBatchDeleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleDelete(id: CommonType.IdType) {
|
|
||||||
// request
|
|
||||||
const { error } = await fetchBatchDeleteDefinition([id]);
|
|
||||||
if (error) return;
|
|
||||||
onDeleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
function edit(id: CommonType.IdType) {
|
|
||||||
handleEdit('id', id);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDeploy() {
|
|
||||||
showImportModal();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handlePublish(id: CommonType.IdType) {
|
|
||||||
const { error } = await fetchPublishDefinition(id);
|
|
||||||
if (error) return;
|
|
||||||
window.$message?.success('发布成功');
|
|
||||||
getDataByPage();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleCopy(id: CommonType.IdType) {
|
|
||||||
const { error } = await fetchCopyDefinition(id);
|
|
||||||
if (error) return;
|
|
||||||
window.$message?.success('复制成功');
|
|
||||||
// 如果当前是已发布状态,则切换到未发布状态
|
|
||||||
if (isPublish.value) {
|
|
||||||
isPublish.value = false;
|
|
||||||
} else {
|
|
||||||
getDataByPage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDesign(id: CommonType.IdType) {
|
|
||||||
routerPushByKey('workflow_design', {
|
|
||||||
query: {
|
|
||||||
definitionId: id.toString(),
|
|
||||||
disabled: 'false'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function handlePreview(id: CommonType.IdType) {
|
|
||||||
routerPushByKey('workflow_design', {
|
|
||||||
query: {
|
|
||||||
definitionId: id.toString(),
|
|
||||||
disabled: 'true'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleExport(row: TableDataWithIndex<Api.Workflow.Definition>) {
|
|
||||||
download(`/workflow/definition/exportDef/${row.id}`, {}, `${row.flowCode}.json`);
|
|
||||||
}
|
|
||||||
const { loading: categoryLoading, startLoading: startCategoryLoading, endLoading: endCategoryLoading } = useLoading();
|
|
||||||
const categoryPattern = ref<string>();
|
|
||||||
const categoryData = ref<Api.Common.CommonTreeRecord>([]);
|
|
||||||
const selectedKeys = ref<string[]>([]);
|
|
||||||
|
|
||||||
async function getTreeData() {
|
|
||||||
startCategoryLoading();
|
|
||||||
const { data: tree, error } = await fetchGetCategoryTree();
|
|
||||||
if (!error) {
|
|
||||||
categoryData.value = tree;
|
|
||||||
}
|
|
||||||
endCategoryLoading();
|
|
||||||
}
|
|
||||||
|
|
||||||
getTreeData();
|
|
||||||
|
|
||||||
function handleClickTree(keys: string[]) {
|
|
||||||
searchParams.category = keys.length ? keys[0] : null;
|
|
||||||
checkedRowKeys.value = [];
|
|
||||||
getDataByPage();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleResetTreeData() {
|
|
||||||
categoryPattern.value = undefined;
|
|
||||||
getTreeData();
|
|
||||||
}
|
|
||||||
|
|
||||||
const expandedKeys = ref<CommonType.IdType[]>(['100']);
|
|
||||||
|
|
||||||
const selectable = computed(() => {
|
|
||||||
return !loading.value;
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<TableSiderLayout sider-title="流程分类">
|
|
||||||
<template #header-extra>
|
|
||||||
<NButton size="small" text class="h-18px" @click.stop="() => handleResetTreeData()">
|
|
||||||
<template #icon>
|
|
||||||
<SvgIcon icon="ic:round-refresh" />
|
|
||||||
</template>
|
|
||||||
</NButton>
|
|
||||||
</template>
|
|
||||||
<template #sider>
|
|
||||||
<NInput v-model:value="categoryPattern" clearable :placeholder="$t('common.keywordSearch')" />
|
|
||||||
<NSpin class="dept-tree" :show="categoryLoading">
|
|
||||||
<NTree
|
|
||||||
v-model:expanded-keys="expandedKeys"
|
|
||||||
v-model:selected-keys="selectedKeys"
|
|
||||||
block-node
|
|
||||||
show-line
|
|
||||||
:data="categoryData as []"
|
|
||||||
:show-irrelevant-nodes="false"
|
|
||||||
:pattern="categoryPattern"
|
|
||||||
class="infinite-scroll h-full min-h-200px py-3"
|
|
||||||
key-field="id"
|
|
||||||
label-field="label"
|
|
||||||
virtual-scroll
|
|
||||||
:selectable="selectable"
|
|
||||||
@update:selected-keys="handleClickTree"
|
|
||||||
>
|
|
||||||
<template #empty>
|
|
||||||
<NEmpty description="暂无流程分类" class="h-full min-h-200px justify-center" />
|
|
||||||
</template>
|
|
||||||
</NTree>
|
|
||||||
</NSpin>
|
|
||||||
</template>
|
|
||||||
<div class="h-full flex-col-stretch gap-12px overflow-hidden lt-sm:overflow-auto">
|
|
||||||
<DefinitionSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getDataByPage" />
|
|
||||||
<NCard :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
|
|
||||||
<template #header>
|
|
||||||
<NSpace>
|
|
||||||
<NRadioGroup v-model:value="isPublish" on-up size="small">
|
|
||||||
<NRadioButton
|
|
||||||
v-for="(status, index) in isPublishOptions"
|
|
||||||
:key="index"
|
|
||||||
:value="status.value"
|
|
||||||
:label="status.label"
|
|
||||||
/>
|
|
||||||
</NRadioGroup>
|
|
||||||
</NSpace>
|
|
||||||
</template>
|
|
||||||
<template #header-extra>
|
|
||||||
<TableHeaderOperation
|
|
||||||
v-model:columns="columnChecks"
|
|
||||||
:disabled-delete="checkedRowKeys.length === 0"
|
|
||||||
:loading="loading"
|
|
||||||
:show-delete="hasAuth('workflow:definition:remove')"
|
|
||||||
@add="handleAdd"
|
|
||||||
@delete="handleBatchDelete"
|
|
||||||
@refresh="getData"
|
|
||||||
>
|
|
||||||
<template #prefix>
|
|
||||||
<NButton size="small" ghost @click="handleDeploy">
|
|
||||||
<template #icon>
|
|
||||||
<icon-material-symbols:upload-rounded class="text-icon" />
|
|
||||||
</template>
|
|
||||||
部署流程文件
|
|
||||||
</NButton>
|
|
||||||
</template>
|
|
||||||
</TableHeaderOperation>
|
|
||||||
</template>
|
|
||||||
<NDataTable
|
|
||||||
v-model:checked-row-keys="checkedRowKeys"
|
|
||||||
:columns="columns"
|
|
||||||
:data="data"
|
|
||||||
size="small"
|
|
||||||
:flex-height="!appStore.isMobile"
|
|
||||||
:scroll-x="962"
|
|
||||||
:loading="loading"
|
|
||||||
remote
|
|
||||||
:row-key="row => row.id"
|
|
||||||
:pagination="mobilePagination"
|
|
||||||
class="sm:h-full"
|
|
||||||
/>
|
|
||||||
<DefinitionOperateDrawer
|
|
||||||
v-model:visible="drawerVisible"
|
|
||||||
:operate-type="operateType"
|
|
||||||
:row-data="editingData"
|
|
||||||
@submitted="getDataByPage"
|
|
||||||
/>
|
|
||||||
<DefinitionImportModal v-model:visible="importVisible" @submitted="getDataByPage" />
|
|
||||||
</NCard>
|
|
||||||
</div>
|
|
||||||
</TableSiderLayout>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
@ -1,160 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { ref, watch } from 'vue';
|
|
||||||
import type { UploadFileInfo } from 'naive-ui';
|
|
||||||
import { getToken } from '@/store/modules/auth/shared';
|
|
||||||
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
|
||||||
import { getServiceBaseURL } from '@/utils/service';
|
|
||||||
import type FileUpload from '@/components/custom/file-upload.vue';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'DefinitionImportModal'
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Emits {
|
|
||||||
(e: 'submitted'): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { baseURL } = getServiceBaseURL(import.meta.env);
|
|
||||||
|
|
||||||
const headers: Record<string, string> = {
|
|
||||||
Authorization: `Bearer ${getToken()}`,
|
|
||||||
clientid: import.meta.env.VITE_APP_CLIENT_ID!
|
|
||||||
};
|
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
|
||||||
|
|
||||||
const uploadRef = ref<typeof FileUpload>();
|
|
||||||
const message = ref<string>('');
|
|
||||||
const success = ref<boolean>(false);
|
|
||||||
|
|
||||||
const visible = defineModel<boolean>('visible', {
|
|
||||||
default: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = ref<Record<string, any>>({
|
|
||||||
category: undefined
|
|
||||||
});
|
|
||||||
|
|
||||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
|
||||||
const { createRequiredRule } = useFormRules();
|
|
||||||
|
|
||||||
const rules: Record<string, App.Global.FormRule> = {
|
|
||||||
category: createRequiredRule('流程分类不能为空')
|
|
||||||
};
|
|
||||||
|
|
||||||
const fileList = ref<UploadFileInfo[]>([]);
|
|
||||||
|
|
||||||
function closeDrawer() {
|
|
||||||
visible.value = false;
|
|
||||||
if (success.value) {
|
|
||||||
emit('submitted');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleSubmit() {
|
|
||||||
await validate();
|
|
||||||
if (fileList.value.length === 0) {
|
|
||||||
window.$message?.error('请选择流程文件');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fileList.value.forEach(item => {
|
|
||||||
item.status = 'pending';
|
|
||||||
});
|
|
||||||
await uploadRef.value?.submit();
|
|
||||||
visible.value = false;
|
|
||||||
emit('submitted');
|
|
||||||
}
|
|
||||||
|
|
||||||
function isErrorState(xhr: XMLHttpRequest) {
|
|
||||||
const responseText = xhr?.responseText;
|
|
||||||
const response = JSON.parse(responseText);
|
|
||||||
return response.code !== 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleFinish(options: { file: UploadFileInfo; event?: ProgressEvent }) {
|
|
||||||
const { file, event } = options;
|
|
||||||
// @ts-expect-error Ignore type errors
|
|
||||||
const responseText = event?.target?.responseText;
|
|
||||||
const response = JSON.parse(responseText);
|
|
||||||
message.value = response.msg;
|
|
||||||
window.$message?.success($t('common.importSuccess'));
|
|
||||||
success.value = true;
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleError(options: { file: UploadFileInfo; event?: ProgressEvent }) {
|
|
||||||
const { event } = options;
|
|
||||||
// @ts-expect-error Ignore type errors
|
|
||||||
const responseText = event?.target?.responseText;
|
|
||||||
const msg = JSON.parse(responseText).msg;
|
|
||||||
message.value = msg;
|
|
||||||
window.$message?.error(msg || $t('common.importFail'));
|
|
||||||
success.value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(visible, () => {
|
|
||||||
if (visible.value) {
|
|
||||||
fileList.value = [];
|
|
||||||
success.value = false;
|
|
||||||
message.value = '';
|
|
||||||
restoreValidation();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NModal
|
|
||||||
v-model:show="visible"
|
|
||||||
title="部署流程文件"
|
|
||||||
preset="card"
|
|
||||||
:bordered="false"
|
|
||||||
display-directive="show"
|
|
||||||
class="max-w-90% w-600px"
|
|
||||||
@close="closeDrawer"
|
|
||||||
>
|
|
||||||
<NForm ref="formRef" label-placement="left" :model="data" :rules="rules">
|
|
||||||
<NFormItem label="流程分类" path="category">
|
|
||||||
<FlowCategorySelect v-model:value="data.category" />
|
|
||||||
</NFormItem>
|
|
||||||
</NForm>
|
|
||||||
|
|
||||||
<NUpload
|
|
||||||
ref="uploadRef"
|
|
||||||
v-model:file-list="fileList"
|
|
||||||
class="mt-12px"
|
|
||||||
:action="`${baseURL}/workflow/definition/importDef`"
|
|
||||||
:headers="headers"
|
|
||||||
:data="data"
|
|
||||||
:max="10"
|
|
||||||
:file-size="50"
|
|
||||||
accept=".json"
|
|
||||||
:multiple="true"
|
|
||||||
directory-dnd
|
|
||||||
:default-upload="false"
|
|
||||||
list-type="text"
|
|
||||||
:is-error-state="isErrorState"
|
|
||||||
@finish="handleFinish"
|
|
||||||
@error="handleError"
|
|
||||||
>
|
|
||||||
<NUploadDragger>
|
|
||||||
<div class="mb-12px flex-center">
|
|
||||||
<SvgIcon icon="material-symbols:unarchive-outline" class="text-58px color-#d8d8db dark:color-#a1a1a2" />
|
|
||||||
</div>
|
|
||||||
<NText class="text-16px">请选择 JSON 流程文件上传</NText>
|
|
||||||
<NP depth="3" class="mt-8px text-center">
|
|
||||||
仅支持 JSON 格式文件
|
|
||||||
<br />
|
|
||||||
PS: 如若部署请部署从本项目模型管理导出的数据
|
|
||||||
</NP>
|
|
||||||
</NUploadDragger>
|
|
||||||
</NUpload>
|
|
||||||
<template #footer>
|
|
||||||
<NSpace justify="end" :size="16">
|
|
||||||
<NButton type="primary" @click="handleSubmit">{{ $t('common.import') }}</NButton>
|
|
||||||
</NSpace>
|
|
||||||
</template>
|
|
||||||
</NModal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
@ -1,214 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { computed, reactive, ref, watch } from 'vue';
|
|
||||||
import type { SelectOption } from 'naive-ui';
|
|
||||||
import { definitionDesignerModeOptions } from '@/constants/workflow';
|
|
||||||
import { fetchCreateDefinition, fetchUpdateDefinition } from '@/service/api/workflow/definition';
|
|
||||||
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
|
||||||
import { useDict } from '@/hooks/business/dict';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'DefinitionOperateDrawer'
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
/** the type of operation */
|
|
||||||
operateType: NaiveUI.TableOperateType;
|
|
||||||
/** the edit row data */
|
|
||||||
rowData?: Api.Workflow.Definition | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = defineProps<Props>();
|
|
||||||
|
|
||||||
interface Emits {
|
|
||||||
(e: 'submitted'): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
|
||||||
|
|
||||||
useDict('sys_yes_no');
|
|
||||||
const visible = defineModel<boolean>('visible', {
|
|
||||||
default: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
|
||||||
const { createRequiredRule } = useFormRules();
|
|
||||||
|
|
||||||
const title = computed(() => {
|
|
||||||
const titles: Record<NaiveUI.TableOperateType, string> = {
|
|
||||||
add: '新增流程定义',
|
|
||||||
edit: '编辑流程定义'
|
|
||||||
};
|
|
||||||
return titles[props.operateType];
|
|
||||||
});
|
|
||||||
|
|
||||||
const formPaths = ref<SelectOption[]>([]);
|
|
||||||
const modules = import.meta.glob('@/components/workflow/form/**/*.vue');
|
|
||||||
Object.keys(modules).forEach(key => {
|
|
||||||
const label = key.replace('/src/components/workflow/form/', '');
|
|
||||||
const value = key.replace('/src/components/workflow/form', '/workflow');
|
|
||||||
formPaths.value.push({ label, value });
|
|
||||||
});
|
|
||||||
|
|
||||||
type Model = Api.Workflow.DefinitionOperateParams;
|
|
||||||
|
|
||||||
const model: Model = reactive(createDefaultModel());
|
|
||||||
|
|
||||||
/** 是否自动通过 */
|
|
||||||
const autoPass = ref<boolean>(false);
|
|
||||||
|
|
||||||
function createDefaultModel(): Model {
|
|
||||||
return {
|
|
||||||
flowCode: '',
|
|
||||||
flowName: '',
|
|
||||||
category: '',
|
|
||||||
formPath: null,
|
|
||||||
formCustom: 'N',
|
|
||||||
modelValue: 'CLASSICS',
|
|
||||||
ext: ''
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
type RuleKey = Extract<keyof Model, 'flowCode' | 'flowName' | 'category' | 'modelValue' | 'formCustom'>;
|
|
||||||
|
|
||||||
const rules: Record<RuleKey, App.Global.FormRule> = {
|
|
||||||
flowCode: createRequiredRule('流程编码不能为空'),
|
|
||||||
flowName: createRequiredRule('流程名称不能为空'),
|
|
||||||
category: createRequiredRule('流程类别不能为空'),
|
|
||||||
modelValue: createRequiredRule('设计器模式不能为空'),
|
|
||||||
formCustom: createRequiredRule('审批表单是否自定义不能为空')
|
|
||||||
};
|
|
||||||
|
|
||||||
function handleUpdateModelWhenEdit() {
|
|
||||||
if (props.operateType === 'add') {
|
|
||||||
Object.assign(model, createDefaultModel());
|
|
||||||
model.formCustom = 'N';
|
|
||||||
autoPass.value = false;
|
|
||||||
// 设置默认的 ext JSON
|
|
||||||
model.ext = JSON.stringify({ autoPass: false });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.operateType === 'edit' && props.rowData) {
|
|
||||||
Object.assign(model, props.rowData);
|
|
||||||
// 从 ext 字段解析 JSON 并设置 autoPass 值
|
|
||||||
try {
|
|
||||||
if (props.rowData.ext) {
|
|
||||||
const extData = JSON.parse(props.rowData.ext);
|
|
||||||
autoPass.value = extData.autoPass || false;
|
|
||||||
} else {
|
|
||||||
autoPass.value = false;
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// 如果解析失败,设置默认值
|
|
||||||
autoPass.value = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeDrawer() {
|
|
||||||
visible.value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleSubmit() {
|
|
||||||
await validate();
|
|
||||||
|
|
||||||
// 将 autoPass 值序列化为 JSON 并存储到 ext 字段
|
|
||||||
model.ext = JSON.stringify({ autoPass: autoPass.value });
|
|
||||||
|
|
||||||
// request
|
|
||||||
if (props.operateType === 'add') {
|
|
||||||
const { flowCode, flowName, category, formPath, modelValue, formCustom, ext } = model;
|
|
||||||
const { error } = await fetchCreateDefinition({
|
|
||||||
flowCode,
|
|
||||||
flowName,
|
|
||||||
category,
|
|
||||||
formPath: formPath || '',
|
|
||||||
modelValue,
|
|
||||||
formCustom,
|
|
||||||
ext
|
|
||||||
});
|
|
||||||
if (error) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.operateType === 'edit') {
|
|
||||||
const { id, flowCode, flowName, category, formPath, modelValue, formCustom, ext } = model;
|
|
||||||
const { error } = await fetchUpdateDefinition({
|
|
||||||
id,
|
|
||||||
flowCode,
|
|
||||||
flowName,
|
|
||||||
category,
|
|
||||||
formPath: formPath || '',
|
|
||||||
modelValue,
|
|
||||||
formCustom,
|
|
||||||
ext
|
|
||||||
});
|
|
||||||
if (error) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.$message?.success($t('common.saveSuccess'));
|
|
||||||
closeDrawer();
|
|
||||||
emit('submitted');
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(visible, () => {
|
|
||||||
if (visible.value) {
|
|
||||||
handleUpdateModelWhenEdit();
|
|
||||||
restoreValidation();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NDrawer v-model:show="visible" :title="title" display-directive="show" :width="800" class="max-w-90%">
|
|
||||||
<NDrawerContent :title="title" :native-scrollbar="false" closable>
|
|
||||||
<NForm ref="formRef" :model="model" :rules="rules">
|
|
||||||
<NFormItem label="流程类别" path="category">
|
|
||||||
<FlowCategorySelect v-model:value="model.category" placeholder="请选择流程类别" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="流程编码" path="flowCode">
|
|
||||||
<NInput v-model:value="model.flowCode" placeholder="请输入流程编码" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="流程名称" path="flowName">
|
|
||||||
<NInput v-model:value="model.flowName" placeholder="请输入流程名称" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="设计器模式" path="modelValue">
|
|
||||||
<NRadioGroup v-model:value="model.modelValue" :disabled="operateType === 'edit'">
|
|
||||||
<NSpace>
|
|
||||||
<NRadioButton
|
|
||||||
v-for="option in definitionDesignerModeOptions"
|
|
||||||
:key="option.value"
|
|
||||||
:value="option.value"
|
|
||||||
:label="option.label"
|
|
||||||
/>
|
|
||||||
</NSpace>
|
|
||||||
</NRadioGroup>
|
|
||||||
</NFormItem>
|
|
||||||
<!-- 流程配置 -->
|
|
||||||
<NFormItem label="流程配置" path="ext">
|
|
||||||
<NCheckbox v-model:checked="autoPass" label="下一节点执行人是当前任务处理人自动审批" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="是否动态表单" path="formCustom">
|
|
||||||
<DictRadio v-model:value="model.formCustom" dict-code="sys_yes_no" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="审批表单路径" path="formPath">
|
|
||||||
<template #label>
|
|
||||||
<div class="flex-center">
|
|
||||||
<FormTip content="需要在 /src/components/workflow/form 路径下创建组件" />
|
|
||||||
<span class="pl-3px">审批表单路径</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<NSelect v-model:value="model.formPath" :options="formPaths" placeholder="请选择审批表单路径" />
|
|
||||||
</NFormItem>
|
|
||||||
</NForm>
|
|
||||||
<template #footer>
|
|
||||||
<NSpace :size="16">
|
|
||||||
<NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
|
|
||||||
<NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
|
|
||||||
</NSpace>
|
|
||||||
</template>
|
|
||||||
</NDrawerContent>
|
|
||||||
</NDrawer>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
@ -1,67 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { useNaiveForm } from '@/hooks/common/form';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'DefinitionSearch'
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Emits {
|
|
||||||
(e: 'reset'): void;
|
|
||||||
(e: 'search'): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
|
||||||
|
|
||||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
|
||||||
|
|
||||||
const model = defineModel<Api.Workflow.DefinitionSearchParams>('model', { required: true });
|
|
||||||
|
|
||||||
async function reset() {
|
|
||||||
Object.assign(model.value.params!, {});
|
|
||||||
await restoreValidation();
|
|
||||||
emit('reset');
|
|
||||||
}
|
|
||||||
|
|
||||||
async function search() {
|
|
||||||
await validate();
|
|
||||||
emit('search');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
|
||||||
<NCollapse>
|
|
||||||
<NCollapseItem :title="$t('common.search')" name="user-search">
|
|
||||||
<NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
|
|
||||||
<NGrid responsive="screen" item-responsive>
|
|
||||||
<NFormItemGi span="24 s:12 m:8" label="流程编码" path="flowCode" class="pr-24px">
|
|
||||||
<NInput v-model:value="model.flowCode" placeholder="请输入流程编码" />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="24 s:12 m:8" label="流程名称" path="flowName" class="pr-24px">
|
|
||||||
<NInput v-model:value="model.flowName" placeholder="请输入流程名称" />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="24 s:12 m:8" class="pr-24px">
|
|
||||||
<NSpace class="w-full" justify="end">
|
|
||||||
<NButton @click="reset">
|
|
||||||
<template #icon>
|
|
||||||
<icon-ic-round-refresh class="text-icon" />
|
|
||||||
</template>
|
|
||||||
{{ $t('common.reset') }}
|
|
||||||
</NButton>
|
|
||||||
<NButton type="primary" ghost @click="search">
|
|
||||||
<template #icon>
|
|
||||||
<icon-ic-round-search class="text-icon" />
|
|
||||||
</template>
|
|
||||||
{{ $t('common.search') }}
|
|
||||||
</NButton>
|
|
||||||
</NSpace>
|
|
||||||
</NFormItemGi>
|
|
||||||
</NGrid>
|
|
||||||
</NForm>
|
|
||||||
</NCollapseItem>
|
|
||||||
</NCollapse>
|
|
||||||
</NCard>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
@ -1,428 +0,0 @@
|
|||||||
<script setup lang="tsx">
|
|
||||||
import { computed, reactive, ref, shallowRef, watch } from 'vue';
|
|
||||||
import { NButton, NDivider, NEmpty, NInput, NRadioButton, NRadioGroup, NSpin, NTag } from 'naive-ui';
|
|
||||||
import { useBoolean, useLoading } from '@sa/hooks';
|
|
||||||
import { workflowActivityStatusRecord } from '@/constants/workflow';
|
|
||||||
import { fetchGetCategoryTree } from '@/service/api/workflow/category';
|
|
||||||
import {
|
|
||||||
fetchBatchDeleteInstance,
|
|
||||||
fetchFlowInvalidOperate,
|
|
||||||
fetchGetFinishedInstanceList,
|
|
||||||
fetchGetRunningInstanceList
|
|
||||||
} from '@/service/api/workflow/instance';
|
|
||||||
import { useAppStore } from '@/store/modules/app';
|
|
||||||
import { useTable, useTableOperate } from '@/hooks/common/table';
|
|
||||||
import { useDict } from '@/hooks/business/dict';
|
|
||||||
import { loadDynamicComponent } from '@/utils/common';
|
|
||||||
import DictTag from '@/components/custom/dict-tag.vue';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
import ButtonIcon from '@/components/custom/button-icon.vue';
|
|
||||||
import InstanceSearch from './modules/process-instance-search.vue';
|
|
||||||
import InstanceVariableModal from './modules/process-instance-variable-modal.vue';
|
|
||||||
|
|
||||||
const dynamicComponent = shallowRef();
|
|
||||||
|
|
||||||
interface RunningStatusOption {
|
|
||||||
label: string;
|
|
||||||
value: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'InstanceList'
|
|
||||||
});
|
|
||||||
|
|
||||||
useDict('wf_business_status');
|
|
||||||
const appStore = useAppStore();
|
|
||||||
|
|
||||||
const { bool: variableVisible, setTrue: showVariableDrawer } = useBoolean(false);
|
|
||||||
const { bool: previewVisible, setTrue: showPreviewDrawer } = useBoolean(false);
|
|
||||||
|
|
||||||
const runningStatus = ref<boolean>(true);
|
|
||||||
const runningStatusOptions = ref<RunningStatusOption[]>([
|
|
||||||
{
|
|
||||||
label: '运行中',
|
|
||||||
value: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '已完成',
|
|
||||||
value: false
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
type CancelModel = Api.Workflow.FlowInvalidOperateParams;
|
|
||||||
|
|
||||||
const cancelModel: CancelModel = reactive(createDefaultModel());
|
|
||||||
|
|
||||||
function createDefaultModel(): CancelModel {
|
|
||||||
return {
|
|
||||||
id: null,
|
|
||||||
comment: ''
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 基础列
|
|
||||||
const baseColumns = ref<NaiveUI.TableColumn<Api.Workflow.Instance>[]>([
|
|
||||||
{
|
|
||||||
type: 'selection',
|
|
||||||
align: 'center',
|
|
||||||
width: 48
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'businessCode',
|
|
||||||
title: '业务编码',
|
|
||||||
align: 'center',
|
|
||||||
width: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'businessTitle',
|
|
||||||
title: '业务名称',
|
|
||||||
align: 'center',
|
|
||||||
width: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'flowName',
|
|
||||||
title: '流程名称',
|
|
||||||
align: 'center',
|
|
||||||
width: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'nodeName',
|
|
||||||
title: '任务名称',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'flowCode',
|
|
||||||
title: '流程编码',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'categoryName',
|
|
||||||
title: '流程分类',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 120,
|
|
||||||
render: row => <NTag type="default">{row.categoryName}</NTag>
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'createByName',
|
|
||||||
title: '申请人',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'version',
|
|
||||||
title: '版本号',
|
|
||||||
align: 'center',
|
|
||||||
width: 80,
|
|
||||||
render: row => <NTag type="info">v{row.version}.0</NTag>
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'activityStatus',
|
|
||||||
title: '状态',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 80,
|
|
||||||
render(row) {
|
|
||||||
return (
|
|
||||||
<NTag type={row.activityStatus === 0 ? 'warning' : 'success'}>
|
|
||||||
{workflowActivityStatusRecord[row.activityStatus]}
|
|
||||||
</NTag>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'flowStatus',
|
|
||||||
title: '流程状态',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 80,
|
|
||||||
render(row) {
|
|
||||||
return <DictTag value={row.flowStatus} dictCode="wf_business_status" />;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'createTime',
|
|
||||||
title: '启动时间',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 150
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 完成列
|
|
||||||
const finishColumns = ref<NaiveUI.TableColumn<Api.Workflow.Instance>[]>([
|
|
||||||
{
|
|
||||||
key: 'updateTime',
|
|
||||||
title: '结束时间',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 150
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 操作列
|
|
||||||
const operateColumns = ref<NaiveUI.TableColumn<Api.Workflow.Instance>[]>([
|
|
||||||
{
|
|
||||||
key: 'operate',
|
|
||||||
title: $t('common.operate'),
|
|
||||||
align: 'center',
|
|
||||||
fixed: 'right',
|
|
||||||
width: 155,
|
|
||||||
render: row => {
|
|
||||||
const id = row.id;
|
|
||||||
const showAll = runningStatus.value;
|
|
||||||
const buttons = [];
|
|
||||||
buttons.push(
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="info"
|
|
||||||
icon="material-symbols:visibility-outline"
|
|
||||||
tooltipContent="流程预览"
|
|
||||||
onClick={() => handlePreview(row)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
buttons.push(
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="info"
|
|
||||||
icon="material-symbols:variable-insert"
|
|
||||||
tooltipContent="流程变量"
|
|
||||||
onClick={() => handleShowVariable(id)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
if (showAll) {
|
|
||||||
buttons.push(
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="error"
|
|
||||||
showPopconfirmIcon={false}
|
|
||||||
icon="material-symbols:cancel-outline-rounded"
|
|
||||||
tooltipContent="作废流程"
|
|
||||||
popconfirmContent={
|
|
||||||
<NInput v-model:value={cancelModel.comment} size="large" type="textarea" placeholder="请输入作废原因" />
|
|
||||||
}
|
|
||||||
onPositiveClick={() => handleCancel(id)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (showAll) {
|
|
||||||
buttons.push(
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="error"
|
|
||||||
icon="material-symbols:delete-outline"
|
|
||||||
tooltipContent={$t('common.delete')}
|
|
||||||
popconfirmContent={$t('common.confirmDelete')}
|
|
||||||
onPositiveClick={() => handleDelete(id)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div class="flex-center gap-1px">
|
|
||||||
{buttons.map((btn, index) => (index > 0 ? [<NDivider vertical />, btn] : btn))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
const {
|
|
||||||
columns,
|
|
||||||
columnChecks,
|
|
||||||
reloadColumns,
|
|
||||||
data,
|
|
||||||
getData,
|
|
||||||
getDataByPage,
|
|
||||||
loading,
|
|
||||||
mobilePagination,
|
|
||||||
searchParams,
|
|
||||||
resetSearchParams,
|
|
||||||
updateApiFn
|
|
||||||
} = useTable({
|
|
||||||
apiFn: fetchGetRunningInstanceList,
|
|
||||||
apiParams: {
|
|
||||||
pageNum: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
category: null,
|
|
||||||
flowName: null,
|
|
||||||
flowCode: null,
|
|
||||||
nodeName: null,
|
|
||||||
createByIds: null
|
|
||||||
},
|
|
||||||
columns: () =>
|
|
||||||
runningStatus.value
|
|
||||||
? [...baseColumns.value, ...operateColumns.value]
|
|
||||||
: [...baseColumns.value, ...finishColumns.value, ...operateColumns.value]
|
|
||||||
});
|
|
||||||
|
|
||||||
const { checkedRowKeys, onBatchDeleted, onDeleted } = useTableOperate(data, getData);
|
|
||||||
// 监听运行状态变化
|
|
||||||
watch(runningStatus, async () => {
|
|
||||||
const newApiFn = runningStatus.value ? fetchGetRunningInstanceList : fetchGetFinishedInstanceList;
|
|
||||||
updateApiFn(newApiFn);
|
|
||||||
await getDataByPage();
|
|
||||||
reloadColumns();
|
|
||||||
});
|
|
||||||
|
|
||||||
const { loading: treeLoading, startLoading: startTreeLoading, endLoading: endTreeLoading } = useLoading();
|
|
||||||
const categoryPattern = ref<string>();
|
|
||||||
const categoryData = ref<Api.Common.CommonTreeRecord>([]);
|
|
||||||
const selectedKeys = ref<string[]>([]);
|
|
||||||
const expandedKeys = ref<CommonType.IdType[]>(['100']);
|
|
||||||
const instanceRowId = ref<CommonType.IdType>();
|
|
||||||
const selectable = computed(() => {
|
|
||||||
return !loading.value;
|
|
||||||
});
|
|
||||||
|
|
||||||
async function getTreeData() {
|
|
||||||
startTreeLoading();
|
|
||||||
const { data: tree, error } = await fetchGetCategoryTree();
|
|
||||||
if (!error) {
|
|
||||||
categoryData.value = tree;
|
|
||||||
}
|
|
||||||
endTreeLoading();
|
|
||||||
}
|
|
||||||
|
|
||||||
getTreeData();
|
|
||||||
|
|
||||||
function handleClickTree(keys: string[]) {
|
|
||||||
searchParams.category = keys.length ? keys[0] : null;
|
|
||||||
checkedRowKeys.value = [];
|
|
||||||
getDataByPage();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleResetTreeData() {
|
|
||||||
categoryPattern.value = undefined;
|
|
||||||
getTreeData();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleResetSearch() {
|
|
||||||
resetSearchParams();
|
|
||||||
selectedKeys.value = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleBatchDelete() {
|
|
||||||
// request
|
|
||||||
const { error } = await fetchBatchDeleteInstance(checkedRowKeys.value);
|
|
||||||
if (error) return;
|
|
||||||
onBatchDeleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleDelete(instanceId: CommonType.IdType) {
|
|
||||||
// request
|
|
||||||
const { error } = await fetchBatchDeleteInstance([instanceId]);
|
|
||||||
if (error) return;
|
|
||||||
onDeleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleCancel(instanceId: CommonType.IdType) {
|
|
||||||
cancelModel.id = instanceId;
|
|
||||||
// request
|
|
||||||
const { error } = await fetchFlowInvalidOperate(cancelModel);
|
|
||||||
if (error) return;
|
|
||||||
window.$message?.success('作废成功');
|
|
||||||
getDataByPage();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleShowVariable(id: CommonType.IdType) {
|
|
||||||
instanceRowId.value = id;
|
|
||||||
showVariableDrawer();
|
|
||||||
}
|
|
||||||
|
|
||||||
const modules = import.meta.glob('@/components/workflow/form/**/*.vue');
|
|
||||||
const businessId = ref<CommonType.IdType>();
|
|
||||||
|
|
||||||
/** 流程预览,动态加载组件 */
|
|
||||||
async function handlePreview(row: Api.Workflow.Instance) {
|
|
||||||
dynamicComponent.value = null;
|
|
||||||
previewVisible.value = false;
|
|
||||||
businessId.value = row.businessId;
|
|
||||||
const formPath = row.formPath;
|
|
||||||
if (!formPath) return;
|
|
||||||
dynamicComponent.value = await loadDynamicComponent(modules, formPath);
|
|
||||||
setTimeout(() => {
|
|
||||||
showPreviewDrawer();
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<TableSiderLayout sider-title="流程分类列表">
|
|
||||||
<template #header-extra>
|
|
||||||
<NButton size="small" text class="h-18px" @click.stop="() => handleResetTreeData()">
|
|
||||||
<template #icon>
|
|
||||||
<SvgIcon icon="ic:round-refresh" />
|
|
||||||
</template>
|
|
||||||
</NButton>
|
|
||||||
</template>
|
|
||||||
<template #sider>
|
|
||||||
<NInput v-model:value="categoryPattern" clearable :placeholder="$t('common.keywordSearch')" />
|
|
||||||
<NSpin class="category-tree" :show="treeLoading">
|
|
||||||
<NTree
|
|
||||||
v-model:selected-keys="selectedKeys"
|
|
||||||
v-model:expanded-keys="expandedKeys"
|
|
||||||
block-node
|
|
||||||
show-line
|
|
||||||
:data="categoryData as []"
|
|
||||||
:show-irrelevant-nodes="false"
|
|
||||||
:pattern="categoryPattern"
|
|
||||||
class="infinite-scroll h-full min-h-200px py-3"
|
|
||||||
key-field="id"
|
|
||||||
label-field="label"
|
|
||||||
virtual-scroll
|
|
||||||
:selectable="selectable"
|
|
||||||
@update:selected-keys="handleClickTree"
|
|
||||||
>
|
|
||||||
<template #empty>
|
|
||||||
<NEmpty description="暂无分类信息" class="h-full min-h-200px justify-center" />
|
|
||||||
</template>
|
|
||||||
</NTree>
|
|
||||||
</NSpin>
|
|
||||||
</template>
|
|
||||||
<div class="h-full flex-col-stretch gap-12px overflow-hidden lt-sm:overflow-auto">
|
|
||||||
<InstanceSearch v-model:model="searchParams" @reset="handleResetSearch" @search="getDataByPage" />
|
|
||||||
<NCard :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
|
|
||||||
<template #header>
|
|
||||||
<NSpace>
|
|
||||||
<NRadioGroup v-model:value="runningStatus" on-up size="small">
|
|
||||||
<NRadioButton
|
|
||||||
v-for="(status, index) in runningStatusOptions"
|
|
||||||
:key="index"
|
|
||||||
:value="status.value"
|
|
||||||
:label="status.label"
|
|
||||||
/>
|
|
||||||
</NRadioGroup>
|
|
||||||
</NSpace>
|
|
||||||
</template>
|
|
||||||
<template #header-extra>
|
|
||||||
<TableHeaderOperation
|
|
||||||
v-model:columns="columnChecks"
|
|
||||||
:disabled-delete="checkedRowKeys.length === 0"
|
|
||||||
:loading="loading"
|
|
||||||
:show-add="false"
|
|
||||||
:show-delete="true"
|
|
||||||
:show-export="false"
|
|
||||||
@delete="handleBatchDelete"
|
|
||||||
@refresh="getData"
|
|
||||||
>
|
|
||||||
<template #prefix></template>
|
|
||||||
</TableHeaderOperation>
|
|
||||||
</template>
|
|
||||||
<NDataTable
|
|
||||||
v-model:checked-row-keys="checkedRowKeys"
|
|
||||||
:columns="columns"
|
|
||||||
:data="data"
|
|
||||||
size="small"
|
|
||||||
:flex-height="!appStore.isMobile"
|
|
||||||
:scroll-x="1405"
|
|
||||||
:loading="loading"
|
|
||||||
remote
|
|
||||||
:row-key="row => row.id"
|
|
||||||
:pagination="mobilePagination"
|
|
||||||
class="sm:h-full"
|
|
||||||
/>
|
|
||||||
<component :is="dynamicComponent" :visible="previewVisible" operate-type="detail" :business-id="businessId" />
|
|
||||||
<InstanceVariableModal v-model:visible="variableVisible" :instance-id="instanceRowId!" />
|
|
||||||
</NCard>
|
|
||||||
</div>
|
|
||||||
</TableSiderLayout>
|
|
||||||
</template>
|
|
@ -1,63 +0,0 @@
|
|||||||
<script setup lang="tsx">
|
|
||||||
import { useNaiveForm } from '@/hooks/common/form';
|
|
||||||
defineOptions({
|
|
||||||
name: 'WorkflowInstanceSearch'
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Emits {
|
|
||||||
(e: 'reset'): void;
|
|
||||||
(e: 'search'): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
|
||||||
|
|
||||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
|
||||||
const model = defineModel<Api.Workflow.InstanceSearchParams>('model', { required: true });
|
|
||||||
|
|
||||||
async function reset() {
|
|
||||||
await restoreValidation();
|
|
||||||
emit('reset');
|
|
||||||
}
|
|
||||||
async function search() {
|
|
||||||
await validate();
|
|
||||||
emit('search');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
|
||||||
<NCollapse>
|
|
||||||
<NCollapseItem :title="$t('common.search')">
|
|
||||||
<NForm ref="formRef" :model="model" label-placement="left" :label-width="100">
|
|
||||||
<NGrid responsive="screen" item-responsive>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" label="任务名称" path="nodeName" class="pr-24px">
|
|
||||||
<NInput v-model:value="model.nodeName" placeholder="请输入任务名称" />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" label="流程定义名称" path="bucketName" class="pr-24px">
|
|
||||||
<NInput v-model:value="model.flowName" placeholder="请输入流程定义名称" />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" label="流程定义编码" path="flowCode" class="pr-24px">
|
|
||||||
<NInput v-model:value="model.flowCode" placeholder="请输入流程定义编码" />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" class="pr-24px">
|
|
||||||
<NSpace class="w-full" justify="end">
|
|
||||||
<NButton @click="reset">
|
|
||||||
<template #icon>
|
|
||||||
<icon-ic-round-refresh class="text-icon" />
|
|
||||||
</template>
|
|
||||||
{{ $t('common.reset') }}
|
|
||||||
</NButton>
|
|
||||||
<NButton type="primary" ghost @click="search">
|
|
||||||
<template #icon>
|
|
||||||
<icon-ic-round-search class="text-icon" />
|
|
||||||
</template>
|
|
||||||
{{ $t('common.search') }}
|
|
||||||
</NButton>
|
|
||||||
</NSpace>
|
|
||||||
</NFormItemGi>
|
|
||||||
</NGrid>
|
|
||||||
</NForm>
|
|
||||||
</NCollapseItem>
|
|
||||||
</NCollapse>
|
|
||||||
</NCard>
|
|
||||||
</template>
|
|
@ -1,123 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { reactive, ref, watch } from 'vue';
|
|
||||||
import { useLoading } from '@sa/hooks';
|
|
||||||
import { fetchGetInstanceVariable, fetchUpdateInstanceVariable } from '@/service/api/workflow/instance';
|
|
||||||
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
|
||||||
import JsonPreview from '@/components/custom/json-preview.vue';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'InstanceVariableModal'
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
instanceId: CommonType.IdType;
|
|
||||||
}
|
|
||||||
const props = defineProps<Props>();
|
|
||||||
|
|
||||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
|
||||||
const { createRequiredRule } = useFormRules();
|
|
||||||
const { loading: updateLoading, startLoading: startUpdateLoading, endLoading: endUpdateLoading } = useLoading();
|
|
||||||
|
|
||||||
const visible = defineModel<boolean>('visible', {
|
|
||||||
default: false
|
|
||||||
});
|
|
||||||
const variableOptions = ref<CommonType.Option<string>[]>([]);
|
|
||||||
|
|
||||||
const instanceVariableInfo = ref<Api.Workflow.InstanceVariableInfo>();
|
|
||||||
|
|
||||||
type Model = Api.Workflow.InstanceVariableOperateParams;
|
|
||||||
|
|
||||||
const model: Model = reactive(createDefaultModel());
|
|
||||||
|
|
||||||
function createDefaultModel(): Model {
|
|
||||||
return {
|
|
||||||
instanceId: props.instanceId,
|
|
||||||
key: null,
|
|
||||||
value: ''
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
type RuleKey = Extract<keyof Model, 'key' | 'value'>;
|
|
||||||
|
|
||||||
const rules: Record<RuleKey, App.Global.FormRule> = {
|
|
||||||
key: createRequiredRule('变量KEY不能为空'),
|
|
||||||
value: createRequiredRule('变量值不能为空')
|
|
||||||
};
|
|
||||||
|
|
||||||
function closeDrawer() {
|
|
||||||
visible.value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getInstanceVariable() {
|
|
||||||
startUpdateLoading();
|
|
||||||
const { error, data } = await fetchGetInstanceVariable(props.instanceId);
|
|
||||||
if (error) return;
|
|
||||||
instanceVariableInfo.value = data;
|
|
||||||
if (data.variableList) {
|
|
||||||
variableOptions.value = data.variableList.map(item => ({
|
|
||||||
label: item.key!,
|
|
||||||
value: item.key!
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
endUpdateLoading();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleUpdateModelWhenEdit() {
|
|
||||||
Object.assign(model, createDefaultModel());
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleSubmit() {
|
|
||||||
await validate();
|
|
||||||
// request
|
|
||||||
const { error } = await fetchUpdateInstanceVariable(model);
|
|
||||||
if (error) return;
|
|
||||||
window.$message?.success($t('common.updateSuccess'));
|
|
||||||
await getInstanceVariable();
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(visible, () => {
|
|
||||||
if (visible.value) {
|
|
||||||
getInstanceVariable();
|
|
||||||
handleUpdateModelWhenEdit();
|
|
||||||
restoreValidation();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NModal
|
|
||||||
v-model:show="visible"
|
|
||||||
title="流程变量"
|
|
||||||
preset="card"
|
|
||||||
:bordered="false"
|
|
||||||
display-directive="show"
|
|
||||||
class="max-w-90% w-600px"
|
|
||||||
@close="closeDrawer"
|
|
||||||
>
|
|
||||||
<NSpin :show="updateLoading">
|
|
||||||
<NSpace vertical :size="16">
|
|
||||||
<div class="max-h-300px overflow-auto">
|
|
||||||
<JsonPreview :code="instanceVariableInfo?.variable as string" />
|
|
||||||
</div>
|
|
||||||
<NDivider>变量管理</NDivider>
|
|
||||||
<NForm ref="formRef" :model="model" :rules="rules">
|
|
||||||
<NGrid responsive="screen" item-responsive>
|
|
||||||
<NFormItemGi span="24 s:12 m:12" label="变量KEY" path="key" class="pr-24px">
|
|
||||||
<NSelect v-model:value="model.key" clearable :options="variableOptions" placeholder="请选择变量KEY" />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="24 s:12 m:12" label="变量值" path="value" class="pr-24px">
|
|
||||||
<NInput v-model:value="model.value" placeholder="请输入变量值" />
|
|
||||||
</NFormItemGi>
|
|
||||||
</NGrid>
|
|
||||||
</NForm>
|
|
||||||
</NSpace>
|
|
||||||
</NSpin>
|
|
||||||
<template #footer>
|
|
||||||
<NSpace justify="end" :size="16">
|
|
||||||
<NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
|
|
||||||
<NButton @click="closeDrawer">{{ $t('common.close') }}</NButton>
|
|
||||||
</NSpace>
|
|
||||||
</template>
|
|
||||||
</NModal>
|
|
||||||
</template>
|
|
@ -1,221 +0,0 @@
|
|||||||
<script setup lang="tsx">
|
|
||||||
import { NDivider } from 'naive-ui';
|
|
||||||
import { fetchBatchDeleteSpel, fetchGetSpelList } from '@/service/api/workflow/spel';
|
|
||||||
import { useAppStore } from '@/store/modules/app';
|
|
||||||
import { useAuth } from '@/hooks/business/auth';
|
|
||||||
import { useDownload } from '@/hooks/business/download';
|
|
||||||
import { useTable, useTableOperate } from '@/hooks/common/table';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
import ButtonIcon from '@/components/custom/button-icon.vue';
|
|
||||||
import DictTag from '@/components/custom/dict-tag.vue';
|
|
||||||
import TagGroup from '@/components/custom/tag-group.vue';
|
|
||||||
import SpelOperateDrawer from './modules/spel-operate-drawer.vue';
|
|
||||||
import SpelSearch from './modules/spel-search.vue';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'SpelList'
|
|
||||||
});
|
|
||||||
|
|
||||||
const appStore = useAppStore();
|
|
||||||
const { download } = useDownload();
|
|
||||||
const { hasAuth } = useAuth();
|
|
||||||
const {
|
|
||||||
columns,
|
|
||||||
columnChecks,
|
|
||||||
data,
|
|
||||||
getData,
|
|
||||||
getDataByPage,
|
|
||||||
loading,
|
|
||||||
mobilePagination,
|
|
||||||
searchParams,
|
|
||||||
resetSearchParams
|
|
||||||
} = useTable({
|
|
||||||
apiFn: fetchGetSpelList,
|
|
||||||
apiParams: {
|
|
||||||
pageNum: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
// if you want to use the searchParams in Form, you need to define the following properties, and the value is null
|
|
||||||
// the value can not be undefined, otherwise the property in Form will not be reactive
|
|
||||||
componentName: null,
|
|
||||||
methodName: null,
|
|
||||||
status: null,
|
|
||||||
params: {}
|
|
||||||
},
|
|
||||||
columns: () => [
|
|
||||||
{
|
|
||||||
type: 'selection',
|
|
||||||
align: 'center',
|
|
||||||
width: 48
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'index',
|
|
||||||
title: $t('common.index'),
|
|
||||||
align: 'center',
|
|
||||||
width: 64
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'componentName',
|
|
||||||
title: '组件名称',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'methodName',
|
|
||||||
title: '方法名',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'methodParams',
|
|
||||||
title: '参数',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 120,
|
|
||||||
render: row => {
|
|
||||||
return <TagGroup threadshold={4} value={row.methodParams} />;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'viewSpel',
|
|
||||||
title: 'spel表达式',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'remark',
|
|
||||||
title: '备注',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'status',
|
|
||||||
title: '状态',
|
|
||||||
align: 'center',
|
|
||||||
minWidth: 120,
|
|
||||||
render: row => {
|
|
||||||
return <DictTag size="small" value={row.status} dict-code="sys_normal_disable" />;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'operate',
|
|
||||||
title: $t('common.operate'),
|
|
||||||
align: 'center',
|
|
||||||
width: 130,
|
|
||||||
render: row => {
|
|
||||||
const divider = () => {
|
|
||||||
if (!hasAuth('workflow:spel:edit') || !hasAuth('workflow:spel:remove')) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return <NDivider vertical />;
|
|
||||||
};
|
|
||||||
|
|
||||||
const editBtn = () => {
|
|
||||||
if (!hasAuth('workflow:spel:edit')) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
icon="material-symbols:drive-file-rename-outline-outline"
|
|
||||||
tooltipContent={$t('common.edit')}
|
|
||||||
onClick={() => edit(row.id!)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteBtn = () => {
|
|
||||||
if (!hasAuth('workflow:spel:remove')) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="error"
|
|
||||||
icon="material-symbols:delete-outline"
|
|
||||||
tooltipContent={$t('common.delete')}
|
|
||||||
popconfirmContent={$t('common.confirmDelete')}
|
|
||||||
onPositiveClick={() => handleDelete(row.id!)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div class="flex-center gap-8px">
|
|
||||||
{editBtn()}
|
|
||||||
{divider()}
|
|
||||||
{deleteBtn()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
const { drawerVisible, operateType, editingData, handleAdd, handleEdit, checkedRowKeys, onBatchDeleted, onDeleted } =
|
|
||||||
useTableOperate(data, getData);
|
|
||||||
|
|
||||||
async function handleBatchDelete() {
|
|
||||||
// request
|
|
||||||
const { error } = await fetchBatchDeleteSpel(checkedRowKeys.value);
|
|
||||||
if (error) return;
|
|
||||||
onBatchDeleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleDelete(id: CommonType.IdType) {
|
|
||||||
// request
|
|
||||||
const { error } = await fetchBatchDeleteSpel([id]);
|
|
||||||
if (error) return;
|
|
||||||
onDeleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
function edit(id: CommonType.IdType) {
|
|
||||||
handleEdit('id', id);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleExport() {
|
|
||||||
download('/workflow/spel/export', searchParams, `流程spel达式定义_${new Date().getTime()}.xlsx`);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
|
|
||||||
<SpelSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getDataByPage" />
|
|
||||||
<NCard title="流程表达式列表" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
|
|
||||||
<template #header-extra>
|
|
||||||
<TableHeaderOperation
|
|
||||||
v-model:columns="columnChecks"
|
|
||||||
:disabled-delete="checkedRowKeys.length === 0"
|
|
||||||
:loading="loading"
|
|
||||||
:show-add="hasAuth('workflow:spel:add')"
|
|
||||||
:show-delete="hasAuth('workflow:spel:remove')"
|
|
||||||
:show-export="hasAuth('workflow:spel:export')"
|
|
||||||
@add="handleAdd"
|
|
||||||
@delete="handleBatchDelete"
|
|
||||||
@export="handleExport"
|
|
||||||
@refresh="getData"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<NDataTable
|
|
||||||
v-model:checked-row-keys="checkedRowKeys"
|
|
||||||
:columns="columns"
|
|
||||||
:data="data"
|
|
||||||
size="small"
|
|
||||||
:flex-height="!appStore.isMobile"
|
|
||||||
:scroll-x="962"
|
|
||||||
:loading="loading"
|
|
||||||
remote
|
|
||||||
:row-key="row => row.id"
|
|
||||||
:pagination="mobilePagination"
|
|
||||||
class="sm:h-full"
|
|
||||||
/>
|
|
||||||
<SpelOperateDrawer
|
|
||||||
v-model:visible="drawerVisible"
|
|
||||||
:operate-type="operateType"
|
|
||||||
:row-data="editingData"
|
|
||||||
@submitted="getDataByPage"
|
|
||||||
/>
|
|
||||||
</NCard>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
@ -1,179 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { computed, reactive, ref, watch } from 'vue';
|
|
||||||
import { fetchCreateSpel, fetchUpdateSpel } from '@/service/api/workflow/spel';
|
|
||||||
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
|
||||||
import { useDict } from '@/hooks/business/dict';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'SpelOperateDrawer'
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
/** the type of operation */
|
|
||||||
operateType: NaiveUI.TableOperateType;
|
|
||||||
/** the edit row data */
|
|
||||||
rowData?: Api.Workflow.Spel | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = defineProps<Props>();
|
|
||||||
|
|
||||||
interface Emits {
|
|
||||||
(e: 'submitted'): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
|
||||||
|
|
||||||
const visible = defineModel<boolean>('visible', {
|
|
||||||
default: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const { options: sysNormalDisableOptions } = useDict('sys_normal_disable');
|
|
||||||
|
|
||||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
|
||||||
const { createRequiredRule } = useFormRules();
|
|
||||||
|
|
||||||
const title = computed(() => {
|
|
||||||
const titles: Record<NaiveUI.TableOperateType, string> = {
|
|
||||||
add: '新增流程表达式',
|
|
||||||
edit: '编辑流程表达式'
|
|
||||||
};
|
|
||||||
return titles[props.operateType];
|
|
||||||
});
|
|
||||||
|
|
||||||
// 参数标签
|
|
||||||
const methodParamTags = ref<string[]>([]);
|
|
||||||
|
|
||||||
type Model = Api.Workflow.SpelOperateParams;
|
|
||||||
|
|
||||||
const model: Model = reactive(createDefaultModel());
|
|
||||||
|
|
||||||
function createDefaultModel(): Model {
|
|
||||||
return {
|
|
||||||
componentName: '',
|
|
||||||
methodName: '',
|
|
||||||
methodParams: '',
|
|
||||||
viewSpel: '',
|
|
||||||
remark: '',
|
|
||||||
status: '0'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
type RuleKey = Extract<keyof Model, 'id'>;
|
|
||||||
|
|
||||||
const rules: Record<RuleKey, App.Global.FormRule> = {
|
|
||||||
id: createRequiredRule('主键id不能为空')
|
|
||||||
};
|
|
||||||
|
|
||||||
function handleUpdateModelWhenEdit() {
|
|
||||||
if (props.operateType === 'add') {
|
|
||||||
Object.assign(model, createDefaultModel());
|
|
||||||
methodParamTags.value = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.operateType === 'edit' && props.rowData) {
|
|
||||||
Object.assign(model, props.rowData);
|
|
||||||
// 如果有参数,将逗号分隔的字符串转为数组
|
|
||||||
methodParamTags.value = model.methodParams ? model.methodParams.split(',') : [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 实时更新 SPEL 表达式
|
|
||||||
function updateSpelExpression() {
|
|
||||||
if (!model.componentName || !model.methodName) {
|
|
||||||
model.viewSpel = '';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 构建参数部分
|
|
||||||
const params = methodParamTags.value.map(param => `#${param}`).join(',');
|
|
||||||
|
|
||||||
// 生成 SPEL 表达式: #{@组件名.方法名(#参数1,#参数2,...)}
|
|
||||||
model.viewSpel = `#{@${model.componentName}.${model.methodName}(${params})}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听组件名、方法名和参数变化
|
|
||||||
watch(() => model.componentName, updateSpelExpression);
|
|
||||||
watch(() => model.methodName, updateSpelExpression);
|
|
||||||
watch(methodParamTags, updateSpelExpression, { deep: true });
|
|
||||||
|
|
||||||
function closeDrawer() {
|
|
||||||
visible.value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleSubmit() {
|
|
||||||
await validate();
|
|
||||||
|
|
||||||
// 将参数标签数组转为逗号分隔的字符串
|
|
||||||
model.methodParams = methodParamTags.value.join(',');
|
|
||||||
|
|
||||||
const { id, componentName, methodName, methodParams, viewSpel, remark, status } = model;
|
|
||||||
|
|
||||||
// request
|
|
||||||
if (props.operateType === 'add') {
|
|
||||||
const { error } = await fetchCreateSpel({ componentName, methodName, methodParams, viewSpel, remark, status });
|
|
||||||
if (error) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.operateType === 'edit') {
|
|
||||||
const { error } = await fetchUpdateSpel({ id, componentName, methodName, methodParams, viewSpel, remark, status });
|
|
||||||
if (error) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.$message?.success($t('common.updateSuccess'));
|
|
||||||
closeDrawer();
|
|
||||||
emit('submitted');
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(visible, () => {
|
|
||||||
if (visible.value) {
|
|
||||||
handleUpdateModelWhenEdit();
|
|
||||||
restoreValidation();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NDrawer v-model:show="visible" :title="title" display-directive="show" :width="800" class="max-w-90%">
|
|
||||||
<NDrawerContent :title="title" :native-scrollbar="false" closable>
|
|
||||||
<NForm ref="formRef" :model="model" :rules="rules">
|
|
||||||
<NFormItem label="组件名称" path="componentName">
|
|
||||||
<NInput v-model:value="model.componentName" placeholder="请输入组件名称" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="方法名" path="methodName">
|
|
||||||
<NInput v-model:value="model.methodName" placeholder="请输入方法名" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="参数" path="methodParams">
|
|
||||||
<NDynamicTags v-model:value="methodParamTags" placeholder="请输入参数后回车" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="spel表达式" path="viewSpel">
|
|
||||||
<NInput v-model:value="model.viewSpel" placeholder="自动生成的spel表达式" disabled />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="备注" path="remark">
|
|
||||||
<NInput v-model:value="model.remark" placeholder="请输入备注" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="状态" path="status">
|
|
||||||
<NRadioGroup v-model:value="model.status">
|
|
||||||
<NSpace>
|
|
||||||
<NRadio
|
|
||||||
v-for="option in sysNormalDisableOptions"
|
|
||||||
:key="option.value"
|
|
||||||
:value="option.value"
|
|
||||||
:label="option.label"
|
|
||||||
/>
|
|
||||||
</NSpace>
|
|
||||||
</NRadioGroup>
|
|
||||||
</NFormItem>
|
|
||||||
</NForm>
|
|
||||||
<template #footer>
|
|
||||||
<NSpace :size="16">
|
|
||||||
<NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
|
|
||||||
<NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
|
|
||||||
</NSpace>
|
|
||||||
</template>
|
|
||||||
</NDrawerContent>
|
|
||||||
</NDrawer>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
@ -1,77 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { useNaiveForm } from '@/hooks/common/form';
|
|
||||||
import { useDict } from '@/hooks/business/dict';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
defineOptions({
|
|
||||||
name: 'SpelSearch'
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Emits {
|
|
||||||
(e: 'reset'): void;
|
|
||||||
(e: 'search'): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
|
||||||
|
|
||||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
|
||||||
|
|
||||||
const model = defineModel<Api.Workflow.SpelSearchParams>('model', { required: true });
|
|
||||||
|
|
||||||
const { options: sysNormalDisableOptions } = useDict('sys_normal_disable', false);
|
|
||||||
|
|
||||||
async function reset() {
|
|
||||||
Object.assign(model.value.params!, {});
|
|
||||||
await restoreValidation();
|
|
||||||
emit('reset');
|
|
||||||
}
|
|
||||||
|
|
||||||
async function search() {
|
|
||||||
await validate();
|
|
||||||
emit('search');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
|
||||||
<NCollapse>
|
|
||||||
<NCollapseItem :title="$t('common.search')" name="user-search">
|
|
||||||
<NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
|
|
||||||
<NGrid responsive="screen" item-responsive>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" label="组件名称" path="componentName" class="pr-24px">
|
|
||||||
<NInput v-model:value="model.componentName" placeholder="请输入组件名称" />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" label="方法名" path="methodName" class="pr-24px">
|
|
||||||
<NInput v-model:value="model.methodName" placeholder="请输入方法名" />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" label="状态" path="status" class="pr-24px">
|
|
||||||
<NSelect
|
|
||||||
v-model:value="model.status"
|
|
||||||
placeholder="请选择状态"
|
|
||||||
:options="sysNormalDisableOptions"
|
|
||||||
clearable
|
|
||||||
/>
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" class="pr-24px">
|
|
||||||
<NSpace class="w-full" justify="end">
|
|
||||||
<NButton @click="reset">
|
|
||||||
<template #icon>
|
|
||||||
<icon-ic-round-refresh class="text-icon" />
|
|
||||||
</template>
|
|
||||||
{{ $t('common.reset') }}
|
|
||||||
</NButton>
|
|
||||||
<NButton type="primary" ghost @click="search">
|
|
||||||
<template #icon>
|
|
||||||
<icon-ic-round-search class="text-icon" />
|
|
||||||
</template>
|
|
||||||
{{ $t('common.search') }}
|
|
||||||
</NButton>
|
|
||||||
</NSpace>
|
|
||||||
</NFormItemGi>
|
|
||||||
</NGrid>
|
|
||||||
</NForm>
|
|
||||||
</NCollapseItem>
|
|
||||||
</NCollapse>
|
|
||||||
</NCard>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
@ -1,365 +0,0 @@
|
|||||||
<script setup lang="tsx">
|
|
||||||
import { computed, ref, shallowRef, watch } from 'vue';
|
|
||||||
import { NButton, NDivider, NEmpty, NInput, NRadioButton, NRadioGroup, NTag } from 'naive-ui';
|
|
||||||
import { useBoolean, useLoading } from '@sa/hooks';
|
|
||||||
import { fetchGetAllFinishedTask, fetchGetAllWaitingTask, fetchTaskAssignee } from '@/service/api/workflow/task';
|
|
||||||
import { fetchGetCategoryTree } from '@/service/api/workflow/category';
|
|
||||||
import { useAppStore } from '@/store/modules/app';
|
|
||||||
import { useTable, useTableOperate } from '@/hooks/common/table';
|
|
||||||
import { useDict } from '@/hooks/business/dict';
|
|
||||||
import { loadDynamicComponent } from '@/utils/common';
|
|
||||||
import TagGroup from '@/components/custom/tag-group.vue';
|
|
||||||
import DictTag from '@/components/custom/dict-tag.vue';
|
|
||||||
import ButtonIcon from '@/components/custom/button-icon.vue';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
import AllTaskWaitingSearch from './modules/all-task-waiting-search.vue';
|
|
||||||
|
|
||||||
interface WaitingStatusOption {
|
|
||||||
label: string;
|
|
||||||
value: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'AllTaskWaitingList'
|
|
||||||
});
|
|
||||||
|
|
||||||
useDict('wf_business_status');
|
|
||||||
useDict('wf_task_status');
|
|
||||||
const appStore = useAppStore();
|
|
||||||
const { bool: viewVisible, setTrue: showViewDrawer } = useBoolean();
|
|
||||||
const { bool: interveneVisible, setTrue: showInterveneDrawer } = useBoolean();
|
|
||||||
const { bool: urgeVisible, setTrue: showUrgeModal } = useBoolean();
|
|
||||||
const { bool: assigneeVisible, setTrue: showAssigneeModal } = useBoolean();
|
|
||||||
const dynamicComponent = shallowRef();
|
|
||||||
|
|
||||||
const waitingStatus = ref<boolean>(true);
|
|
||||||
const waitingStatusOptions = ref<WaitingStatusOption[]>([
|
|
||||||
{ label: '待办任务', value: true },
|
|
||||||
{ label: '已办任务', value: false }
|
|
||||||
]);
|
|
||||||
|
|
||||||
const commonColumns: NaiveUI.TableColumn<Api.Workflow.TaskOrHisTask>[] = [
|
|
||||||
{ type: 'selection', align: 'center', width: 50 },
|
|
||||||
{ key: 'businessCode', title: '业务编码', align: 'center', width: 120 },
|
|
||||||
{ key: 'businessTitle', title: '业务名称', align: 'center', width: 120 },
|
|
||||||
{ key: 'flowName', title: '流程定义名称', align: 'center', width: 120 },
|
|
||||||
{ key: 'flowCode', title: '流程定义编码', align: 'center', width: 120 },
|
|
||||||
{
|
|
||||||
key: 'categoryName',
|
|
||||||
title: '流程分类',
|
|
||||||
align: 'center',
|
|
||||||
width: 120,
|
|
||||||
render: row => <NTag>{row.categoryName}</NTag>
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'version',
|
|
||||||
title: '版本号',
|
|
||||||
align: 'center',
|
|
||||||
width: 120,
|
|
||||||
render: row => <NTag type="info">v{row.version}.0</NTag>
|
|
||||||
},
|
|
||||||
{ key: 'nodeName', title: '任务名称', align: 'center', width: 120 },
|
|
||||||
{ key: 'createByName', title: '申请人', align: 'center', width: 120 },
|
|
||||||
{
|
|
||||||
key: 'flowStatus',
|
|
||||||
title: '流程状态',
|
|
||||||
align: 'center',
|
|
||||||
width: 120,
|
|
||||||
render: row => <DictTag size="small" value={row.flowStatus} dict-code="wf_business_status" />
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const waitingColumns = ref<NaiveUI.TableColumn<Api.Workflow.Task>[]>([
|
|
||||||
...(commonColumns as NaiveUI.TableColumn<Api.Workflow.Task>[]),
|
|
||||||
{
|
|
||||||
key: 'assigneeNames',
|
|
||||||
title: '办理人',
|
|
||||||
align: 'center',
|
|
||||||
width: 120,
|
|
||||||
render: row => <TagGroup value={row.assigneeNames} />
|
|
||||||
},
|
|
||||||
{ key: 'createTime', title: '创建时间', align: 'center', width: 120 }
|
|
||||||
]);
|
|
||||||
|
|
||||||
const finishColumns = ref<NaiveUI.TableColumn<Api.Workflow.HisTask>[]>([
|
|
||||||
...(commonColumns as NaiveUI.TableColumn<Api.Workflow.HisTask>[]),
|
|
||||||
{
|
|
||||||
key: 'approveName',
|
|
||||||
title: '办理人',
|
|
||||||
align: 'center',
|
|
||||||
width: 120,
|
|
||||||
render: row => <NTag type="info">{row.approveName}</NTag>
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'flowTaskStatus',
|
|
||||||
title: '任务状态',
|
|
||||||
align: 'center',
|
|
||||||
width: 120,
|
|
||||||
render: row => <DictTag size="small" value={row.flowTaskStatus} dict-code="wf_task_status" />
|
|
||||||
},
|
|
||||||
{ key: 'createTime', title: '创建时间', align: 'center', width: 120 }
|
|
||||||
]);
|
|
||||||
|
|
||||||
const operateColumns = ref<NaiveUI.TableColumn<Api.Workflow.TaskOrHisTask>[]>([
|
|
||||||
{
|
|
||||||
key: 'operate',
|
|
||||||
title: $t('common.operate'),
|
|
||||||
align: 'center',
|
|
||||||
fixed: 'right',
|
|
||||||
width: 100,
|
|
||||||
render: row => {
|
|
||||||
const buttons = [
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="info"
|
|
||||||
icon="material-symbols:visibility-outline"
|
|
||||||
tooltipContent="查看"
|
|
||||||
onClick={() => handleView(row)}
|
|
||||||
/>
|
|
||||||
];
|
|
||||||
|
|
||||||
if (waitingStatus.value && row.flowStatus !== 'draft') {
|
|
||||||
buttons.push(
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="info"
|
|
||||||
icon="material-symbols:edit-document"
|
|
||||||
tooltipContent="流程干预"
|
|
||||||
onClick={() => handleIntervene(row as Api.Workflow.Task)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div class="flex-center gap-8px">
|
|
||||||
{buttons.map((btn, index) => (index > 0 ? [<NDivider vertical />, btn] : btn))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
const {
|
|
||||||
columns,
|
|
||||||
reloadColumns,
|
|
||||||
columnChecks,
|
|
||||||
data,
|
|
||||||
getData,
|
|
||||||
getDataByPage,
|
|
||||||
loading,
|
|
||||||
mobilePagination,
|
|
||||||
searchParams,
|
|
||||||
resetSearchParams,
|
|
||||||
updateApiFn
|
|
||||||
} = useTable({
|
|
||||||
apiFn: fetchGetAllWaitingTask,
|
|
||||||
apiParams: {
|
|
||||||
pageNum: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
category: null,
|
|
||||||
flowName: null,
|
|
||||||
flowCode: null,
|
|
||||||
nodeName: null,
|
|
||||||
createByIds: null
|
|
||||||
},
|
|
||||||
columns: () => {
|
|
||||||
const baseColumns = waitingStatus.value ? waitingColumns.value : finishColumns.value;
|
|
||||||
return [...baseColumns, ...operateColumns.value] as NaiveUI.TableColumn<Api.Workflow.TaskOrHisTask>[];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const { checkedRowKeys } = useTableOperate(data, getData);
|
|
||||||
|
|
||||||
watch(waitingStatus, async () => {
|
|
||||||
const newApiFn = waitingStatus.value ? fetchGetAllWaitingTask : fetchGetAllFinishedTask;
|
|
||||||
// @ts-expect-error - This is a workaround for the type issue
|
|
||||||
updateApiFn(newApiFn);
|
|
||||||
await getDataByPage();
|
|
||||||
reloadColumns();
|
|
||||||
});
|
|
||||||
|
|
||||||
const { loading: treeLoading, startLoading: startTreeLoading, endLoading: endTreeLoading } = useLoading();
|
|
||||||
const categoryPattern = ref<string>();
|
|
||||||
const categoryData = ref<Api.Common.CommonTreeRecord>([]);
|
|
||||||
const selectedKeys = ref<string[]>([]);
|
|
||||||
const expandedKeys = ref<CommonType.IdType[]>(['100']);
|
|
||||||
|
|
||||||
const selectable = computed(() => !loading.value);
|
|
||||||
|
|
||||||
async function getTreeData() {
|
|
||||||
startTreeLoading();
|
|
||||||
const { data: tree, error } = await fetchGetCategoryTree();
|
|
||||||
if (!error) {
|
|
||||||
categoryData.value = tree;
|
|
||||||
}
|
|
||||||
endTreeLoading();
|
|
||||||
}
|
|
||||||
|
|
||||||
getTreeData();
|
|
||||||
|
|
||||||
function handleClickTree(keys: string[]) {
|
|
||||||
searchParams.category = keys.length ? keys[0] : null;
|
|
||||||
checkedRowKeys.value = [];
|
|
||||||
getDataByPage();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleResetTreeData() {
|
|
||||||
categoryPattern.value = undefined;
|
|
||||||
getTreeData();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleResetSearch() {
|
|
||||||
resetSearchParams();
|
|
||||||
selectedKeys.value = [];
|
|
||||||
}
|
|
||||||
const modules = import.meta.glob('@/components/workflow/**/*.vue');
|
|
||||||
const businessId = ref<CommonType.IdType>();
|
|
||||||
|
|
||||||
async function handleView(row: Api.Workflow.TaskOrHisTask) {
|
|
||||||
dynamicComponent.value = null;
|
|
||||||
viewVisible.value = false;
|
|
||||||
businessId.value = row.businessId;
|
|
||||||
const formPath = row.formPath;
|
|
||||||
if (!formPath) return;
|
|
||||||
dynamicComponent.value = await loadDynamicComponent(modules, formPath);
|
|
||||||
setTimeout(() => {
|
|
||||||
showViewDrawer();
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
|
|
||||||
const taskId = ref<CommonType.IdType>('');
|
|
||||||
const assigneeIds = ref<CommonType.IdType[]>([]);
|
|
||||||
const assigneeNames = ref<string[]>([]);
|
|
||||||
function handleIntervene(row: Api.Workflow.Task) {
|
|
||||||
taskId.value = row.id;
|
|
||||||
assigneeIds.value = row.assigneeIds?.split(',') || [];
|
|
||||||
assigneeNames.value = row.assigneeNames?.split(',') || [];
|
|
||||||
showInterveneDrawer();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleAssigneeConfirm(userIds: CommonType.IdType[]) {
|
|
||||||
const { error } = await fetchTaskAssignee(checkedRowKeys.value, userIds[0]);
|
|
||||||
if (error) return;
|
|
||||||
window.$message?.success('修改办理人成功');
|
|
||||||
assigneeVisible.value = false;
|
|
||||||
await getData();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<TableSiderLayout sider-title="流程分类列表">
|
|
||||||
<template #header-extra>
|
|
||||||
<NButton size="small" text class="h-18px" @click.stop="() => handleResetTreeData()">
|
|
||||||
<template #icon>
|
|
||||||
<SvgIcon icon="ic:round-refresh" />
|
|
||||||
</template>
|
|
||||||
</NButton>
|
|
||||||
</template>
|
|
||||||
<template #sider>
|
|
||||||
<NInput v-model:value="categoryPattern" clearable :placeholder="$t('common.keywordSearch')" />
|
|
||||||
<NSpin class="category-tree" :show="treeLoading">
|
|
||||||
<NTree
|
|
||||||
v-model:selected-keys="selectedKeys"
|
|
||||||
v-model:expanded-keys="expandedKeys"
|
|
||||||
block-node
|
|
||||||
show-line
|
|
||||||
:data="categoryData as []"
|
|
||||||
:show-irrelevant-nodes="false"
|
|
||||||
:pattern="categoryPattern"
|
|
||||||
class="infinite-scroll h-full min-h-200px py-3"
|
|
||||||
key-field="id"
|
|
||||||
label-field="label"
|
|
||||||
virtual-scroll
|
|
||||||
:selectable="selectable"
|
|
||||||
@update:selected-keys="handleClickTree"
|
|
||||||
>
|
|
||||||
<template #empty>
|
|
||||||
<NEmpty description="暂无分类信息" class="h-full min-h-200px justify-center" />
|
|
||||||
</template>
|
|
||||||
</NTree>
|
|
||||||
</NSpin>
|
|
||||||
</template>
|
|
||||||
<div class="h-full flex-col-stretch gap-12px overflow-hidden lt-sm:overflow-auto">
|
|
||||||
<AllTaskWaitingSearch v-model:model="searchParams" @reset="handleResetSearch" @search="getDataByPage" />
|
|
||||||
<NCard :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
|
|
||||||
<template #header>
|
|
||||||
<NSpace>
|
|
||||||
<NRadioGroup v-model:value="waitingStatus" on-up size="small">
|
|
||||||
<NRadioButton
|
|
||||||
v-for="(status, index) in waitingStatusOptions"
|
|
||||||
:key="index"
|
|
||||||
:value="status.value"
|
|
||||||
:label="status.label"
|
|
||||||
/>
|
|
||||||
</NRadioGroup>
|
|
||||||
</NSpace>
|
|
||||||
</template>
|
|
||||||
<template #header-extra>
|
|
||||||
<TableHeaderOperation
|
|
||||||
v-model:columns="columnChecks"
|
|
||||||
:disabled-delete="checkedRowKeys.length === 0"
|
|
||||||
:loading="loading"
|
|
||||||
:show-add="false"
|
|
||||||
:show-delete="false"
|
|
||||||
:show-export="false"
|
|
||||||
@refresh="getData"
|
|
||||||
>
|
|
||||||
<template #prefix>
|
|
||||||
<NButton
|
|
||||||
v-if="waitingStatus"
|
|
||||||
:disabled="checkedRowKeys.length === 0"
|
|
||||||
size="small"
|
|
||||||
type="warning"
|
|
||||||
ghost
|
|
||||||
@click="showAssigneeModal"
|
|
||||||
>
|
|
||||||
<template #icon>
|
|
||||||
<icon-material-symbols:drive-file-rename-outline-outline class="text-icon" />
|
|
||||||
</template>
|
|
||||||
修改办理人
|
|
||||||
</NButton>
|
|
||||||
<NButton
|
|
||||||
v-if="waitingStatus"
|
|
||||||
:disabled="checkedRowKeys.length === 0"
|
|
||||||
size="small"
|
|
||||||
type="success"
|
|
||||||
ghost
|
|
||||||
@click="showUrgeModal"
|
|
||||||
>
|
|
||||||
<template #icon>
|
|
||||||
<SvgIcon local-icon="bell" class="text-16px" />
|
|
||||||
</template>
|
|
||||||
催办
|
|
||||||
</NButton>
|
|
||||||
</template>
|
|
||||||
</TableHeaderOperation>
|
|
||||||
</template>
|
|
||||||
<NDataTable
|
|
||||||
v-model:checked-row-keys="checkedRowKeys"
|
|
||||||
:columns="columns"
|
|
||||||
:data="data"
|
|
||||||
size="small"
|
|
||||||
:flex-height="!appStore.isMobile"
|
|
||||||
:scroll-x="1405"
|
|
||||||
:loading="loading"
|
|
||||||
remote
|
|
||||||
:row-key="row => row.id"
|
|
||||||
:pagination="mobilePagination"
|
|
||||||
class="sm:h-full"
|
|
||||||
/>
|
|
||||||
<component :is="dynamicComponent" :visible="viewVisible" operate-type="detail" :business-id="businessId" />
|
|
||||||
<FlowInterveneModal
|
|
||||||
v-model:visible="interveneVisible"
|
|
||||||
:task-id="taskId"
|
|
||||||
:assignee-ids="assigneeIds"
|
|
||||||
:assignee-names="assigneeNames"
|
|
||||||
@refresh="getData"
|
|
||||||
/>
|
|
||||||
<!-- 催办 -->
|
|
||||||
<FlowUrgeModal v-model:visible="urgeVisible" :task-ids="checkedRowKeys" @submit="getData" />
|
|
||||||
<!-- 修改办理人 -->
|
|
||||||
<UserSelectModal v-model:visible="assigneeVisible" @confirm="handleAssigneeConfirm" />
|
|
||||||
</NCard>
|
|
||||||
</div>
|
|
||||||
</TableSiderLayout>
|
|
||||||
</template>
|
|
@ -1,63 +0,0 @@
|
|||||||
<script setup lang="tsx">
|
|
||||||
import { useNaiveForm } from '@/hooks/common/form';
|
|
||||||
defineOptions({
|
|
||||||
name: 'AllTaskWaitingSearch'
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Emits {
|
|
||||||
(e: 'reset'): void;
|
|
||||||
(e: 'search'): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
|
||||||
|
|
||||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
|
||||||
const model = defineModel<Api.Workflow.TaskSearchParams>('model', { required: true });
|
|
||||||
|
|
||||||
async function reset() {
|
|
||||||
await restoreValidation();
|
|
||||||
emit('reset');
|
|
||||||
}
|
|
||||||
async function search() {
|
|
||||||
await validate();
|
|
||||||
emit('search');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
|
||||||
<NCollapse>
|
|
||||||
<NCollapseItem :title="$t('common.search')">
|
|
||||||
<NForm ref="formRef" :model="model" label-placement="left" :label-width="100">
|
|
||||||
<NGrid responsive="screen" item-responsive>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" label="任务名称" path="nodeName" class="pr-24px">
|
|
||||||
<NInput v-model:value="model.nodeName" placeholder="请输入任务名称" />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" label="流程定义名称" path="bucketName" class="pr-24px">
|
|
||||||
<NInput v-model:value="model.flowName" placeholder="请输入流程定义名称" />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" label="流程定义编码" path="flowCode" class="pr-24px">
|
|
||||||
<NInput v-model:value="model.flowCode" placeholder="流程定义编码" />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" class="pr-24px">
|
|
||||||
<NSpace class="w-full" justify="end">
|
|
||||||
<NButton @click="reset">
|
|
||||||
<template #icon>
|
|
||||||
<icon-ic-round-refresh class="text-icon" />
|
|
||||||
</template>
|
|
||||||
{{ $t('common.reset') }}
|
|
||||||
</NButton>
|
|
||||||
<NButton type="primary" ghost @click="search">
|
|
||||||
<template #icon>
|
|
||||||
<icon-ic-round-search class="text-icon" />
|
|
||||||
</template>
|
|
||||||
{{ $t('common.search') }}
|
|
||||||
</NButton>
|
|
||||||
</NSpace>
|
|
||||||
</NFormItemGi>
|
|
||||||
</NGrid>
|
|
||||||
</NForm>
|
|
||||||
</NCollapseItem>
|
|
||||||
</NCollapse>
|
|
||||||
</NCard>
|
|
||||||
</template>
|
|
@ -1,319 +0,0 @@
|
|||||||
<script setup lang="tsx">
|
|
||||||
import { computed, ref, shallowRef } from 'vue';
|
|
||||||
import { NButton, NDivider, NEmpty, NInput, NTag } from 'naive-ui';
|
|
||||||
import { useBoolean, useLoading } from '@sa/hooks';
|
|
||||||
import { workflowActivityStatusRecord } from '@/constants/workflow';
|
|
||||||
import { fetchBatchDeleteInstance, fetchCancelProcessApply, fetchGetMyDocument } from '@/service/api/workflow/instance';
|
|
||||||
import { fetchGetCategoryTree } from '@/service/api/workflow/category';
|
|
||||||
import { useAppStore } from '@/store/modules/app';
|
|
||||||
import { useTable, useTableOperate } from '@/hooks/common/table';
|
|
||||||
import { useDict } from '@/hooks/business/dict';
|
|
||||||
import { loadDynamicComponent } from '@/utils/common';
|
|
||||||
import DictTag from '@/components/custom/dict-tag.vue';
|
|
||||||
import ButtonIcon from '@/components/custom/button-icon.vue';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
import MyDocumentSearch from './modules/my-document-search.vue';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'MyDocumentList'
|
|
||||||
});
|
|
||||||
|
|
||||||
useDict('wf_business_status');
|
|
||||||
const appStore = useAppStore();
|
|
||||||
const { bool: viewVisible, setTrue: showViewDrawer } = useBoolean();
|
|
||||||
const dynamicComponent = shallowRef();
|
|
||||||
|
|
||||||
const {
|
|
||||||
columns,
|
|
||||||
columnChecks,
|
|
||||||
data,
|
|
||||||
getData,
|
|
||||||
getDataByPage,
|
|
||||||
loading,
|
|
||||||
mobilePagination,
|
|
||||||
searchParams,
|
|
||||||
resetSearchParams
|
|
||||||
} = useTable({
|
|
||||||
apiFn: fetchGetMyDocument,
|
|
||||||
apiParams: {
|
|
||||||
pageNum: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
category: null,
|
|
||||||
flowName: null,
|
|
||||||
flowCode: null,
|
|
||||||
nodeName: null,
|
|
||||||
createByIds: null
|
|
||||||
},
|
|
||||||
columns: () => [
|
|
||||||
{
|
|
||||||
key: 'index',
|
|
||||||
title: $t('common.index'),
|
|
||||||
align: 'center',
|
|
||||||
width: 64
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '流程定义名称',
|
|
||||||
key: 'flowName',
|
|
||||||
align: 'center',
|
|
||||||
width: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '流程定义编码',
|
|
||||||
key: 'flowCode',
|
|
||||||
align: 'center',
|
|
||||||
width: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '流程分类',
|
|
||||||
key: 'categoryName',
|
|
||||||
align: 'center',
|
|
||||||
width: 80,
|
|
||||||
render: row => {
|
|
||||||
return <NTag>{row.categoryName}</NTag>;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '版本号',
|
|
||||||
key: 'version',
|
|
||||||
align: 'center',
|
|
||||||
width: 80,
|
|
||||||
render: row => {
|
|
||||||
return <NTag type="info">v{row.version}.0</NTag>;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '流程状态',
|
|
||||||
key: 'flowStatus',
|
|
||||||
align: 'center',
|
|
||||||
width: 80,
|
|
||||||
render(row) {
|
|
||||||
return <DictTag value={row.flowStatus} dict-code="wf_business_status" />;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '状态',
|
|
||||||
key: 'activityStatus',
|
|
||||||
align: 'center',
|
|
||||||
width: 80,
|
|
||||||
render(row) {
|
|
||||||
return (
|
|
||||||
<NTag type={row.activityStatus === 0 ? 'warning' : 'success'}>
|
|
||||||
{workflowActivityStatusRecord[row.activityStatus]}
|
|
||||||
</NTag>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '启动时间',
|
|
||||||
key: 'createTime',
|
|
||||||
align: 'center',
|
|
||||||
width: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '操作',
|
|
||||||
key: 'operate',
|
|
||||||
align: 'center',
|
|
||||||
fixed: 'right',
|
|
||||||
width: 100,
|
|
||||||
render(row) {
|
|
||||||
const buttons = [];
|
|
||||||
|
|
||||||
buttons.push(
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="info"
|
|
||||||
icon="material-symbols:visibility-outline"
|
|
||||||
tooltipContent="查看"
|
|
||||||
onClick={() => handleOpen(row, 'detail')}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
const showEditAndDelete =
|
|
||||||
row.flowStatus === 'draft' || row.flowStatus === 'cancel' || row.flowStatus === 'back';
|
|
||||||
if (showEditAndDelete) {
|
|
||||||
buttons.push(
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="info"
|
|
||||||
icon="material-symbols:drive-file-rename-outline-outline"
|
|
||||||
tooltipContent="编辑"
|
|
||||||
onClick={() => handleOpen(row, 'edit')}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showEditAndDelete) {
|
|
||||||
buttons.push(
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="error"
|
|
||||||
icon="material-symbols:delete-outline"
|
|
||||||
tooltipContent={$t('common.delete')}
|
|
||||||
popconfirmContent={$t('common.confirmDelete')}
|
|
||||||
onPositiveClick={() => handleDelete(row)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (row.flowStatus === 'waiting') {
|
|
||||||
buttons.push(
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="error"
|
|
||||||
showPopconfirmIcon={false}
|
|
||||||
icon="material-symbols:cancel-outline-rounded"
|
|
||||||
tooltipContent="撤销"
|
|
||||||
popconfirmContent="确认撤销此流程申请?"
|
|
||||||
onPositiveClick={() => handleCancelProcessApply(row.businessId)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div class="flex-center gap-1px">
|
|
||||||
{buttons.map((btn, index) => (index > 0 ? [<NDivider vertical />, btn] : btn))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
const { checkedRowKeys } = useTableOperate(data, getData);
|
|
||||||
|
|
||||||
const { loading: treeLoading, startLoading: startTreeLoading, endLoading: endTreeLoading } = useLoading();
|
|
||||||
const categoryPattern = ref<string>();
|
|
||||||
const categoryData = ref<Api.Common.CommonTreeRecord>([]);
|
|
||||||
const selectedKeys = ref<string[]>([]);
|
|
||||||
const expandedKeys = ref<CommonType.IdType[]>(['100']);
|
|
||||||
|
|
||||||
const selectable = computed(() => !loading.value);
|
|
||||||
|
|
||||||
async function getTreeData() {
|
|
||||||
startTreeLoading();
|
|
||||||
const { data: tree, error } = await fetchGetCategoryTree();
|
|
||||||
if (!error) {
|
|
||||||
categoryData.value = tree;
|
|
||||||
}
|
|
||||||
endTreeLoading();
|
|
||||||
}
|
|
||||||
|
|
||||||
getTreeData();
|
|
||||||
|
|
||||||
function handleClickTree(keys: string[]) {
|
|
||||||
searchParams.category = keys.length ? keys[0] : null;
|
|
||||||
checkedRowKeys.value = [];
|
|
||||||
getDataByPage();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleResetTreeData() {
|
|
||||||
categoryPattern.value = undefined;
|
|
||||||
getTreeData();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleResetSearch() {
|
|
||||||
resetSearchParams();
|
|
||||||
selectedKeys.value = [];
|
|
||||||
}
|
|
||||||
const modules = import.meta.glob('@/components/workflow/**/*.vue');
|
|
||||||
const businessId = ref<CommonType.IdType>();
|
|
||||||
const operateType = ref<CommonType.WorkflowTableOperateType>();
|
|
||||||
|
|
||||||
async function handleOpen(row: Api.Workflow.Instance, type: 'edit' | 'detail') {
|
|
||||||
dynamicComponent.value = null;
|
|
||||||
viewVisible.value = false;
|
|
||||||
operateType.value = type;
|
|
||||||
businessId.value = row.businessId;
|
|
||||||
const formPath = row.formPath;
|
|
||||||
if (!formPath) return;
|
|
||||||
dynamicComponent.value = await loadDynamicComponent(modules, formPath);
|
|
||||||
setTimeout(() => {
|
|
||||||
showViewDrawer();
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleDelete(row: Api.Workflow.Instance) {
|
|
||||||
const { error } = await fetchBatchDeleteInstance([row.id]);
|
|
||||||
if (error) return;
|
|
||||||
window.$message?.success('删除成功');
|
|
||||||
getData();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleCancelProcessApply(id: CommonType.IdType) {
|
|
||||||
const { error } = await fetchCancelProcessApply({ businessId: id, message: '申请人撤销流程!' });
|
|
||||||
if (error) return;
|
|
||||||
window.$message?.success('撤销成功');
|
|
||||||
getData();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<TableSiderLayout sider-title="流程分类列表">
|
|
||||||
<template #header-extra>
|
|
||||||
<NButton size="small" text class="h-18px" @click.stop="() => handleResetTreeData()">
|
|
||||||
<template #icon>
|
|
||||||
<SvgIcon icon="ic:round-refresh" />
|
|
||||||
</template>
|
|
||||||
</NButton>
|
|
||||||
</template>
|
|
||||||
<template #sider>
|
|
||||||
<NInput v-model:value="categoryPattern" clearable :placeholder="$t('common.keywordSearch')" />
|
|
||||||
<NSpin class="category-tree" :show="treeLoading">
|
|
||||||
<NTree
|
|
||||||
v-model:selected-keys="selectedKeys"
|
|
||||||
v-model:expanded-keys="expandedKeys"
|
|
||||||
block-node
|
|
||||||
show-line
|
|
||||||
:data="categoryData as []"
|
|
||||||
:show-irrelevant-nodes="false"
|
|
||||||
:pattern="categoryPattern"
|
|
||||||
class="infinite-scroll h-full min-h-200px py-3"
|
|
||||||
key-field="id"
|
|
||||||
label-field="label"
|
|
||||||
virtual-scroll
|
|
||||||
:selectable="selectable"
|
|
||||||
@update:selected-keys="handleClickTree"
|
|
||||||
>
|
|
||||||
<template #empty>
|
|
||||||
<NEmpty description="暂无分类信息" class="h-full min-h-200px justify-center" />
|
|
||||||
</template>
|
|
||||||
</NTree>
|
|
||||||
</NSpin>
|
|
||||||
</template>
|
|
||||||
<div class="h-full flex-col-stretch gap-12px overflow-hidden lt-sm:overflow-auto">
|
|
||||||
<MyDocumentSearch v-model:model="searchParams" @reset="handleResetSearch" @search="getDataByPage" />
|
|
||||||
<NCard title="我发起的" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
|
|
||||||
<template #header-extra>
|
|
||||||
<TableHeaderOperation
|
|
||||||
v-model:columns="columnChecks"
|
|
||||||
:disabled-delete="checkedRowKeys.length === 0"
|
|
||||||
:loading="loading"
|
|
||||||
:show-add="false"
|
|
||||||
:show-delete="false"
|
|
||||||
:show-export="false"
|
|
||||||
@refresh="getData"
|
|
||||||
></TableHeaderOperation>
|
|
||||||
</template>
|
|
||||||
<NDataTable
|
|
||||||
v-model:checked-row-keys="checkedRowKeys"
|
|
||||||
:columns="columns"
|
|
||||||
:data="data"
|
|
||||||
size="small"
|
|
||||||
:flex-height="!appStore.isMobile"
|
|
||||||
:scroll-x="1405"
|
|
||||||
:loading="loading"
|
|
||||||
remote
|
|
||||||
:row-key="row => row.id"
|
|
||||||
:pagination="mobilePagination"
|
|
||||||
class="sm:h-full"
|
|
||||||
/>
|
|
||||||
<component
|
|
||||||
:is="dynamicComponent"
|
|
||||||
:visible="viewVisible"
|
|
||||||
:operate-type="operateType"
|
|
||||||
:business-id="businessId"
|
|
||||||
@submitted="getData"
|
|
||||||
/>
|
|
||||||
</NCard>
|
|
||||||
</div>
|
|
||||||
</TableSiderLayout>
|
|
||||||
</template>
|
|
@ -1,57 +0,0 @@
|
|||||||
<script setup lang="tsx">
|
|
||||||
import { useNaiveForm } from '@/hooks/common/form';
|
|
||||||
defineOptions({
|
|
||||||
name: 'MyDocumentSearch'
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Emits {
|
|
||||||
(e: 'reset'): void;
|
|
||||||
(e: 'search'): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
|
||||||
|
|
||||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
|
||||||
const model = defineModel<Api.Workflow.TaskSearchParams>('model', { required: true });
|
|
||||||
|
|
||||||
async function reset() {
|
|
||||||
await restoreValidation();
|
|
||||||
emit('reset');
|
|
||||||
}
|
|
||||||
async function search() {
|
|
||||||
await validate();
|
|
||||||
emit('search');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
|
||||||
<NCollapse>
|
|
||||||
<NCollapseItem :title="$t('common.search')">
|
|
||||||
<NForm ref="formRef" :model="model" label-placement="left" :label-width="100">
|
|
||||||
<NGrid responsive="screen" item-responsive>
|
|
||||||
<NFormItemGi span="24 s:12 m:12" label="流程定义编码" path="flowCode" class="pr-24px">
|
|
||||||
<NInput v-model:value="model.flowCode" placeholder="请输入流程定义编码" />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="24 s:12 m:12" class="pr-24px">
|
|
||||||
<NSpace class="w-full" justify="end">
|
|
||||||
<NButton @click="reset">
|
|
||||||
<template #icon>
|
|
||||||
<icon-ic-round-refresh class="text-icon" />
|
|
||||||
</template>
|
|
||||||
{{ $t('common.reset') }}
|
|
||||||
</NButton>
|
|
||||||
<NButton type="primary" ghost @click="search">
|
|
||||||
<template #icon>
|
|
||||||
<icon-ic-round-search class="text-icon" />
|
|
||||||
</template>
|
|
||||||
{{ $t('common.search') }}
|
|
||||||
</NButton>
|
|
||||||
</NSpace>
|
|
||||||
</NFormItemGi>
|
|
||||||
</NGrid>
|
|
||||||
</NForm>
|
|
||||||
</NCollapseItem>
|
|
||||||
</NCollapse>
|
|
||||||
</NCard>
|
|
||||||
</template>
|
|
@ -1,268 +0,0 @@
|
|||||||
<script setup lang="tsx">
|
|
||||||
import { computed, ref, shallowRef } from 'vue';
|
|
||||||
import { NButton, NEmpty, NInput, NTag } from 'naive-ui';
|
|
||||||
import { useBoolean, useLoading } from '@sa/hooks';
|
|
||||||
import { fetchGetCategoryTree } from '@/service/api/workflow/category';
|
|
||||||
import { fetchGetCopyTask } from '@/service/api/workflow/task';
|
|
||||||
import { useAppStore } from '@/store/modules/app';
|
|
||||||
import { useTable, useTableOperate } from '@/hooks/common/table';
|
|
||||||
import { useDict } from '@/hooks/business/dict';
|
|
||||||
import { loadDynamicComponent } from '@/utils/common';
|
|
||||||
import DictTag from '@/components/custom/dict-tag.vue';
|
|
||||||
import ButtonIcon from '@/components/custom/button-icon.vue';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
import TaskCopySearch from './modules/task-copy-search.vue';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'TaskCopyList'
|
|
||||||
});
|
|
||||||
|
|
||||||
useDict('wf_business_status');
|
|
||||||
useDict('wf_task_status');
|
|
||||||
const appStore = useAppStore();
|
|
||||||
const { bool: viewVisible, setTrue: showViewDrawer } = useBoolean();
|
|
||||||
const dynamicComponent = shallowRef();
|
|
||||||
|
|
||||||
const {
|
|
||||||
columns,
|
|
||||||
columnChecks,
|
|
||||||
data,
|
|
||||||
getData,
|
|
||||||
getDataByPage,
|
|
||||||
loading,
|
|
||||||
mobilePagination,
|
|
||||||
searchParams,
|
|
||||||
resetSearchParams
|
|
||||||
} = useTable({
|
|
||||||
apiFn: fetchGetCopyTask,
|
|
||||||
apiParams: {
|
|
||||||
pageNum: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
category: null,
|
|
||||||
flowName: null,
|
|
||||||
flowCode: null,
|
|
||||||
nodeName: null,
|
|
||||||
createByIds: null
|
|
||||||
},
|
|
||||||
columns: () => [
|
|
||||||
{
|
|
||||||
key: 'index',
|
|
||||||
title: $t('common.index'),
|
|
||||||
align: 'center',
|
|
||||||
width: 64
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '业务编码',
|
|
||||||
key: 'businessCode',
|
|
||||||
align: 'center',
|
|
||||||
width: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '业务名称',
|
|
||||||
key: 'businessTitle',
|
|
||||||
align: 'center',
|
|
||||||
width: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '流程定义名称',
|
|
||||||
key: 'flowName',
|
|
||||||
align: 'center',
|
|
||||||
width: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '流程定义编码',
|
|
||||||
key: 'flowCode',
|
|
||||||
align: 'center',
|
|
||||||
width: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '流程分类',
|
|
||||||
key: 'categoryName',
|
|
||||||
align: 'center',
|
|
||||||
width: 80,
|
|
||||||
render: row => {
|
|
||||||
return <NTag>{row.categoryName}</NTag>;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '版本号',
|
|
||||||
key: 'version',
|
|
||||||
align: 'center',
|
|
||||||
width: 100,
|
|
||||||
render: row => <NTag type="info">v{row.version}.0</NTag>
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '任务名称',
|
|
||||||
key: 'nodeName',
|
|
||||||
align: 'center',
|
|
||||||
width: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '流程状态',
|
|
||||||
key: 'flowStatus',
|
|
||||||
align: 'center',
|
|
||||||
width: 80,
|
|
||||||
render(row) {
|
|
||||||
return <DictTag value={row.flowStatus} dict-code="wf_business_status" />;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '申请人',
|
|
||||||
key: 'createByName',
|
|
||||||
align: 'center',
|
|
||||||
width: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'createTime',
|
|
||||||
title: '创建时间',
|
|
||||||
align: 'center',
|
|
||||||
width: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '操作',
|
|
||||||
key: 'operate',
|
|
||||||
align: 'center',
|
|
||||||
fixed: 'right',
|
|
||||||
width: 50,
|
|
||||||
render(row) {
|
|
||||||
return (
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
icon="material-symbols:visibility-outline"
|
|
||||||
tooltipContent="查看"
|
|
||||||
onClick={() => handleView(row)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
const { checkedRowKeys } = useTableOperate(data, getData);
|
|
||||||
|
|
||||||
const { loading: treeLoading, startLoading: startTreeLoading, endLoading: endTreeLoading } = useLoading();
|
|
||||||
const categoryPattern = ref<string>();
|
|
||||||
const categoryData = ref<Api.Common.CommonTreeRecord>([]);
|
|
||||||
const selectedKeys = ref<string[]>([]);
|
|
||||||
const expandedKeys = ref<CommonType.IdType[]>(['100']);
|
|
||||||
|
|
||||||
const selectable = computed(() => !loading.value);
|
|
||||||
|
|
||||||
async function getTreeData() {
|
|
||||||
startTreeLoading();
|
|
||||||
const { data: tree, error } = await fetchGetCategoryTree();
|
|
||||||
if (!error) {
|
|
||||||
categoryData.value = tree;
|
|
||||||
}
|
|
||||||
endTreeLoading();
|
|
||||||
}
|
|
||||||
|
|
||||||
getTreeData();
|
|
||||||
|
|
||||||
function handleClickTree(keys: string[]) {
|
|
||||||
searchParams.category = keys.length ? keys[0] : null;
|
|
||||||
checkedRowKeys.value = [];
|
|
||||||
getDataByPage();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleResetTreeData() {
|
|
||||||
categoryPattern.value = undefined;
|
|
||||||
getTreeData();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleResetSearch() {
|
|
||||||
resetSearchParams();
|
|
||||||
selectedKeys.value = [];
|
|
||||||
}
|
|
||||||
const modules = import.meta.glob('@/components/workflow/**/*.vue');
|
|
||||||
const businessId = ref<CommonType.IdType>();
|
|
||||||
const taskId = ref<CommonType.IdType>();
|
|
||||||
|
|
||||||
async function handleView(row: Api.Workflow.Task) {
|
|
||||||
dynamicComponent.value = null;
|
|
||||||
viewVisible.value = false;
|
|
||||||
businessId.value = row.businessId;
|
|
||||||
taskId.value = row.id;
|
|
||||||
const formPath = row.formPath;
|
|
||||||
if (!formPath) return;
|
|
||||||
dynamicComponent.value = await loadDynamicComponent(modules, formPath);
|
|
||||||
setTimeout(() => {
|
|
||||||
showViewDrawer();
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<TableSiderLayout sider-title="流程分类列表">
|
|
||||||
<template #header-extra>
|
|
||||||
<NButton size="small" text class="h-18px" @click.stop="() => handleResetTreeData()">
|
|
||||||
<template #icon>
|
|
||||||
<SvgIcon icon="ic:round-refresh" />
|
|
||||||
</template>
|
|
||||||
</NButton>
|
|
||||||
</template>
|
|
||||||
<template #sider>
|
|
||||||
<NInput v-model:value="categoryPattern" clearable :placeholder="$t('common.keywordSearch')" />
|
|
||||||
<NSpin class="category-tree" :show="treeLoading">
|
|
||||||
<NTree
|
|
||||||
v-model:selected-keys="selectedKeys"
|
|
||||||
v-model:expanded-keys="expandedKeys"
|
|
||||||
block-node
|
|
||||||
show-line
|
|
||||||
:data="categoryData as []"
|
|
||||||
:show-irrelevant-nodes="false"
|
|
||||||
:pattern="categoryPattern"
|
|
||||||
class="infinite-scroll h-full min-h-200px py-3"
|
|
||||||
key-field="id"
|
|
||||||
label-field="label"
|
|
||||||
virtual-scroll
|
|
||||||
:selectable="selectable"
|
|
||||||
@update:selected-keys="handleClickTree"
|
|
||||||
>
|
|
||||||
<template #empty>
|
|
||||||
<NEmpty description="暂无分类信息" class="h-full min-h-200px justify-center" />
|
|
||||||
</template>
|
|
||||||
</NTree>
|
|
||||||
</NSpin>
|
|
||||||
</template>
|
|
||||||
<div class="h-full flex-col-stretch gap-12px overflow-hidden lt-sm:overflow-auto">
|
|
||||||
<TaskCopySearch v-model:model="searchParams" @reset="handleResetSearch" @search="getDataByPage" />
|
|
||||||
<NCard title="我的抄送" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
|
|
||||||
<template #header-extra>
|
|
||||||
<TableHeaderOperation
|
|
||||||
v-model:columns="columnChecks"
|
|
||||||
:disabled-delete="checkedRowKeys.length === 0"
|
|
||||||
:loading="loading"
|
|
||||||
:show-add="false"
|
|
||||||
:show-delete="false"
|
|
||||||
:show-export="false"
|
|
||||||
@refresh="getData"
|
|
||||||
></TableHeaderOperation>
|
|
||||||
</template>
|
|
||||||
<NDataTable
|
|
||||||
v-model:checked-row-keys="checkedRowKeys"
|
|
||||||
:columns="columns"
|
|
||||||
:data="data"
|
|
||||||
size="small"
|
|
||||||
:flex-height="!appStore.isMobile"
|
|
||||||
:scroll-x="1405"
|
|
||||||
:loading="loading"
|
|
||||||
remote
|
|
||||||
:row-key="row => row.id"
|
|
||||||
:pagination="mobilePagination"
|
|
||||||
class="sm:h-full"
|
|
||||||
/>
|
|
||||||
<component
|
|
||||||
:is="dynamicComponent"
|
|
||||||
v-if="dynamicComponent"
|
|
||||||
:visible="viewVisible"
|
|
||||||
operate-type="detail"
|
|
||||||
:business-id="businessId"
|
|
||||||
:task-id="taskId"
|
|
||||||
@submitted="getData"
|
|
||||||
/>
|
|
||||||
</NCard>
|
|
||||||
</div>
|
|
||||||
</TableSiderLayout>
|
|
||||||
</template>
|
|
@ -1,63 +0,0 @@
|
|||||||
<script setup lang="tsx">
|
|
||||||
import { useNaiveForm } from '@/hooks/common/form';
|
|
||||||
defineOptions({
|
|
||||||
name: 'MyDocumentSearch'
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Emits {
|
|
||||||
(e: 'reset'): void;
|
|
||||||
(e: 'search'): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
|
||||||
|
|
||||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
|
||||||
const model = defineModel<Api.Workflow.TaskSearchParams>('model', { required: true });
|
|
||||||
|
|
||||||
async function reset() {
|
|
||||||
await restoreValidation();
|
|
||||||
emit('reset');
|
|
||||||
}
|
|
||||||
async function search() {
|
|
||||||
await validate();
|
|
||||||
emit('search');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
|
||||||
<NCollapse>
|
|
||||||
<NCollapseItem :title="$t('common.search')">
|
|
||||||
<NForm ref="formRef" :model="model" label-placement="left" :label-width="100">
|
|
||||||
<NGrid responsive="screen" item-responsive>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" label="任务名称" path="nodeName" class="pr-24px">
|
|
||||||
<NInput v-model:value="model.nodeName" placeholder="请输入任务名称" />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" label="流程定义名称" path="bucketName" class="pr-24px">
|
|
||||||
<NInput v-model:value="model.flowName" placeholder="请输入流程定义名称" />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" label="流程定义编码" path="flowCode" class="pr-24px">
|
|
||||||
<NInput v-model:value="model.flowCode" placeholder="流程定义编码" />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" class="pr-24px">
|
|
||||||
<NSpace class="w-full" justify="end">
|
|
||||||
<NButton @click="reset">
|
|
||||||
<template #icon>
|
|
||||||
<icon-ic-round-refresh class="text-icon" />
|
|
||||||
</template>
|
|
||||||
{{ $t('common.reset') }}
|
|
||||||
</NButton>
|
|
||||||
<NButton type="primary" ghost @click="search">
|
|
||||||
<template #icon>
|
|
||||||
<icon-ic-round-search class="text-icon" />
|
|
||||||
</template>
|
|
||||||
{{ $t('common.search') }}
|
|
||||||
</NButton>
|
|
||||||
</NSpace>
|
|
||||||
</NFormItemGi>
|
|
||||||
</NGrid>
|
|
||||||
</NForm>
|
|
||||||
</NCollapseItem>
|
|
||||||
</NCollapse>
|
|
||||||
</NCard>
|
|
||||||
</template>
|
|
@ -1,280 +0,0 @@
|
|||||||
<script setup lang="tsx">
|
|
||||||
import { computed, ref, shallowRef } from 'vue';
|
|
||||||
import { NButton, NEmpty, NInput, NTag } from 'naive-ui';
|
|
||||||
import { useBoolean, useLoading } from '@sa/hooks';
|
|
||||||
import { fetchGetCategoryTree } from '@/service/api/workflow/category';
|
|
||||||
import { fetchGetFinishedTask } from '@/service/api/workflow/task';
|
|
||||||
import { useAppStore } from '@/store/modules/app';
|
|
||||||
import { useTable, useTableOperate } from '@/hooks/common/table';
|
|
||||||
import { useDict } from '@/hooks/business/dict';
|
|
||||||
import { loadDynamicComponent } from '@/utils/common';
|
|
||||||
import DictTag from '@/components/custom/dict-tag.vue';
|
|
||||||
import ButtonIcon from '@/components/custom/button-icon.vue';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
import TaskFinishSearch from './modules/task-finish-search.vue';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'TaskFinishList'
|
|
||||||
});
|
|
||||||
|
|
||||||
useDict('wf_business_status');
|
|
||||||
useDict('wf_task_status');
|
|
||||||
const appStore = useAppStore();
|
|
||||||
const { bool: viewVisible, setTrue: showViewDrawer } = useBoolean();
|
|
||||||
const dynamicComponent = shallowRef();
|
|
||||||
|
|
||||||
const {
|
|
||||||
columns,
|
|
||||||
columnChecks,
|
|
||||||
data,
|
|
||||||
getData,
|
|
||||||
getDataByPage,
|
|
||||||
loading,
|
|
||||||
mobilePagination,
|
|
||||||
searchParams,
|
|
||||||
resetSearchParams
|
|
||||||
} = useTable({
|
|
||||||
apiFn: fetchGetFinishedTask,
|
|
||||||
apiParams: {
|
|
||||||
pageNum: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
category: null,
|
|
||||||
flowName: null,
|
|
||||||
flowCode: null,
|
|
||||||
nodeName: null,
|
|
||||||
createByIds: null
|
|
||||||
},
|
|
||||||
columns: () => [
|
|
||||||
{
|
|
||||||
key: 'index',
|
|
||||||
title: $t('common.index'),
|
|
||||||
align: 'center',
|
|
||||||
width: 64
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '业务编码',
|
|
||||||
key: 'businessCode',
|
|
||||||
align: 'center',
|
|
||||||
width: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '业务名称',
|
|
||||||
key: 'businessTitle',
|
|
||||||
align: 'center',
|
|
||||||
width: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '流程定义名称',
|
|
||||||
key: 'flowName',
|
|
||||||
align: 'center',
|
|
||||||
width: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '流程定义编码',
|
|
||||||
key: 'flowCode',
|
|
||||||
align: 'center',
|
|
||||||
width: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '流程分类',
|
|
||||||
key: 'categoryName',
|
|
||||||
align: 'center',
|
|
||||||
width: 80,
|
|
||||||
render: row => {
|
|
||||||
return <NTag>{row.categoryName}</NTag>;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '版本号',
|
|
||||||
key: 'version',
|
|
||||||
align: 'center',
|
|
||||||
width: 100,
|
|
||||||
render: row => <NTag type="info">v{row.version}.0</NTag>
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '任务名称',
|
|
||||||
key: 'nodeName',
|
|
||||||
align: 'center',
|
|
||||||
width: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '流程状态',
|
|
||||||
key: 'flowStatus',
|
|
||||||
align: 'center',
|
|
||||||
width: 80,
|
|
||||||
render(row) {
|
|
||||||
return <DictTag value={row.flowStatus} dict-code="wf_business_status" />;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'flowTaskStatus',
|
|
||||||
title: '任务状态',
|
|
||||||
align: 'center',
|
|
||||||
width: 80,
|
|
||||||
render: row => <DictTag size="small" value={row.flowTaskStatus} dict-code="wf_task_status" />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '申请人',
|
|
||||||
key: 'createByName',
|
|
||||||
align: 'center',
|
|
||||||
width: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '办理人',
|
|
||||||
key: 'approveName',
|
|
||||||
align: 'center',
|
|
||||||
width: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'createTime',
|
|
||||||
title: '创建时间',
|
|
||||||
align: 'center',
|
|
||||||
width: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '操作',
|
|
||||||
key: 'operate',
|
|
||||||
align: 'center',
|
|
||||||
fixed: 'right',
|
|
||||||
width: 50,
|
|
||||||
render(row) {
|
|
||||||
return (
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
icon="material-symbols:visibility-outline"
|
|
||||||
tooltipContent="查看"
|
|
||||||
onClick={() => handleView(row)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
const { checkedRowKeys } = useTableOperate(data, getData);
|
|
||||||
|
|
||||||
const { loading: treeLoading, startLoading: startTreeLoading, endLoading: endTreeLoading } = useLoading();
|
|
||||||
const categoryPattern = ref<string>();
|
|
||||||
const categoryData = ref<Api.Common.CommonTreeRecord>([]);
|
|
||||||
const selectedKeys = ref<string[]>([]);
|
|
||||||
const expandedKeys = ref<CommonType.IdType[]>(['100']);
|
|
||||||
|
|
||||||
const selectable = computed(() => !loading.value);
|
|
||||||
|
|
||||||
async function getTreeData() {
|
|
||||||
startTreeLoading();
|
|
||||||
const { data: tree, error } = await fetchGetCategoryTree();
|
|
||||||
if (!error) {
|
|
||||||
categoryData.value = tree;
|
|
||||||
}
|
|
||||||
endTreeLoading();
|
|
||||||
}
|
|
||||||
|
|
||||||
getTreeData();
|
|
||||||
|
|
||||||
function handleClickTree(keys: string[]) {
|
|
||||||
searchParams.category = keys.length ? keys[0] : null;
|
|
||||||
checkedRowKeys.value = [];
|
|
||||||
getDataByPage();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleResetTreeData() {
|
|
||||||
categoryPattern.value = undefined;
|
|
||||||
getTreeData();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleResetSearch() {
|
|
||||||
resetSearchParams();
|
|
||||||
selectedKeys.value = [];
|
|
||||||
}
|
|
||||||
const modules = import.meta.glob('@/components/workflow/**/*.vue');
|
|
||||||
const businessId = ref<CommonType.IdType>();
|
|
||||||
const taskId = ref<CommonType.IdType>();
|
|
||||||
|
|
||||||
async function handleView(row: Api.Workflow.HisTask) {
|
|
||||||
dynamicComponent.value = null;
|
|
||||||
viewVisible.value = false;
|
|
||||||
businessId.value = row.businessId;
|
|
||||||
taskId.value = row.id;
|
|
||||||
const formPath = row.formPath;
|
|
||||||
if (!formPath) return;
|
|
||||||
dynamicComponent.value = await loadDynamicComponent(modules, formPath);
|
|
||||||
setTimeout(() => {
|
|
||||||
showViewDrawer();
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<TableSiderLayout sider-title="流程分类列表">
|
|
||||||
<template #header-extra>
|
|
||||||
<NButton size="small" text class="h-18px" @click.stop="() => handleResetTreeData()">
|
|
||||||
<template #icon>
|
|
||||||
<SvgIcon icon="ic:round-refresh" />
|
|
||||||
</template>
|
|
||||||
</NButton>
|
|
||||||
</template>
|
|
||||||
<template #sider>
|
|
||||||
<NInput v-model:value="categoryPattern" clearable :placeholder="$t('common.keywordSearch')" />
|
|
||||||
<NSpin class="category-tree" :show="treeLoading">
|
|
||||||
<NTree
|
|
||||||
v-model:selected-keys="selectedKeys"
|
|
||||||
v-model:expanded-keys="expandedKeys"
|
|
||||||
block-node
|
|
||||||
show-line
|
|
||||||
:data="categoryData as []"
|
|
||||||
:show-irrelevant-nodes="false"
|
|
||||||
:pattern="categoryPattern"
|
|
||||||
class="infinite-scroll h-full min-h-200px py-3"
|
|
||||||
key-field="id"
|
|
||||||
label-field="label"
|
|
||||||
virtual-scroll
|
|
||||||
:selectable="selectable"
|
|
||||||
@update:selected-keys="handleClickTree"
|
|
||||||
>
|
|
||||||
<template #empty>
|
|
||||||
<NEmpty description="暂无分类信息" class="h-full min-h-200px justify-center" />
|
|
||||||
</template>
|
|
||||||
</NTree>
|
|
||||||
</NSpin>
|
|
||||||
</template>
|
|
||||||
<div class="h-full flex-col-stretch gap-12px overflow-hidden lt-sm:overflow-auto">
|
|
||||||
<TaskFinishSearch v-model:model="searchParams" @reset="handleResetSearch" @search="getDataByPage" />
|
|
||||||
<NCard title="我的已办" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
|
|
||||||
<template #header-extra>
|
|
||||||
<TableHeaderOperation
|
|
||||||
v-model:columns="columnChecks"
|
|
||||||
:disabled-delete="checkedRowKeys.length === 0"
|
|
||||||
:loading="loading"
|
|
||||||
:show-add="false"
|
|
||||||
:show-delete="false"
|
|
||||||
:show-export="false"
|
|
||||||
@refresh="getData"
|
|
||||||
></TableHeaderOperation>
|
|
||||||
</template>
|
|
||||||
<NDataTable
|
|
||||||
v-model:checked-row-keys="checkedRowKeys"
|
|
||||||
:columns="columns"
|
|
||||||
:data="data"
|
|
||||||
size="small"
|
|
||||||
:flex-height="!appStore.isMobile"
|
|
||||||
:scroll-x="1405"
|
|
||||||
:loading="loading"
|
|
||||||
remote
|
|
||||||
:row-key="row => row.id"
|
|
||||||
:pagination="mobilePagination"
|
|
||||||
class="sm:h-full"
|
|
||||||
/>
|
|
||||||
<component
|
|
||||||
:is="dynamicComponent"
|
|
||||||
:visible="viewVisible"
|
|
||||||
operate-type="detail"
|
|
||||||
:business-id="businessId"
|
|
||||||
:task-id="taskId"
|
|
||||||
@submitted="getData"
|
|
||||||
/>
|
|
||||||
</NCard>
|
|
||||||
</div>
|
|
||||||
</TableSiderLayout>
|
|
||||||
</template>
|
|
@ -1,63 +0,0 @@
|
|||||||
<script setup lang="tsx">
|
|
||||||
import { useNaiveForm } from '@/hooks/common/form';
|
|
||||||
defineOptions({
|
|
||||||
name: 'MyDocumentSearch'
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Emits {
|
|
||||||
(e: 'reset'): void;
|
|
||||||
(e: 'search'): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
|
||||||
|
|
||||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
|
||||||
const model = defineModel<Api.Workflow.TaskSearchParams>('model', { required: true });
|
|
||||||
|
|
||||||
async function reset() {
|
|
||||||
await restoreValidation();
|
|
||||||
emit('reset');
|
|
||||||
}
|
|
||||||
async function search() {
|
|
||||||
await validate();
|
|
||||||
emit('search');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
|
||||||
<NCollapse>
|
|
||||||
<NCollapseItem :title="$t('common.search')">
|
|
||||||
<NForm ref="formRef" :model="model" label-placement="left" :label-width="100">
|
|
||||||
<NGrid responsive="screen" item-responsive>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" label="任务名称" path="nodeName" class="pr-24px">
|
|
||||||
<NInput v-model:value="model.nodeName" placeholder="请输入任务名称" />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" label="流程定义名称" path="bucketName" class="pr-24px">
|
|
||||||
<NInput v-model:value="model.flowName" placeholder="请输入流程定义名称" />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" label="流程定义编码" path="flowCode" class="pr-24px">
|
|
||||||
<NInput v-model:value="model.flowCode" placeholder="流程定义编码" />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" class="pr-24px">
|
|
||||||
<NSpace class="w-full" justify="end">
|
|
||||||
<NButton @click="reset">
|
|
||||||
<template #icon>
|
|
||||||
<icon-ic-round-refresh class="text-icon" />
|
|
||||||
</template>
|
|
||||||
{{ $t('common.reset') }}
|
|
||||||
</NButton>
|
|
||||||
<NButton type="primary" ghost @click="search">
|
|
||||||
<template #icon>
|
|
||||||
<icon-ic-round-search class="text-icon" />
|
|
||||||
</template>
|
|
||||||
{{ $t('common.search') }}
|
|
||||||
</NButton>
|
|
||||||
</NSpace>
|
|
||||||
</NFormItemGi>
|
|
||||||
</NGrid>
|
|
||||||
</NForm>
|
|
||||||
</NCollapseItem>
|
|
||||||
</NCollapse>
|
|
||||||
</NCard>
|
|
||||||
</template>
|
|
@ -1,261 +0,0 @@
|
|||||||
<script setup lang="tsx">
|
|
||||||
import { computed, ref, shallowRef } from 'vue';
|
|
||||||
import { NButton, NEmpty, NInput, NTag } from 'naive-ui';
|
|
||||||
import { useBoolean, useLoading } from '@sa/hooks';
|
|
||||||
import { fetchGetCategoryTree } from '@/service/api/workflow/category';
|
|
||||||
import { fetchGetTaskWaitList } from '@/service/api/workflow/task';
|
|
||||||
import { useAppStore } from '@/store/modules/app';
|
|
||||||
import { useTable, useTableOperate } from '@/hooks/common/table';
|
|
||||||
import { useDict } from '@/hooks/business/dict';
|
|
||||||
import { loadDynamicComponent } from '@/utils/common';
|
|
||||||
import TagGroup from '@/components/custom/tag-group.vue';
|
|
||||||
import DictTag from '@/components/custom/dict-tag.vue';
|
|
||||||
import ButtonIcon from '@/components/custom/button-icon.vue';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
import TaskWaitingSearch from './modules/task-waiting-search.vue';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'TaskWaitingList'
|
|
||||||
});
|
|
||||||
|
|
||||||
useDict('wf_business_status');
|
|
||||||
const appStore = useAppStore();
|
|
||||||
const { bool: viewVisible, setTrue: showViewDrawer } = useBoolean();
|
|
||||||
const dynamicComponent = shallowRef();
|
|
||||||
|
|
||||||
const {
|
|
||||||
columns,
|
|
||||||
columnChecks,
|
|
||||||
data,
|
|
||||||
getData,
|
|
||||||
getDataByPage,
|
|
||||||
loading,
|
|
||||||
mobilePagination,
|
|
||||||
searchParams,
|
|
||||||
resetSearchParams
|
|
||||||
} = useTable({
|
|
||||||
apiFn: fetchGetTaskWaitList,
|
|
||||||
apiParams: {
|
|
||||||
pageNum: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
category: null,
|
|
||||||
flowName: null,
|
|
||||||
flowCode: null,
|
|
||||||
nodeName: null,
|
|
||||||
createByIds: null
|
|
||||||
},
|
|
||||||
columns: () => [
|
|
||||||
{
|
|
||||||
key: 'index',
|
|
||||||
title: $t('common.index'),
|
|
||||||
align: 'center',
|
|
||||||
width: 64
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '业务编码',
|
|
||||||
key: 'businessCode',
|
|
||||||
align: 'center',
|
|
||||||
width: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '业务名称',
|
|
||||||
key: 'businessTitle',
|
|
||||||
align: 'center',
|
|
||||||
width: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '流程定义名称',
|
|
||||||
key: 'flowName',
|
|
||||||
align: 'center',
|
|
||||||
width: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '流程定义编码',
|
|
||||||
key: 'flowCode',
|
|
||||||
align: 'center',
|
|
||||||
width: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '流程分类',
|
|
||||||
key: 'categoryName',
|
|
||||||
align: 'center',
|
|
||||||
width: 80,
|
|
||||||
render: row => {
|
|
||||||
return <NTag>{row.categoryName}</NTag>;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '任务名称',
|
|
||||||
key: 'nodeName',
|
|
||||||
align: 'center',
|
|
||||||
width: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '申请人',
|
|
||||||
key: 'createByName',
|
|
||||||
align: 'center',
|
|
||||||
width: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '办理人',
|
|
||||||
key: 'assigneeNames',
|
|
||||||
align: 'center',
|
|
||||||
width: 100,
|
|
||||||
render: row => <TagGroup value={row.assigneeNames} />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '流程状态',
|
|
||||||
key: 'flowStatus',
|
|
||||||
align: 'center',
|
|
||||||
width: 80,
|
|
||||||
render(row) {
|
|
||||||
return <DictTag value={row.flowStatus} dict-code="wf_business_status" />;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '操作',
|
|
||||||
key: 'operate',
|
|
||||||
align: 'center',
|
|
||||||
fixed: 'right',
|
|
||||||
width: 50,
|
|
||||||
render(row) {
|
|
||||||
return (
|
|
||||||
<ButtonIcon
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
icon="ph:check-circle-bold"
|
|
||||||
tooltipContent="办理"
|
|
||||||
onClick={() => handleApproval(row)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
const { checkedRowKeys } = useTableOperate(data, getData);
|
|
||||||
|
|
||||||
const { loading: treeLoading, startLoading: startTreeLoading, endLoading: endTreeLoading } = useLoading();
|
|
||||||
const categoryPattern = ref<string>();
|
|
||||||
const categoryData = ref<Api.Common.CommonTreeRecord>([]);
|
|
||||||
const selectedKeys = ref<string[]>([]);
|
|
||||||
const expandedKeys = ref<CommonType.IdType[]>(['100']);
|
|
||||||
|
|
||||||
const selectable = computed(() => !loading.value);
|
|
||||||
|
|
||||||
async function getTreeData() {
|
|
||||||
startTreeLoading();
|
|
||||||
const { data: tree, error } = await fetchGetCategoryTree();
|
|
||||||
if (!error) {
|
|
||||||
categoryData.value = tree;
|
|
||||||
}
|
|
||||||
endTreeLoading();
|
|
||||||
}
|
|
||||||
|
|
||||||
getTreeData();
|
|
||||||
|
|
||||||
function handleClickTree(keys: string[]) {
|
|
||||||
searchParams.category = keys.length ? keys[0] : null;
|
|
||||||
checkedRowKeys.value = [];
|
|
||||||
getDataByPage();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleResetTreeData() {
|
|
||||||
categoryPattern.value = undefined;
|
|
||||||
getTreeData();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleResetSearch() {
|
|
||||||
resetSearchParams();
|
|
||||||
selectedKeys.value = [];
|
|
||||||
}
|
|
||||||
const modules = import.meta.glob('@/components/workflow/**/*.vue');
|
|
||||||
const businessId = ref<CommonType.IdType>();
|
|
||||||
const taskId = ref<CommonType.IdType>();
|
|
||||||
|
|
||||||
async function handleApproval(row: Api.Workflow.Task) {
|
|
||||||
dynamicComponent.value = null;
|
|
||||||
viewVisible.value = false;
|
|
||||||
businessId.value = row.businessId;
|
|
||||||
taskId.value = row.id;
|
|
||||||
const formPath = row.formPath;
|
|
||||||
if (!formPath) return;
|
|
||||||
dynamicComponent.value = await loadDynamicComponent(modules, formPath);
|
|
||||||
setTimeout(() => {
|
|
||||||
showViewDrawer();
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<TableSiderLayout sider-title="流程分类列表">
|
|
||||||
<template #header-extra>
|
|
||||||
<NButton size="small" text class="h-18px" @click.stop="() => handleResetTreeData()">
|
|
||||||
<template #icon>
|
|
||||||
<SvgIcon icon="ic:round-refresh" />
|
|
||||||
</template>
|
|
||||||
</NButton>
|
|
||||||
</template>
|
|
||||||
<template #sider>
|
|
||||||
<NInput v-model:value="categoryPattern" clearable :placeholder="$t('common.keywordSearch')" />
|
|
||||||
<NSpin class="category-tree" :show="treeLoading">
|
|
||||||
<NTree
|
|
||||||
v-model:selected-keys="selectedKeys"
|
|
||||||
v-model:expanded-keys="expandedKeys"
|
|
||||||
block-node
|
|
||||||
show-line
|
|
||||||
:data="categoryData as []"
|
|
||||||
:show-irrelevant-nodes="false"
|
|
||||||
:pattern="categoryPattern"
|
|
||||||
class="infinite-scroll h-full min-h-200px py-3"
|
|
||||||
key-field="id"
|
|
||||||
label-field="label"
|
|
||||||
virtual-scroll
|
|
||||||
:selectable="selectable"
|
|
||||||
@update:selected-keys="handleClickTree"
|
|
||||||
>
|
|
||||||
<template #empty>
|
|
||||||
<NEmpty description="暂无分类信息" class="h-full min-h-200px justify-center" />
|
|
||||||
</template>
|
|
||||||
</NTree>
|
|
||||||
</NSpin>
|
|
||||||
</template>
|
|
||||||
<div class="h-full flex-col-stretch gap-12px overflow-hidden lt-sm:overflow-auto">
|
|
||||||
<TaskWaitingSearch v-model:model="searchParams" @reset="handleResetSearch" @search="getDataByPage" />
|
|
||||||
<NCard title="我的待办" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
|
|
||||||
<template #header-extra>
|
|
||||||
<TableHeaderOperation
|
|
||||||
v-model:columns="columnChecks"
|
|
||||||
:disabled-delete="checkedRowKeys.length === 0"
|
|
||||||
:loading="loading"
|
|
||||||
:show-add="false"
|
|
||||||
:show-delete="false"
|
|
||||||
:show-export="false"
|
|
||||||
@refresh="getData"
|
|
||||||
></TableHeaderOperation>
|
|
||||||
</template>
|
|
||||||
<NDataTable
|
|
||||||
v-model:checked-row-keys="checkedRowKeys"
|
|
||||||
:columns="columns"
|
|
||||||
:data="data"
|
|
||||||
size="small"
|
|
||||||
:flex-height="!appStore.isMobile"
|
|
||||||
:scroll-x="1405"
|
|
||||||
:loading="loading"
|
|
||||||
remote
|
|
||||||
:row-key="row => row.id"
|
|
||||||
:pagination="mobilePagination"
|
|
||||||
class="sm:h-full"
|
|
||||||
/>
|
|
||||||
<component
|
|
||||||
:is="dynamicComponent"
|
|
||||||
:visible="viewVisible"
|
|
||||||
operate-type="approval"
|
|
||||||
:business-id="businessId"
|
|
||||||
:task-id="taskId"
|
|
||||||
@submitted="getData"
|
|
||||||
/>
|
|
||||||
</NCard>
|
|
||||||
</div>
|
|
||||||
</TableSiderLayout>
|
|
||||||
</template>
|
|
@ -1,63 +0,0 @@
|
|||||||
<script setup lang="tsx">
|
|
||||||
import { useNaiveForm } from '@/hooks/common/form';
|
|
||||||
defineOptions({
|
|
||||||
name: 'TaskWaitingSearch'
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Emits {
|
|
||||||
(e: 'reset'): void;
|
|
||||||
(e: 'search'): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
|
||||||
|
|
||||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
|
||||||
const model = defineModel<Api.Workflow.TaskSearchParams>('model', { required: true });
|
|
||||||
|
|
||||||
async function reset() {
|
|
||||||
await restoreValidation();
|
|
||||||
emit('reset');
|
|
||||||
}
|
|
||||||
async function search() {
|
|
||||||
await validate();
|
|
||||||
emit('search');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
|
||||||
<NCollapse>
|
|
||||||
<NCollapseItem :title="$t('common.search')">
|
|
||||||
<NForm ref="formRef" :model="model" label-placement="left" :label-width="100">
|
|
||||||
<NGrid responsive="screen" item-responsive>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" label="任务名称" path="nodeName" class="pr-24px">
|
|
||||||
<NInput v-model:value="model.nodeName" placeholder="请输入任务名称" />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" label="流程定义名称" path="bucketName" class="pr-24px">
|
|
||||||
<NInput v-model:value="model.flowName" placeholder="请输入流程定义名称" />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" label="流程定义编码" path="flowCode" class="pr-24px">
|
|
||||||
<NInput v-model:value="model.flowCode" placeholder="流程定义编码" />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" class="pr-24px">
|
|
||||||
<NSpace class="w-full" justify="end">
|
|
||||||
<NButton @click="reset">
|
|
||||||
<template #icon>
|
|
||||||
<icon-ic-round-refresh class="text-icon" />
|
|
||||||
</template>
|
|
||||||
{{ $t('common.reset') }}
|
|
||||||
</NButton>
|
|
||||||
<NButton type="primary" ghost @click="search">
|
|
||||||
<template #icon>
|
|
||||||
<icon-ic-round-search class="text-icon" />
|
|
||||||
</template>
|
|
||||||
{{ $t('common.search') }}
|
|
||||||
</NButton>
|
|
||||||
</NSpace>
|
|
||||||
</NFormItemGi>
|
|
||||||
</NGrid>
|
|
||||||
</NForm>
|
|
||||||
</NCollapseItem>
|
|
||||||
</NCollapse>
|
|
||||||
</NCard>
|
|
||||||
</template>
|
|
Reference in New Issue
Block a user