584 Commits
Author SHA1 Message Date
YunaiV 1cafde3877 V2.4.1 发布~ 2025-02-09 12:43:11 +08:00
YunaiV 10a698b9ad 【代码评审】全局:TableSelectForm 可能移除,暂时没用到 2025-02-09 08:27:12 +08:00
芋道源码 bffa42aec6 !687 fix: 修复定时任务模块,字段名称错误
Merge pull request !687 from 李恒/master
2025-02-08 23:46:41 +00:00
芋道源码 a39698d6ab !689 fix:修复重置密码接口错误导致管理员忘记密码功能异常
Merge pull request !689 from Baron.Bai/fix-admin-reset-password
2025-02-08 23:45:31 +00:00
YunaiV c6f70cce00 Merge branch 'feature/bpm' of https://gitee.com/yudaocode/yudao-ui-admin-vue3
# Conflicts:
#	src/views/system/menu/index.vue
2025-02-09 07:39:48 +08:00
芋道源码 f4a83c091d Merge pull request #125 from DevDengChao/feat/wechat-pay-channel-form-shortcut
feat: add shortcut to weichat pay
2025-02-09 07:38:12 +08:00
芋道源码 b0d4e39e68 Merge pull request #129 from zws-code/feature/bpm_fix_250206
Feature/bpm fix 250206
2025-02-09 07:02:02 +08:00
芋道源码 d40f1c3150 !686 fix: 代码评审修复
Merge pull request !686 from Lesan/feature/bpm-new
2025-02-08 22:56:32 +00:00
YunaiV 60af2c6e7e 【代码评审】BPM:触发器 - 表单修改 2025-02-09 06:56:04 +08:00
baron.bai e0880db65c fix:修复重置密码接口错误导致管理员忘记密码功能异常 2025-02-08 21:04:10 +08:00
jason e32fc81df6 【功能新增】新增修改表单数据触发器 2025-02-07 22:50:53 +08:00
zws 59842b09c9 fix: 解决 设计器审批节点设置历史数据无监听器报错问题 2025-02-06 09:09:45 +08:00
zws 9c2619851a feat: 设计器审批节点设置 开启下拉过滤方便选择 2025-02-06 09:09:08 +08:00
liheng 25f5b104ce fix: 修复定时任务模块,字段名称错误 2025-02-01 16:42:08 +08:00
LesanOuO 5d2605fcae fix: 代码评审修复 2025-01-27 09:34:15 +08:00
jason 5e801a8e63 fix: 【功能修复】自定义标题。流程发起人等设置问题 2025-01-26 17:35:02 +08:00
jason 62ad543163 Merge branch 'feature/bpm' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm 2025-01-26 14:34:47 +08:00
jason 503a35f44e 【缺陷修复】延迟器节点名称修复问题,延迟器节点高亮问题 2025-01-26 14:34:23 +08:00
YunaiV bd48210d7f 【代码评审】Bpm:数据报表 2025-01-26 14:13:04 +08:00
YunaiV 79a53412d6 【代码评审】Bpm:数据报表 2025-01-26 13:35:07 +08:00
jason f35f62d2f8 Merge branch 'feature/bpm' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm 2025-01-26 13:14:36 +08:00
jason 475385bd43 【功能新增】Http 请求触发器,增加返回值设置 2025-01-26 13:14:16 +08:00
芋道源码 cea84173da !685 feat: BPM-数据报表
Merge pull request !685 from Lesan/feature/bpm-报表
2025-01-26 04:59:47 +00:00
LesanOuO 00dfe00d46 feat: BPM-数据报表 2025-01-26 11:07:16 +08:00
YunaiV 5f5ae354d2 【功能修复】system:菜单管理列表,虚拟 table 的告警问题 2025-01-25 18:03:41 +08:00
YunaiV 410e257a47 【功能优化】Bpm:流程分类维护时,增加 description 2025-01-25 17:57:09 +08:00
YunaiV e726c1dcba 【代码评审】Bpm:数据报表 2025-01-25 17:16:48 +08:00
芋道源码 107b1d3778 !684 feat: BPM-报表
Merge pull request !684 from Lesan/feature/bpm-报表
2025-01-25 09:12:51 +00:00
芋道源码 473d681c11 !683 代码审查修改
Merge pull request !683 from Lesan/feature/bpm-代码审查
2025-01-25 09:02:00 +00:00
LesanOuO 52c393a04b feat: BPM-报表 2025-01-25 13:29:00 +08:00
YunaiV e9a4efbf6a Merge branch 'feature/bpm' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 2025-01-25 10:56:42 +08:00
LesanOuO 092a8acde4 feat: 抄送添加摘要 2025-01-25 10:41:43 +08:00
LesanOuO a88c86d44c feat: 摘要设置代码评审修改 2025-01-25 10:26:56 +08:00
LesanOuO d2954776a5 feat: 更改HttpRequestParam相关命名 2025-01-25 09:42:19 +08:00
YunaiV 7db5ac81ae 【代码评审】Bpm:触发器 2025-01-25 09:31:02 +08:00
jason e3db7d3014 Merge branch 'feature/bpm' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm 2025-01-24 23:36:43 +08:00
jason b7329b0c44 【功能新增】Simple 设计器新增触发器节点 2025-01-24 23:36:26 +08:00
YunaiV a61a405346 【代码评审】Bpm:更多设置-摘要设置 2025-01-24 20:18:36 +08:00
YunaiV 6c504adcbc Merge branch 'feature/bpm' of https://github.com/GoldenZqqq/yudao-ui-admin-vue3 into feature/bpm
# Conflicts:
#	src/views/bpm/model/CategoryDraggableModel.vue
2025-01-24 20:03:40 +08:00
YunaiV 8e8475ffdc 【代码评审】Bpm:更多设置-摘要设置 2025-01-24 19:59:02 +08:00
芋道源码 ab83cee50f !681 feat: BPM-更多设置-摘要设置
Merge pull request !681 from Lesan/feature/bpm-摘要设置
2025-01-24 11:30:14 +00:00
芋道源码 99a44e8c01 !682 fix: 服务任务无法启动
Merge pull request !682 from Lesan/bugfix/bpm-服务任务
2025-01-24 11:25:43 +00:00
LesanOuO 0bbf4c9143 fix: 服务任务无法启动 2025-01-24 13:58:52 +08:00
LesanOuO 9e26e902a7 fix: label拼写错误 2025-01-24 13:19:44 +08:00
LesanOuO 8ff63622ec feat: BPM-更多设置-摘要设置 2025-01-24 13:11:12 +08:00
GoldenZqqq 4319d86978 Revert "fix: 优化Simple设计器接口调用逻辑与去除loading效果"
This reverts commit b3a97b0889.
2025-01-24 11:13:09 +08:00
GoldenZqqq 7f50584a02 perf: 优化流程模型分类组件性能和类型定义 2025-01-24 10:30:57 +08:00
jason 7043dea354 【功能优化】替换 iconfont 图标 2025-01-23 22:12:22 +08:00
jason 1cb06911c7 【功能修复】修复控制台警告问题 2025-01-23 20:52:18 +08:00
YunaiV 3ae486c39f Merge branch 'devlop' of https://gitee.com/puhui999/yudao-ui-admin-vue3 into dev
# Conflicts:
#	src/views/system/menu/index.vue
2025-01-23 19:58:43 +08:00
YunaiV c514c799ff Merge branch 'master' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into dev 2025-01-23 19:43:55 +08:00
YunaiV 58455cd0a4 【代码评审】Bpm:模型列表的优化 2025-01-23 19:40:49 +08:00
YunaiV bf437421bb Merge branch 'feature/bpm_fix_250121' of https://github.com/zws-code/yudao-ui-admin-vue3-fix into feature/bpm 2025-01-23 19:33:21 +08:00
YunaiV 09e406618c 【代码评审】Bpm:更多设置-自定义标题 2025-01-23 19:19:35 +08:00
puhui999 5832fd687f 【代码优化】系统菜单管理使用虚拟列表优化加载速度 2025-01-23 16:32:01 +08:00
芋道源码 a96873ef5d !677 feat: BPM-更多设置-标题设置
Merge pull request !677 from Lesan/feature/bpm-标题设置
2025-01-23 04:57:23 +00:00
芋道源码 951ce1cf50 !675 fix: mall kefu
Merge pull request !675 from puhui999/devlop
2025-01-23 04:26:29 +00:00
LesanOuO 9495c2c63a feat: BPM-更多设置-标题设置 2025-01-23 12:05:52 +08:00
芋道源码 c435e2c9e8 !674 BPM:更多设置-自动去重
Merge pull request !674 from Lesan/feature/bpm-自动去重
2025-01-23 00:16:45 +00:00
芋道源码 b60693036d !671 BPM:更多设置-流程编码
Merge pull request !671 from Lesan/feature/bpm-流程编码
2025-01-23 00:16:04 +00:00
puhui999 c50f59d181 【缺陷修复】mall: 修复当客服消息为空时按回车键会发送空消息的 bug 2025-01-22 18:11:02 +08:00
puhui999 39106843f3 【缺陷修复】mall: 修复客服中心页面接收到消息时,重复调用了多次获取会话接口的问题 2025-01-22 18:10:51 +08:00
LesanOuO 15b0414604 fix: 自动去重枚举变更 2025-01-22 09:51:24 +08:00
DevDengChao 1b8430e2fc feat: add shortcut to weichat pay 2025-01-21 14:47:40 +08:00
芋道源码 051fc134cb !673 BPM-更多设置-自动去重
Merge pull request !673 from Lesan/feature/bpm-自动去重
2025-01-21 01:39:33 +00:00
zws c9c03bed13 perf: 模型列表 权限检查用computed 替代vue指令 减少检查次数 2025-01-21 09:24:12 +08:00
zws d710c9d27e perf: 添加权限Set 替换检查权限数组 提高检查权限性能 2025-01-21 09:23:37 +08:00
Lesan 0215e8d4a8 fix: 使用枚举BpmAutoApproveType 2025-01-21 08:27:06 +08:00
Lesan 0f58e968b1 feat: BPM-更多设置-自动去重 2025-01-21 08:25:09 +08:00
Lesan 3dbe74c1e9 feat: BPM-更多设置-流程编码 2025-01-20 13:36:42 +08:00
YunaiV 187fa08524 【代码评审】Bpm:更多设置-流程编码 2025-01-20 12:40:59 +08:00
YunaiV 0677173856 【代码优化】Bpm:更多设置-流程编码 2025-01-20 12:33:28 +08:00
芋道源码 e6765b7e61 !670 BPM-更多设置-流程编码
Merge pull request !670 from Lesan/feature/bpm-流程编码
2025-01-20 04:30:58 +00:00
GoldenZqqq 3d591912c2 Merge branch 'feature/bpm' of https://github.com/GoldenZqqq/yudao-ui-admin-vue3 into feature/bpm 2025-01-20 11:08:56 +08:00
Lesan 5efdd93789 feat: BPM-更多设置-流程编码 2025-01-20 11:02:20 +08:00
芋道源码 22eb2ccdbf !669 BPM:重命名RouterCondition->RouterSetting
Merge pull request !669 from Lesan/feature/bpm-路由分支
2025-01-20 01:15:59 +00:00
Lesan c3fb2a3ca1 fix: 重命名RouterCondition->RouterSetting 2025-01-20 08:40:00 +08:00
jason b047fd9929 【功能修复】条件节点配置问题修复 2025-01-19 22:42:05 +08:00
jason aeb096a311 冲突修改 Merge branch 'feature/bpm' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm 2025-01-19 20:45:27 +08:00
jason 42de3d9127 【功能优化】条件节点设置优化 2025-01-19 18:40:18 +08:00
YunaiV 2659165220 Merge branch 'dev' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 2025-01-19 18:29:42 +08:00
芋道源码 fef86bcc56 !666 【功能完善】MALL: 模板装修优惠券组件优惠券列表回显
Merge pull request !666 from puhui999/dev
2025-01-19 10:29:32 +00:00
YunaiV 70e6b54642 【代码优化】Bpm:提交人权限的 review 2025-01-19 18:28:45 +08:00
芋道源码 d4432acec3 !665 BPM:审批意见
Merge pull request !665 from Lesan/feature/bpm-审批意见
2025-01-19 10:24:35 +00:00
芋道源码 8330f61eef !667 BPM:更多设置-提交人权限
Merge pull request !667 from Lesan/feature/bpm-提交人权限
2025-01-19 10:23:15 +00:00
Lesan 9694ad0fa3 feat: 提交人权限-允许撤销审批中的申请 2025-01-19 16:43:30 +08:00
puhui999 5551c594ab 【功能完善】MALL: 模板装修优惠券组件优惠券列表回显 2025-01-19 13:45:56 +08:00
puhui999 391a1481c6 【代码优化】MALL: 修复模板装修时组件属性响应式丢失的问题 2025-01-19 13:44:04 +08:00
Lesan 82ee62cb14 feat: 审批意见 2025-01-19 10:28:54 +08:00
Lesan 506d87ec71 feat: 审批意见 2025-01-19 10:15:43 +08:00
YunaiV ced9ad6bba Merge remote-tracking branch 'origin/master' 2025-01-18 19:21:40 +08:00
YunaiV 34081915d4 Merge branch 'dev' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 2025-01-18 19:21:19 +08:00
芋道源码 a2ef21b27f !664 【代码优化】MALL: 修复模板装修时组件属性响应式丢失的问题
Merge pull request !664 from puhui999/dev
2025-01-18 11:16:23 +00:00
YunaiV fea238ef15 【代码优化】Bpm:编辑流程时,“谁可以发起”默认就是“全体” 2025-01-18 19:01:12 +08:00
YunaiV 7349a0d080 【代码优化】Bpm:编辑流程时,“流程管理员”默认就是“指定人员” 2025-01-18 18:51:13 +08:00
YunaiV 39da8118b8 【代码优化】Bpm:移除废弃的 ModelForm.vue 2025-01-18 18:49:18 +08:00
puhui999 625426566c 【代码优化】MALL: 修复模板装修时组件属性响应式丢失的问题 2025-01-18 17:47:10 +08:00
puhui999 ae0cc94da0 【代码优化】MALL: usePropertyForm 替换为 useVModel 实现 modelValue 代理 2025-01-18 16:20:09 +08:00
YunaiV 99790bae63 【功能评审】Bpm:复制流程 2025-01-17 19:49:39 +08:00
YunaiV 6b1c0e7369 Merge branch 'feature/bpm' of https://github.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm 2025-01-17 19:38:28 +08:00
芋道源码 cde758d324 Merge pull request #123 from zws-code/feature/bpm_fix_250116
Feature/bpm fix 250116
2025-01-17 19:38:03 +08:00
芋道源码 43ebd50a42 !658 【功能修复】用户管理:如果没有修改权限,则禁用状态开关
Merge pull request !658 from jinmh/master
2025-01-17 11:37:20 +00:00
YunaiV f484f95de3 【功能评审】Bpm:清理进程 2025-01-17 19:32:57 +08:00
YunaiV b82959118e Merge branch 'feature/bpm' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm 2025-01-17 19:30:22 +08:00
芋道源码 121a6d85ff !661 BPM:流程清理
Merge pull request !661 from Lesan/feature/bpm-流程清理
2025-01-17 11:30:14 +00:00
YunaiV 2486f0d3e0 Merge branch 'feature/bpm-跳过表达式' of https://gitee.com/LesanOuO/yudao-ui-admin-vue3 into feature/bpm
# Conflicts:
#	src/components/bpmnProcessDesigner/package/designer/plugins/descriptor/flowableDescriptor.json
2025-01-17 19:14:07 +08:00
YunaiV c8447fe4f7 【功能评审】Bpm:审批签名 2025-01-17 19:12:06 +08:00
芋道源码 e8e357b8a2 !659 BPM:签名功能完善
Merge pull request !659 from Lesan/feature/bpm-n
2025-01-17 11:09:22 +00:00
zws 5c0ee866a0 Merge remote-tracking branch 'refs/remotes/git-origin/feature/bpm' into feature/bpm_fix 2025-01-17 14:22:05 +08:00
Lesan 1af93e502d feat: 模型的流程数据清理功能 2025-01-17 13:59:29 +08:00
Lesan 4e0e8dd220 feat: bpmn跳过表达式 2025-01-17 09:57:45 +08:00
Lesan 9308eceb81 feat: 抽离UserTaskListener组件 2025-01-16 15:25:20 +08:00
Lesan 38578c3e50 feat: 获取分支中的节点 2025-01-16 14:55:32 +08:00
Lesan 69ccd83af3 feat: bpmn设计器添加UserTask签名配置 2025-01-16 14:40:14 +08:00
Lesan 37964e740f feat: 审批签名代码评审 2025-01-16 13:55:15 +08:00
YunaiV 8e5271a6d6 【功能评审】Bpm:审批签名 2025-01-16 13:01:06 +08:00
zws d52e779131 fix: 解决导入模型xml 数据没有保存问题 2025-01-16 10:18:20 +08:00
zws 66d8cf1233 fix: 解决bpmn设计器 初次创建流程key不一致问题 2025-01-16 10:10:46 +08:00
zws 84552a6253 fix: 解决导入模型json 数据没有保存问题 2025-01-16 09:29:34 +08:00
zws 9febee7abe perf: 用download.json 替换模型json下载 2025-01-16 09:29:11 +08:00
zws e44f48c0c4 perf: bpm模型复制优化 2025-01-16 09:27:30 +08:00
zws 2f7200abdb feat: 从vue-element-plus-admin项目 同步TagsView代码 实现设置路由标题功能 2025-01-16 09:26:38 +08:00
zws 9849a040c0 feat: 添加download.json()方法 2025-01-16 09:20:57 +08:00
芋道源码 753e44ccd0 !655 Simple设计器完善及优化
Merge pull request !655 from Lesan/feature/bpm-n
2025-01-15 13:14:58 +00:00
YunaiV e4fab335fd 【功能修复】ERP:采购订单的产品单价缺少为空校验 2025-01-15 21:10:03 +08:00
YunaiV c391ba7689 Merge branch 'master' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm 2025-01-15 20:50:22 +08:00
YunaiV 2b38c02165 Merge branch 'master' of https://github.com/yudaocode/yudao-ui-admin-vue3 2025-01-15 20:46:17 +08:00
芋道源码 d5d667353a Merge pull request #122 from yudaocode/revert-117-master
Revert "fix:Vite CJS Node API deprecated waring"
2025-01-15 20:46:02 +08:00
芋道源码 026b2f2ba5 Revert "fix:Vite CJS Node API deprecated waring" 2025-01-15 20:45:49 +08:00
芋道源码 ece0ccd6df !657 修复表单构建页面,json显示和复制问题,调整highlight废弃的方法
Merge pull request !657 from SamllNorth_Lee/form-build/dev
2025-01-15 12:42:49 +00:00
YunaiV 395c5862fe Merge branch 'master' of https://github.com/yudaocode/yudao-ui-admin-vue3 2025-01-15 20:33:22 +08:00
YunaiV c62833fcf6 Merge branch 'master' of https://github.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm 2025-01-15 20:31:58 +08:00
芋道源码 954f2b6353 Merge pull request #117 from 2234839/master
fix:Vite CJS Node API deprecated waring
2025-01-15 20:31:41 +08:00
YunaiV 5676ac125d Merge branch 'master' of https://github.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm 2025-01-15 20:31:04 +08:00
芋道源码 7e715dd03b Merge pull request #119 from edgexie/patch-1
fix: Update useRenderLayout.tsx add tagsView.value when rendertop
2025-01-15 20:30:44 +08:00
YunaiV 6fba4b7228 【代码评审】BPM:流程编辑 2025-01-15 20:25:07 +08:00
YunaiV d32a9a37bf Merge branch 'feature/bpm' of https://github.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm
# Conflicts:
#	src/views/bpm/simple/SimpleModelDesign.vue
2025-01-15 19:13:48 +08:00
芋道源码 45cf482c74 Merge pull request #121 from zws-code/master_fix_bpm
perf: bpm 流程模型表单代码优化. 去掉大量watch 代码. 用依赖注入替换props 传递流程数据. 增加导入导出
2025-01-15 19:12:47 +08:00
YunaiV f1ac8ba622 Merge branch 'feature/bpm' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm 2025-01-15 19:08:03 +08:00
YunaiV 89c3af5207 Merge branch 'master' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm 2025-01-15 19:07:07 +08:00
zws b117431318 feat: bpm 添加流程模型复制功能 2025-01-15 15:04:23 +08:00
zws 8df285aefb fix: bpm 用onActivated替换onMounted
解决新增模型返回后不刷新新的数据
2025-01-15 14:57:23 +08:00
zws e76eda8af3 fix: 修复组件在keepAlive下刷新不触发onActivated钩子问题 2025-01-15 14:54:23 +08:00
jinmh716 86bb36fc2f 【功能修复】用户管理:如果没有修改权限,则禁用状态开关 2025-01-15 10:22:06 +08:00
lizhixian 0fa31be851 fix:表单构建页面,json显示和生成问题 2025-01-14 17:20:51 +08:00
lizhixian aba4705b8e fix:表单构建页面,json显示和生成问题 2025-01-14 17:17:30 +08:00
zws 4fe158b2ec perf: bpm 修复bpmn设计器不显示问题 2025-01-14 12:00:08 +08:00
zws 99fe894eda Merge remote-tracking branch 'refs/remotes/git-origin/master' into master_fix_bpm 2025-01-14 11:55:46 +08:00
Lesan 42c1618202 fix: 创建UserTask无signEnable字段 2025-01-14 09:12:53 +08:00
zws 3d8f1290c3 perf: bpm 流程模型表单代码优化. 去掉大量watch 代码. 用依赖注入替换props 传递流程数据. 增加导入导出 2025-01-14 08:56:36 +08:00
jason baa4116697 【功能修复】修复审批详情页面,控制台警告问题 2025-01-13 12:59:16 +08:00
GoldenZqqq b3a97b0889 fix: 优化Simple设计器接口调用逻辑与去除loading效果 2025-01-13 11:10:53 +08:00
EchoByte d896996f27 fix: Update useRenderLayout.tsx add tagsView.value when rendertop
if I set tagsview value `false` in app.ts initial. the content will have margin top style.
2025-01-10 16:31:57 +08:00
Lesan dc5aa6d8df feat: 完善监听器表单校验 2025-01-10 14:48:16 +08:00
Lesan 401481662f feat: 补充等式左右值form校验 2025-01-10 14:33:59 +08:00
Lesan 04f4f630ad feat: 完善UserTask审批签名 2025-01-09 13:50:08 +08:00
Lesan dd72d35fe4 feat: ConditionNodeConfig使用Condition组件 2025-01-09 10:16:46 +08:00
Lesan 96d1fcedde fix: ConditionRule后端无type、opName字段 2025-01-09 10:14:50 +08:00
Lesan a1933a781e fix: ConditionRule后端无type、opName字段 2025-01-09 09:50:08 +08:00
Lesan 777564673e fix: 检查所有route->router 2025-01-09 09:19:23 +08:00
Lesan 077dfdcddd Merge remote-tracking branch 'refs/remotes/yudao/feature/bpm' into feature/bpm-n 2025-01-09 09:13:54 +08:00
Lesan 4741f3737f fix: defaultFlowId由后端生成 2025-01-09 09:04:22 +08:00
YunaiV 77938c38ca 【代码评审】BPM:签名组件 2025-01-08 22:18:47 +08:00
芋道源码 d11a82a521 !654 Simple设计器功能完善
Merge pull request !654 from Lesan/feature/bpm-n
2025-01-08 14:12:37 +00:00
Lesan 0c433552f9 feat: Simple设计器-userTask-添加是否需要签名字段 2025-01-08 16:58:14 +08:00
Lesan 5fa2a78e84 feat: 添加监听器请求地址非空校验 2025-01-08 15:03:06 +08:00
Lesan 35a0e69cd4 chore: 部分文件更名 2025-01-08 10:51:47 +08:00
Lesan 2144034e1f fix: 使用ConditionType枚举类型 2025-01-08 10:18:12 +08:00
Lesan 53e6d6aa61 feat: 更换使用条件组件 2025-01-08 10:16:11 +08:00
崮生 7303bcd91a fix prettier 2025-01-08 01:48:48 +00:00
崮生 5e31eb8ebc fix typo 2025-01-08 01:22:44 +00:00
崮生 90fa5ffd81 fix:Vite CJS Node API deprecated waring 2025-01-08 01:18:50 +00:00
Lesan 099b81cb2d feat: 路由分支流程图展示高亮 2025-01-08 08:56:45 +08:00
Lesan e5f6020582 fix: routeGroups命名问题 2025-01-08 08:52:08 +08:00
Lesan a2759680f8 fix: 使用vue3-signature签名组件 2025-01-08 08:31:50 +08:00
YunaiV 72dcb0c1f7 【代码评审】BPM:签名组件 2025-01-07 22:42:24 +08:00
YunaiV 528ebf3ea3 Merge branch 'feature/bpm-n' of https://gitee.com/LesanOuO/yudao-ui-admin-vue3 into feature/bpm
# Conflicts:
#	src/components/SimpleProcessDesignerV2/src/consts.ts
2025-01-07 22:30:18 +08:00
YunaiV 3f9408bfbe 【代码评审】BPM:Simple 设计器 - 路由分支 2025-01-07 22:27:13 +08:00
Lesan 65eee157b9 feat: Simple设计器-监听器-表单使用选择器 2025-01-07 17:02:44 +08:00
Lesan 7683aa2e7d feat: Simple设计器-监听器 2025-01-07 16:33:16 +08:00
Lesan 0da3a9bf7b Merge remote-tracking branch 'yudao/feature/bpm' into feature/bpm-n 2025-01-07 13:34:45 +08:00
芋道源码 eca1c83c56 !648 Simple设计器-路由分支
Merge pull request !648 from Lesan/feature/bpm-路由分支
2025-01-07 04:53:16 +00:00
YunaiV 42a3d2efab 【代码评审】BPM:Simple 设计器 - userTask 监听器 2025-01-07 12:28:27 +08:00
Lesan 0168f9ff9d feat: 签名组件 2025-01-07 10:28:56 +08:00
芋道源码 1ebd37a0e9 !651 Simple设计器-监听器
Merge pull request !651 from Lesan/feature/bpm-监听器
2025-01-07 01:42:28 +00:00
Lesan 739a1c29a2 feat: Simple设计器-监听器 2025-01-06 17:25:59 +08:00
LesanOuO 0a15eca573 feat: 审批人设置一行三个 2025-01-06 12:49:55 +08:00
Lesan 3118d2abd9 feat: Simple设计器-路由分支 2025-01-06 11:22:59 +08:00
芋道源码 e9950929de !647 【修复】修复 npm 流程模拟插件 bpmn-js-token-simulation
Merge pull request !647 from 半栈幼儿员/hotfix/css
2025-01-05 08:40:26 +00:00
preschooler 63c9b8d163 🐞 fix:修复 npm 流程模拟插件 bpmn-js-token-simulation 2025-01-05 15:54:20 +08:00
jason be60018908 Merge branch 'feature/bpm' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm 2025-01-04 15:25:33 +08:00
jason d7e6523dad 【功能优化】延迟器节点的高亮显示 2025-01-04 15:24:38 +08:00
YunaiV 49fc9b5412 Merge branch 'feature/bpm' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 2025-01-04 15:24:09 +08:00
YunaiV 5198b2ee01 【代码优化】BPM:模型列表界面 2025-01-04 15:23:48 +08:00
芋道源码 4aa44abf25 !646 fix: Simple设计器修改不生效
Merge pull request !646 from Lesan/feature/bpm-n
2025-01-04 07:15:25 +00:00
YunaiV 2a9290ce86 【代码优化】支持通过短信重置后台密码 2025-01-04 11:41:40 +08:00
杨宇庆 65924bb921 !617 feat: 支持通过短信重置后台密码
* feat: 支持通过短信重置后台密码
2025-01-04 03:36:45 +00:00
YunaiV 791d59020a 【功能优化】菜单管理:调整宽度,避免过窄 2025-01-04 11:33:55 +08:00
YunaiV 09da6579b6 【功能移除】菜单管理:全部展开/关闭,没有使用场景,所以清理掉 2025-01-04 11:30:14 +08:00
YunaiV ce60f630c4 【功能优化】菜单管理:使用 el-table-v2 解决菜单过多后,存在卡顿的问题 2025-01-04 11:14:33 +08:00
LesanOuO a9b5a7f006 fix: Simple设计器修改不生效 2025-01-04 09:35:44 +08:00
芋道源码 ea133da1d8 !627 菜单管理使用虚拟化树形控件实现
Merge pull request !627 from Jason0z/feat-menu-tree-v2
2025-01-04 01:34:08 +00:00
YunaiV 101ed368c3 Merge branch 'dev' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 2025-01-04 09:33:06 +08:00
芋道源码 48e7e7e5c6 !629 feat: add hiddenSearch function to manage search visibility in RouterSearch component
Merge pull request !629 from AhJindeg/fix/router-serch
2025-01-04 01:32:33 +00:00
YunaiV 452d6024b0 【功能完善】文件列表: 添加复制链接功能 2025-01-04 09:31:28 +08:00
芋道源码 7b7ce96c66 !631 【功能完善】文件列表: 添加复制链接功能
Merge pull request !631 from jinmh/master
2025-01-04 01:30:37 +00:00
YunaiV e508da7ff5 632 【轻量级 PR】:update src/components/Echart/src/Echart.vue. 2025-01-04 09:25:43 +08:00
芋道源码 6b6d2ff806 !632 update src/components/Echart/src/Echart.vue.
Merge pull request !632 from ddlu/N/A
2025-01-04 01:25:13 +00:00
芋道源码 5fce1afe29 !636 修复菜单总是显示无效的问题
Merge pull request !636 from chenting/N/A
2025-01-04 01:15:21 +00:00
芋道源码 69f76705fb !637 升级 element-plus,修复 el-table-v2 滚动条 bug
Merge pull request !637 from chenting/N/A
2025-01-04 01:13:44 +00:00
芋道源码 166e95298b !634 修复菜单显示 Please set titile 的问题
Merge pull request !634 from chenting/master
2025-01-04 01:12:39 +00:00
芋道源码 39486d6696 !644 feat: ✨ add current year calculation to footer component
Merge pull request !644 from AhJindeg/feature/footer-currentYear
2025-01-04 00:24:28 +00:00
YunaiV 98774ecc6d 【代码评审】工作流:延迟器的支持 2025-01-03 20:00:47 +08:00
芋道源码 8bbc5e22d1 !645 Simple设计器-延迟器
Merge pull request !645 from Lesan/feature/bpm-延迟器
2025-01-03 11:19:19 +00:00
Lesan c3dd741d97 Merge branch 'feature/bpm-延迟器' of https://gitee.com/LesanOuO/yudao-ui-admin-vue3 into feature/bpm-延时器 2025-01-03 16:29:16 +08:00
Lesan f470e0d65b feat: Simple设计器-延时器 2025-01-03 16:20:06 +08:00
AhJindeg 700efd7ee2 feat: ✨ add current year calculation to footer component
- 根据"DRY"(Don't Repeat Yourself)原则,使用动态年份代替硬编码,确保年份始终保持最新
- Introduced a new computed property `currentYear` in `Footer.vue` to dynamically display the current year in the copyright notice.
- Updated the copyright span to use `currentYear` instead of a hardcoded year, enhancing maintainability and accuracy.
2025-01-03 15:20:04 +08:00
chenting 739e43a31e 升级 element-plus,修复 el-table-v2 滚动条 bug
Signed-off-by: chenting <1016830869@qq.com>
2025-01-02 08:58:47 +00:00
chenting 7faea8c341 修复菜单总是显示无效的问题
Signed-off-by: chenting <1016830869@qq.com>
2025-01-02 05:55:11 +00:00
LesanOuO 3607b1d468 fix: 当存在重复执行的节点时审批意见展示有误 2025-01-02 12:23:33 +08:00
chenting 2436075337 修复菜单显示 Please set titile 的问题
Signed-off-by: chenting <1016830869@qq.com>
2025-01-02 03:30:26 +00:00
YunaiV c144439e5b V2.4.0 发布~ 2024-12-31 12:43:15 +08:00
YunaiV 42f5223941 Merge branch 'feature/bpm' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 2024-12-31 12:38:31 +08:00
YunaiV 1b2c7ceb90 Merge branch 'dev' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 2024-12-31 12:38:14 +08:00
YunaiV 8855b77770 Merge branch 'feature/bpm' of https://github.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm
# Conflicts:
#	src/components/SimpleProcessDesignerV2/src/SimpleProcessDesigner.vue
#	src/views/bpm/model/form/ProcessDesign.vue
#	src/views/bpm/simple/SimpleModelDesign.vue
2024-12-31 09:50:00 +08:00
芋道源码 f3b28f43f1 Merge pull request #116 from GoldenZqqq/feature/bpm
流程新增/修改页面bug修复
2024-12-31 09:46:32 +08:00
jason 732b1741c1 【功能完善】 Simple 设计器,发起节点权限展示
【缺陷修复】流程表单字段变化时,节点字段权限不会变化
2024-12-30 23:43:20 +08:00
GoldenZqqq 1e161d2e64 Merge remote-tracking branch 'upstream/feature/bpm' into feature/bpm 2024-12-30 15:25:52 +08:00
GoldenZqqq 59281a7126 fix: 修复第三步骤两个设计器绘制完的流程无法回显及在其他步骤点击保存/发布按钮无法获取到第三步骤的流程图数据bug 2024-12-30 15:19:30 +08:00
YunaiV 51cffa4e03 【功能优化】登录界面,如果禁用验证码,不初始化 Verify 组件 2024-12-28 20:30:51 +08:00
YunaiV e985b2507a 【功能修复】Editor 无法刷新令牌的问题 2024-12-28 20:02:09 +08:00
YunaiV aa37021a68 【功能修复】公众号:图片上传时,accountId 参数不正确的问题 2024-12-28 19:52:49 +08:00
YunaiV c56eeccaef 【功能修复】商城:商品排行的“支付金额”缺少分转元的逻辑 2024-12-28 19:27:09 +08:00
ddlu b9326463c2 update src/components/Echart/src/Echart.vue.
echart 没有显示markLine、markPoint、markArea
如果延后到 vue显示页面加入此三行,线上会出错

Signed-off-by: ddlu <ludd@mail.ustc.edu.cn>
2024-12-28 07:40:57 +00:00
jinmh716 74b62bf05e 【功能完善】文件列表: 添加复制链接功能 2024-12-28 12:59:42 +08:00
GoldenZqqqq d8851721b8 feat: 移除了Simple编辑器使用流程名称作为第一个节点名称的逻辑 2024-12-27 23:10:09 +08:00
YunaiV 472da9274e Merge branch 'feature/bpm' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 2024-12-27 21:50:35 +08:00
YunaiV 6f724d9d52 【代码评审】Bpm:新的编辑界面 2024-12-27 21:28:12 +08:00
YunaiV 6791de9d7c Merge branch 'feature/bpm' of https://github.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm 2024-12-27 21:08:48 +08:00
芋道源码 241e27137c Merge pull request #115 from GoldenZqqq/feature/bpm
工作流流程新增/修改页面bug修复;保存/发布按钮展示与否逻辑、样式优化
2024-12-27 21:08:21 +08:00
GoldenZqqq eeda821b74 fix: 区分兼容bpmn设计器与simple设计器不同传参与不同处理方式 2024-12-27 15:22:02 +08:00
GoldenZqqq 3ba5cb460c chore: 乱码文字恢复 2024-12-27 15:01:31 +08:00
GoldenZqqq a8c1fd4473 feat: Simple设计器同步获取bpmnXml数据相关逻辑 2024-12-27 14:58:43 +08:00
GoldenZqqq e50cd2231c feat: 删除设计流程中两个流程设计器的保存模型按钮,并在点击保存/提交按钮时直接获取内部组件xml逻辑 2024-12-27 14:32:39 +08:00
GoldenZqqq 40c2cb72ff feat: 优化保存/提交按钮样式与完善保存/提交后的确认逻辑 2024-12-27 11:50:56 +08:00
GoldenZqqq 6cf3de0166 feat: 样式布局优化 2024-12-27 09:43:32 +08:00
YunaiV 2f4599f664 Merge branch 'feature/bpm' of https://github.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm 2024-12-26 14:04:07 +08:00
芋道源码 7c7fbba2a3 Merge pull request #114 from GoldenZqqq/feature/bpm
工作流流程新增/修改/设计页面路由与子组件拆分、样式与逻辑优化bug修复
2024-12-26 14:03:42 +08:00
AhJindeg d815c60340 feat: add hiddenSearch function to manage search visibility in RouterSearch component 2024-12-26 11:40:25 +08:00
GoldenZqqq 36157a8f24 fix: 修复新增流程时无法通过第三步的bpm必填校验问题 2024-12-25 14:33:21 +08:00
GoldenZqqq a82862be47 fix: 修改流程时,修改了流程设计保存不生效问题修复 2024-12-25 14:10:16 +08:00
GoldenZqqq f86bd0df0b fix: 第一步骤中tooltip icon未展示问题修复与样式细节优化 2024-12-25 09:52:19 +08:00
GoldenZqqqq 53841a598f fix: 修复第三步骤流程设计中点击保存模型xml保存无效问题 2024-12-24 22:24:54 +08:00
GoldenZqqqq ab11818ce2 feat: 优化切换步骤时的必填校验,限制往后切换时才触发必填校验优化用户体验 2024-12-24 20:10:58 +08:00
zhuguojian 02fa679d62 feat:菜单列表改为虚拟化树形控件实现 2024-12-24 11:48:24 +08:00
YunaiV 5a0bcfe6b5 【代码评审】Bpm:新的编辑界面 2024-12-20 09:25:13 +08:00
芋道源码 ecfe1b6145 !613 GoView域名外置配置文件
Merge pull request !613 from 杨宇庆/N/A
2024-12-19 12:49:29 +00:00
芋道源码 1df1850ef9 !615 解决商机、合同金额无法自动计算的问题
Merge pull request !615 from 杨宇庆/N/A
2024-12-19 12:48:17 +00:00
jason ea97abc0af 【功能优化】发起流程时候,表单字段权限设置 2024-12-18 23:23:07 +08:00
jason 0ae9acdfd6 【缺陷修复】Simple 流程设计器,当横向内容过多时,左侧会被顶出去 2024-12-18 21:18:55 +08:00
芋道源码 25484a941b !623 fix: prevent default browser action for ctrl + k key combination in RouterSearch component
Merge pull request !623 from AhJindeg/fix/router-search
2024-12-18 12:45:14 +00:00
芋道源码 169e3600d9 !625 防御性编程,规避服务端children返回的不是数组时…… update src/utils/tree.ts.
Merge pull request !625 from 山野羡民/N/A
2024-12-18 12:44:52 +00:00
山野羡民 b5aa9d24ac 防御性编程,规避服务端children返回的不是数组时…… update src/utils/tree.ts.
Signed-off-by: 山野羡民 <liyujiang_tk@yeah.net>
2024-12-18 03:02:00 +00:00
AhJindeg 413721bdfd fix: prevent default browser action for ctrl + k key combination in RouterSearch component
- 阻止 RouterSearch 组件中 ctrl + k 组合键的默认浏览器操作
2024-12-17 16:30:58 +08:00
GoldenZqqq a6461da0f8 feat: 流程新增/修改页面中-第二步骤中表单预览功能增加 2024-12-17 09:19:04 +08:00
GoldenZqqq 6e6754e44a feat: 流程模型新增/修改页面-优化路由配置、新增修改分为两个并全部使用name进行跳转,每个步骤拆分出一个子组件 2024-12-16 17:23:27 +08:00
YunaiV e891a4bfd2 【缺陷修复】全局:"legacy-js-api" 报错 2024-12-15 17:15:02 +08:00
YunaiV 5e386336b9 【代码评审】Bpm:新的编辑界面 2024-12-15 17:08:02 +08:00
芋道源码 f87b37eaac !619 同步最新的 bpm 改动
Merge pull request !619 from 芋道源码/feature/bpm
2024-12-15 08:45:44 +00:00
YunaiV c083a0c986 Merge branch 'feature/bpm' of https://github.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm
# Conflicts:
#	src/components/bpmnProcessDesigner/package/penal/PropertiesPanel.vue
#	src/views/bpm/model/editor/index.vue
2024-12-15 16:28:33 +08:00
芋道源码 9685fbf822 Merge pull request #111 from GoldenZqqq/feature/bpm
工作流模块改造优化
2024-12-15 16:23:51 +08:00
GoldenZqqqq 746aeaff35 fix: 创建/修改流程页面-恢复填写流程标识与流程名称才可以切换到第三步逻辑 2024-12-14 15:43:06 +08:00
GoldenZqqqq 21c28c3665 feat: 修改设计流程切换到第三步自动赋值流程标识与流程名称逻辑 2024-12-14 00:21:43 +08:00
jason 9f0f5b0e6c 【代码优化】 发起流程页面时,加 Loading 遮罩 2024-12-13 22:37:42 +08:00
芋道源码 0ab7f2342c !616 bpmn设计器优化(添加CallActivity配置支持)
Merge pull request !616 from Lesan/feature/bpm
2024-12-12 01:05:34 +00:00
Lesan a389392c8e feat: bpmn设计器添加CallActivity支持 2024-12-12 08:33:46 +08:00
Lesan 5d31881f48 feat: 调用活动(子任务) 2024-12-11 13:56:01 +08:00
Lesan 3e826776f4 fix: 属性命名错误 2024-12-11 13:46:09 +08:00
杨宇庆 4c5c6ff5c3 fix: 解决商机、合同金额无法自动计算的问题
Signed-off-by: 杨宇庆 <hiyyq@qq.com>
2024-12-11 05:25:43 +00:00
YunaiV d76df547ae 【功能优化】Bpm:UserTask 选择表达式按钮 2024-12-11 12:51:43 +08:00
芋道源码 bfb9e2e77f !614 bpm设计器优化
Merge pull request !614 from Lesan/feature/bpm
2024-12-11 04:40:11 +00:00
Lesan fe2ac97f9d refactor: 使用Simple设计器的useFormFieldsPermission 2024-12-11 09:18:15 +08:00
Lesan 4bd020857f feat: 添加UserTask自定义配置,审批类型 2024-12-11 08:57:35 +08:00
Lesan a9c924a421 feat: 同步bpm设计器审批人配置,表单内部门负责人 2024-12-11 08:48:49 +08:00
Lesan b09ff417c3 feat: 同步bpm设计器审批人配置,发起人部门负责人,发起人连续多级部门的负责人 2024-12-10 16:42:17 +08:00
Lesan 6b397104fc feat: 同步bpm设计器审批人配置,连续多级部门的负责人 2024-12-10 16:07:10 +08:00
Lesan 9e2958f7d0 feat: 同步bpm设计器审批人配置,表单内用户字段 2024-12-10 14:37:17 +08:00
Lesan dc8f5cf977 feat: 同步bpm设计器审批人配置 2024-12-10 13:46:17 +08:00
GoldenZqqqq 9459a7296f feat: 流程模型新增/修改/设计合为一个页面基本切换tab逻辑校验与页面样式优化 2024-12-09 23:05:36 +08:00
杨宇庆 8c52c6867f GoView域名外置配置文件
Signed-off-by: 杨宇庆 <hiyyq@qq.com>
2024-12-09 00:55:53 +00:00
芋道源码 46985c6e1b !612 bpm设计器优化
Merge pull request !612 from Lesan/feature/bpm
2024-12-08 23:56:03 +00:00
YunaiV 7697054918 【代码评审】Bpm:钉钉表单设计器 2024-12-09 07:52:52 +08:00
jason acb00ba866 【缺陷修复】两个取消按钮,一个改成确认 2024-12-08 19:59:45 +08:00
jason cff65aa901 【缺陷修复】修复审批意见校验的问题 2024-12-08 16:58:50 +08:00
jason a6de16a02d 【功能完善】流程发起时,增加流程表单的校验 2024-12-08 12:22:11 +08:00
jason 0b6af855ac 【功能完善】审批时,流程表单可编辑字段,可修改。 2024-12-08 11:58:46 +08:00
LesanOuO 5785d741bb fix: 多实例默认选中SEQUENTIAL_APPROVE 2024-12-07 11:22:07 +08:00
LesanOuO e52049876e fix: 新创建节点时无values属性 2024-12-07 11:19:50 +08:00
LesanOuO 7474c6b83b fix: unplugin-vue-components报错,同名UserTask 2024-12-07 10:45:47 +08:00
YunaiV 1f19b567c2 【缺陷修复】分销: 推广订单列表用户类型检索 2024-12-07 10:12:44 +08:00
芋道源码 2efd0d69d9 !611 【缺陷修复】商城装修
Merge pull request !611 from puhui999/dev
2024-12-07 02:06:57 +00:00
YunaiV ee46a31a81 【文案调整】工作流:多实例(会签配置) => 多人审批方式 2024-12-07 09:59:58 +08:00
芋道源码 4835316fca !610 bpmn设计器适配simple设计器
Merge pull request !610 from Lesan/feature/bpm
2024-12-07 01:52:43 +00:00
jason 9bdafb0f6f 【缺陷修复】1、条件配置的表单字段必须为必填 2、配置方式 bug 2024-12-06 22:40:37 +08:00
puhui999 740de45a82 【缺陷修复】分销: 推广订单列表用户类型检索 2024-12-06 11:44:39 +08:00
puhui999 7917bca9fd 【缺陷修复】商城装修: 顶部导航栏-文字链接支持 2024-12-06 11:28:11 +08:00
puhui999 2835c9d111 Merge remote-tracking branch 'refs/remotes/yudao/dev' into dev 2024-12-05 17:48:01 +08:00
puhui999 f7477c3264 【缺陷修复】商城装修:解决模版修改不生效的问题 2024-12-05 17:46:36 +08:00
puhui999 9c2b13f2d0 【缺陷修复】商城装修: TitleBar 背景图设置统一为使用组件样式进行设置 2024-12-05 15:35:44 +08:00
Lesan e8315e2864 fix: CandidateParam类型应为String 2024-12-05 10:45:30 +08:00
Lesan c2f51b3c12 Merge branch 'feature/bpm' of https://gitee.com/LesanOuO/yudao-ui-admin-vue3 into feature/bpm 2024-12-05 09:29:10 +08:00
Lesan c00e6988a5 fix: 修改表单后字段权限需更新 2024-12-05 09:28:56 +08:00
Lesan 8ea43f30af fix: 雪花id导致int精度丢失 2024-12-04 15:49:58 +08:00
Lesan abdaf2e0ca refactor: 通过extensionElements来存储候选审批人 2024-12-04 15:39:40 +08:00
Lesan eb6f3a3e49 refactor: 通过ExtensionElements获取多实例类型 2024-12-04 15:21:22 +08:00
Lesan 175d6ba0cb feat: 添加所需flowableDescriptor 2024-12-04 13:39:11 +08:00
Lesan f0c797deac fix: 雪花id导致int精度丢失 2024-12-04 11:41:06 +08:00
Lesan 6e077ac7d9 feat: BPM设计器适配Simple设计器,多实例 2024-12-04 11:32:57 +08:00
Lesan 557bc29d24 fix: 业务表单字段权限为空报错 2024-12-04 08:37:45 +08:00
GoldenZqqq 5063db47f3 feat: 将流程模型新增/修改/设计整合到同一个页面中,分三个步骤进行;跳转传参逻辑与页面绘制 2024-12-03 11:13:53 +08:00
芋道源码 8e4d91f43e !609 bpm设计器适配simple设计器
Merge pull request !609 from Lesan/feature/bpm
2024-12-03 00:54:37 +00:00
Lesan 97b1ac6ad3 feat: bpm设计器适配simple设计器,超时审批 2024-12-02 14:47:06 +08:00
Lesan be33226178 fix: 边界事件切换类别时需监听businessObject 2024-12-02 09:50:29 +08:00
YunaiV 1a95687f61 【代码评审】工作流:审批列表 2024-12-01 15:37:20 +08:00
YunaiV f2f9391328 Merge branch 'master' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm
# Conflicts:
#	pnpm-lock.yaml
2024-12-01 15:21:12 +08:00
YunaiV 2de62667dc Merge branch 'feature/bpm' of https://gitee.com/tuituji111/yudao-ui-admin-vue3 into feature/bpm
# Conflicts:
#	src/views/bpm/processInstance/index.vue
2024-12-01 15:20:12 +08:00
芋道源码 c79f027d62 !608 update src/config/axios/service.ts.
Merge pull request !608 from nil/N/A
2024-12-01 07:19:14 +00:00
YunaiV 4754685d84 Merge branch 'master' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into dev 2024-12-01 15:18:23 +08:00
YunaiV 1597891244 【代码优化】商城:分销用户的创建 2024-12-01 15:07:50 +08:00
YunaiV b108b07a96 【代码优化】商城:分销用户的创建 2024-12-01 15:01:52 +08:00
芋道源码 7ee8cb89a8 !607 【功能新增】商城分销: 创建分销员
Merge pull request !607 from puhui999/dev
2024-12-01 06:22:44 +00:00
nil c665fad8bc update src/config/axios/service.ts.
判断条件没有在 if 中,没起到作用

Signed-off-by: nil <10466852+lkjj@user.noreply.gitee.com>
2024-11-30 10:54:45 +00:00
芋道源码 019bd9533e !592 导出权限问题、支付应用选择问题:update src/views/pay/order/index.vue.
Merge pull request !592 from 山野羡民/N/A
2024-11-30 00:59:07 +00:00
芋道源码 8ff9443f28 !606 修复引用表单设计器折叠面板和卡片组件
Merge pull request !606 from aho/master
2024-11-30 00:57:03 +00:00
芋道源码 e591aa8867 !605 合并(!549 工作流增加对serviceTask支持),并优化
Merge pull request !605 from Lesan/feature/bpm
2024-11-29 11:58:55 +00:00
芋道源码 9da5beb879 !604 【优化】优化路由是否为目录判断条件
Merge pull request !604 from 半栈幼儿员/hotfix/router
2024-11-29 11:30:18 +00:00
YunaiV e3a39b7732 降低 vue-router 从 4.5.0 到 4.4.5,解决 Redirect 重复的问题 2024-11-29 19:25:36 +08:00
puhui999 e0e287c88b 【功能新增】商城分销: 创建分销员 2024-11-29 17:33:06 +08:00
aho d0ae37da4e fix:修复引用表单设计器折叠面板和卡片组件 2024-11-28 15:20:59 +08:00
Lesan d017ab55db refactor: 优化任务折叠面板处理逻辑 2024-11-28 11:32:33 +08:00
preschooler e0bc260fc1 🎈 perf:优化路由目录判断 2024-11-28 11:22:18 +08:00
Lesan 70c3a09f76 Merge remote-tracking branch 'xunchangguo/bpm/serviceTask' into feature/bpm 2024-11-28 11:00:10 +08:00
芋道源码 f93919b750 !603 bpm设计器适配Simple设计器
Merge pull request !603 from Lesan/feature/bpm
2024-11-27 01:15:16 +00:00
Lesan 3c7526de2f feat: 重构自定义配置,支持对不同类型节点绩效配置 2024-11-26 14:38:50 +08:00
Lesan 886f3391aa Merge remote-tracking branch 'yudao/feature/bpm' into feature/bpm 2024-11-26 09:49:07 +08:00
Lesan 22e9eb2add feat: bpm设计器适配Simple设计器,字段权限 2024-11-26 09:45:35 +08:00
芋道源码 6dc5f5dcb5 !597 fix-雪花算法ID精度丢失
Merge pull request !597 from shixiaohe/master
2024-11-26 00:33:21 +00:00
tuituji111 455c192a9a 【优化】BPM 界面优化:工作流程/审批中心/我的流程 +3 2024-11-25 22:57:39 +08:00
YunaiV 9f8f790ec9 【代码评审】工作流:我的流程优化 2024-11-25 21:12:08 +08:00
YunaiV 7c391a315e Merge branch 'master' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm 2024-11-25 20:52:50 +08:00
YunaiV 9d8e06a54b Merge branch 'feature/bpm' of https://gitee.com/tuituji111/yudao-ui-admin-vue3 into feature/bpm
# Conflicts:
#	src/views/bpm/processInstance/index.vue
2024-11-25 20:50:59 +08:00
芋道源码 929b67b9ab !600 bpm设计器适配Simple设计器
Merge pull request !600 from Lesan/feature/bpm
2024-11-25 12:46:42 +00:00
YunaiV 690243dc4f Merge remote-tracking branch 'origin/master' 2024-11-25 20:36:24 +08:00
YunaiV a4dd4773ca Merge branch 'dev' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 2024-11-25 20:36:10 +08:00
芋道源码 6eb013b2a6 !601 同步商城的客服优化、门店自提优化
Merge pull request !601 from 芋道源码/dev
2024-11-25 12:35:35 +00:00
芋道源码 6dee7415cb !599 客服文本消息样式优化
Merge pull request !599 from puhui999/dev
2024-11-25 12:34:22 +00:00
Lesan 513ea33f3f fix: 审批通过时表单无法操作,由于拒绝时共用了approveFormFApi 2024-11-25 14:36:58 +08:00
Lesan 41f8f0cc9a feat: bpm设计器适配Simple设计器,操作按钮 2024-11-25 14:12:27 +08:00
Lesan 6041a6eb31 Merge remote-tracking branch 'yudao/feature/bpm' into feature/bpm 2024-11-25 13:37:44 +08:00
puhui999 ad0d4803e2 【功能完善】商城: 客服文本消息样式优化,文字两端对齐 2024-11-25 11:58:16 +08:00
Lesan e587d68fef fix: 解决存在循环节点时的bug 2024-11-25 02:14:21 +00:00
芋道源码 8e5a4d09d2 !598 商城客服优化
Merge pull request !598 from puhui999/dev
2024-11-25 01:18:43 +00:00
puhui999 101c704a8f 【功能完善】商城: 会员信息的优化 2024-11-24 16:17:10 +08:00
shixiaohe 154dd6c40b 修复雪花算法ID精度丢失 2024-11-24 15:53:31 +08:00
puhui999 d228df5210 【功能完善】商城: 客服商城的配色优化 2024-11-24 12:41:39 +08:00
puhui999 5cdc15387c 【功能完善】商城: 客服文本消息样式调整,内容居中 2024-11-24 12:37:27 +08:00
puhui999 64e2ddfa18 【功能完善】商城: 客服整体的每个区域的线框优化 2024-11-24 11:52:24 +08:00
puhui999 90c71804e2 【功能完善】商城: 客服会话列表的样式优化 2024-11-24 11:35:40 +08:00
tuituji111 f125df7dd2 【优化】BPM 界面优化:工作流程/审批中心/我的流程 +2 2024-11-23 15:34:34 +08:00
芋道源码 22199c64fd !595 bpm 仿钉钉/飞书模式
Merge pull request !595 from 芋道源码/feature/bpm
2024-11-23 02:36:14 +00:00
YunaiV 4ffaa2dc6e README:增加工作流的演示图片 2024-11-23 10:32:52 +08:00
芋道源码 7b4b5ebbd1 !594 bpm设计器适配Simple设计器
Merge pull request !594 from Lesan/feature/bpm
2024-11-23 01:56:08 +00:00
YunaiV 16aef13573 【功能修复】商城:prop 错误拼成 props 2024-11-22 20:09:13 +08:00
YunaiV bcc75bdd14 【功能完善】打包:max_old_space_size 默认 4096 2024-11-22 20:06:51 +08:00
YunaiV 4423c5f84a 【功能完善】工作流:分类新建时,默认选中开启 2024-11-22 20:04:18 +08:00
YunaiV 4b0d3ec29c Merge branch 'master' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm 2024-11-22 20:02:13 +08:00
YunaiV 97a68ce4ba Merge branch 'master' of https://github.com/yudaocode/yudao-ui-admin-vue3 2024-11-22 19:59:18 +08:00
芋道源码 068b32d0d4 !591 表单校验问题:update src/views/pay/app/components/AppForm.vue.
Merge pull request !591 from 山野羡民/N/A
2024-11-22 11:48:35 +00:00
Lesan 00a19cfc78 feat: bpm设计器适配Simple设计器,审批人为空时 2024-11-22 16:10:30 +08:00
Lesan 1d4339fac8 feat: bpm设计器适配Simple设计器,审批人拒绝时 2024-11-22 15:29:42 +08:00
GoldenZqqq 295a43d516 feat: 流程模型新增/修改弹窗-变更“谁可以发起“、”流程管理员”字段的选择逻辑与人员展示样式 2024-11-22 11:39:23 +08:00
山野羡民 41b8407841 导出权限问题、支付应用选择问题:update src/views/pay/order/index.vue.
Signed-off-by: 山野羡民 <liyujiang_tk@yeah.net>
2024-11-22 03:25:24 +00:00
山野羡民 f1e161279c 表单校验问题:update src/views/pay/app/components/AppForm.vue.
Signed-off-by: 山野羡民 <liyujiang_tk@yeah.net>
2024-11-22 03:15:18 +00:00
GoldenZqqq 8f7a75bf3b Merge branch 'feature/bpm' of https://github.com/GoldenZqqq/yudao-ui-admin-vue3 into feature/bpm 2024-11-22 09:27:02 +08:00
GoldenZqqq 29a0c691a0 feat: 优化全局人员选择弹窗逻辑-部门树切换改为前端筛选过滤部门下用户 2024-11-22 09:26:11 +08:00
jason 47f1f13ca2 【代码评审修改】审批人为表单用户 2024-11-22 08:21:34 +08:00
YunaiV 24e1374993 【功能完善】工作流:详情优化 2024-11-21 19:48:09 +08:00
YunaiV 3a9f8db3fa Merge branch 'master' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm
# Conflicts:
#	src/views/bpm/processInstance/detail/index.vue
2024-11-21 19:46:25 +08:00
YunaiV 397c09a195 Merge branch 'feature/bpm' of https://github.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm 2024-11-21 19:35:13 +08:00
芋道源码 738289bc06 Merge pull request #106 from GoldenZqqq/feature/bpm
工作流模块优化与bug修复
2024-11-21 19:34:57 +08:00
芋道源码 f8b0cd53e5 Merge pull request #104 from chenlei65368/patch-1
Update Breadcrumb.vue  面包屑 css 处理
2024-11-21 19:29:26 +08:00
YunaiV 7895d184e8 【功能完善】商城: 客服动态计算消息最后发送时间距离现在过去了多久 2024-11-21 10:00:19 +08:00
芋道源码 fc5df39b1a !589 【功能完善】商城: 客服动态计算消息最后发送时间距离现在过去了多久
Merge pull request !589 from puhui999/dev
2024-11-21 01:57:22 +00:00
YunaiV a07f61a9ed 【代码评审】工作流:BPMN 设计器,增加 UserTask 自定义配置 2024-11-21 09:53:12 +08:00
芋道源码 2cc9652d4b !586 BPM模块的一些优化
Merge pull request !586 from Lesan/feature/bpm
2024-11-21 01:48:46 +00:00
芋道源码 a283dc8cba !585 【轻量级PR】右侧设置按钮没有z-index,导致表格滚动是会覆盖按钮
Merge pull request !585 from 熊猫大侠/master
2024-11-21 01:45:28 +00:00
芋道源码 b5d99e2c75 !590 判断如果没有权限设置不进行可编辑表单过滤,避免表单变量被过滤
Merge pull request !590 from shifeng1983/master
2024-11-21 01:44:49 +00:00
GoldenZqqq 39f183bb3e fix: 修复工作流审批页面不展示详情表单bug 2024-11-21 09:42:17 +08:00
shifeng 2d1b31a841 判断如果没有权限设置不进行可编辑表单过滤,避免表单变量被过滤 2024-11-21 08:34:43 +08:00
puhui999 4d2c038972 【功能完善】商城: 客服动态计算消息最后发送时间距离现在过去了多久 2024-11-20 23:44:25 +08:00
GoldenZqqq 138d535e87 fix: 发起流程页面-进入页面时左侧第一个分类没有active样式bug修复 2024-11-20 11:26:30 +08:00
Lei Chen 522e358eaf Update Breadcrumb.vue 面包屑 css 处理
面包屑 css 处理不起作用
2024-11-20 11:02:28 +08:00
芋道源码 056684e291 !588 【修复】恢复 axios 自定义参数序列化函数,并支持所有请求的 params 自定义序列化,修复刷新 token 后二次请求参数丢失问题。
Merge pull request !588 from 半栈幼儿员/hotfix/axios
2024-11-20 01:38:29 +00:00
YunaiV 12bd71e70b 【代码评审】工作流:表单选择审批人 2024-11-19 21:24:11 +08:00
YunaiV 158708c1b1 Merge branch 'feature/bpm' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm 2024-11-19 21:06:03 +08:00
preschooler 6cd6429102 🐞 fix:恢复 axios 自定义参数序列化函数,并支持所有请求的 params 自定义序列化,修复刷新 token 后二次请求参数丢失问题,优化 GET 请求缓存问题。 2024-11-19 19:24:49 +08:00
GoldenZqqq 7b8e4ea238 fix: 流程审核详情页-流程图未能撑满剩余区域bug修复 2024-11-19 16:27:31 +08:00
GoldenZqqq c34bfc18bc feat: 工作流详情审批页面-优化timeline组件头像右下角状态icon样式 2024-11-19 16:13:26 +08:00
GoldenZqqq caa31a67fe feat: 发起流程页面-左侧分类列表只展示有流程的分类 2024-11-19 15:54:52 +08:00
GoldenZqqq 5f2e23f4da feat: 流程发起页面-右侧流程滚动到对应的分类区域左侧分类active响应式变化 2024-11-19 15:09:45 +08:00
YunaiV 6304a8e9b6 【代码评审】工作流:详情界面 2024-11-19 13:18:58 +08:00
芋道源码 3783582b33 Merge pull request #102 from GoldenZqqq/feature/bpm
工作流发起与审核页面具体细节优化
2024-11-19 12:41:32 +08:00
芋道源码 ba2cdcc310 !587 【修复】修复对 axios 的处理
Merge pull request !587 from 半栈幼儿员/hoxfix/axios
2024-11-19 01:10:15 +00:00
preschooler c119e910d0 🐞 fix:修复对 axios 的处理 2024-11-18 11:39:57 +08:00
jason 2fe28af05d 【功能新增】候选人策略:新增表单部门负责人 2024-11-18 09:03:45 +08:00
jason 756addd423 【功能优化】条件节点:条件增加发起人选项 2024-11-17 16:47:18 +08:00
LesanOuO eefabf34a5 feat: bpmn设计器添加assignStartUserHandlerType适配Simple模式 2024-11-17 14:43:52 +08:00
jason 0924f50266 Merge branch 'feature/bpm' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm 2024-11-17 10:50:34 +08:00
jason 67769c299e 【功能新增】候选人策略:新增表单内成员字段 2024-11-17 10:49:57 +08:00
LesanOuO 6ac0ee5844 fix: 前端int类型导致精度丢失 2024-11-16 17:58:34 +08:00
LesanOuO da0a780163 fix: 选中节点无法新增属性,报错filter is not a function 2024-11-16 14:44:14 +08:00
LesanOuO 046e6ce5a7 fix: 当id为雪花id时无法传递id至路由 2024-11-16 14:37:42 +08:00
GoldenZqqq 2719541462 feat: 工作流审批页面-头像右下角状态icon本体颜色与背景色调整优化 2024-11-14 10:00:48 +08:00
GoldenZqqq 0db0b66051 feat: 审批详情页-流程图和流转记录的高度会随着父容器高度自动调整 2024-11-13 09:34:21 +08:00
GoldenZqqq 735837f0f9 feat: 优化审批详情页流程状态svg图标命名 2024-11-13 08:53:52 +08:00
GoldenZqqq 2bca7bb44a feat: 流程发起页面-timeline组件细节样式与icon优化 2024-11-12 13:49:33 +08:00
GoldenZqqq d03fdecc26 fix: 人选选择穿梭框组件切换部门不过滤已选中数据逻辑,优化布局样式 2024-11-12 13:25:50 +08:00
GoldenZqqq e451f483c5 feat: 发起流程时不展示timeline组件头像右下角小状态icon 2024-11-12 11:34:55 +08:00
GoldenZqqq 5eb415437d fix: 解决发起流程页面左侧分类与右侧流程分组顺序不统一问题 2024-11-12 11:22:37 +08:00
panda 2c8125e218 修正没有z-index会被表格层覆盖 2024-11-11 15:19:05 +08:00
YunaiV 3b49f7dec9 【功能完善】商城: 完善相关注释 2024-11-10 18:49:04 +08:00
芋道源码 18c7693ed6 !584 【功能完善】商城: 客服
Merge pull request !584 from puhui999/dev
2024-11-10 10:41:54 +00:00
puhui999 5e7afae972 【功能完善】商城: 客服消息JSON 化 2024-11-10 18:09:22 +08:00
puhui999 b9e1529623 【功能完善】商城: 客服消息JSON 化 2024-11-10 16:36:05 +08:00
puhui999 649c24ed51 【功能完善】商城: 客服商品组件显示优化 2024-11-10 15:25:27 +08:00
puhui999 fdee8d8628 【功能完善】商城: 客服会员信息 2024-11-10 14:15:15 +08:00
puhui999 33a4100ed4 【功能完善】商城: 客服会话排序调整 2024-11-10 13:09:27 +08:00
YunaiV 52a8c46a0a 【代码优化】工作流:去除多余的 :startUserSelectAssignees 传参 2024-11-10 11:46:45 +08:00
jason 8f32dce2d6 Merge branch 'feature/bpm' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm 2024-11-10 09:31:09 +08:00
jason 81d2a38363 【功能优化】发起人自选判断优化 2024-11-10 09:30:30 +08:00
YunaiV 4ba5dad22e 【代码评审】工作流:我发起的审批列表 2024-11-09 21:36:43 +08:00
芋道源码 91b1ecd3bb !583 【优化】BPM 界面优化:工作流程/审批中心/我的流程
Merge pull request !583 from tuituji111/feature/bpm
2024-11-09 13:17:33 +00:00
YunaiV 8b0778ca0c 【代码评审】商城:在线客服 2024-11-09 19:10:46 +08:00
芋道源码 e4b57bd8c0 !579 【功能完善】商城: 客服缓存
Merge pull request !579 from puhui999/dev-crm
2024-11-09 11:04:32 +00:00
芋道源码 1c23298d19 !578 【优化】添加自定义指令消除IDE警告
Merge pull request !578 from jinmh/master
2024-11-09 11:02:53 +00:00
YunaiV 2aa2b4609f Merge branch 'master' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm
# Conflicts:
#	src/components/bpmnProcessDesigner/package/theme/index.scss
2024-11-09 19:01:12 +08:00
tuituji111 7d29c6319f 【优化】BPM 界面优化:工作流程/审批中心/我的流程 2024-11-09 18:47:42 +08:00
puhui999 df34f65df8 【功能完善】商城: 客服会话消息缓存 2024-11-09 17:29:49 +08:00
puhui999 e342e91f79 【功能完善】商城: 客服会话缓存 2024-11-09 16:58:32 +08:00
YunaiV 066607ab08 【代码评审】工作流:新发起页的优化 2024-11-08 23:01:23 +08:00
芋道源码 d052356ff0 Merge pull request #100 from GoldenZqqq/feature/bpm
发起流程页面-修复右侧分类名称都为null的bug
2024-11-08 19:24:12 +08:00
GoldenZqqq 1e6762ea69 fix: 发起流程页面-修复右侧分类名称都为null的bug 2024-11-08 13:21:57 +08:00
YunaiV 0fae9a4ba4 【代码评审】工作流:新发起页的优化 2024-11-08 09:53:19 +08:00
YunaiV 5814826fa8 Merge branch 'feature/bpm' of https://github.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm
# Conflicts:
#	src/views/bpm/processInstance/create/ProcessDefinitionDetail.vue
#	src/views/bpm/processInstance/detail/ProcessInstanceTimeline.vue
2024-11-07 22:28:15 +08:00
芋道源码 32d0093488 Merge pull request #99 from GoldenZqqq/feature/bpm
发起流程界面功能点优化与bug修复
2024-11-07 22:24:16 +08:00
GoldenZqqq 6dd6f9c2a5 chore: 删除模拟假数据引入 2024-11-07 13:57:57 +08:00
GoldenZqqq 8df5991940 fix: 流程发起页面-修复重新发起时表单数据未回显问题 2024-11-07 13:49:18 +08:00
GoldenZqqq 8b5740d05c feat: 工作流发起页面-需要自定义选择审批人的节点改为在右侧timeline组件中跳出弹窗选择 2024-11-07 13:21:04 +08:00
GoldenZqqq 92314ee945 feat: 自定义选择审批人逻辑TODO,选择用户弹窗绘制与选择交互逻辑处理 2024-11-06 17:17:44 +08:00
GoldenZqqq 0fbd98291e fix: 修复时间线组件中存在审批意见时,候选人名称头像样式布局错位问题 2024-11-06 15:55:48 +08:00
芋道源码 31cef4f3bc !577 【功能优化】商城: 修复活动选择商品时 sku 价格显示不正确的问题
Merge pull request !577 from puhui999/dev-crm
2024-11-05 12:54:18 +00:00
jinmh716 8a97a33a29 【优化】添加自定义指令消除IDE警告 2024-11-05 17:30:23 +08:00
GoldenZqqq cd69659c93 feat: 发起流程页面-增加搜索框、前端搜索功能完成;右侧展示所有流程,通过滚动或者点击,可以切换分类;鼠标hover时,展示描述 2024-11-05 17:22:37 +08:00
puhui999 1c3be2b92a Merge remote-tracking branch 'yudao/dev' into dev-crm 2024-11-05 16:30:13 +08:00
puhui999 078407fa10 【功能优化】商城: 修复活动选择商品时 sku 价格显示不正确的问题 2024-11-05 16:07:44 +08:00
YunaiV 1e39af84d3 【代码删除】工作流:删除老的加签组件 2024-11-05 13:59:33 +08:00
YunaiV 6f5cc3248f 【代码评审】商城:完善 MemberInfo.vue 的注释 2024-11-05 09:50:47 +08:00
芋道源码 d3f3044655 !576 【功能完善】商城客服
Merge pull request !576 from puhui999/dev-crm
2024-11-05 01:38:37 +00:00
puhui999 70abc5fc17 【功能完善】商城客服: 消息获取改为游标查询 2024-11-04 17:59:30 +08:00
puhui999 ce3380fa3f 【功能完善】商城客服: 消息样式调整 2024-11-04 16:44:10 +08:00
puhui999 40d7f5786d 【功能完善】商城客服: 会员足迹样式调整 2024-11-04 16:14:58 +08:00
puhui999 34fe09e2db 【功能完善】商城客服: 客服布局样式调整 2024-11-04 15:25:43 +08:00
jason 55da358336 【功能修改】发起流程时,如果是 Simple 模型展示 Simple 流程图 2024-11-03 23:12:13 +08:00
jason 65f0b2f8ab 【功能修复】 修复流程详情页面 请求模型视图接口两次的问题 2024-11-03 22:06:35 +08:00
jason 13272c2293 【代码修复】 Simple 设计器缩小留白问题 2024-11-03 20:12:49 +08:00
jason 21fc3fd7e6 【功能重构】减签功能页面重构 2024-11-02 22:30:05 +08:00
YunaiV 71de0c3ecf 【代码优化】工作流:1)完善相关注释;2)解决模型每次操作后,刷新列表后,重渲染会导致滚动条的位置不对的问题 2024-11-02 19:55:13 +08:00
YunaiV c3cf479f6f 【功能新增】工作流:流程模型增加批量修改 sort 交互 2024-11-02 18:11:35 +08:00
YunaiV 77062cb391 【功能新增】工作流:流程分类增加批量修改 sort 界面 2024-11-02 16:58:32 +08:00
YunaiV 1bb6a23ff8 【功能实现】工作流:流程模型的分类、模型读取 list 接口
【功能实现】工作流:流程模型的分类名的修改实现
2024-11-02 16:36:54 +08:00
YunaiV fec7af453f 【代码优化】工作流:去除 DELEGATE 委托中的状态,适合通过 DELEGATE STATE 读取 2024-11-02 14:12:05 +08:00
YunaiV 2df0aa20ff 【代码优化】工作流:去除多余的 debugger 断点 2024-11-02 14:02:03 +08:00
YunaiV 98e9ba49ae 【功能修改】工作流:流程审批详情的“流程记录”,从 timeline 改成 list table 2024-11-02 11:58:34 +08:00
YunaiV 5121d5694d 【功能合并】工作流:发起流程,使用新的界面 2024-11-02 10:52:31 +08:00
芋道源码 bf87c46d0f !574 【修复】开发模式下从“同时使用了用户信息和权限判断”的页面退出登录时异常问题
Merge pull request !574 from 半栈幼儿员/hotfix/user
2024-11-02 02:41:02 +00:00
YunaiV c52a7e5012 Merge branch 'feature/bpm' of https://github.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm
# Conflicts:
#	src/views/bpm/processInstance/detail/ProcessInstanceTimeline.vue
2024-11-02 09:54:56 +08:00
芋道源码 12f52b288b Merge pull request #98 from GoldenZqqq/feature/bpm
发起流程页面优化
2024-11-02 09:53:45 +08:00
YunaiV 3202378b04 【功能修复】审批详情页,审批通过后,bpmn 不会重新渲染高亮的问题 2024-11-02 09:32:35 +08:00
YunaiV a097560f81 【功能优化】条件分支、包容分支后面不允许直接添加并行分支 2024-11-02 09:11:19 +08:00
jason e504be7d42 【功能优化】条件分支、包容分支后面不允许直接添加并行分支 2024-11-01 23:04:00 +08:00
YunaiV 812e5b5865 【功能修复】工作流:修复流程预测的 candidateUsers 展示不正确 2024-10-31 22:47:10 +08:00
jason 8465f8fada 【功能完善】仿钉钉流程模型浏览,增加弹窗显示用户任务信息 2024-10-29 23:54:51 +08:00
jason 7044bcee60 Merge branch 'feature/bpm' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm 2024-10-29 21:24:46 +08:00
jason 0284893b4a 【功能完善】仿钉钉流程浏览功能完善 2024-10-29 21:24:03 +08:00
GoldenZqqq a57840c804 feat: 发起流程页面-流程时间线的数据获取接口联调与展示 2024-10-29 16:32:08 +08:00
preschooler 9b290ae3ce 🐞 fix:修复从“同时使用了用户信息和权限判断”的页面退出登录时异常问题 2024-10-29 12:35:44 +08:00
jason c9b12c8b39 【功能优化】仿钉钉流程设计优化 2024-10-29 10:52:46 +08:00
YunaiV 67430a83fb 【功能修复】工作流:流程审批不通过后,重启发起时,直接报错的问题 2024-10-28 19:02:20 +08:00
GoldenZqqq c1109e90c4 feat: 发起流程-填写表单页面单独抽取子组件,总体布局调整与样式绘制 2024-10-28 16:17:14 +08:00
jason a43bca5785 Merge branch 'feature/bpm' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm 2024-10-28 10:02:59 +08:00
jason 137b33e7cf 【功能新增】 仿钉钉流程模型增加浏览模式 2024-10-28 10:02:35 +08:00
YunaiV 458242a27b README 更新:单体、微服务的地址 2024-10-28 09:56:55 +08:00
YunaiV cf200b2618 README 更新:单体、微服务的地址 2024-10-28 09:54:08 +08:00
芋道源码 c1924d566f !570 【新增】TagsView 支持多个 path 相同但 fullPath 不相同情况。
Merge pull request !570 from 半栈幼儿员/hotfix/tagsView
2024-10-27 09:29:03 +00:00
芋道源码 b4139676f3 !571 【优化】@import 修改为 @use 以应对 sass 升级弃用 @import。
Merge pull request !571 from 半栈幼儿员/hotfix/sass
2024-10-27 08:42:32 +00:00
芋道源码 92f4160fe1 !572 修复:将超级管理员的角色code由admin改为super_admin
Merge pull request !572 from ZG4vin/master
2024-10-27 08:40:35 +00:00
YunaiV c1a2228022 Merge branch 'master' of https://github.com/yudaocode/yudao-ui-admin-vue3
# Conflicts:
#	src/utils/is.ts
2024-10-27 16:40:00 +08:00
芋道源码 71a2fddb1b !568 修复hash路由无法跳转
Merge pull request !568 from hecongyuan/master
2024-10-27 08:37:35 +00:00
芋道源码 8bdf60c886 !573 【修复】修复退出登录后依旧在轮询消息未读数,导致反复弹窗。
Merge pull request !573 from 半栈幼儿员/hotfix/message
2024-10-27 08:36:40 +00:00
芋道源码 38360eef12 Merge pull request #95 from hecongyuan/master
修复hash路由无法跳转的问题
2024-10-27 16:25:53 +08:00
YunaiV d599134de6 【功能完善】工作流:审批详情 approve-nodes => activity-nodes 字段 2024-10-26 22:55:28 +08:00
YunaiV dca54d0760 【功能完善】工作流:审批详情界面,增加 end 图标 2024-10-26 21:30:59 +08:00
GoldenZqqq bd4ac52a2d feat: 流程发起模块-第一步根据流程分类选择对应的流程页面样式与布局优化 2024-10-25 15:17:31 +08:00
preschooler 17225ef3df 🐞 fix:修复退出登录后依旧在轮询消息未读数,导致反复弹窗。 2024-10-25 10:11:42 +08:00
YunaiV d477b35f83 Merge branch 'feature/bpm' of https://github.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm 2024-10-24 19:06:24 +08:00
芋道源码 a6f325b14d Merge pull request #96 from GoldenZqqq/feature/bpm
工作流审批页面-时间线组件样式布局重构与展示逻辑调整
2024-10-24 19:06:05 +08:00
ZG4vin 57ab876886 将超级管理员的角色code由admin改为super_admin 2024-10-24 17:48:35 +08:00
preschooler 8a8ae557f7 🐞 fix:@import 修改为 @use 以应对 sass 升级弃用 @import。 2024-10-23 11:01:23 +08:00
GoldenZqqq c7ecb0a69f feat: 工作流审批页面-时间线组件样式布局重构与展示逻辑调整 2024-10-23 10:58:43 +08:00
preschooler a401d8ede4 ✨ feat:TagsView 支持多个 path 相同但 fullPath 不相同情况。 2024-10-22 17:22:18 +08:00
GoldenZqqq de073e82bc feat: 工作流审批页面timeline组件节点icon展示样式自定义特殊处理 2024-10-22 16:23:35 +08:00
GoldenZqqq dfa9cd5a3c fix: 点击修改流程模型按钮报错问题修复 2024-10-22 15:42:20 +08:00
hecongyuan 3859a48825 修复hash路由无法跳转的问题 2024-10-21 17:10:36 +08:00
hecongyuan 0a4a9e0e55 修复hash路由无法跳转的问题 2024-10-21 17:05:05 +08:00
jason 30052b05f5 Merge branch 'feature/bpm' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm 2024-10-20 21:51:48 +08:00
jason 520fc784bc 【功能修改】 新增包容分支 2024-10-20 21:45:02 +08:00
YunaiV cd9cefa2c8 【功能修改】工作流:审批意见直接展示,不通过 tooltip 2024-10-20 10:24:35 +08:00
YunaiV ffc09e8e35 【功能优化】工作流:表单默认从 125px 到 100px,整体预览表单更好看一点~ 2024-10-20 08:58:52 +08:00
YunaiV aa6610e36c 【文案修改】工作流:“撤销”统一改成“取消”,更好理解 2024-10-19 22:03:37 +08:00
YunaiV 43b7dff0d0 【功能调整】工作流:工作流程的流转记录,不区分父子任务 2024-10-19 21:53:05 +08:00
YunaiV dcdce4120d 【文案修改】工作流:“回退”统一改成“退回”,和飞书保持一致 2024-10-19 19:01:38 +08:00
YunaiV 410c3bc9a7 【功能修改】工作流:仿钉钉模式,默认任务的权限都开启 2024-10-19 18:51:56 +08:00
YunaiV d6e89db05c 【功能修改】工作流:调整 cc 抄送表的字段 2024-10-19 18:23:22 +08:00
YunaiV 1f34aa8dee Merge branch 'feature/bpm' of https://github.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm 2024-10-19 11:00:35 +08:00
芋道源码 5116b94873 Merge pull request #94 from GoldenZqqq/feature/bpm
流程模型模块-优化一些样式细节、解决拖动排序逻辑问题
2024-10-19 10:52:48 +08:00
jason 5267fe459f 【功能修改】 新审批页面 v4 部分修改 2024-10-18 21:16:44 +08:00
GoldenZqqqq 498244020d fix: 修复流程分类组不在拖拽状态下也能拖动的问题,优化样式细节 2024-10-18 20:49:08 +08:00
jason 0f8522e306 【功能修改】 新审批页面新增取消和再次提交 2024-10-17 23:23:35 +08:00
jason f47e8f3a25 【功能修改】 审批意见 使用 tooltip 显示 2024-10-17 23:23:35 +08:00
GoldenZqqq d818e338e3 feat: 弃用el-collapse组件,改用el-collapse-transition代替,提高组件自定义程度 2024-10-17 16:18:02 +08:00
GoldenZqqq fb2ea334b9 fix: 修复流程搜索框按回车后刷新页面bug 2024-10-17 15:36:15 +08:00
YunaiV ac13524515 Merge branch 'bpm-3st-stage' of https://github.com/GoldenZqqq/yudao-ui-admin-vue3 into feature/bpm
# Conflicts:
#	pnpm-lock.yaml
2024-10-17 12:48:55 +08:00
YunaiV c7bd9f7138 Merge branch 'dev' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm
# Conflicts:
#	pnpm-lock.yaml
#	src/router/modules/remaining.ts
2024-10-17 12:46:55 +08:00
GoldenZqqq 4bc79c382b feat: 空流程分类展示新建按钮、回显分类逻辑 2024-10-17 09:40:40 +08:00
jason 3a9197dfdf 【功能修改】 整合审批详情接口 2024-10-16 22:31:20 +08:00
芋道源码 a31bafbb4a !566 【优化】文章列表显示ID方便构造链接,调整退款状态列宽显示完整
Merge pull request !566 from 杨宇庆/N/A
2024-10-16 11:27:58 +00:00
GoldenZqqq 708e12b6ce feat: 优化交互细节、删除流程分类接口联调 2024-10-16 16:23:41 +08:00
GoldenZqqq 91ea4b7936 feat: 流程模型分类排序功能实现、数据获取分组逻辑优化、排序状态下按钮显示逻辑控制 2024-10-16 15:19:56 +08:00
GoldenZqqq 2db1f726c3 feat: 流程模型分类排序按钮显示逻辑完善 2024-10-15 16:29:45 +08:00
GoldenZqqq 57820cb3e1 faet: 分类下表格数据拖拽排序功能实现;重命名分类弹窗绘制;删除分类逻辑判断与提示框 2024-10-15 15:17:40 +08:00
杨宇庆 10e432243e 【优化】文章列表显示ID方便构造链接,调整退款状态列宽显示完整
Signed-off-by: 杨宇庆 <hiyyq@qq.com>
2024-10-15 04:46:34 +00:00
GoldenZqqq a033706fb4 feat: 流程模型列表页面样式与文字细节调整优化 2024-10-15 08:44:57 +08:00
GoldenZqqq ea02192b64 chore 2024-10-15 08:39:11 +08:00
芋道源码 1d01955b8b Merge pull request #89 from GoldenZqqq/bpm-2st-stage
流程模型列表UI重构以及数组分类分组展示逻辑
2024-10-13 21:47:25 +08:00
YunaiV 13706de227 Merge branch 'master' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into dev 2024-10-13 21:46:38 +08:00
GoldenZqqqq b056db55b2 feat: 分类表格拖拽排序功能实现 2024-10-13 21:11:03 +08:00
jason 842fe09e8b 【功能修改】 新审批页面增加减签 2024-10-13 20:07:42 +08:00
YunaiV 5ef5ee6e14 【功能修复】工作流:业务表单 + 字段可编辑场景,审批、不审批点击没效果 2024-10-13 16:45:19 +08:00
YunaiV d10a3af3d1 【功能优化】支付:转账回调地址,非必填。 2024-10-13 12:39:38 +08:00
芋道源码 6a9f5cc10f !561 【新增功能】添加微信转账到零钱提现功能
Merge pull request !561 from 痴货/jh-wxzz
2024-10-13 04:39:00 +00:00
YunaiV dcf1ad6b38 【代码优化】商城:门店自提的员工绑定 2024-10-13 09:47:12 +08:00
芋道源码 ca9a389ae8 !560 【新增功能】添加扫描枪、门店店员绑定功能
Merge pull request !560 from 痴货/jh-smq
2024-10-13 00:49:54 +00:00
YunaiV 2aefcd9070 【升级】vue from 3.4.21 to 3.5.12 2024-10-12 20:11:20 +08:00
YunaiV dad97e2f17 【修复】工作流:流程实例的查询界面,缺少搜索、重置按钮 2024-10-12 20:09:01 +08:00
芋道源码 22e425208c !564 【升级插件】升级 form-create 插件版本
Merge pull request !564 from 半栈幼儿员/hotfix/form-create
2024-10-12 11:59:53 +00:00
芋道源码 77e8827f8b !562 【修复】修复菜单搜索点击输入框会折叠问题
Merge pull request !562 from 半栈幼儿员/hotfix/search
2024-10-12 11:56:31 +00:00
YunaiV 6b06f320b9 Merge remote-tracking branch 'origin/feature/bpm' into feature/bpm
# Conflicts:
#	src/views/bpm/processInstance/detail/index_new.vue
2024-10-12 19:42:43 +08:00
YunaiV 33f3bc0567 【功能优化】工作流:BPMN 流程图高亮的计算,切换到后端为主 2024-10-12 19:41:38 +08:00
jason 4342c3ee05 【功能修改】 新审批界面气泡卡按钮功能 2024-10-10 23:09:05 +08:00
preschooler c828517c55 🎈 perf:升级 form-create 插件版本 2024-10-10 16:25:09 +08:00
preschooler 2ff9904f63 🐞 fix:初始化 form-create 配置,修复“字段 ID”不可修改问题。 2024-10-10 16:19:05 +08:00
preschooler fe21db3704 🐞 fix:修复因缺失引入 element plus 组件导致 form-create 表单设计器部分功能缺失。 2024-10-10 14:03:20 +08:00
preschooler abcecc92c5 🐞 fix:修复菜单搜索点击输入框会折叠问题 2024-10-10 10:48:14 +08:00
GoldenZqqq 9dc268efb4 feat: 样式UI细节优化 2024-10-09 13:25:53 +08:00
GoldenZqqq c49e8bd3f9 feat: 流程模型列表UI重构以及数组分类分组展示逻辑 2024-10-09 10:03:36 +08:00
jason a4a4dcdfc2 【功能修改】多人审批方式默认为按顺序依次审批,随机挑选一人功能可能被去掉 2024-10-08 21:49:04 +08:00
痴货 9ec660817c 【功能优化】增加转账回调接口 2024-10-08 21:28:37 +08:00
GoldenZqqqq b293f15779 chore: mock测试数据 2024-10-08 20:39:28 +08:00
痴货 c6b6d2f404 【功能优化】优化门店店员功能 2024-10-08 20:16:44 +08:00
痴货 a69dc3a187 【功能】添加扫描枪核销功能、根据当前登录人查询自提点功能和绑定自提员工功能 2024-10-08 20:16:39 +08:00
GoldenZqqq 753a32aecc feat: 流程模型页面布局重构 2024-10-08 17:26:32 +08:00
jason 019104f460 【代码评审修改】- 新审批界面部分修改 2024-10-08 12:42:38 +08:00
xunchangguo@126.com f545dcc63d Merge remote-tracking branch 'origin/master' 2024-09-10 16:16:14 +08:00
xunchangguo@126.com 09d1dbedf9 增加对serviceTask支持 2024-09-10 16:10:40 +08:00
260 changed files with 22131 additions and 14021 deletions

No files matched your search

+3
View File
@@ -32,3 +32,6 @@ VITE_MALL_H5_DOMAIN='http://mall.yudao.iocoder.cn'
# 验证码的开关
VITE_APP_CAPTCHA_ENABLE=true
# GoView域名
VITE_GOVIEW_URL='http://127.0.0.1:3000'
+3
View File
@@ -29,3 +29,6 @@ VITE_MALL_H5_DOMAIN='http://localhost:3000'
# 验证码的开关
VITE_APP_CAPTCHA_ENABLE=false
# GoView域名
VITE_GOVIEW_URL='http://127.0.0.1:3000'
+3
View File
@@ -29,3 +29,6 @@ VITE_OUT_DIR=dist-prod
# 商城H5会员端域名
VITE_MALL_H5_DOMAIN='http://mall.yudao.iocoder.cn'
# GoView域名
VITE_GOVIEW_URL='http://127.0.0.1:3000'
+3
View File
@@ -29,3 +29,6 @@ VITE_OUT_DIR=dist-stage
# 商城H5会员端域名
VITE_MALL_H5_DOMAIN='http://mall.yudao.iocoder.cn'
# GoView域名
VITE_GOVIEW_URL='http://127.0.0.1:3000'
+3
View File
@@ -29,3 +29,6 @@ VITE_OUT_DIR=dist-test
# 商城H5会员端域名
VITE_MALL_H5_DOMAIN='http://mall.yudao.iocoder.cn'
# GoView域名
VITE_GOVIEW_URL='http://127.0.0.1:3000'
Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

+15 -11
View File
@@ -69,11 +69,11 @@
支持 Spring Boot、Spring Cloud 两种架构:
① Spring Boot 单体架构:<https://github.com/YunaiV/ruoyi-vue-pro>
① Spring Boot 单体架构:<https://doc.iocoder.cn>
![架构图](/.image/common/ruoyi-vue-pro-architecture.png)
② Spring Cloud 微服务架构:<https://github.com/YunaiV/yudao-cloud>
② Spring Cloud 微服务架构:<https://cloud.iocoder.cn>
![架构图](/.image/common/yudao-cloud-architecture.png)
@@ -120,18 +120,22 @@
### 工作流程
| | 功能 | 描述 |
|-----|-------|----------------------------------------|
| 🚀 | 流程模型 | 配置工作流的流程模型,支持文件导入与在线设计流程图,提供 7 种任务分配规则 |
| 🚀 | 流程表单 | 拖动表单元素生成相应的工作流表单,覆盖 Element UI 所有的表单组件 |
| 🚀 | 用户分组 | 自定义用户分组,可用于工作流的审批分组 |
| 🚀 | 我的流程 | 查看我发起的工作流程,支持新建、取消流程等操作,高亮流程图、审批时间线 |
| 🚀 | 待办任务 | 查看自己【未】审批的工作任务,支持通过、不通过、转发、委派、退回等操作 |
| 🚀 | 已办任务 | 查看自己【已】审批的工作任务,未来会支持回退操作 |
| 🚀 | OA 请假 | 作为业务自定义接入工作流的使用示例,只需创建请求对应的工作流程,即可进行审批 |
| | 功能 | 描述 |
|----|-------|-----------------------------------------|
| 🚀 | 流程模型 | 配置工作流的流程模型,支持 BPMN 和仿钉钉/飞书设计器 |
| 🚀 | 流程表单 | 拖动表单元素生成相应的工作流表单,覆盖 Element UI 所有的表单组件 |
| 🚀 | 用户分组 | 自定义用户分组,可用于工作流的审批分组 |
| 🚀 | 我的流程 | 查看我发起的工作流程,支持新建、取消流程等操作,高亮流程图、审批时间线 |
| 🚀 | 待办任务 | 查看自己【未】审批的工作任务,支持通过、不通过、转派、委派、退回、加减签等操作 |
| 🚀 | 已办任务 | 查看自己【已】审批的工作任务,支持流程预测,展示未来审批人信息 |
| 🚀 | OA 请假 | 作为业务自定义接入工作流的使用示例,只需创建请求对应的工作流程,即可进行审批 |
![功能图](/.image/common/bpm-feature.png)
| BPMN 设计器 | 钉钉/飞书设计器 |
|------------------------------|--------------------------------|
| ![](/.image/工作流设计器-bpmn.jpg) | ![](/.image/工作流设计器-simple.jpg) |
### 支付系统
| | 功能 | 描述 |
+17 -14
View File
@@ -1,6 +1,6 @@
{
"name": "yudao-ui-admin-vue3",
"version": "2.3.0-snapshot",
"version": "2.4.1-snapshot",
"description": "基于vue3、vite4、element-plus、typesScript",
"author": "xingyu",
"private": false,
@@ -9,11 +9,11 @@
"dev": "vite --mode env.local",
"dev-server": "vite --mode dev",
"ts:check": "vue-tsc --noEmit",
"build:local": "node ./node_modules/vite/bin/vite.js build",
"build:dev": "node ./node_modules/vite/bin/vite.js build --mode dev",
"build:test": "node ./node_modules/vite/bin/vite.js build --mode test",
"build:stage": "node ./node_modules/vite/bin/vite.js build --mode stage",
"build:prod": "node ./node_modules/vite/bin/vite.js build --mode prod",
"build:local": "node --max_old_space_size=4096 ./node_modules/vite/bin/vite.js build",
"build:dev": "node --max_old_space_size=4096 ./node_modules/vite/bin/vite.js build --mode dev",
"build:test": "node --max_old_space_size=4096 ./node_modules/vite/bin/vite.js build --mode test",
"build:stage": "node --max_old_space_size=4096 ./node_modules/vite/bin/vite.js build --mode stage",
"build:prod": "node --max_old_space_size=4096 ./node_modules/vite/bin/vite.js build --mode prod",
"serve:dev": "vite preview --mode dev",
"serve:prod": "vite preview --mode prod",
"preview": "pnpm build:local && vite preview",
@@ -26,8 +26,8 @@
},
"dependencies": {
"@element-plus/icons-vue": "^2.1.0",
"@form-create/designer": "^3.1.3",
"@form-create/element-ui": "^3.1.24",
"@form-create/designer": "^3.2.6",
"@form-create/element-ui": "^3.2.11",
"@iconify/iconify": "^3.1.1",
"@microsoft/fetch-event-source": "^2.0.1",
"@videojs-player/vue": "^1.0.0",
@@ -38,7 +38,7 @@
"animate.css": "^4.1.1",
"axios": "^1.6.8",
"benz-amr-recorder": "^1.1.5",
"bpmn-js-token-simulation": "^0.10.0",
"bpmn-js-token-simulation": "^0.36.0",
"camunda-bpmn-moddle": "^7.0.1",
"cropperjs": "^1.6.1",
"crypto-js": "^4.2.0",
@@ -47,7 +47,7 @@
"driver.js": "^1.3.1",
"echarts": "^5.5.0",
"echarts-wordcloud": "^2.1.0",
"element-plus": "2.8.4",
"element-plus": "2.9.1",
"fast-xml-parser": "^4.3.2",
"highlight.js": "^11.9.0",
"jsencrypt": "^3.3.2",
@@ -64,14 +64,16 @@
"pinia-plugin-persistedstate": "^3.2.1",
"qrcode": "^1.5.3",
"qs": "^6.12.0",
"sortablejs": "^1.15.3",
"steady-xml": "^0.1.0",
"url": "^0.11.3",
"video.js": "^7.21.5",
"vue": "3.4.21",
"vue": "3.5.12",
"vue-dompurify-html": "^4.1.4",
"vue-i18n": "9.10.2",
"vue-router": "^4.3.0",
"vue-router": "4.4.5",
"vue-types": "^5.1.1",
"vue3-signature": "^0.2.4",
"vuedraggable": "^4.1.0",
"web-storage-cache": "^1.1.1",
"xml-js": "^1.6.11"
@@ -95,8 +97,8 @@
"@vitejs/plugin-vue": "^5.0.4",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"autoprefixer": "^10.4.17",
"bpmn-js": "8.9.0",
"bpmn-js-properties-panel": "0.46.0",
"bpmn-js": "^17.9.2",
"bpmn-js-properties-panel": "5.23.0",
"consola": "^3.2.3",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
@@ -143,6 +145,7 @@
"url": "https://gitee.com/yudaocode/yudao-ui-admin-vue3/issues"
},
"homepage": "https://gitee.com/yudaocode/yudao-ui-admin-vue3",
"web-types": "./web-types.json",
"engines": {
"node": ">= 16.0.0",
"pnpm": ">=8.6.0"
+7002 -8042
View File
File diff suppressed because it is too large. Load diff
-8
View File
@@ -1,8 +0,0 @@
import request from '@/config/axios'
export const getActivityList = async (params) => {
return await request.get({
url: '/bpm/activity/list',
params
})
}
+10
View File
@@ -36,6 +36,16 @@ export const CategoryApi = {
return await request.put({ url: `/bpm/category/update`, data })
},
// 批量修改流程分类的排序
updateCategorySortBatch: async (ids: number[]) => {
return await request.put({
url: `/bpm/category/update-sort-batch`,
params: {
ids: ids.join(',')
}
})
},
// 删除流程分类
deleteCategory: async (id: number) => {
return await request.delete({ url: `/bpm/category/delete?id=` + id })
+16 -2
View File
@@ -26,8 +26,8 @@ export type ModelVO = {
bpmnXml: string
}
export const getModelPage = async (params) => {
return await request.get({ url: '/bpm/model/page', params })
export const getModelList = async (name: string | undefined) => {
return await request.get({ url: '/bpm/model/list', params: { name } })
}
export const getModel = async (id: string) => {
@@ -38,6 +38,16 @@ export const updateModel = async (data: ModelVO) => {
return await request.put({ url: '/bpm/model/update', data: data })
}
// 批量修改流程分类的排序
export const updateModelSortBatch = async (ids: number[]) => {
return await request.put({
url: `/bpm/model/update-sort-batch`,
params: {
ids: ids.join(',')
}
})
}
export const updateModelBpmn = async (data: ModelVO) => {
return await request.put({ url: '/bpm/model/update-bpmn', data: data })
}
@@ -62,3 +72,7 @@ export const deleteModel = async (id: number) => {
export const deployModel = async (id: number) => {
return await request.post({ url: '/bpm/model/deploy?id=' + id })
}
export const cleanModel = async (id: number) => {
return await request.delete({ url: '/bpm/model/clean?id=' + id })
}
+18 -13
View File
@@ -1,6 +1,6 @@
import request from '@/config/axios'
import { ProcessDefinitionVO } from '@/api/bpm/model'
import { NodeType } from '@/components/SimpleProcessDesignerV2/src/consts'
import { NodeType, CandidateStrategy } from '@/components/SimpleProcessDesignerV2/src/consts'
export type Task = {
id: string
name: string
@@ -24,30 +24,31 @@ export type ProcessInstanceVO = {
// 用户信息
export type User = {
id: number,
nickname: string,
id: number
nickname: string
avatar: string
}
// 审批任务信息
export type ApprovalTaskInfo = {
id: number,
ownerUser: User,
assigneeUser: User,
status: number,
id: number
ownerUser: User
assigneeUser: User
status: number
reason: string
signPicUrl: string
}
// 审批节点信息
export type ApprovalNodeInfo = {
id : number
id: number
name: string
nodeType: NodeType
candidateStrategy?: CandidateStrategy
status: number
startTime?: Date
endTime?: Date
candidateUserList?: User[]
candidateUsers?: User[]
tasks: ApprovalTaskInfo[]
}
@@ -88,12 +89,16 @@ export const getProcessInstanceCopyPage = async (params: any) => {
}
// 获取审批详情
export const getApprovalDetail = async (processInstanceId?:string, processDefinitionId?:string) => {
const param = processInstanceId ? '?processInstanceId='+ processInstanceId : '?processDefinitionId='+ processDefinitionId
return await request.get({ url: 'bpm/process-instance/get-approval-detail'+ param })
export const getApprovalDetail = async (params: any) => {
return await request.get({ url: 'bpm/process-instance/get-approval-detail', params })
}
// 获取表单字段权限
export const getFormFieldsPermission = async (params: any) => {
return await request.get({ url: '/bpm/process-instance/get-form-fields-permission', params })
}
// 获取流程实例的 BPMN 模型视图
export const getProcessInstanceBpmnModelView = async (id: string) => {
return await request.get({ url: '/bpm/process-instance/get-bpmn-model-view?id=' + id })
}
+16 -15
View File
@@ -9,10 +9,10 @@ export enum TaskStatusEnum {
*/
NOT_START = -1,
/**
/**
* 待审批
*/
WAIT = 0,
WAIT = 0,
/**
* 审批中
*/
@@ -26,7 +26,7 @@ export enum TaskStatusEnum {
* 审批不通过
*/
REJECT = 3,
/**
* 已取消
*/
@@ -35,19 +35,10 @@ export enum TaskStatusEnum {
* 已退回
*/
RETURN = 5,
/**
* 委派中
*/
DELEGATE = 6,
/**
* 审批通过中
*/
APPROVING = 7,
}
export type TaskVO = {
id: number
APPROVING = 7
}
export const getTaskTodoPage = async (params: any) => {
@@ -76,12 +67,12 @@ export const getTaskListByProcessInstanceId = async (processInstanceId: string)
})
}
// 获取所有可回退的节点
// 获取所有可退回的节点
export const getTaskListByReturn = async (id: string) => {
return await request.get({ url: '/bpm/task/list-by-return', params: { id } })
}
// 回退
// 退回
export const returnTask = async (data: any) => {
return await request.put({ url: '/bpm/task/return', data })
}
@@ -106,6 +97,16 @@ export const signDeleteTask = async (data: any) => {
return await request.delete({ url: '/bpm/task/delete-sign', data })
}
// 抄送
export const copyTask = async (data: any) => {
return await request.put({ url: '/bpm/task/copy', data })
}
// 获取我的待办任务
export const myTodoTask = async (processInstanceId: string) => {
return await request.get({ url: '/bpm/task/my-todo?processInstanceId=' + processInstanceId })
}
// 获取减签任务列表
export const getChildrenTaskList = async (id: string) => {
return await request.get({ url: '/bpm/task/list-by-parent-task-id?parentTaskId=' + id })
+8 -7
View File
@@ -22,11 +22,6 @@ export const register = (data: RegisterVO) => {
return request.post({ url: '/system/auth/register', data })
}
// 刷新访问令牌
export const refreshToken = () => {
return request.post({ url: '/system/auth/refresh-token?refreshToken=' + getRefreshToken() })
}
// 使用租户名,获得租户编号
export const getTenantIdByName = (name: string) => {
return request.get({ url: '/system/tenant/get-id-by-name?name=' + name })
@@ -76,11 +71,17 @@ export const socialAuthRedirect = (type: number, redirectUri: string) => {
})
}
// 获取验证图片以及 token
export const getCode = (data) => {
export const getCode = (data: any) => {
debugger
return request.postOriginal({ url: 'system/captcha/get', data })
}
// 滑动或者点选验证
export const reqCheck = (data) => {
export const reqCheck = (data: any) => {
return request.postOriginal({ url: 'system/captcha/check', data })
}
// 通过短信重置密码
export const smsResetPassword = (data: any) => {
return request.post({ url: '/system/auth/reset-password', data })
}
+1 -1
View File
@@ -27,7 +27,7 @@ export const authorize = (
return request.post({
url: '/system/oauth2/authorize',
headers: {
'Content-type': 'application/x-www-form-urlencoded'
'Content-Type': 'application/x-www-form-urlencoded'
},
params: {
response_type: responseType,
@@ -21,6 +21,10 @@ export const KeFuConversationApi = {
getConversationList: async () => {
return await request.get({ url: '/promotion/kefu-conversation/list' })
},
// 获得客服会话
getConversation: async (id: number) => {
return await request.get({ url: `/promotion/kefu-conversation/get?id=` + id })
},
// 客服会话置顶
updateConversationPinned: async (data: any) => {
return await request.put({
@@ -30,6 +34,6 @@ export const KeFuConversationApi = {
},
// 删除客服会话
deleteConversation: async (id: number) => {
return await request.delete({ url: `/promotion/kefu-conversation/delete?id=${id}`})
return await request.delete({ url: `/promotion/kefu-conversation/delete?id=${id}` })
}
}
+3 -3
View File
@@ -29,8 +29,8 @@ export const KeFuMessageApi = {
url: '/promotion/kefu-message/update-read-status?conversationId=' + conversationId
})
},
// 获得消息分页数据
getKeFuMessagePage: async (params: any) => {
return await request.get({ url: '/promotion/kefu-message/page', params })
// 获得消息列表(流式加载)
getKeFuMessageList: async (params: any) => {
return await request.get({ url: '/promotion/kefu-message/list', params })
}
}
@@ -13,6 +13,11 @@ export interface BrokerageUserVO {
avatar: string
}
// 创建分销用户
export const createBrokerageUser = (data: any) => {
return request.post({ url: '/trade/brokerage-user/create', data })
}
// 查询分销用户列表
export const getBrokerageUserPage = async (params: any) => {
return await request.get({ url: `/trade/brokerage-user/page`, params })
@@ -13,10 +13,11 @@ export interface DeliveryPickUpStoreVO {
latitude: number
longitude: number
status: number
verifyUserIds: number[] // 绑定用户编号组数
}
// 查询自提门店列表
export const getDeliveryPickUpStorePage = async (params) => {
export const getDeliveryPickUpStorePage = async (params: any) => {
return await request.get({ url: '/trade/delivery/pick-up-store/page', params })
}
@@ -26,8 +27,8 @@ export const getDeliveryPickUpStore = async (id: number) => {
}
// 查询自提门店精简列表
export const getListAllSimple = async (): Promise<DeliveryPickUpStoreVO[]> => {
return await request.get({ url: '/trade/delivery/pick-up-store/list-all-simple' })
export const getSimpleDeliveryPickUpStoreList = async (): Promise<DeliveryPickUpStoreVO[]> => {
return await request.get({ url: '/trade/delivery/pick-up-store/simple-list' })
}
// 新增自提门店
@@ -44,3 +45,8 @@ export const updateDeliveryPickUpStore = async (data: DeliveryPickUpStoreVO) =>
export const deleteDeliveryPickUpStore = async (id: number) => {
return await request.delete({ url: '/trade/delivery/pick-up-store/delete?id=' + id })
}
// 绑定自提店员
export const bindStoreStaffId = async (data: any) => {
return await request.post({ url: '/trade/delivery/pick-up-store/bind', data })
}
+2
View File
@@ -8,6 +8,7 @@ export interface AppVO {
remark: string
payNotifyUrl: string
refundNotifyUrl: string
transferNotifyUrl: string
merchantId: number
merchantName: string
createTime: Date
@@ -19,6 +20,7 @@ export interface AppPageReqVO extends PageParam {
remark?: string
payNotifyUrl?: string
refundNotifyUrl?: string
transferNotifyUrl?: string
merchantName?: string
createTime?: Date[]
}
+1
View File
@@ -0,0 +1 @@
<svg t="1731390087280" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4297" width="200" height="200"><path d="M639.9 541.7c76.4-44.2 127.9-126.8 127.9-221.5C767.7 179 653.2 64.5 512 64.5S256.3 179 256.3 320.2c0 89.6 46.1 168.4 115.8 214.1C193.5 593 64.5 761.2 64.5 959.5h63.9c0-211.5 172.1-383.6 383.6-383.6 44.9 0 87.8 8.1 127.9 22.4v-56.6zM320.2 320.2c0-105.8 86-191.8 191.8-191.8s191.8 86 191.8 191.8S617.7 512 512 512s-191.8-86-191.8-191.8zM831.6 767.7V639.9h-63.9v127.8H639.9v63.9h127.8v127.9h63.9V831.6h127.9v-63.9z" fill="#5f6266" p-id="4298"></path></svg>

After

Width:  |  Height:  |  Size: 608 B

File renamed without changes.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

+1
View File
@@ -0,0 +1 @@
<svg t="1729561718271" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8640" width="200" height="200"><path d="M908.5952 920.4224H164.7616a31.0784 31.0784 0 0 1-30.976-30.976c0-17.0496 13.9264-30.976 30.976-30.976h743.8336c17.0496 0 31.0272 13.9264 31.0272 30.976a31.0784 31.0784 0 0 1-31.0272 30.976z m0-123.9552H164.7616a31.0784 31.0784 0 0 1-30.976-30.976v-154.9824c0-51.1488 41.8304-92.9792 92.9792-92.9792h198.3488c-6.1952-37.1712-24.7808-72.8064-51.1488-103.8336a216.576 216.576 0 0 1-54.2208-144.128c0-58.88 23.2448-114.688 66.6112-156.4672C429.7728 71.168 485.5296 51.0976 545.9968 52.6848c111.5648 4.608 206.08 100.6592 207.616 212.2752 1.536 55.808-20.1216 110.0288-57.344 151.8592-26.3168 27.904-41.8304 61.952-48.0256 100.7104h198.3488c51.2 0 93.0304 41.8304 93.0304 92.9792v154.9824a31.0784 31.0784 0 0 1-31.0272 30.976z m-712.8064-61.952H877.568v-124.0064a31.0784 31.0784 0 0 0-31.0272-30.976h-232.448a31.0784 31.0784 0 0 1-30.976-31.0272c0-65.024 23.2448-127.0784 66.6624-173.568 27.8528-29.3888 41.8304-68.1472 41.8304-108.4416-1.536-80.5888-68.1984-148.7872-148.7872-151.8592a150.528 150.528 0 0 0-113.152 43.3664 153.6 153.6 0 0 0-48.0256 111.616c0 37.1712 13.9776 74.3424 38.7584 102.2464 44.9536 51.1488 69.7344 113.152 69.7344 176.64a31.0784 31.0784 0 0 1-30.976 31.0272h-232.448a31.0784 31.0784 0 0 0-30.976 30.976v123.9552z" fill="#fff" p-id="8641"></path></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

+1
View File
@@ -0,0 +1 @@
<svg t="1729178183592" class="icon" viewBox="0 0 1300 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4332" width="200" height="200"><path d="M784.074702 99.196443l10.927871 18.473304-21.302843-2.56935-14.180213 16.066571-4.130475-21.042655-19.676671-8.521137 18.733492-10.440019 2.016452-21.335366 15.708814 14.603017 20.945085-4.683373-9.041512 19.449008zM1067.22363 642.402668l-18.440781 10.92787 2.56935-21.302842-16.099094-14.180213 21.042655-4.130475 8.521137-19.676671 10.440019 18.733492 21.367889 2.016452-14.603017 15.708814 4.683373 20.945085-19.481531-9.041512z" fill="#8a8a8a" p-id="4333"></path><path d="M1067.22363 642.402668l-18.440781 10.92787 2.56935-21.302842-16.099094-14.180213 21.042655-4.130475 8.521137-19.676671 10.440019 18.733492 21.367889 2.016452-14.603017 15.708814 4.683373 20.945085-19.481531-9.041512zM571.924408 100.009528l-17.400031-12.488994 20.52228-6.211974 6.504685-20.457234 12.261331 17.595172 21.432936-0.09757-12.944323 17.074798 6.732349 20.359663-20.262093-7.02506-17.269938 12.716659 0.422804-21.46546zM991.444053 784.43246l-21.172749 3.480006 10.114785-18.928632-9.822074-19.026203 21.107702 3.772717 15.090868-15.253486 2.927109 21.237796 19.156296 9.626933-19.318914 9.366746-3.219819 21.205273-14.863204-15.48115zM428.008258 156.795426l-20.749945-5.333841 16.879657-13.237034-1.365983-21.400413 17.822836 11.936097 19.936859-7.870669-5.88674 20.619851 13.692361 16.521899-21.432936 0.813086-11.513292 18.083024-7.382817-20.132zM854.260251 896.475655l-20.749945-5.333841 16.879657-13.237034-1.365983-21.400413 17.822836 11.96862 19.936859-7.903192-5.854217 20.619851 13.659838 16.554423-21.432936 0.780562-11.513292 18.115547-7.382817-20.164523zM562.460092 923.665237l10.895347 18.440782-21.302843-2.569351-14.180212 16.099095-4.130475-21.042655-19.676672-8.521137 18.733493-10.440019 2.016452-21.36789 15.708814 14.603018 20.945085-4.683373-9.008989 19.48153zM242.787359 420.788058l-18.473305 10.895347 2.569351-21.302843-16.066572-14.180213 21.042656-4.130474 8.521137-19.676672 10.440019 18.733492 21.335366 2.016453-14.603018 15.708813 4.683374 20.945085-19.449008-9.008988z" fill="#8a8a8a" p-id="4334"></path><path d="M242.787359 420.788058l-18.473305 10.895347 2.569351-21.302843-16.066572-14.180213 21.042656-4.130474 8.521137-19.676672 10.440019 18.733492 21.335366 2.016453-14.603018 15.708813 4.683374 20.945085-19.449008-9.008988zM700.814737 943.959854l-17.400032-12.521518 20.522281-6.211974 6.504685-20.42471 12.26133 17.595172 21.432937-0.130094-12.944323 17.107321 6.732349 20.359663-20.262093-7.025059-17.269938 12.684135 0.422804-21.432936zM303.541115 278.823313l-21.140226 3.480006 10.114785-18.928633-9.854597-19.058726 21.107702 3.772717 15.090868-15.220962 2.927109 21.237796 19.156296 9.626933-19.28639 9.366746-3.252342 21.172749-14.863205-15.448626z" fill="#8a8a8a" p-id="4335"></path><path d="M407.648595 90.642782a486.713038 486.713038 0 0 1 504.568397 11.578339l25.010513-14.407877A512.081309 512.081309 0 0 0 139.850723 547.401747l24.977989-14.407877a486.778085 486.778085 0 0 1 242.819883-442.351088zM893.28836 933.422265a486.810608 486.810608 0 0 1-504.568398-11.610863l-25.010513 14.407877a512.081309 512.081309 0 0 0 797.5394-459.621026l-24.97799 14.505447a486.843132 486.843132 0 0 1-242.982499 442.318565z" fill="#8a8a8a" p-id="4336"></path><path d="M814.061299 795.880705a326.665269 326.665269 0 0 1-258.170939 29.563792l-29.791456 17.172368a353.236906 353.236906 0 0 0 472.793013-272.448721l-29.693886 17.172367a326.762839 326.762839 0 0 1-155.136732 208.540194zM486.875655 228.119295a326.795363 326.795363 0 0 1 258.170939-29.563792l29.791456-17.172368a353.236906 353.236906 0 0 0-472.793013 272.448721l29.82398-17.172367a326.762839 326.762839 0 0 1 155.006638-208.540194zM1288.350389 374.73489a53.923837 53.923837 0 0 1-14.34283 12.001143L229.420232 988.712085A53.793743 53.793743 0 0 1 156.112434 968.937843l-148.924757-258.235985a53.76122 53.76122 0 0 1 19.741718-73.437891L1071.516722 35.352962A53.826266 53.826266 0 0 1 1144.82452 55.062157l148.827187 258.268508a53.793743 53.793743 0 0 1-5.398888 61.404225zM32.19819 665.754486a28.360426 28.360426 0 0 0-5.626553 10.73273 28.067715 28.067715 0 0 0 2.699444 21.432936L178.195839 956.188661a28.165285 28.165285 0 0 0 38.442687 10.342449l1044.587328-601.976052a28.132762 28.132762 0 0 0 10.440019-38.442687l-148.924758-258.268509a28.197808 28.197808 0 0 0-38.442687-10.342449L39.711101 659.444942a28.230332 28.230332 0 0 0-7.512911 6.309544z" fill="#8a8a8a" p-id="4337"></path><path d="M498.941845 597.390249l-138.322121 79.877529 38.637827 66.933207q8.000762 13.854979 21.595554 5.98431l114.254788-65.957504a21.172749 21.172749 0 0 0 9.952167-11.123011q2.634397-9.757027-16.91218-47.321582l18.440781-4.130474q20.489757 43.22363 18.148071 56.167953a36.166047 36.166047 0 0 1-16.261712 19.514054l-123.068636 71.031158q-25.17313 14.603017-40.394092-11.77348L317.103383 639.020232l16.066571-9.269176 18.570875 32.133143 122.027886-70.47826-33.596697-Line truncated

After

Width:  |  Height:  |  Size: 7.4 KiB

+1
View File
@@ -0,0 +1 @@
<svg t="1729585232424" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1602" width="200" height="200"><path d="M925.5 898.9H804.9c-19 0-34.5-15.4-34.5-34.4V761.3c0-19 15.4-34.4 34.5-34.4h34.5V572.2c0-19-15.4-34.4-34.5-34.4H529.2V727h34.5c19 0 34.5 15.4 34.5 34.4v103.2c0 19-15.4 34.4-34.5 34.4H443.1c-19 0-34.5-15.4-34.5-34.4V761.3c0-19 15.4-34.4 34.5-34.4h34.5V537.8H219.1c-19 0-34.5 15.4-34.5 34.4V727h34.5c19 0 34.5 15.4 34.5 34.4v103.2c0 19-15.4 34.4-34.5 34.4H98.5c-19 0-34.5-15.4-34.5-34.4V761.3c0-19 15.4-34.4 34.5-34.4H133V555c0-38 30.9-68.8 68.9-68.8h275.7V297.1h-34.5c-19 0-34.5-15.4-34.5-34.4V159.5c0-19 15.4-34.4 34.5-34.4h120.6c19 0 34.5 15.4 34.5 34.4v103.2c0 19-15.4 34.4-34.5 34.4h-34.5v189.2h292.9c38.1 0 68.9 30.8 68.9 68.8v172h34.5c19 0 34.5 15.4 34.5 34.4v103.2c0 18.8-15.4 34.2-34.5 34.2z m0 0" p-id="1603" fill="#fff"></path></svg>

After

Width:  |  Height:  |  Size: 897 B

+1
View File
@@ -0,0 +1 @@
<svg t="1729649333541" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1644" width="200" height="200"><path d="M647.888 893.84L491.904 571.52l393.888-393.888-237.904 716.208zM872.32 123.232L459.872 535.68 134.96 380.88 872.32 123.232z m90.72-68.32a23.968 23.968 0 0 0-24.784-5.568L64.08 354.816a23.984 23.984 0 0 0-2.4 44.32l381.392 181.728 187.36 387.088a24.048 24.048 0 0 0 23.152 13.504 24.032 24.032 0 0 0 21.232-16.4L968.96 79.552c2.88-8.672 0.592-18.24-5.92-24.64z" fill="#fff" p-id="1645"></path></svg>

After

Width:  |  Height:  |  Size: 553 B

+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1735905505218" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4277" width="200" height="200" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M561.778 454.929h198.117c0.549 0 0.994 0.444 0.994 1.001v97.553a0.998 0.998 0 0 1-0.994 1.001H463.224a1.005 1.005 0 0 1-1.002-1V207.04c0-0.552 0.444-1 1.002-1h97.552c0.553 0 1.002 0.455 1.002 1v247.89zM512 952.706c-247.424 0-448-200.576-448-448 0-247.423 200.576-448 448-448s448 200.577 448 448c0 247.424-200.576 448-448 448z m0-99.555c192.44 0 348.444-156.004 348.444-348.445 0-192.44-156.003-348.444-348.444-348.444-192.44 0-348.444 156.004-348.444 348.444 0 192.441 156.003 348.445 348.444 348.445z" fill="#3296FA" p-id="4278"></path></svg>

After

Width:  |  Height:  |  Size: 876 B

+1
View File
@@ -0,0 +1 @@
<svg t="1730189225011" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2651" id="mx_n_1730189225011" width="200" height="200"><path d="M793.889347 200.380242c27.648573 20.615681 42.196018 32.710677 63.781037 56.119312 25.313864 27.453234 43.242957 48.52047 64.502857 86.507991 44.537416 79.580127 53.527718 136.949077 53.517684 212.063821 0 64.933675-15.452562 130.459388-40.138263 187.311893-22.076044 50.841799-61.545336 104.359483-101.886297 138.933914-45.506755 39.001681-81.214423 60.462941-137.605337 81.826531-55.699867 21.102023-114.070267 28.641326-181.379458 27.791064-68.274516-0.862973-129.364283-11.040029-180.533878-31.80489-46.159002-18.731189-98.338744-46.827973-141.596418-87.541551-43.946046-41.361142-70.369064-75.958317-93.88139-127.198155-26.157437-57.004361-40.094111-129.065922-39.680686-191.781288 0-36.980719 4.033895-70.902234 12.252873-105.241856 8.532726-35.651474 20.069131-69.572989 38.13135-102.35257 18.856956-34.221214 36.754607-62.067803 58.869452-88.973149 23.248751-28.285434 39.2104-46.417894 64.295476-63.475987 18.297696-12.442861 36.879036-9.295353 47.199252-2.306612 4.403836 2.982273 8.919391 6.577992 12.933218 12.933217 9.572307 15.156208-0.334486 29.769212-6.69038 38.465836-7.148625 9.781026-23.130343 26.023643-38.738775 43.218205-38.192895 42.075603-55.133918 65.965228-74.986303 106.965794-30.772668 63.552249-37.495827 115.718611-38.131349 166.573791-0.668971 53.517684 9.995096 99.647251 27.427813 140.483919 33.916163 80.572211 94.807915 144.44289 175.270414 178.615938 41.108271 17.845472 113.812713 37.319888 181.960793 38.13135 56.193568 0.668971 125.919751-11.321666 166.574459-28.096784 45.935566-18.954626 97.223569-56.862539 127.10383-94.324918 23.013273-28.852721 52.179742-70.910931 64.413884-105.694749 14.863868-42.260239 24.806784-87.661297 24.559934-132.458943 0-54.414105-11.53373-108.417461-36.918505-156.856317-20.16747-38.483228-46.480777-74.607665-84.66899-108.048189-13.377414-11.714352-23.822728-20.067124-38.808348-31.619586-10.191774-7.857065-36.059546-25.027545-28.923632-47.326356 4.970455-15.53217 18.303717-25.294464 31.887843-27.205046 19.456354-2.736092 28.565733 2.427027 43.705885 12.041479l6.179955 4.322891zM510.755379 531.65738c-8.696624-0.668971-10.034566-0.446204-20.738102-6.689711-11.031333-6.434832-17.839451-21.183637-16.514219-35.175166V92.220334c0-18.178619 0.386665-22.815926 8.988295-31.685813 5.351768-5.519011 10.963097-11.381873 26.08987-11.539751 16.055305-0.167243 21.407073 3.846584 27.929542 9.700081 9.70677 8.711341 10.703537 17.56049 10.377078 33.525483v397.5715c-0.509756 15.273947 0.326458 22.967114-11.380535 33.502739-3.884046 3.495374-8.027653 7.693167-20.96087 8.362138l-3.791059 0.000669z m4.453341 0.573308" p-id="2652" fill="#ffffff"></path></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

+1
View File
@@ -0,0 +1 @@
<svg t="1729585239190" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1755" width="200" height="200"><path d="M901.489435 536.822664v-0.931601l-1.001722-198.240726c-0.100172-19.162936-9.21584-37.474409-25.043042-50.246361-14.024104-11.349507-32.265456-17.60025-51.348255-17.610268l-618.062295-0.18031c-19.142902 0-37.424323 6.280795-51.478478 17.690405-15.827203 12.842072-24.902802 31.2437-24.892785 50.486775v196.798247A114.987635 114.987635 0 1 0 195.295664 536.922836V338.782282c1.15198-1.252152 4.808264-3.596181 10.768509-3.596181l276.725622 0.090155v199.753326a114.987635 114.987635 0 1 0 65.612772 1.412428V335.326342l275.693849 0.080138c6.01033 0 9.626546 2.344029 10.768508 3.596181l1.001722 195.70637a114.987635 114.987635 0 1 0 65.592737 2.113633zM214.979496 645.910158a56.437001 56.437001 0 1 1-56.437001-56.437001 56.507122 56.507122 0 0 1 56.437001 56.437001z m354.689623 0a56.437001 56.437001 0 1 1-56.437001-56.437001 56.507122 56.507122 0 0 1 56.437001 56.437001z m295.507904 56.437001a56.437001 56.437001 0 1 1 56.437001-56.437001 56.507122 56.507122 0 0 1-56.457035 56.437001z" p-id="1756" fill='#fff'></path></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

@@ -0,0 +1 @@
<svg width="22" height="22" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path fill="#FAFAFA" d="M0 0h22v22H0z"/><circle fill="#919BAE" cx="1" cy="1" r="1"/></g></svg>

After

Width:  |  Height:  |  Size: 192 B

+1
View File
@@ -0,0 +1 @@
<svg t="1729561814171" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1359" width="200" height="200"><path d="M674.496 603.456c120.256 0 218.176 90.752 221.44 203.84l0.064 5.888v125.888c0 11.52-9.92 20.928-22.144 20.928h-44.352a21.568 21.568 0 0 1-22.144-20.928v-125.888c0-67.712-56.512-123.264-128-125.76l-4.928-0.064H349.568c-71.488 0-130.176 53.504-132.864 121.152l-0.064 4.672v125.888c0 11.52-9.92 20.928-22.144 20.928h-44.352A21.568 21.568 0 0 1 128 939.072v-125.888c0-113.92 95.872-206.528 215.36-209.664l6.208-0.064h324.928zM497.216 128c122.368 0 221.568 93.888 221.568 209.728s-99.2 209.792-221.568 209.792c-122.304 0-221.44-93.952-221.44-209.728C275.712 221.952 374.848 128 497.152 128z m0 83.904c-73.408 0-132.864 56.32-132.864 125.888 0 69.504 59.52 125.824 132.864 125.824 73.408 0 132.928-56.32 132.928-125.824 0-69.504-59.52-125.888-132.928-125.888z" fill="#fff" p-id="1360"></path></svg>

After

Width:  |  Height:  |  Size: 947 B

@@ -51,7 +51,8 @@
</template>
<script setup lang="ts">
import { ComponentStyle, usePropertyForm } from '@/components/DiyEditor/util'
import { ComponentStyle } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
/**
* 组件容器属性:目前右边部分
@@ -61,7 +62,7 @@ defineOptions({ name: 'ComponentContainer' })
const props = defineProps<{ modelValue: ComponentStyle }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
const treeData = [
{
@@ -93,14 +93,14 @@
<script setup lang="ts">
import { CarouselProperty } from './config'
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
// 轮播图属性面板
defineOptions({ name: 'CarouselProperty' })
const props = defineProps<{ modelValue: CarouselProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
</script>
<style scoped lang="scss"></style>
@@ -68,15 +68,20 @@
</el-form>
</ComponentContainerProperty>
<!-- 优惠券选择 -->
<CouponSelect ref="couponSelectDialog" v-model:multiple-selection="couponList" />
<CouponSelect
ref="couponSelectDialog"
v-model:multiple-selection="couponList"
:take-type="CouponTemplateTakeTypeEnum.USER.type"
@change="handleCouponSelect"
/>
</template>
<script setup lang="ts">
import { CouponCardProperty } from './config'
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
import * as CouponTemplateApi from '@/api/mall/promotion/coupon/couponTemplate'
import { floatToFixed2 } from '@/utils'
import { PromotionDiscountTypeEnum } from '@/utils/constants'
import { CouponTemplateTakeTypeEnum, PromotionDiscountTypeEnum } from '@/utils/constants'
import CouponSelect from '@/views/mall/promotion/coupon/components/CouponSelect.vue'
// 优惠券卡片属性面板
@@ -84,7 +89,7 @@ defineOptions({ name: 'CouponCardProperty' })
const props = defineProps<{ modelValue: CouponCardProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
// 优惠券列表
const couponList = ref<CouponTemplateApi.CouponTemplateVO[]>([])
@@ -93,10 +98,20 @@ const couponSelectDialog = ref()
const handleAddCoupon = () => {
couponSelectDialog.value.open()
}
const handleCouponSelect = () => {
formData.value.couponIds = couponList.value.map((coupon) => coupon.id)
}
watch(
() => couponList.value,
() => {
formData.value.couponIds = couponList.value.map((coupon) => coupon.id)
() => formData.value.couponIds,
async () => {
if (formData.value.couponIds?.length > 0) {
couponList.value = await CouponTemplateApi.getCouponTemplateList(formData.value.couponIds)
}
},
{
immediate: true,
deep: true
}
)
</script>
@@ -45,12 +45,12 @@
<script setup lang="ts">
import { DividerProperty } from './config'
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
// 导航栏属性面板
defineOptions({ name: 'DividerProperty' })
const props = defineProps<{ modelValue: DividerProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
//线类型
const BORDER_TYPES = [
@@ -31,14 +31,14 @@
<script setup lang="ts">
import { FloatingActionButtonProperty } from './config'
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
// 悬浮按钮属性面板
defineOptions({ name: 'FloatingActionButtonProperty' })
const props = defineProps<{ modelValue: FloatingActionButtonProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
</script>
<style scoped lang="scss"></style>
@@ -20,7 +20,7 @@
</template>
<script setup lang="ts">
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
import { HotZoneProperty } from '@/components/DiyEditor/components/mobile/HotZone/config'
import HotZoneEditDialog from './components/HotZoneEditDialog/index.vue'
@@ -29,7 +29,7 @@ defineOptions({ name: 'HotZoneProperty' })
const props = defineProps<{ modelValue: HotZoneProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
// 热区编辑对话框
const editDialogRef = ref()
@@ -21,14 +21,14 @@
<script setup lang="ts">
import { ImageBarProperty } from './config'
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
// 图片展示属性面板
defineOptions({ name: 'ImageBarProperty' })
const props = defineProps<{ modelValue: ImageBarProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
</script>
<style scoped lang="scss"></style>
@@ -56,7 +56,7 @@
</template>
<script setup lang="ts">
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
import { MagicCubeProperty } from '@/components/DiyEditor/components/mobile/MagicCube/config'
/** 广告魔方属性面板 */
@@ -64,7 +64,7 @@ defineOptions({ name: 'MagicCubeProperty' })
const props = defineProps<{ modelValue: MagicCubeProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
// 选中的热区
const selectedHotAreaIndex = ref(-1)
@@ -48,7 +48,7 @@
</template>
<script setup lang="ts">
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
import {
EMPTY_MENU_GRID_ITEM_PROPERTY,
MenuGridProperty
@@ -59,7 +59,7 @@ defineOptions({ name: 'MenuGridProperty' })
const props = defineProps<{ modelValue: MenuGridProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
</script>
<style scoped lang="scss"></style>
@@ -28,7 +28,7 @@
</template>
<script setup lang="ts">
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
import {
EMPTY_MENU_LIST_ITEM_PROPERTY,
MenuListProperty
@@ -39,7 +39,7 @@ defineOptions({ name: 'MenuListProperty' })
const props = defineProps<{ modelValue: MenuListProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
</script>
<style scoped lang="scss"></style>
@@ -58,7 +58,7 @@
</template>
<script setup lang="ts">
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
import {
EMPTY_MENU_SWIPER_ITEM_PROPERTY,
MenuSwiperProperty
@@ -70,7 +70,7 @@ defineOptions({ name: 'MenuSwiperProperty' })
const props = defineProps<{ modelValue: MenuSwiperProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
</script>
<style scoped lang="scss"></style>
@@ -2,17 +2,17 @@
<div class="h-40px flex items-center justify-center">
<MagicCubeEditor
v-model="cellList"
class="m-b-16px"
:rows="1"
:cols="cellCount"
:cube-size="38"
:rows="1"
class="m-b-16px"
@hot-area-selected="handleHotAreaSelected"
/>
<img src="@/assets/imgs/diy/app-nav-bar-mp.png" alt="" class="h-30px w-76px" v-if="isMp" />
<img v-if="isMp" alt="" class="h-30px w-76px" src="@/assets/imgs/diy/app-nav-bar-mp.png" />
</div>
<template v-for="(cell, cellIndex) in cellList" :key="cellIndex">
<template v-if="selectedHotAreaIndex === cellIndex">
<el-form-item label="类型" :prop="`cell[${cellIndex}].type`">
<el-form-item :prop="`cell[${cellIndex}].type`" label="类型">
<el-radio-group v-model="cell.type">
<el-radio value="text">文字</el-radio>
<el-radio value="image">图片</el-radio>
@@ -21,37 +21,40 @@
</el-form-item>
<!-- 1. 文字 -->
<template v-if="cell.type === 'text'">
<el-form-item label="内容" :prop="`cell[${cellIndex}].text`">
<el-form-item :prop="`cell[${cellIndex}].text`" label="内容">
<el-input v-model="cell!.text" maxlength="10" show-word-limit />
</el-form-item>
<el-form-item label="颜色" :prop="`cell[${cellIndex}].text`">
<el-form-item :prop="`cell[${cellIndex}].text`" label="颜色">
<ColorInput v-model="cell!.textColor" />
</el-form-item>
<el-form-item :prop="`cell[${cellIndex}].url`" label="链接">
<AppLinkInput v-model="cell.url" />
</el-form-item>
</template>
<!-- 2. 图片 -->
<template v-else-if="cell.type === 'image'">
<el-form-item label="图片" :prop="`cell[${cellIndex}].imgUrl`">
<el-form-item :prop="`cell[${cellIndex}].imgUrl`" label="图片">
<UploadImg v-model="cell.imgUrl" :limit="1" height="56px" width="56px">
<template #tip>建议尺寸 56*56</template>
</UploadImg>
</el-form-item>
<el-form-item label="链接" :prop="`cell[${cellIndex}].url`">
<el-form-item :prop="`cell[${cellIndex}].url`" label="链接">
<AppLinkInput v-model="cell.url" />
</el-form-item>
</template>
<!-- 3. 搜索框 -->
<template v-else>
<el-form-item label="提示文字" :prop="`cell[${cellIndex}].placeholder`">
<el-form-item :prop="`cell[${cellIndex}].placeholder`" label="提示文字">
<el-input v-model="cell.placeholder" maxlength="10" show-word-limit />
</el-form-item>
<el-form-item label="圆角" :prop="`cell[${cellIndex}].borderRadius`">
<el-form-item :prop="`cell[${cellIndex}].borderRadius`" label="圆角">
<el-slider
v-model="cell.borderRadius"
:max="100"
:min="0"
show-input
input-size="small"
:show-input-controls="false"
input-size="small"
show-input
/>
</el-form-item>
</template>
@@ -59,19 +62,24 @@
</template>
</template>
<script setup lang="ts">
<script lang="ts" setup>
import { NavigationBarCellProperty } from '../config'
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
// 导航栏属性面板
defineOptions({ name: 'NavigationBarCellProperty' })
const props = defineProps<{
modelValue: NavigationBarCellProperty[]
isMp: boolean
}>()
const props = withDefaults(
defineProps<{
modelValue: NavigationBarCellProperty[]
isMp: boolean
}>(),
{
modelValue: () => [],
isMp: true
}
)
const emit = defineEmits(['update:modelValue'])
const { formData: cellList } = usePropertyForm(props.modelValue, emit)
if (!cellList.value) cellList.value = []
const cellList = useVModel(props, 'modelValue', emit)
// 单元格数量:小程序6个(右侧胶囊按钮占了2个),其它平台8个
const cellCount = computed(() => (props.isMp ? 6 : 8))
@@ -87,4 +95,4 @@ const handleHotAreaSelected = (cellValue: NavigationBarCellProperty, index: numb
}
</script>
<style scoped lang="scss"></style>
<style lang="scss" scoped></style>
@@ -4,7 +4,7 @@
<div v-for="(cell, cellIndex) in cellList" :key="cellIndex" :style="getCellStyle(cell)">
<span v-if="cell.type === 'text'">{{ cell.text }}</span>
<img v-else-if="cell.type === 'image'" :src="cell.imgUrl" alt="" class="h-full w-full" />
<SearchBar v-else :property="getSearchProp" />
<SearchBar v-else :property="getSearchProp(cell)" />
</div>
</div>
<img
@@ -51,14 +51,14 @@ const getCellStyle = (cell: NavigationBarCellProperty) => {
} as StyleValue
}
// 获得搜索框属性
const getSearchProp = (cell: NavigationBarCellProperty) => {
const getSearchProp = computed(() => (cell: NavigationBarCellProperty) => {
return {
height: 30,
showScan: false,
placeholder: cell.placeholder,
borderRadius: cell.borderRadius
} as SearchProperty
}
})
</script>
<style lang="scss" scoped>
.navigation-bar {
@@ -66,7 +66,7 @@
<script setup lang="ts">
import { NavigationBarProperty } from './config'
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
import NavigationBarCellProperty from '@/components/DiyEditor/components/mobile/NavigationBar/components/CellProperty.vue'
// 导航栏属性面板
defineOptions({ name: 'NavigationBarProperty' })
@@ -77,7 +77,7 @@ const rules = {
const props = defineProps<{ modelValue: NavigationBarProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
if (!formData.value._local) {
formData.value._local = { previewMp: true, previewOther: false }
}
@@ -30,7 +30,7 @@
<script setup lang="ts">
import { NoticeBarProperty } from './config'
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
// 通知栏属性面板
defineOptions({ name: 'NoticeBarProperty' })
// 表单校验
@@ -40,7 +40,7 @@ const rules = {
const props = defineProps<{ modelValue: NoticeBarProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
</script>
<style scoped lang="scss"></style>
@@ -20,7 +20,7 @@
<script setup lang="ts">
import { PageConfigProperty } from './config'
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
// 导航栏属性面板
defineOptions({ name: 'PageConfigProperty' })
// 表单校验
@@ -28,7 +28,7 @@ const rules = {}
const props = defineProps<{ modelValue: PageConfigProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
</script>
<style scoped lang="scss"></style>
@@ -25,14 +25,14 @@
<script setup lang="ts">
import { PopoverProperty } from './config'
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
// 弹窗广告属性面板
defineOptions({ name: 'PopoverProperty' })
const props = defineProps<{ modelValue: PopoverProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
</script>
<style scoped lang="scss"></style>
@@ -135,7 +135,7 @@
<script setup lang="ts">
import { ProductCardProperty } from './config'
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
import SpuShowcase from '@/views/mall/product/spu/components/SpuShowcase.vue'
// 商品卡片属性面板
@@ -143,7 +143,7 @@ defineOptions({ name: 'ProductCardProperty' })
const props = defineProps<{ modelValue: ProductCardProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
</script>
<style scoped lang="scss"></style>
@@ -85,7 +85,7 @@
<script setup lang="ts">
import { ProductListProperty } from './config'
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
import SpuShowcase from '@/views/mall/product/spu/components/SpuShowcase.vue'
// 商品栏属性面板
@@ -93,7 +93,7 @@ defineOptions({ name: 'ProductListProperty' })
const props = defineProps<{ modelValue: ProductListProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
</script>
<style scoped lang="scss"></style>
@@ -25,7 +25,7 @@
<script setup lang="ts">
import { PromotionArticleProperty } from './config'
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
import * as ArticleApi from '@/api/mall/promotion/article/index'
// 营销文章属性面板
@@ -33,7 +33,7 @@ defineOptions({ name: 'PromotionArticleProperty' })
const props = defineProps<{ modelValue: PromotionArticleProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
// 文章列表
const articles = ref<ArticleApi.ArticleVO>([])
@@ -140,7 +140,7 @@
<script setup lang="ts">
import { PromotionCombinationProperty } from './config'
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
import * as CombinationActivityApi from '@/api/mall/promotion/combination/combinationActivity'
import { CommonStatusEnum } from '@/utils/constants'
import CombinationShowcase from '@/views/mall/promotion/combination/components/CombinationShowcase.vue'
@@ -150,7 +150,7 @@ defineOptions({ name: 'PromotionCombinationProperty' })
const props = defineProps<{ modelValue: PromotionCombinationProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
// 活动列表
const activityList = ref<CombinationActivityApi.CombinationActivityVO[]>([])
onMounted(async () => {
@@ -140,7 +140,7 @@
<script lang="ts" setup>
import { PromotionPointProperty } from './config'
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
import PointShowcase from '@/views/mall/promotion/point/components/PointShowcase.vue'
// 秒杀属性面板
@@ -148,7 +148,7 @@ defineOptions({ name: 'PromotionPointProperty' })
const props = defineProps<{ modelValue: PromotionPointProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
</script>
<style lang="scss" scoped></style>
@@ -140,7 +140,7 @@
<script setup lang="ts">
import { PromotionSeckillProperty } from './config'
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
import * as SeckillActivityApi from '@/api/mall/promotion/seckill/seckillActivity'
import { CommonStatusEnum } from '@/utils/constants'
import SeckillShowcase from '@/views/mall/promotion/seckill/components/SeckillShowcase.vue'
@@ -150,7 +150,7 @@ defineOptions({ name: 'PromotionSeckillProperty' })
const props = defineProps<{ modelValue: PromotionSeckillProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
// 活动列表
const activityList = ref<SeckillActivityApi.SeckillActivityVO[]>([])
onMounted(async () => {
@@ -59,7 +59,7 @@
</template>
<script setup lang="ts">
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
import { SearchProperty } from '@/components/DiyEditor/components/mobile/SearchBar/config'
/** 搜索框属性面板 */
@@ -67,7 +67,7 @@ defineOptions({ name: 'SearchProperty' })
const props = defineProps<{ modelValue: SearchProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
</script>
<style scoped lang="scss"></style>
@@ -80,13 +80,13 @@
<script setup lang="ts">
import { TabBarProperty, component, THEME_LIST } from './config'
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
// 底部导航栏
defineOptions({ name: 'TabBarProperty' })
const props = defineProps<{ modelValue: TabBarProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
// 将数据库的值更新到右侧属性栏
component.property.items = formData.value.items
@@ -1,9 +1,7 @@
import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
import {ComponentStyle, DiyComponent} from '@/components/DiyEditor/util'
/** 标题栏属性 */
export interface TitleBarProperty {
// 背景图
bgImgUrl: string
// 偏移
marginLeft: number
// 显示位置
@@ -1,21 +1,30 @@
<template>
<div class="title-bar">
<el-image v-if="property.bgImgUrl" :src="property.bgImgUrl" fit="cover" class="w-full" />
<div class="absolute left-0 top-0 w-full">
<div
:style="{
background:
property.style.bgType === 'color' ? property.style.bgColor : `url(${property.style.bgImg})`,
backgroundSize: '100% 100%',
backgroundRepeat: 'no-repeat'
}"
class="title-bar"
>
<!-- 内容 -->
<div>
<!-- 标题 -->
<div
v-if="property.title"
:style="{
fontSize: `${property.titleSize}px`,
fontWeight: property.titleWeight,
color: property.titleColor,
textAlign: property.textAlign
}"
v-if="property.title"
>
{{ property.title }}
</div>
<!-- 副标题 -->
<div
v-if="property.description"
:style="{
fontSize: `${property.descriptionSize}px`,
fontWeight: property.descriptionWeight,
@@ -23,25 +32,24 @@
textAlign: property.textAlign
}"
class="m-t-8px"
v-if="property.description"
>
{{ property.description }}
</div>
</div>
<!-- 更多 -->
<div
class="more"
v-show="property.more.show"
:style="{
color: property.descriptionColor
}"
class="more"
>
<span v-if="property.more.type !== 'icon'"> {{ property.more.text }} </span>
<Icon icon="ep:arrow-right" v-if="property.more.type !== 'text'" />
<Icon v-if="property.more.type !== 'text'" icon="ep:arrow-right" />
</div>
</div>
</template>
<script setup lang="ts">
<script lang="ts" setup>
import { TitleBarProperty } from './config'
/** 标题栏 */
@@ -49,7 +57,7 @@ defineOptions({ name: 'TitleBar' })
defineProps<{ property: TitleBarProperty }>()
</script>
<style scoped lang="scss">
<style lang="scss" scoped>
.title-bar {
position: relative;
width: 100%;
@@ -1,12 +1,7 @@
<template>
<ComponentContainerProperty v-model="formData.style">
<el-form label-width="85px" :model="formData" :rules="rules">
<el-card header="风格" class="property-group" shadow="never">
<el-form-item label="背景图片" prop="bgImgUrl">
<UploadImg v-model="formData.bgImgUrl" width="100%" height="40px">
<template #tip>建议尺寸 750*80</template>
</UploadImg>
</el-form-item>
<el-form :model="formData" :rules="rules" label-width="85px">
<el-card class="property-group" header="风格" shadow="never">
<el-form-item label="标题位置" prop="textAlign">
<el-radio-group v-model="formData!.textAlign">
<el-tooltip content="居左" placement="top">
@@ -22,65 +17,65 @@
</el-radio-group>
</el-form-item>
</el-card>
<el-card header="主标题" class="property-group" shadow="never">
<el-form-item label="文字" prop="title" label-width="40px">
<el-card class="property-group" header="主标题" shadow="never">
<el-form-item label="文字" label-width="40px" prop="title">
<InputWithColor
v-model="formData.title"
v-model:color="formData.titleColor"
show-word-limit
maxlength="20"
show-word-limit
/>
</el-form-item>
<el-form-item label="大小" prop="titleSize" label-width="40px">
<el-form-item label="大小" label-width="40px" prop="titleSize">
<el-slider
v-model="formData.titleSize"
:max="60"
:min="10"
show-input
input-size="small"
show-input
/>
</el-form-item>
<el-form-item label="粗细" prop="titleWeight" label-width="40px">
<el-form-item label="粗细" label-width="40px" prop="titleWeight">
<el-slider
v-model="formData.titleWeight"
:min="100"
:max="900"
:min="100"
:step="100"
show-input
input-size="small"
show-input
/>
</el-form-item>
</el-card>
<el-card header="副标题" class="property-group" shadow="never">
<el-form-item label="文字" prop="description" label-width="40px">
<el-card class="property-group" header="副标题" shadow="never">
<el-form-item label="文字" label-width="40px" prop="description">
<InputWithColor
v-model="formData.description"
v-model:color="formData.descriptionColor"
show-word-limit
maxlength="50"
show-word-limit
/>
</el-form-item>
<el-form-item label="大小" prop="descriptionSize" label-width="40px">
<el-form-item label="大小" label-width="40px" prop="descriptionSize">
<el-slider
v-model="formData.descriptionSize"
:max="60"
:min="10"
show-input
input-size="small"
show-input
/>
</el-form-item>
<el-form-item label="粗细" prop="descriptionWeight" label-width="40px">
<el-form-item label="粗细" label-width="40px" prop="descriptionWeight">
<el-slider
v-model="formData.descriptionWeight"
:min="100"
:max="900"
:min="100"
:step="100"
show-input
input-size="small"
show-input
/>
</el-form-item>
</el-card>
<el-card header="查看更多" class="property-group" shadow="never">
<el-card class="property-group" header="查看更多" shadow="never">
<el-form-item label="是否显示" prop="more.show">
<el-checkbox v-model="formData.more.show" />
</el-form-item>
@@ -93,7 +88,7 @@
<el-radio value="all">文字+图标</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="更多文字" prop="more.text" v-show="formData.more.type !== 'icon'">
<el-form-item v-show="formData.more.type !== 'icon'" label="更多文字" prop="more.text">
<el-input v-model="formData.more.text" />
</el-form-item>
<el-form-item label="跳转链接" prop="more.url">
@@ -104,18 +99,18 @@
</el-form>
</ComponentContainerProperty>
</template>
<script setup lang="ts">
<script lang="ts" setup>
import { TitleBarProperty } from './config'
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
// 导航栏属性面板
defineOptions({ name: 'TitleBarProperty' })
const props = defineProps<{ modelValue: TitleBarProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
// 表单校验
const rules = {}
</script>
<style scoped lang="scss"></style>
<style lang="scss" scoped></style>
@@ -4,14 +4,14 @@
<script setup lang="ts">
import { UserCardProperty } from './config'
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
// 用户卡片属性面板
defineOptions({ name: 'UserCardProperty' })
const props = defineProps<{ modelValue: UserCardProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
</script>
<style scoped lang="scss"></style>
@@ -4,14 +4,14 @@
<script setup lang="ts">
import { UserCouponProperty } from './config'
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
// 用户卡券属性面板
defineOptions({ name: 'UserCouponProperty' })
const props = defineProps<{ modelValue: UserCouponProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
</script>
<style scoped lang="scss"></style>
@@ -4,14 +4,14 @@
<script setup lang="ts">
import { UserOrderProperty } from './config'
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
// 用户订单属性面板
defineOptions({ name: 'UserOrderProperty' })
const props = defineProps<{ modelValue: UserOrderProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
</script>
<style scoped lang="scss"></style>
@@ -4,14 +4,14 @@
<script setup lang="ts">
import { UserWalletProperty } from './config'
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
// 用户资产属性面板
defineOptions({ name: 'UserWalletProperty' })
const props = defineProps<{ modelValue: UserWalletProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
</script>
<style scoped lang="scss"></style>
@@ -42,14 +42,14 @@
<script setup lang="ts">
import { VideoPlayerProperty } from './config'
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
// 视频播放属性面板
defineOptions({ name: 'VideoPlayerProperty' })
const props = defineProps<{ modelValue: VideoPlayerProperty }>()
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
</script>
<style scoped lang="scss"></style>
+77 -43
View File
@@ -12,17 +12,17 @@
<el-button-group class="header-right">
<el-tooltip content="重置">
<el-button @click="handleReset">
<Icon icon="system-uicons:reset-alt" :size="24" />
<Icon :size="24" icon="system-uicons:reset-alt" />
</el-button>
</el-tooltip>
<el-tooltip content="预览" v-if="previewUrl">
<el-tooltip v-if="previewUrl" content="预览">
<el-button @click="handlePreview">
<Icon icon="ep:view" :size="24" />
<Icon :size="24" icon="ep:view" />
</el-button>
</el-tooltip>
<el-tooltip content="保存">
<el-button @click="handleSave">
<Icon icon="ep:check" :size="24" />
<Icon :size="24" icon="ep:check" />
</el-button>
</el-tooltip>
</el-button-group>
@@ -31,21 +31,21 @@
<!-- 中心区域 -->
<el-container class="editor-container">
<!-- 左侧:组件库(ComponentLibrary) -->
<ComponentLibrary ref="componentLibrary" :list="libs" v-if="libs && libs.length > 0" />
<ComponentLibrary v-if="libs && libs.length > 0" ref="componentLibrary" :list="libs" />
<!-- 中心:设计区域(ComponentContainer) -->
<div class="editor-center page-prop-area" @click="handlePageSelected">
<!-- 手机顶部 -->
<div class="editor-design-top">
<!-- 手机顶部状态栏 -->
<img src="@/assets/imgs/diy/statusBar.png" alt="" class="status-bar" />
<img alt="" class="status-bar" src="@/assets/imgs/diy/statusBar.png" />
<!-- 手机顶部导航栏 -->
<ComponentContainer
v-if="showNavigationBar"
:active="selectedComponent?.id === navigationBarComponent.id"
:component="navigationBarComponent"
:show-toolbar="false"
:active="selectedComponent?.id === navigationBarComponent.id"
@click="handleNavigationBarSelected"
class="cursor-pointer!"
@click="handleNavigationBarSelected"
/>
</div>
<!-- 绝对定位的组件:例如 弹窗、浮动按钮等 -->
@@ -55,43 +55,43 @@
@click="handleComponentSelected(component, index)"
>
<component
v-if="component.position === 'fixed' && selectedComponent?.uid === component.uid"
:is="component.id"
v-if="component.position === 'fixed' && selectedComponent?.uid === component.uid"
:property="component.property"
/>
</div>
<!-- 手机页面编辑区域 -->
<el-scrollbar
height="100%"
wrap-class="editor-design-center page-prop-area"
view-class="phone-container"
:view-style="{
backgroundColor: pageConfigComponent.property.backgroundColor,
backgroundImage: `url(${pageConfigComponent.property.backgroundImage})`
}"
height="100%"
view-class="phone-container"
wrap-class="editor-design-center page-prop-area"
>
<draggable
class="page-prop-area drag-area"
v-model="pageComponents"
item-key="index"
:animation="200"
:force-fallback="true"
class="page-prop-area drag-area"
filter=".component-toolbar"
ghost-class="draggable-ghost"
:force-fallback="true"
group="component"
item-key="index"
@change="handleComponentChange"
>
<template #item="{ element, index }">
<ComponentContainer
v-if="!element.position || element.position === 'center'"
:component="element"
:active="selectedComponentIndex === index"
:can-move-up="index > 0"
:can-move-down="index < pageComponents.length - 1"
@move="(direction) => handleMoveComponent(index, direction)"
:can-move-up="index > 0"
:component="element"
@click="handleComponentSelected(element, index)"
@copy="handleCopyComponent(index)"
@delete="handleDeleteComponent(index)"
@click="handleComponentSelected(element, index)"
@move="(direction) => handleMoveComponent(index, direction)"
/>
</template>
</draggable>
@@ -99,9 +99,9 @@
<!-- 手机底部导航 -->
<div v-if="showTabBar" :class="['editor-design-bottom', 'component', 'cursor-pointer!']">
<ComponentContainer
:active="selectedComponent?.id === tabBarComponent.id"
:component="tabBarComponent"
:show-toolbar="false"
:active="selectedComponent?.id === tabBarComponent.id"
@click="handleTabBarSelected"
/>
</div>
@@ -109,9 +109,9 @@
<div class="fixed-component-action-group">
<el-tag
v-if="showPageConfig"
size="large"
:effect="selectedComponent?.uid === pageConfigComponent.uid ? 'dark' : 'plain'"
:type="selectedComponent?.uid === pageConfigComponent.uid ? '' : 'info'"
:type="selectedComponent?.uid === pageConfigComponent.uid ? 'primary' : 'info'"
size="large"
@click="handleComponentSelected(pageConfigComponent)"
>
<Icon :icon="pageConfigComponent.icon" :size="12" />
@@ -120,10 +120,10 @@
<template v-for="(component, index) in pageComponents" :key="index">
<el-tag
v-if="component.position === 'fixed'"
size="large"
closable
:effect="selectedComponent?.uid === component.uid ? 'dark' : 'plain'"
:type="selectedComponent?.uid === component.uid ? '' : 'info'"
:type="selectedComponent?.uid === component.uid ? 'primary' : 'info'"
closable
size="large"
@click="handleComponentSelected(component)"
@close="handleDeleteComponent(index)"
>
@@ -134,11 +134,11 @@
</div>
</div>
<!-- 右侧:属性面板(ComponentContainerProperty) -->
<el-aside class="editor-right" width="350px" v-if="selectedComponent?.property">
<el-aside v-if="selectedComponent?.property" class="editor-right" width="350px">
<el-card
shadow="never"
body-class="h-[calc(100%-var(--el-card-padding)-var(--el-card-padding))]"
class="h-full"
shadow="never"
>
<!-- 组件名称 -->
<template #header>
@@ -152,8 +152,8 @@
view-class="p-[var(--el-card-padding)] p-b-[calc(var(--el-card-padding)+var(--el-card-padding))] property"
>
<component
:key="selectedComponent?.uid || selectedComponent?.id"
:is="selectedComponent?.id + 'Property'"
:key="selectedComponent?.uid || selectedComponent?.id"
v-model="selectedComponent.property"
/>
</el-scrollbar>
@@ -166,8 +166,8 @@
<Dialog v-model="previewDialogVisible" title="预览" width="700">
<div class="flex justify-around">
<IFrame
class="w-375px border-4px border-rounded-8px border-solid p-2px h-667px!"
:src="previewUrl"
class="w-375px border-4px border-rounded-8px border-solid p-2px h-667px!"
/>
<div class="flex flex-col">
<el-text>手机扫码预览</el-text>
@@ -179,6 +179,7 @@
<script lang="ts">
// 注册所有的组件
import { components } from './components/mobile/index'
export default {
components: { ...components }
}
@@ -190,7 +191,7 @@ import { cloneDeep, includes } from 'lodash-es'
import { component as PAGE_CONFIG_COMPONENT } from '@/components/DiyEditor/components/mobile/PageConfig/config'
import { component as NAVIGATION_BAR_COMPONENT } from './components/mobile/NavigationBar/config'
import { component as TAB_BAR_COMPONENT } from './components/mobile/TabBar/config'
import { isString } from '@/utils/is'
import { isEmpty, isString } from '@/utils/is'
import { DiyComponent, DiyComponentLibrary, PageConfig } from '@/components/DiyEditor/util'
import { componentConfigs } from '@/components/DiyEditor/components/mobile'
import { array, oneOfType } from 'vue-types'
@@ -237,26 +238,49 @@ const props = defineProps({
watch(
() => props.modelValue,
() => {
const modelValue = isString(props.modelValue)
? (JSON.parse(props.modelValue) as PageConfig)
: props.modelValue
pageConfigComponent.value.property = modelValue?.page || PAGE_CONFIG_COMPONENT.property
const modelValue =
isString(props.modelValue) && !isEmpty(props.modelValue)
? (JSON.parse(props.modelValue) as PageConfig)
: props.modelValue
pageConfigComponent.value.property =
(typeof modelValue !== 'string' && modelValue?.page) || PAGE_CONFIG_COMPONENT.property
navigationBarComponent.value.property =
modelValue?.navigationBar || NAVIGATION_BAR_COMPONENT.property
tabBarComponent.value.property = modelValue?.tabBar || TAB_BAR_COMPONENT.property
(typeof modelValue !== 'string' && modelValue?.navigationBar) ||
NAVIGATION_BAR_COMPONENT.property
tabBarComponent.value.property =
(typeof modelValue !== 'string' && modelValue?.tabBar) || TAB_BAR_COMPONENT.property
// 查找对应的页面组件
pageComponents.value = (modelValue?.components || []).map((item) => {
const component = componentConfigs[item.id]
return { ...component, property: item.property }
})
pageComponents.value = ((typeof modelValue !== 'string' && modelValue?.components) || []).map(
(item) => {
const component = componentConfigs[item.id]
return { ...component, property: item.property }
}
)
},
{
immediate: true
}
)
/** 选择组件修改其属性后更新它的配置 */
watch(
selectedComponent,
(val: any) => {
if (!val || selectedComponentIndex.value === -1) {
return
}
pageComponents.value[selectedComponentIndex.value] = selectedComponent.value!
},
{ deep: true }
)
// 保存
const handleSave = () => {
// 发送保存通知
emits('save')
}
// 监听配置修改
const pageConfigChange = () => {
const pageConfig = {
page: pageConfigComponent.value.property,
navigationBar: navigationBarComponent.value.property,
@@ -272,10 +296,19 @@ const handleSave = () => {
// 发送数据更新通知
const modelValue = isString(props.modelValue) ? JSON.stringify(pageConfig) : pageConfig
emits('update:modelValue', modelValue)
// 发送保存通知
emits('save', pageConfig)
}
watch(
() => [
pageConfigComponent.value.property,
navigationBarComponent.value.property,
tabBarComponent.value.property,
pageComponents.value
],
() => {
pageConfigChange()
},
{ deep: true }
)
// 处理页面选中:显示属性表单
const handlePageSelected = (event: any) => {
if (!props.showPageConfig) return
@@ -547,6 +580,7 @@ $toolbar-height: 42px;
:deep(.el-tag) {
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.1);
border: none;
.el-tag__content {
width: 100%;
display: flex;
-29
View File
@@ -1,4 +1,3 @@
import { ref, Ref } from 'vue'
import { PageConfigProperty } from '@/components/DiyEditor/components/mobile/PageConfig/config'
import { NavigationBarProperty } from '@/components/DiyEditor/components/mobile/NavigationBar/config'
import { TabBarProperty } from '@/components/DiyEditor/components/mobile/TabBar/config'
@@ -78,34 +77,6 @@ export interface PageConfig {
// 页面组件,只保留组件ID,组件属性
export interface PageComponent extends Pick<DiyComponent<any>, 'id' | 'property'> {}
// 属性表单监听
export function usePropertyForm<T>(modelValue: T, emit: Function): { formData: Ref<T> } {
const formData = ref<T>()
// 监听属性数据变动
watch(
() => modelValue,
() => {
formData.value = modelValue
},
{
deep: true,
immediate: true
}
)
// 监听表单数据变动
watch(
() => formData.value,
() => {
emit('update:modelValue', formData.value)
},
{
deep: true
}
)
return { formData } as { formData: Ref<T> }
}
// 页面组件库
export const PAGE_LIBS = [
{
+11 -4
View File
@@ -13,9 +13,16 @@
class="mb-4px flex flex-col gap-4px border border-gray-2 border-rounded rounded border-solid p-8px"
>
<!-- 操作按钮区 -->
<div class="m--8px m-b-4px flex flex-row items-center justify-between p-8px" style="background-color: var(--app-content-bg-color);">
<div
class="m--8px m-b-4px flex flex-row items-center justify-between p-8px"
style="background-color: var(--app-content-bg-color)"
>
<el-tooltip content="拖动排序">
<Icon icon="ic:round-drag-indicator" class="drag-icon cursor-move" style="color: #8a909c;" />
<Icon
icon="ic:round-drag-indicator"
class="drag-icon cursor-move"
style="color: #8a909c"
/>
</el-tooltip>
<el-tooltip content="删除">
<Icon
@@ -47,7 +54,7 @@
<script setup lang="ts">
// 拖拽组件
import VueDraggable from 'vuedraggable'
import { usePropertyForm } from '@/components/DiyEditor/util'
import { useVModel } from '@vueuse/core'
import { any, array } from 'vue-types'
import { propTypes } from '@/utils/propTypes'
import { cloneDeep } from 'lodash-es'
@@ -66,7 +73,7 @@ const props = defineProps({
})
// 定义事件
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
const formData = useVModel(props, 'modelValue', emit)
// 处理添加
const handleAdd = () => formData.value.push(cloneDeep(props.emptyItem || {}))
+4
View File
@@ -9,6 +9,10 @@ import { useAppStore } from '@/store/modules/app'
import { isString } from '@/utils/is'
import { useDesign } from '@/hooks/web/useDesign'
import 'echarts/lib/component/markPoint'
import 'echarts/lib/component/markLine'
import 'echarts/lib/component/markArea'
defineOptions({ name: 'EChart' })
const { getPrefixCls, variables } = useDesign()
+3 -3
View File
@@ -6,7 +6,7 @@ import { propTypes } from '@/utils/propTypes'
import { isNumber } from '@/utils/is'
import { ElMessage } from 'element-plus'
import { useLocaleStore } from '@/store/modules/locale'
import { getAccessToken, getTenantId } from '@/utils/auth'
import { getRefreshToken, getTenantId } from '@/utils/auth'
import { getUploadUrl } from '@/components/UploadFile/src/useUpload'
defineOptions({ name: 'Editor' })
@@ -100,7 +100,7 @@ const editorConfig = computed((): IEditorConfig => {
// 自定义增加 http header
headers: {
Accept: '*',
Authorization: 'Bearer ' + getAccessToken(),
Authorization: 'Bearer ' + getRefreshToken(), // 使用 getRefreshToken() 方法,而不使用 getAccessToken() 方法的原因:Editor 无法方便的刷新访问令牌
'tenant-id': getTenantId()
},
@@ -148,7 +148,7 @@ const editorConfig = computed((): IEditorConfig => {
// 自定义增加 http header
headers: {
Accept: '*',
Authorization: 'Bearer ' + getAccessToken(),
Authorization: 'Bearer ' + getRefreshToken(), // 使用 getRefreshToken() 方法,而不使用 getAccessToken() 方法的原因:Editor 无法方便的刷新访问令牌
'tenant-id': getTenantId()
},
@@ -185,7 +185,6 @@ export const useApiSelect = (option: ApiSelectProps) => {
</el-select>
)
}
debugger
return (
<el-select
class="w-1/1"
@@ -16,3 +16,46 @@ export const localeProps = (t, prefix, rules) => {
return rule
})
}
/**
* 解析表单组件的 field, title 等字段(递归,如果组件包含子组件)
*
* @param rule 组件的生成规则 https://www.form-create.com/v3/guide/rule
* @param fields 解析后表单组件字段
* @param parentTitle 如果是子表单,子表单的标题,默认为空
*/
export const parseFormFields = (
rule: Record<string, any>,
fields: Array<Record<string, any>> = [],
parentTitle: string = ''
) => {
const { type, field, $required, title: tempTitle, children } = rule
if (field && tempTitle) {
let title = tempTitle
if (parentTitle) {
title = `${parentTitle}.${tempTitle}`
}
let required = false
if ($required) {
required = true
}
fields.push({
field,
title,
type,
required
})
// TODO 子表单 需要处理子表单字段
// if (type === 'group' && rule.props?.rule && Array.isArray(rule.props.rule)) {
// // 解析子表单的字段
// rule.props.rule.forEach((item) => {
// parseFields(item, fieldsPermission, title)
// })
// }
}
if (children && Array.isArray(children)) {
children.forEach((rule) => {
parseFormFields(rule, fields)
})
}
}
+4 -28
View File
@@ -1,7 +1,7 @@
<template>
<el-input v-model="valueRef" v-bind="$attrs">
<el-input v-model="modelValue" v-bind="$attrs">
<template #append>
<el-color-picker v-model="colorRef" :predefine="PREDEFINE_COLORS" />
<el-color-picker v-model="color" :predefine="PREDEFINE_COLORS" />
</template>
</el-input>
</template>
@@ -9,6 +9,7 @@
<script lang="ts" setup>
import { propTypes } from '@/utils/propTypes'
import { PREDEFINE_COLORS } from '@/utils/color'
import { useVModels } from '@vueuse/core'
/**
* 带颜色选择器输入框
@@ -19,33 +20,8 @@ const props = defineProps({
modelValue: propTypes.string.def('').isRequired,
color: propTypes.string.def('').isRequired
})
watch(
() => props.modelValue,
(val: string) => {
if (val === unref(valueRef)) return
valueRef.value = val
}
)
const emit = defineEmits(['update:modelValue', 'update:color'])
// 输入框的值
const valueRef = ref(props.modelValue)
watch(
() => valueRef.value,
(val: string) => {
emit('update:modelValue', val)
}
)
// 颜色
const colorRef = ref(props.color)
watch(
() => colorRef.value,
(val: string) => {
emit('update:color', val)
}
)
const { modelValue, color } = useVModels(props, emit)
</script>
<style scoped lang="scss">
:deep(.el-input-group__append) {
+8
View File
@@ -20,6 +20,7 @@
<div v-else class="custom-hover" @click.stop="showTopSearch = !showTopSearch">
<Icon icon="ep:search" />
<el-select
@click.stop
filterable
:reserve-keyword="false"
remote
@@ -78,9 +79,14 @@ function remoteMethod(data) {
function handleChange(path) {
router.push({ path })
hiddenSearch()
hiddenTopSearch()
}
function hiddenSearch() {
showSearch.value = false
}
function hiddenTopSearch() {
showTopSearch.value = false
}
@@ -98,6 +104,8 @@ onUnmounted(() => {
// 监听 ctrl + k
function listenKey(event) {
if ((event.ctrlKey || event.metaKey) && event.key === 'k') {
// 阻止触发浏览器默认事件
event.preventDefault()
showSearch.value = !showSearch.value
// 这里可以执行相应的操作(例如打开搜索框等)
}
@@ -1,11 +1,12 @@
<template>
<div class="node-handler-wrapper">
<div class="node-handler" v-if="props.showAdd">
<div class="node-handler">
<el-popover
trigger="hover"
v-model:visible="popoverShow"
placement="right-start"
width="auto"
v-if="!readonly"
>
<div class="handler-item-wrapper">
<div class="handler-item" @click="addNode(NodeType.USER_TASK_NODE)">
@@ -27,12 +28,36 @@
<div class="handler-item-text">条件分支</div>
</div>
<div class="handler-item" @click="addNode(NodeType.PARALLEL_BRANCH_NODE)">
<div class="handler-item-icon condition">
<div class="handler-item-icon parallel">
<span class="iconfont icon-size icon-parallel"></span>
</div>
<div class="handler-item-text">并行分支</div>
</div>
</div>
<div class="handler-item" @click="addNode(NodeType.INCLUSIVE_BRANCH_NODE)">
<div class="handler-item-icon inclusive">
<span class="iconfont icon-size icon-inclusive"></span>
</div>
<div class="handler-item-text">包容分支</div>
</div>
<div class="handler-item" @click="addNode(NodeType.DELAY_TIMER_NODE)">
<div class="handler-item-icon delay">
<span class="iconfont icon-size icon-delay"></span>
</div>
<div class="handler-item-text">延迟器</div>
</div>
<div class="handler-item" @click="addNode(NodeType.ROUTER_BRANCH_NODE)">
<div class="handler-item-icon router">
<span class="iconfont icon-size icon-router"></span>
</div>
<div class="handler-item-text">路由分支</div>
</div>
<div class="handler-item" @click="addNode(NodeType.TRIGGER_NODE)">
<div class="handler-item-icon trigger">
<span class="iconfont icon-size icon-trigger"></span>
</div>
<div class="handler-item-text">触发器</div>
</div>
</div>
<template #reference>
<div class="add-icon"><Icon icon="ep:plus" /></div>
</template>
@@ -46,33 +71,48 @@ import {
ApproveMethodType,
AssignEmptyHandlerType,
AssignStartUserHandlerType,
ConditionType,
NODE_DEFAULT_NAME,
NodeType,
RejectHandlerType,
SimpleFlowNode
SimpleFlowNode,
DEFAULT_CONDITION_GROUP_VALUE
} from './consts'
import { generateUUID } from '@/utils'
import {generateUUID} from '@/utils'
defineOptions({
name: 'NodeHandler'
})
const popoverShow = ref(false)
const message = useMessage() // 消息弹窗
const popoverShow = ref(false)
const props = defineProps({
childNode: {
type: Object as () => SimpleFlowNode,
default: null
},
showAdd: {
// 是否显示添加节点
type: Boolean,
default: true
currentNode: {
type: Object as () => SimpleFlowNode,
required: true
}
})
const emits = defineEmits(['update:childNode'])
const readonly = inject<Boolean>('readonly') // 是否只读
const addNode = (type: number) => {
// 校验:条件分支、包容分支后面,不允许直接添加并行分支
if (
type === NodeType.PARALLEL_BRANCH_NODE &&
[NodeType.CONDITION_BRANCH_NODE, NodeType.INCLUSIVE_BRANCH_NODE].includes(
props.currentNode?.type
)
) {
message.error('条件分支、包容分支后面,不允许直接添加并行分支')
return
}
popoverShow.value = false
if (type === NodeType.USER_TASK_NODE) {
const id = 'Activity_' + generateUUID()
@@ -93,7 +133,16 @@ const addNode = (type: number) => {
type: AssignEmptyHandlerType.APPROVE
},
assignStartUserHandlerType: AssignStartUserHandlerType.START_USER_AUDIT,
childNode: props.childNode
childNode: props.childNode,
taskCreateListener: {
enable: false
},
taskAssignListener: {
enable: false
},
taskCompleteListener: {
enable: false
}
}
emits('update:childNode', data)
}
@@ -120,18 +169,21 @@ const addNode = (type: number) => {
showText: '',
type: NodeType.CONDITION_NODE,
childNode: undefined,
conditionType: 1,
defaultFlow: false
conditionSetting: {
defaultFlow: false,
conditionType: ConditionType.RULE,
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE
}
},
{
id: 'Flow_' + generateUUID(),
name: '其它情况',
showText: '其它情况进入此流程',
showText: '未满足其它条件时,将进入此分支',
type: NodeType.CONDITION_NODE,
childNode: undefined,
conditionType: undefined,
defaultFlow: true
conditionSetting: {
defaultFlow: true
}
}
]
}
@@ -162,6 +214,69 @@ const addNode = (type: number) => {
}
emits('update:childNode', data)
}
if (type === NodeType.INCLUSIVE_BRANCH_NODE) {
const data: SimpleFlowNode = {
name: '包容分支',
type: NodeType.INCLUSIVE_BRANCH_NODE,
id: 'GateWay_' + generateUUID(),
childNode: props.childNode,
conditionNodes: [
{
id: 'Flow_' + generateUUID(),
name: '包容条件1',
showText: '',
type: NodeType.CONDITION_NODE,
childNode: undefined,
conditionSetting: {
defaultFlow: false,
conditionType: ConditionType.RULE,
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE
}
},
{
id: 'Flow_' + generateUUID(),
name: '其它情况',
showText: '未满足其它条件时,将进入此分支',
type: NodeType.CONDITION_NODE,
childNode: undefined,
conditionSetting: {
defaultFlow: true
}
}
]
}
emits('update:childNode', data)
}
if (type === NodeType.DELAY_TIMER_NODE) {
const data: SimpleFlowNode = {
id: 'Activity_' + generateUUID(),
name: NODE_DEFAULT_NAME.get(NodeType.DELAY_TIMER_NODE) as string,
showText: '',
type: NodeType.DELAY_TIMER_NODE,
childNode: props.childNode
}
emits('update:childNode', data)
}
if (type === NodeType.ROUTER_BRANCH_NODE) {
const data: SimpleFlowNode = {
id: 'GateWay_' + generateUUID(),
name: NODE_DEFAULT_NAME.get(NodeType.ROUTER_BRANCH_NODE) as string,
showText: '',
type: NodeType.ROUTER_BRANCH_NODE,
childNode: props.childNode
}
emits('update:childNode', data)
}
if (type === NodeType.TRIGGER_NODE) {
const data: SimpleFlowNode = {
id: 'Activity_' + generateUUID(),
name: NODE_DEFAULT_NAME.get(NodeType.TRIGGER_NODE) as string,
showText: '',
type: NodeType.TRIGGER_NODE,
childNode: props.childNode
}
emits('update:childNode', data)
}
}
</script>
@@ -31,6 +31,31 @@
@update:model-value="handleModelValueUpdate"
@find:parent-node="findFromParentNode"
/>
<!-- 包容分支节点 -->
<InclusiveNode
v-if="currentNode && currentNode.type === NodeType.INCLUSIVE_BRANCH_NODE"
:flow-node="currentNode"
@update:model-value="handleModelValueUpdate"
@find:parent-node="findFromParentNode"
/>
<!-- 延迟器节点 -->
<DelayTimerNode
v-if="currentNode && currentNode.type === NodeType.DELAY_TIMER_NODE"
:flow-node="currentNode"
@update:flow-node="handleModelValueUpdate"
/>
<!-- 路由分支节点 -->
<RouterNode
v-if="currentNode && currentNode.type === NodeType.ROUTER_BRANCH_NODE"
:flow-node="currentNode"
@update:flow-node="handleModelValueUpdate"
/>
<!-- 触发器节点 -->
<TriggerNode
v-if="currentNode && currentNode.type === NodeType.TRIGGER_NODE"
:flow-node="currentNode"
@update:flow-node="handleModelValueUpdate"
/>
<!-- 递归显示孩子节点 -->
<ProcessNodeTree
v-if="currentNode && currentNode.childNode"
@@ -40,7 +65,10 @@
/>
<!-- 结束节点 -->
<EndEventNode v-if="currentNode && currentNode.type === NodeType.END_EVENT_NODE" />
<EndEventNode
v-if="currentNode && currentNode.type === NodeType.END_EVENT_NODE"
:flow-node="currentNode"
/>
</template>
<script setup lang="ts">
import StartUserNode from './nodes/StartUserNode.vue'
@@ -49,6 +77,10 @@ import UserTaskNode from './nodes/UserTaskNode.vue'
import CopyTaskNode from './nodes/CopyTaskNode.vue'
import ExclusiveNode from './nodes/ExclusiveNode.vue'
import ParallelNode from './nodes/ParallelNode.vue'
import InclusiveNode from './nodes/InclusiveNode.vue'
import DelayTimerNode from './nodes/DelayTimerNode.vue'
import RouterNode from './nodes/RouterNode.vue'
import TriggerNode from './nodes/TriggerNode.vue'
import { SimpleFlowNode, NodeType } from './consts'
import { useWatchNode } from './node'
defineOptions({
@@ -1,23 +1,12 @@
<template>
<div class="simple-flow-canvas" v-loading="loading">
<div class="simple-flow-container">
<div class="top-area-container">
<div class="top-actions">
<div class="canvas-control">
<span class="control-scale-group">
<span class="control-scale-button"> <Icon icon="ep:plus" @click="zoomOut()" /></span>
<span class="control-scale-label">{{ scaleValue }}%</span>
<span class="control-scale-button"><Icon icon="ep:minus" @click="zoomIn()" /></span>
</span>
</div>
<el-button type="primary" @click="saveSimpleFlowModel">保存</el-button>
<!-- <el-button type="primary">全局设置</el-button> -->
</div>
</div>
<div class="scale-container" :style="`transform: scale(${scaleValue / 100});`">
<ProcessNodeTree v-if="processNodeTree" v-model:flow-node="processNodeTree" />
</div>
</div>
<div v-loading="loading" class="overflow-auto">
<SimpleProcessModel
ref="simpleProcessModelRef"
v-if="processNodeTree"
:flow-node="processNodeTree"
:readonly="false"
@save="saveSimpleFlowModel"
/>
<Dialog v-model="errorDialogVisible" title="保存失败" width="400" :fullscreen="false">
<div class="mb-2">以下节点内容不完善,请修改后保存</div>
<div
@@ -35,7 +24,7 @@
</template>
<script setup lang="ts">
import ProcessNodeTree from './ProcessNodeTree.vue'
import SimpleProcessModel from './SimpleProcessModel.vue'
import { updateBpmSimpleModel, getBpmSimpleModel } from '@/api/bpm/simple'
import { SimpleFlowNode, NodeType, NodeId, NODE_DEFAULT_TEXT } from './consts'
import { getModel } from '@/api/bpm/model'
@@ -50,14 +39,31 @@ import * as UserGroupApi from '@/api/bpm/userGroup'
defineOptions({
name: 'SimpleProcessDesigner'
})
const router = useRouter() // 路由
const emits = defineEmits(['success']) // 保存成功事件
const props = defineProps({
modelId: {
type: String,
required: true
required: false
},
modelKey: {
type: String,
required: false
},
modelName: {
type: String,
required: false
},
// 可发起流程的人员编号
startUserIds: {
type: Array,
required: false
}
})
const loading = ref(true)
const processData = inject('processData') as Ref
const loading = ref(false)
const formFields = ref<string[]>([])
const formType = ref(20)
const roleOptions = ref<RoleApi.RoleVO[]>([]) // 角色列表
@@ -66,6 +72,7 @@ const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
const deptOptions = ref<DeptApi.DeptVO[]>([]) // 部门列表
const deptTreeOptions = ref()
const userGroupOptions = ref<UserGroupApi.UserGroupVO[]>([]) // 用户组列表
provide('formFields', formFields)
provide('formType', formType)
provide('roleList', roleOptions)
@@ -74,35 +81,46 @@ provide('userList', userOptions)
provide('deptList', deptOptions)
provide('userGroupList', userGroupOptions)
provide('deptTree', deptTreeOptions)
provide('startUserIds', props.startUserIds)
provide('tasks', [])
provide('processInstance', {})
const message = useMessage() // 国际化
const processNodeTree = ref<SimpleFlowNode | undefined>()
provide('processNodeTree', processNodeTree)
const errorDialogVisible = ref(false)
let errorNodes: SimpleFlowNode[] = []
const saveSimpleFlowModel = async () => {
if (!props.modelId) {
message.error('缺少模型 modelId 编号')
return
}
errorNodes = []
validateNode(processNodeTree.value, errorNodes)
if (errorNodes.length > 0) {
errorDialogVisible.value = true
return
}
const data = {
id: props.modelId,
simpleModel: processNodeTree.value
}
const result = await updateBpmSimpleModel(data)
if (result) {
message.success('修改成功')
close()
} else {
message.alert('修改失败')
// 添加更新模型的方法
const updateModel = () => {
if (!processNodeTree.value) {
processNodeTree.value = {
name: '发起人',
type: NodeType.START_USER_NODE,
id: NodeId.START_USER_NODE_ID,
childNode: {
id: NodeId.END_EVENT_NODE_ID,
name: '结束',
type: NodeType.END_EVENT_NODE
}
}
// 初始化时也触发一次保存
saveSimpleFlowModel(processNodeTree.value)
}
}
const saveSimpleFlowModel = async (simpleModelNode: SimpleFlowNode) => {
if (!simpleModelNode) {
return
}
try {
processData.value = simpleModelNode
emits('success', simpleModelNode)
} catch (error) {
console.error('保存失败:', error)
}
}
// 校验节点设置。 暂时以 showText 为空 未节点错误配置
const validateNode = (node: SimpleFlowNode | undefined, errorNodes: SimpleFlowNode[]) => {
if (node) {
@@ -111,68 +129,49 @@ const validateNode = (node: SimpleFlowNode | undefined, errorNodes: SimpleFlowNo
return
}
if (type == NodeType.START_USER_NODE) {
// 发起人节点暂时不用校验,直接校验孩子节点
validateNode(node.childNode, errorNodes)
}
if (type === NodeType.USER_TASK_NODE) {
if (!showText) {
errorNodes.push(node)
}
validateNode(node.childNode, errorNodes)
}
if (type === NodeType.COPY_TASK_NODE) {
if (!showText) {
errorNodes.push(node)
}
validateNode(node.childNode, errorNodes)
}
if (type === NodeType.CONDITION_NODE) {
if (
type === NodeType.USER_TASK_NODE ||
type === NodeType.COPY_TASK_NODE ||
type === NodeType.CONDITION_NODE
) {
if (!showText) {
errorNodes.push(node)
}
validateNode(node.childNode, errorNodes)
}
if (type == NodeType.CONDITION_BRANCH_NODE) {
if (
type == NodeType.CONDITION_BRANCH_NODE ||
type == NodeType.PARALLEL_BRANCH_NODE ||
type == NodeType.INCLUSIVE_BRANCH_NODE
) {
// 分支节点
// 1. 先校验各个分支
conditionNodes?.forEach((item) => {
validateNode(item, errorNodes)
})
// 2. 校验孩子节点
validateNode(node.childNode, errorNodes)
}
}
}
const close = () => {
router.push({ path: '/bpm/manager/model' })
}
let scaleValue = ref(100)
const MAX_SCALE_VALUE = 200
const MIN_SCALE_VALUE = 50
// 放大
const zoomOut = () => {
if (scaleValue.value == MAX_SCALE_VALUE) {
return
}
scaleValue.value += 10
}
// 缩小
const zoomIn = () => {
if (scaleValue.value == MIN_SCALE_VALUE) {
return
}
scaleValue.value -= 10
}
onMounted(async () => {
try {
loading.value = true
// 获取表单字段
const bpmnModel = await getModel(props.modelId)
if (bpmnModel) {
formType.value = bpmnModel.formType
if (formType.value === 10) {
const bpmnForm = (await getForm(bpmnModel.formId)) as unknown as FormVO
formFields.value = bpmnForm?.fields
if (props.modelId) {
const bpmnModel = await getModel(props.modelId)
if (bpmnModel) {
formType.value = bpmnModel.formType
if (formType.value === 10) {
const bpmnForm = (await getForm(bpmnModel.formId)) as unknown as FormVO
formFields.value = bpmnForm?.fields
}
}
}
// 获得角色列表
@@ -183,30 +182,21 @@ onMounted(async () => {
userOptions.value = await UserApi.getSimpleUserList()
// 获得部门列表
deptOptions.value = await DeptApi.getSimpleDeptList()
deptTreeOptions.value = handleTree(deptOptions.value as DeptApi.DeptVO[], 'id')
// 获取用户组列表
userGroupOptions.value = await UserGroupApi.getUserGroupSimpleList()
// 获取 SIMPLE 设计器模型
const result = await getBpmSimpleModel(props.modelId)
if (result) {
processNodeTree.value = result
// 加载流程数据
if (processData.value) {
processNodeTree.value = processData?.value
} else {
// 初始值
processNodeTree.value = {
name: '发起人',
type: NodeType.START_USER_NODE,
id: NodeId.START_USER_NODE_ID,
childNode: {
id: NodeId.END_EVENT_NODE_ID,
name: '结束',
type: NodeType.END_EVENT_NODE
}
}
updateModel()
}
} finally {
loading.value = false
}
})
const simpleProcessModelRef = ref()
defineExpose({})
</script>
@@ -0,0 +1,198 @@
<template>
<div class="simple-process-model-container position-relative">
<div class="position-absolute top-0px right-0px bg-#fff">
<el-row type="flex" justify="end">
<el-button-group key="scale-control" size="default">
<el-button v-if="!readonly" size="default" @click="exportJson">
<Icon icon="ep:download" /> 导出
</el-button>
<el-button v-if="!readonly" size="default" @click="importJson">
<Icon icon="ep:upload" />导入
</el-button>
<!-- 用于打开本地文件-->
<input
v-if="!readonly"
type="file"
id="files"
ref="refFile"
style="display: none"
accept=".json"
@change="importLocalFile"
/>
<el-button size="default" :icon="ScaleToOriginal" @click="processReZoom()" />
<el-button size="default" :plain="true" :icon="ZoomOut" @click="zoomOut()" />
<el-button size="default" class="w-80px"> {{ scaleValue }}% </el-button>
<el-button size="default" :plain="true" :icon="ZoomIn" @click="zoomIn()" />
</el-button-group>
</el-row>
</div>
<div class="simple-process-model" :style="`transform: scale(${scaleValue / 100});`">
<ProcessNodeTree v-if="processNodeTree" v-model:flow-node="processNodeTree" />
</div>
</div>
<Dialog v-model="errorDialogVisible" title="保存失败" width="400" :fullscreen="false">
<div class="mb-2">以下节点内容不完善,请修改后保存</div>
<div
class="mb-3 b-rounded-1 bg-gray-100 p-2 line-height-normal"
v-for="(item, index) in errorNodes"
:key="index"
>
{{ item.name }} : {{ NODE_DEFAULT_TEXT.get(item.type) }}
</div>
<template #footer>
<el-button type="primary" @click="errorDialogVisible = false">知道了</el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import ProcessNodeTree from './ProcessNodeTree.vue'
import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from './consts'
import { useWatchNode } from './node'
import { ZoomOut, ZoomIn, ScaleToOriginal } from '@element-plus/icons-vue'
import { isString } from '@/utils/is'
import download from '@/utils/download'
defineOptions({
name: 'SimpleProcessModel'
})
const props = defineProps({
flowNode: {
type: Object as () => SimpleFlowNode,
required: true
},
readonly: {
type: Boolean,
required: false,
default: true
}
})
const emits = defineEmits<{
save: [node: SimpleFlowNode | undefined]
}>()
const processNodeTree = useWatchNode(props)
provide('readonly', props.readonly)
let scaleValue = ref(100)
const MAX_SCALE_VALUE = 200
const MIN_SCALE_VALUE = 50
// 放大
const zoomIn = () => {
if (scaleValue.value == MAX_SCALE_VALUE) {
return
}
scaleValue.value += 10
}
// 缩小
const zoomOut = () => {
if (scaleValue.value == MIN_SCALE_VALUE) {
return
}
scaleValue.value -= 10
}
const processReZoom = () => {
scaleValue.value = 100
}
const errorDialogVisible = ref(false)
let errorNodes: SimpleFlowNode[] = []
const saveSimpleFlowModel = async () => {
errorNodes = []
validateNode(processNodeTree.value, errorNodes)
if (errorNodes.length > 0) {
errorDialogVisible.value = true
return
}
emits('save', processNodeTree.value)
}
// 校验节点设置。 暂时以 showText 为空 未节点错误配置
const validateNode = (node: SimpleFlowNode | undefined, errorNodes: SimpleFlowNode[]) => {
if (node) {
const { type, showText, conditionNodes } = node
if (type == NodeType.END_EVENT_NODE) {
return
}
if (type == NodeType.START_USER_NODE) {
// 发起人节点暂时不用校验,直接校验孩子节点
validateNode(node.childNode, errorNodes)
}
if (
type === NodeType.USER_TASK_NODE ||
type === NodeType.COPY_TASK_NODE ||
type === NodeType.CONDITION_NODE
) {
if (!showText) {
errorNodes.push(node)
}
validateNode(node.childNode, errorNodes)
}
if (
type == NodeType.CONDITION_BRANCH_NODE ||
type == NodeType.PARALLEL_BRANCH_NODE ||
type == NodeType.INCLUSIVE_BRANCH_NODE
) {
// 分支节点
// 1. 先校验各个分支
conditionNodes?.forEach((item) => {
validateNode(item, errorNodes)
})
// 2. 校验孩子节点
validateNode(node.childNode, errorNodes)
}
}
}
/** 获取当前流程数据 */
const getCurrentFlowData = async () => {
try {
errorNodes = []
validateNode(processNodeTree.value, errorNodes)
if (errorNodes.length > 0) {
errorDialogVisible.value = true
return undefined
}
return processNodeTree.value
} catch (error) {
console.error('获取流程数据失败:', error)
return undefined
}
}
defineExpose({
getCurrentFlowData
})
/** 导出 JSON */
const exportJson = () => {
download.json(new Blob([JSON.stringify(processNodeTree.value)]), 'model.json')
}
/** 导入 JSON */
const refFile = ref()
const importJson = () => {
refFile.value.click()
}
const importLocalFile = () => {
const file = refFile.value.files[0]
const reader = new FileReader()
reader.readAsText(file)
reader.onload = function () {
if (isString(this.result)) {
processNodeTree.value = JSON.parse(this.result)
emits('save', processNodeTree.value)
}
}
}
</script>
<style lang="scss" scoped></style>
@@ -0,0 +1,48 @@
<template>
<SimpleProcessModel :flow-node="simpleModel" :readonly="true" />
</template>
<script setup lang="ts">
import { useWatchNode } from './node'
import { SimpleFlowNode } from './consts'
defineOptions({
name: 'SimpleProcessViewer'
})
const props = defineProps({
flowNode: {
type: Object as () => SimpleFlowNode,
required: true
},
// 流程任务
tasks: {
type: Array,
default: () => [] as any[]
},
// 流程实例
processInstance: {
type: Object,
default: () => undefined
}
})
const approveTasks = ref<any[]>(props.tasks)
const currentProcessInstance = ref(props.processInstance)
const simpleModel = useWatchNode(props)
watch(
() => props.tasks,
(newValue) => {
approveTasks.value = newValue
}
)
watch(
() => props.processInstance,
(newValue) => {
currentProcessInstance.value = newValue
}
)
provide('tasks', approveTasks)
provide('processInstance', currentProcessInstance)
</script>
p
@@ -1,6 +1,6 @@
// @ts-ignore
import { DictDataVO } from '@/api/system/dict/types'
import { TaskStatusEnum } from '@/api/bpm/task'
/**
* 节点类型
*/
@@ -23,6 +23,16 @@ export enum NodeType {
*/
COPY_TASK_NODE = 12,
/**
* 延迟器节点
*/
DELAY_TIMER_NODE = 14,
/**
* 触发器节点
*/
TRIGGER_NODE = 15,
/**
* 条件节点
*/
@@ -39,7 +49,11 @@ export enum NodeType {
/**
* 包容分支节点 (对应包容网关)
*/
INCLUSIVE_BRANCH_NODE = 53
INCLUSIVE_BRANCH_NODE = 53,
/**
* 路由分支节点
*/
ROUTER_BRANCH_NODE = 54
}
export enum NodeId {
@@ -79,7 +93,7 @@ export interface SimpleFlowNode {
// 审批按钮设置
buttonsSetting?: any[]
// 表单权限
fieldsPermission?: Array<Record<string, string>>
fieldsPermission?: Array<Record<string, any>>
// 审批任务超时处理
timeoutHandler?: TimeoutHandler
// 审批任务拒绝处理
@@ -88,15 +102,27 @@ export interface SimpleFlowNode {
assignEmptyHandler?: AssignEmptyHandler
// 审批节点的审批人与发起人相同时,对应的处理类型
assignStartUserHandlerType?: number
// 条件类型
conditionType?: ConditionType
// 条件表达式
conditionExpression?: string
// 条件组
conditionGroups?: ConditionGroup
// 是否默认的条件
defaultFlow?: boolean
// 创建任务监听器
taskCreateListener?: ListenerHandler
// 创建任务监听器
taskAssignListener?: ListenerHandler
// 创建任务监听器
taskCompleteListener?: ListenerHandler
// 条件设置
conditionSetting?: ConditionSetting
// 活动的状态,用于前端节点状态展示
activityStatus?: TaskStatusEnum
// 延迟设置
delaySetting?: DelaySetting
// 路由分支
routerGroups?: RouterSetting[]
defaultFlowId?: string
// 签名
signEnable?: boolean
// 审批意见
reasonRequire?: boolean
// 触发器设置
triggerSetting?: TriggerSetting
}
// 候选人策略枚举 ( 用于审批节点。抄送节点 )
export enum CandidateStrategy {
@@ -144,6 +170,14 @@ export enum CandidateStrategy {
* 指定用户组
*/
USER_GROUP = 40,
/**
* 表单内用户字段
*/
FORM_USER = 50,
/**
* 表单内部门负责人
*/
FORM_DEPT_LEADER = 51,
/**
* 流程表达式
*/
@@ -178,7 +212,7 @@ export enum ApproveMethodType {
export type RejectHandler = {
// 审批拒绝类型
type: RejectHandlerType
// 回退节点 Id
// 退回节点 Id
returnNodeId?: string
}
@@ -206,6 +240,41 @@ export type AssignEmptyHandler = {
userIds?: number[]
}
/**
* 监听器的结构定义
*/
export type ListenerHandler = {
enable: boolean
path?: string
header?: HttpRequestParam[]
body?: HttpRequestParam[]
}
export type HttpRequestParam = {
key: string
type: number
value: string
}
export enum BpmHttpRequestParamTypeEnum {
/**
* 固定值
*/
FIXED_VALUE = 1,
/**
* 表单
*/
FROM_FORM = 2
}
export const BPM_HTTP_REQUEST_PARAM_TYPES = [
{
value: 1,
label: '固定值'
},
{
value: 2,
label: '表单'
}
]
// 审批拒绝类型枚举
export enum RejectHandlerType {
/**
@@ -299,6 +368,20 @@ export enum TimeUnitType {
DAY = 3
}
/**
* 条件节点设置结构定义,用于条件节点
*/
export type ConditionSetting = {
// 条件类型
conditionType?: ConditionType
// 条件表达式
conditionExpression?: string
// 条件组
conditionGroups?: ConditionGroup
// 是否默认的条件
defaultFlow?: boolean
}
// 条件配置类型 ( 用于条件节点配置 )
export enum ConditionType {
/**
@@ -360,17 +443,19 @@ export enum OperationButtonType {
*/
ADD_SIGN = 5,
/**
* 回退
* 退回
*/
RETURN = 6
RETURN = 6,
/**
* 抄送
*/
COPY = 7
}
/**
* 条件规则结构定义
*/
export type ConditionRule = {
type: number
opName: string
opCode: string
leftSide: string
rightSide: string
@@ -385,6 +470,24 @@ export type ConditionGroup = {
// 条件数组
conditions: Condition[]
}
/**
* 条件组默认值
*/
export const DEFAULT_CONDITION_GROUP_VALUE = {
and: true,
conditions: [
{
and: true,
rules: [
{
opCode: '==',
leftSide: '',
rightSide: ''
}
]
}
]
}
/**
* 条件结构定义
@@ -400,12 +503,18 @@ NODE_DEFAULT_TEXT.set(NodeType.USER_TASK_NODE, '请配置审批人')
NODE_DEFAULT_TEXT.set(NodeType.COPY_TASK_NODE, '请配置抄送人')
NODE_DEFAULT_TEXT.set(NodeType.CONDITION_NODE, '请设置条件')
NODE_DEFAULT_TEXT.set(NodeType.START_USER_NODE, '请设置发起人')
NODE_DEFAULT_TEXT.set(NodeType.DELAY_TIMER_NODE, '请设置延迟器')
NODE_DEFAULT_TEXT.set(NodeType.ROUTER_BRANCH_NODE, '请设置路由节点')
NODE_DEFAULT_TEXT.set(NodeType.TRIGGER_NODE, '请设置触发器')
export const NODE_DEFAULT_NAME = new Map<number, string>()
NODE_DEFAULT_NAME.set(NodeType.USER_TASK_NODE, '审批人')
NODE_DEFAULT_NAME.set(NodeType.COPY_TASK_NODE, '抄送人')
NODE_DEFAULT_NAME.set(NodeType.CONDITION_NODE, '条件')
NODE_DEFAULT_NAME.set(NodeType.START_USER_NODE, '发起人')
NODE_DEFAULT_NAME.set(NodeType.DELAY_TIMER_NODE, '延迟器')
NODE_DEFAULT_NAME.set(NodeType.ROUTER_BRANCH_NODE, '路由分支')
NODE_DEFAULT_NAME.set(NodeType.TRIGGER_NODE, '触发器')
// 候选人策略。暂时不从字典中取。 后续可能调整。控制显示顺序
export const CANDIDATE_STRATEGY: DictDataVO[] = [
@@ -419,6 +528,8 @@ export const CANDIDATE_STRATEGY: DictDataVO[] = [
{ label: '发起人部门负责人', value: CandidateStrategy.START_USER_DEPT_LEADER },
{ label: '发起人连续部门负责人', value: CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER },
{ label: '用户组', value: CandidateStrategy.USER_GROUP },
{ label: '表单内用户字段', value: CandidateStrategy.FORM_USER },
{ label: '表单内部门负责人', value: CandidateStrategy.FORM_DEPT_LEADER },
{ label: '流程表达式', value: CandidateStrategy.EXPRESSION }
]
// 审批节点 的审批类型
@@ -436,8 +547,8 @@ export const APPROVE_METHODS: DictDataVO[] = [
]
export const CONDITION_CONFIG_TYPES: DictDataVO[] = [
{ label: '条件表达式', value: ConditionType.EXPRESSION },
{ label: '条件规则', value: ConditionType.RULE }
{ label: '条件规则', value: ConditionType.RULE },
{ label: '条件表达式', value: ConditionType.EXPRESSION }
]
// 时间单位类型
@@ -503,16 +614,17 @@ OPERATION_BUTTON_NAME.set(OperationButtonType.REJECT, '拒绝')
OPERATION_BUTTON_NAME.set(OperationButtonType.TRANSFER, '转办')
OPERATION_BUTTON_NAME.set(OperationButtonType.DELEGATE, '委派')
OPERATION_BUTTON_NAME.set(OperationButtonType.ADD_SIGN, '加签')
OPERATION_BUTTON_NAME.set(OperationButtonType.RETURN, '回退')
OPERATION_BUTTON_NAME.set(OperationButtonType.RETURN, '退回')
OPERATION_BUTTON_NAME.set(OperationButtonType.COPY, '抄送')
// 默认的按钮权限设置
export const DEFAULT_BUTTON_SETTING: ButtonSetting[] = [
{ id: OperationButtonType.APPROVE, displayName: '通过', enable: true },
{ id: OperationButtonType.REJECT, displayName: '拒绝', enable: true },
{ id: OperationButtonType.TRANSFER, displayName: '转办', enable: false },
{ id: OperationButtonType.DELEGATE, displayName: '委派', enable: false },
{ id: OperationButtonType.ADD_SIGN, displayName: '加签', enable: false },
{ id: OperationButtonType.RETURN, displayName: '回退', enable: false }
{ id: OperationButtonType.TRANSFER, displayName: '转办', enable: true },
{ id: OperationButtonType.DELEGATE, displayName: '委派', enable: true },
{ id: OperationButtonType.ADD_SIGN, displayName: '加签', enable: true },
{ id: OperationButtonType.RETURN, displayName: '退回', enable: true }
]
// 发起人的按钮权限。暂时定死,不可以编辑
@@ -522,7 +634,7 @@ export const START_USER_BUTTON_SETTING: ButtonSetting[] = [
{ id: OperationButtonType.TRANSFER, displayName: '转办', enable: false },
{ id: OperationButtonType.DELEGATE, displayName: '委派', enable: false },
{ id: OperationButtonType.ADD_SIGN, displayName: '加签', enable: false },
{ id: OperationButtonType.RETURN, displayName: '回退', enable: false }
{ id: OperationButtonType.RETURN, displayName: '退回', enable: false }
]
export const MULTI_LEVEL_DEPT: DictDataVO = [
@@ -542,3 +654,109 @@ export const MULTI_LEVEL_DEPT: DictDataVO = [
{ label: '第 14 级部门', value: 14 },
{ label: '第 15 级部门', value: 15 }
]
/**
* 流程实例的变量枚举
*/
export enum ProcessVariableEnum {
/**
* 发起用户 ID
*/
START_USER_ID = 'PROCESS_START_USER_ID',
/**
* 发起时间
*/
START_TIME = 'PROCESS_START_TIME',
/**
* 流程定义名称
*/
PROCESS_DEFINITION_NAME = 'PROCESS_DEFINITION_NAME'
}
/**
* 延迟设置
*/
export type DelaySetting = {
// 延迟类型
delayType: number
// 延迟时间表达式
delayTime: string
}
/**
* 延迟类型
*/
export enum DelayTypeEnum {
/**
* 固定时长
*/
FIXED_TIME_DURATION = 1,
/**
* 固定日期时间
*/
FIXED_DATE_TIME = 2
}
export const DELAY_TYPE = [
{ label: '固定时长', value: DelayTypeEnum.FIXED_TIME_DURATION },
{ label: '固定日期', value: DelayTypeEnum.FIXED_DATE_TIME }
]
/**
* 路由分支结构定义
*/
export type RouterSetting = {
nodeId: string
conditionType: ConditionType
conditionExpression: string
conditionGroups: ConditionGroup
}
// ==================== 触发器相关定义 ====================
/**
* 触发器节点结构定义
*/
export type TriggerSetting = {
type: TriggerTypeEnum
httpRequestSetting?: HttpRequestTriggerSetting
normalFormSetting?: NormalFormTriggerSetting
}
/**
* 触发器类型枚举
*/
export enum TriggerTypeEnum {
/**
* 发送 HTTP 请求触发器
*/
HTTP_REQUEST = 1,
/**
* 更新流程表单触发器
*/
UPDATE_NORMAL_FORM = 2 // TODO @jason:FORM_UPDATE?
}
/**
* HTTP 请求触发器结构定义
*/
export type HttpRequestTriggerSetting = {
// 请求 URL
url: string
// 请求头参数设置
header?: HttpRequestParam[]
// 请求体参数设置
body?: HttpRequestParam[]
// 请求响应设置
response?: Record<string, string>[]
}
/**
* 流程表单触发器配置结构定义
*/
export type NormalFormTriggerSetting = {
// 更新表单字段
updateFormFields?: Record<string, any>
}
export const TRIGGER_TYPES: DictDataVO[] = [
{ label: 'HTTP 请求', value: TriggerTypeEnum.HTTP_REQUEST },
{ label: '修改表单数据', value: TriggerTypeEnum.UPDATE_NORMAL_FORM }
]
@@ -1,4 +1,5 @@
import SimpleProcessDesigner from './SimpleProcessDesigner.vue'
import SimpleProcessViewer from './SimpleProcessViewer.vue'
import '../theme/simple-process-designer.scss'
export { SimpleProcessDesigner }
export { SimpleProcessDesigner, SimpleProcessViewer}
@@ -1,4 +1,4 @@
import { cloneDeep } from 'lodash-es'
import { TaskStatusEnum } from '@/api/bpm/task'
import * as RoleApi from '@/api/system/role'
import * as DeptApi from '@/api/system/dept'
import * as PostApi from '@/api/system/post'
@@ -13,8 +13,12 @@ import {
NODE_DEFAULT_NAME,
AssignStartUserHandlerType,
AssignEmptyHandlerType,
FieldPermissionType
FieldPermissionType,
HttpRequestParam,
ProcessVariableEnum
} from './consts'
import { parseFormFields } from '@/components/FormCreate/src/utils'
export function useWatchNode(props: { flowNode: SimpleFlowNode }): Ref<SimpleFlowNode> {
const node = ref<SimpleFlowNode>(props.flowNode)
watch(
@@ -26,122 +30,108 @@ export function useWatchNode(props: { flowNode: SimpleFlowNode }): Ref<SimpleFlo
return node
}
// 解析 formCreate 所有表单字段, 并返回
const parseFormCreateFields = (formFields?: string[]) => {
const result: Array<Record<string, any>> = []
if (formFields) {
formFields.forEach((fieldStr: string) => {
parseFormFields(JSON.parse(fieldStr), result)
})
}
return result
}
/**
* @description 表单数据权限配置,用于发起人节点 、审批节点、抄送节点
*/
export function useFormFieldsPermission(defaultPermission: FieldPermissionType) {
// 字段权限配置. 需要有 field, title, permissioin 属性
const fieldsPermissionConfig = ref<Array<Record<string, string>>>([])
const fieldsPermissionConfig = ref<Array<Record<string, any>>>([])
const formType = inject<Ref<number>>('formType') // 表单类型
const formType = inject<Ref<number | undefined>>('formType', ref()) // 表单类型
const formFields = inject<Ref<string[]>>('formFields') // 流程表单字段
const formFields = inject<Ref<string[]>>('formFields', ref([])) // 流程表单字段
const getNodeConfigFormFields = (nodeFormFields?: Array<Record<string, string>>) => {
nodeFormFields = toRaw(nodeFormFields)
fieldsPermissionConfig.value =
cloneDeep(nodeFormFields) || getDefaultFieldsPermission(unref(formFields))
if (!nodeFormFields || nodeFormFields.length === 0) {
fieldsPermissionConfig.value = getDefaultFieldsPermission(unref(formFields))
} else {
fieldsPermissionConfig.value = mergeFieldsPermission(nodeFormFields, unref(formFields))
}
}
// 合并已经设置的表单字段权限,当前流程表单字段 (可能新增,或删除了字段)
const mergeFieldsPermission = (
formFieldsPermisson: Array<Record<string, string>>,
formFields?: string[]
) => {
let mergedFieldsPermission: Array<Record<string, any>> = []
if (formFields) {
mergedFieldsPermission = parseFormCreateFields(formFields).map((item) => {
const found = formFieldsPermisson.find(
(fieldPermission) => fieldPermission.field == item.field
)
return {
field: item.field,
title: item.title,
permission: found ? found.permission : defaultPermission
}
})
}
return mergedFieldsPermission
}
// 默认的表单权限: 获取表单的所有字段,设置字段默认权限为只读
const getDefaultFieldsPermission = (formFields?: string[]) => {
const defaultFieldsPermission: Array<Record<string, string>> = []
let defaultFieldsPermission: Array<Record<string, any>> = []
if (formFields) {
formFields.forEach((fieldStr: string) => {
parseFieldsSetDefaultPermission(JSON.parse(fieldStr), defaultFieldsPermission)
defaultFieldsPermission = parseFormCreateFields(formFields).map((item) => {
return {
field: item.field,
title: item.title,
permission: defaultPermission
}
})
}
return defaultFieldsPermission
}
// 解析字段。赋给默认权限
const parseFieldsSetDefaultPermission = (
rule: Record<string, any>,
fieldsPermission: Array<Record<string, string>>,
parentTitle: string = ''
) => {
const { /**type,*/ field, title: tempTitle, children } = rule
if (field && tempTitle) {
let title = tempTitle
if (parentTitle) {
title = `${parentTitle}.${tempTitle}`
}
fieldsPermission.push({
field,
title,
permission: defaultPermission
})
// TODO 子表单 需要处理子表单字段
// if (type === 'group' && rule.props?.rule && Array.isArray(rule.props.rule)) {
// // 解析子表单的字段
// rule.props.rule.forEach((item) => {
// parseFieldsSetDefaultPermission(item, fieldsPermission, title)
// })
// }
}
if (children && Array.isArray(children)) {
children.forEach((rule) => {
parseFieldsSetDefaultPermission(rule, fieldsPermission)
})
}
}
// 获取表单的所有字段,作为下拉框选项
const formFieldOptions = parseFormCreateFields(unref(formFields))
return {
formType,
fieldsPermissionConfig,
formFieldOptions,
getNodeConfigFormFields
}
}
/**
* @description 获取表单的字段
* @description 获取流程表单的字段
*/
export function useFormFields() {
// 解析后的表单字段
const formFields = inject<Ref<string[]>>('formFields') // 流程表单字段
const parseFormFields = () => {
const parsedFormFields: Array<Record<string, string>> = []
if (formFields) {
formFields.value.forEach((fieldStr: string) => {
parseField(JSON.parse(fieldStr), parsedFormFields)
})
}
return parsedFormFields
}
// 解析字段。
const parseField = (
rule: Record<string, any>,
parsedFormFields: Array<Record<string, string>>,
parentTitle: string = ''
) => {
const { field, title: tempTitle, children, type } = rule
if (field && tempTitle) {
let title = tempTitle
if (parentTitle) {
title = `${parentTitle}.${tempTitle}`
}
parsedFormFields.push({
field,
title,
type
})
// TODO 子表单 需要处理子表单字段
// if (type === 'group' && rule.props?.rule && Array.isArray(rule.props.rule)) {
// // 解析子表单的字段
// rule.props.rule.forEach((item) => {
// parseFieldsSetDefaultPermission(item, fieldsPermission, title)
// })
// }
}
if (children && Array.isArray(children)) {
children.forEach((rule) => {
parseField(rule, parsedFormFields)
})
}
}
const formFields = inject<Ref<string[]>>('formFields', ref([])) // 流程表单字段
return parseFormCreateFields(unref(formFields))
}
return parseFormFields()
// TODO @芋艿:后续需要把各种类似 useFormFieldsPermission 的逻辑,抽成一个通用方法。
/**
* @description 获取流程表单的字段和发起人字段
*/
export function useFormFieldsAndStartUser() {
const injectFormFields = inject<Ref<string[]>>('formFields', ref([])) // 流程表单字段
const formFields = parseFormCreateFields(unref(injectFormFields))
// 添加发起人
formFields.unshift({
field: ProcessVariableEnum.START_USER_ID,
title: '发起人',
required: true
})
return formFields
}
export type UserTaskFormType = {
//candidateParamArray: any[]
candidateStrategy: CandidateStrategy
approveMethod: ApproveMethodType
roleIds?: number[] // 角色
@@ -151,6 +141,8 @@ export type UserTaskFormType = {
userGroups?: number[] // 用户组
postIds?: number[] // 岗位
expression?: string // 流程表达式
formUser?: string // 表单内用户字段
formDept?: string // 表单内部门字段
approveRatio?: number
rejectHandlerType?: RejectHandlerType
returnNodeId?: string
@@ -162,10 +154,29 @@ export type UserTaskFormType = {
timeDuration?: number
maxRemindCount?: number
buttonsSetting: any[]
taskCreateListenerEnable?: boolean
taskCreateListenerPath?: string
taskCreateListener?: {
header: HttpRequestParam[]
body: HttpRequestParam[]
}
taskAssignListenerEnable?: boolean
taskAssignListenerPath?: string
taskAssignListener?: {
header: HttpRequestParam[]
body: HttpRequestParam[]
}
taskCompleteListenerEnable?: boolean
taskCompleteListenerPath?: string
taskCompleteListener?: {
header: HttpRequestParam[]
body: HttpRequestParam[]
}
signEnable: boolean
reasonRequire: boolean
}
export type CopyTaskFormType = {
// candidateParamArray: any[]
candidateStrategy: CandidateStrategy
roleIds?: number[] // 角色
deptIds?: number[] // 部门
@@ -173,6 +184,8 @@ export type CopyTaskFormType = {
userIds?: number[] // 用户
userGroups?: number[] // 用户组
postIds?: number[] // 岗位
formUser?: string // 表单内用户字段
formDept?: string // 表单内部门字段
expression?: string // 流程表达式
}
@@ -180,12 +193,13 @@ export type CopyTaskFormType = {
* @description 节点表单数据。 用于审批节点、抄送节点
*/
export function useNodeForm(nodeType: NodeType) {
const roleOptions = inject<Ref<RoleApi.RoleVO[]>>('roleList') // 角色列表
const postOptions = inject<Ref<PostApi.PostVO[]>>('postList') // 岗位列表
const userOptions = inject<Ref<UserApi.UserVO[]>>('userList') // 用户列表
const deptOptions = inject<Ref<DeptApi.DeptVO[]>>('deptList') // 部门列表
const userGroupOptions = inject<Ref<UserGroupApi.UserGroupVO[]>>('userGroupList') // 用户组列表
const deptTreeOptions = inject('deptTree') // 部门树
const roleOptions = inject<Ref<RoleApi.RoleVO[]>>('roleList', ref([])) // 角色列表
const postOptions = inject<Ref<PostApi.PostVO[]>>('postList', ref([])) // 岗位列表
const userOptions = inject<Ref<UserApi.UserVO[]>>('userList', ref([])) // 用户列表
const deptOptions = inject<Ref<DeptApi.DeptVO[]>>('deptList', ref([])) // 部门列表
const userGroupOptions = inject<Ref<UserGroupApi.UserGroupVO[]>>('userGroupList', ref([])) // 用户组列表
const deptTreeOptions = inject('deptTree', ref()) // 部门树
const formFields = inject<Ref<string[]>>('formFields', ref([])) // 流程表单字段
const configForm = ref<UserTaskFormType | CopyTaskFormType>()
if (nodeType === NodeType.USER_TASK_NODE) {
configForm.value = {
@@ -281,6 +295,18 @@ export function useNodeForm(nodeType: NodeType) {
}
}
// 表单内用户字段
if (configForm.value?.candidateStrategy === CandidateStrategy.FORM_USER) {
const formFieldOptions = parseFormCreateFields(unref(formFields))
const item = formFieldOptions.find((item) => item.field === configForm.value?.formUser)
showText = `表单用户:${item?.title}`
}
// 表单内部门负责人
if (configForm.value?.candidateStrategy === CandidateStrategy.FORM_DEPT_LEADER) {
showText = `表单内部门负责人`
}
// 发起人自选
if (configForm.value?.candidateStrategy === CandidateStrategy.START_USER_SELECT) {
showText = `发起人自选`
@@ -327,6 +353,9 @@ export function useNodeForm(nodeType: NodeType) {
case CandidateStrategy.USER_GROUP:
candidateParam = configForm.value.userGroups!.join(',')
break
case CandidateStrategy.FORM_USER:
candidateParam = configForm.value.formUser!
break
case CandidateStrategy.EXPRESSION:
candidateParam = configForm.value.expression!
break
@@ -346,6 +375,13 @@ export function useNodeForm(nodeType: NodeType) {
candidateParam = deptIds.concat('|' + configForm.value.deptLevel + '')
break
}
// 表单内部门的负责人
case CandidateStrategy.FORM_DEPT_LEADER: {
// 候选人参数格式: | 分隔 。左边为表单内部门字段。 右边为部门层级
const deptFieldOnForm = configForm.value.formDept!
candidateParam = deptFieldOnForm.concat('|' + configForm.value.deptLevel + '')
break
}
default:
break
}
@@ -375,6 +411,9 @@ export function useNodeForm(nodeType: NodeType) {
case CandidateStrategy.USER_GROUP:
configForm.value.userGroups = candidateParam.split(',').map((item) => +item)
break
case CandidateStrategy.FORM_USER:
configForm.value.formUser = candidateParam
break
case CandidateStrategy.EXPRESSION:
configForm.value.expression = candidateParam
break
@@ -395,6 +434,14 @@ export function useNodeForm(nodeType: NodeType) {
configForm.value.deptLevel = +paramArray[1]
break
}
// 表单内的部门负责人
case CandidateStrategy.FORM_DEPT_LEADER: {
// 候选人参数格式: | 分隔 。左边为表单内的部门字段。 右边为部门层级
const paramArray = candidateParam.split('|')
configForm.value.formDept = paramArray[0]
configForm.value.deptLevel = +paramArray[1]
break
}
default:
break
}
@@ -476,3 +523,26 @@ export function useNodeName2(node: Ref<SimpleFlowNode>, nodeType: NodeType) {
blurEvent
}
}
/**
* @description 根据节点任务状态,获取节点任务状态样式
*/
export function useTaskStatusClass(taskStatus: TaskStatusEnum | undefined): string {
if (!taskStatus) {
return ''
}
if (taskStatus === TaskStatusEnum.APPROVE) {
return 'status-pass'
}
if (taskStatus === TaskStatusEnum.RUNNING) {
return 'status-running'
}
if (taskStatus === TaskStatusEnum.REJECT) {
return 'status-reject'
}
if (taskStatus === TaskStatusEnum.CANCEL) {
return 'status-cancel'
}
return ''
}
@@ -26,126 +26,11 @@
</div>
</template>
<div>
<div class="mb-3 font-size-16px" v-if="currentNode.defaultFlow">其它条件不满足进入此分支(该分支不可编辑和删除)</div>
<div class="mb-3 font-size-16px" v-if="currentNode.conditionSetting?.defaultFlow"
>未满足其它条件时,将进入此分支(该分支不可编辑和删除)</div
>
<div v-else>
<el-form
ref="formRef"
:model="currentNode"
:rules="formRules"
label-position="top"
>
<el-form-item label="配置方式" prop="conditionType">
<el-radio-group
v-model="currentNode.conditionType"
@change="changeConditionType"
>
<el-radio
v-for="(dict, index) in conditionConfigTypes"
:key="index"
:value="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-if="currentNode.conditionType === 1"
label="条件表达式"
prop="conditionExpression"
>
<el-input
type="textarea"
v-model="currentNode.conditionExpression"
clearable
style="width: 100%"
/>
</el-form-item>
<el-form-item v-if="currentNode.conditionType === 2" label="条件规则">
<div class="condition-group-tool">
<div class="flex items-center">
<div class="mr-4">条件组关系</div>
<el-switch
v-model="conditionGroups.and"
inline-prompt
active-text="且"
inactive-text="或"
/>
</div>
</div>
<el-space direction="vertical" :spacer="conditionGroups.and ? '且' : '或'">
<el-card
class="condition-group"
style="width: 530px"
v-for="(condition, cIdx) in conditionGroups.conditions"
:key="cIdx"
>
<div class="condition-group-delete" v-if="conditionGroups.conditions.length > 1">
<Icon
color="#0089ff"
icon="ep:circle-close-filled"
:size="18"
@click="deleteConditionGroup(cIdx)"
/>
</div>
<template #header>
<div class="flex items-center justify-between">
<div>条件组</div>
<div class="flex">
<div class="mr-4">规则关系</div>
<el-switch
v-model="condition.and"
inline-prompt
active-text="且"
inactive-text="或"
/>
</div>
</div>
</template>
<div class="flex pt-2" v-for="(rule, rIdx) in condition.rules" :key="rIdx">
<div class="mr-2">
<el-select style="width: 160px" v-model="rule.leftSide">
<el-option
v-for="(item, index) in fieldsInfo"
:key="index"
:label="item.title"
:value="item.field"
/>
</el-select>
</div>
<div class="mr-2">
<el-select v-model="rule.opCode" style="width: 100px">
<el-option
v-for="item in COMPARISON_OPERATORS"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
<div class="mr-2">
<el-input v-model="rule.rightSide" style="width: 160px" />
</div>
<div class="mr-1 flex items-center" v-if="condition.rules.length > 1">
<Icon
icon="ep:delete"
:size="18"
@click="deleteConditionRule(condition, rIdx)"
/>
</div>
<div class="flex items-center">
<Icon icon="ep:plus" :size="18" @click="addConditionRule(condition, rIdx)" />
</div>
</div>
</el-card>
</el-space>
<div title="添加条件组" class="mt-4 cursor-pointer">
<Icon color="#0089ff" icon="ep:plus" :size="24" @click="addConditionGroup" />
</div>
</el-form-item>
</el-form>
<Condition ref="conditionRef" v-model="condition" />
</div>
</div>
<template #footer>
@@ -160,31 +45,16 @@
<script setup lang="ts">
import {
SimpleFlowNode,
CONDITION_CONFIG_TYPES,
ConditionType,
COMPARISON_OPERATORS,
ConditionGroup,
Condition,
ConditionRule
} from '../consts'
import { getDefaultConditionNodeName } from '../utils'
import { useFormFields } from '../node'
import { useFormFieldsAndStartUser } from '../node'
import Condition from './components/Condition.vue'
const message = useMessage() // 消息弹窗
defineOptions({
name: 'ConditionNodeConfig'
})
const formType = inject<Ref<number>>('formType') // 表单类型
const conditionConfigTypes = computed(() => {
return CONDITION_CONFIG_TYPES.filter((item) => {
// 业务表单暂时去掉条件规则选项
if (formType?.value !== 10) {
return item.value === ConditionType.RULE
} else {
return true
}
})
})
const props = defineProps({
conditionNode: {
type: Object as () => SimpleFlowNode,
@@ -196,12 +66,10 @@ const props = defineProps({
}
})
const settingVisible = ref(false)
const currentNode = ref<SimpleFlowNode>(props.conditionNode)
const condition = ref<any>()
const open = () => {
if (currentNode.value.conditionType === ConditionType.RULE) {
if (currentNode.value.conditionGroups) {
conditionGroups.value = currentNode.value.conditionGroups
}
}
condition.value = currentNode.value.conditionSetting
settingVisible.value = true
}
@@ -222,10 +90,10 @@ const blurEvent = () => {
showInput.value = false
currentNode.value.name =
currentNode.value.name ||
getDefaultConditionNodeName(props.nodeIndex, currentNode.value?.defaultFlow)
getDefaultConditionNodeName(props.nodeIndex, currentNode.value?.conditionSetting?.defaultFlow)
}
const currentNode = ref<SimpleFlowNode>(props.conditionNode)
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
@@ -242,31 +110,27 @@ const handleClose = async (done: (cancel?: boolean) => void) => {
done()
}
}
// 表单校验规则
const formRules = reactive({
conditionType: [{ required: true, message: '配置方式不能为空', trigger: 'blur' }],
conditionExpression: [{ required: true, message: '条件表达式不能为空', trigger: 'blur' }]
})
const formRef = ref() // 表单 Ref
const conditionRef = ref()
// 保存配置
const saveConfig = async () => {
if (!currentNode.value.defaultFlow) {
if (!currentNode.value.conditionSetting?.defaultFlow) {
// 校验表单
if (!formRef) return false
const valid = await formRef.value.validate()
const valid = await conditionRef.value.validate()
if (!valid) return false
const showText = getShowText()
if (!showText) {
return false
}
currentNode.value.showText = showText
if (currentNode.value.conditionType === ConditionType.EXPRESSION) {
currentNode.value.conditionGroups = undefined
currentNode.value.conditionSetting!.conditionType = condition.value?.conditionType
if (currentNode.value.conditionSetting?.conditionType === ConditionType.EXPRESSION) {
currentNode.value.conditionSetting.conditionGroups = undefined
currentNode.value.conditionSetting.conditionExpression = condition.value?.conditionExpression
}
if (currentNode.value.conditionType === ConditionType.RULE) {
currentNode.value.conditionExpression = undefined
currentNode.value.conditionGroups = conditionGroups.value
if (currentNode.value.conditionSetting!.conditionType === ConditionType.RULE) {
currentNode.value.conditionSetting!.conditionExpression = undefined
currentNode.value.conditionSetting!.conditionGroups = condition.value?.conditionGroups
}
}
settingVisible.value = false
@@ -274,16 +138,16 @@ const saveConfig = async () => {
}
const getShowText = (): string => {
let showText = ''
if (currentNode.value.conditionType === ConditionType.EXPRESSION) {
if (currentNode.value.conditionExpression) {
showText = `表达式:${currentNode.value.conditionExpression}`
if (condition.value?.conditionType === ConditionType.EXPRESSION) {
if (condition.value.conditionExpression) {
showText = `表达式:${condition.value.conditionExpression}`
}
}
if (currentNode.value.conditionType === ConditionType.RULE) {
if (condition.value?.conditionType === ConditionType.RULE) {
// 条件组是否为与关系
const groupAnd = conditionGroups.value.and
const groupAnd = condition.value.conditionGroups?.and
let warningMesg: undefined | string = undefined
const conditionGroup = conditionGroups.value.conditions.map((item) => {
const conditionGroup = condition.value.conditionGroups?.conditions.map((item) => {
return (
'(' +
item.rules
@@ -306,78 +170,23 @@ const getShowText = (): string => {
message.warning(warningMesg)
showText = ''
} else {
showText = conditionGroup.join(groupAnd ? ' 且 ' : ' 或 ')
showText = conditionGroup!.join(groupAnd ? ' 且 ' : ' 或 ')
}
}
return showText
}
// 流程表单字段和发起人字段
const fieldOptions = useFormFieldsAndStartUser()
// 改变条件配置方式
const changeConditionType = () => {}
const conditionGroups = ref<ConditionGroup>({
and: true,
conditions: [
{
and: true,
rules: [
{
type: 1,
opName: '等于',
opCode: '==',
leftSide: '',
rightSide: ''
}
]
}
]
})
// 添加条件组
const addConditionGroup = () => {
const condition = {
and: true,
rules: [
{
type: 1,
opName: '等于',
opCode: '==',
leftSide: '',
rightSide: ''
}
]
}
conditionGroups.value.conditions.push(condition)
}
// 删除条件组
const deleteConditionGroup = (idx: number) => {
conditionGroups.value.conditions.splice(idx, 1)
}
// 添加条件规则
const addConditionRule = (condition: Condition, idx: number) => {
const rule: ConditionRule = {
type: 1,
opName: '等于',
opCode: '==',
leftSide: '',
rightSide: ''
}
condition.rules.splice(idx + 1, 0, rule)
}
const deleteConditionRule = (condition: Condition, idx: number) => {
condition.rules.splice(idx, 1)
}
const fieldsInfo = useFormFields()
/** 获取字段名称 */
const getFieldTitle = (field: string) => {
const item = fieldsInfo.find((item) => item.field === field)
const item = fieldOptions.find((item) => item.field === field)
return item?.title
}
/** 获取操作符名称 */
const getOpName = (opCode: string): string => {
const opName = COMPARISON_OPERATORS.find((item) => item.value === opCode)
const opName = COMPARISON_OPERATORS.find((item: any) => item.value === opCode)
return opName?.label
}
</script>
@@ -60,7 +60,8 @@
<el-form-item
v-if="
configForm.candidateStrategy == CandidateStrategy.DEPT_MEMBER ||
configForm.candidateStrategy == CandidateStrategy.DEPT_LEADER
configForm.candidateStrategy == CandidateStrategy.DEPT_LEADER ||
configForm.candidateStrategy == CandidateStrategy.MULTI_LEVEL_DEPT_LEADER
"
label="指定部门"
prop="deptIds"
@@ -122,7 +123,57 @@
/>
</el-select>
</el-form-item>
<el-form-item
v-if="configForm.candidateStrategy === CandidateStrategy.FORM_USER"
label="表单内用户字段"
prop="formUser"
>
<el-select v-model="configForm.formUser" clearable style="width: 100%">
<el-option
v-for="(item, idx) in userFieldOnFormOptions"
:key="idx"
:label="item.title"
:value="item.field"
:disabled ="!item.required"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="configForm.candidateStrategy === CandidateStrategy.FORM_DEPT_LEADER"
label="表单内部门字段"
prop="formDept"
>
<el-select v-model="configForm.formDept" clearable style="width: 100%">
<el-option
v-for="(item, idx) in deptFieldOnFormOptions"
:key="idx"
:label="item.title"
:value="item.field"
:disabled ="!item.required"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="
configForm.candidateStrategy == CandidateStrategy.MULTI_LEVEL_DEPT_LEADER ||
configForm.candidateStrategy == CandidateStrategy.START_USER_DEPT_LEADER ||
configForm.candidateStrategy ==
CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER ||
configForm.candidateStrategy == CandidateStrategy.FORM_DEPT_LEADER
"
:label="deptLevelLabel!"
prop="deptLevel"
span="24"
>
<el-select v-model="configForm.deptLevel" clearable>
<el-option
v-for="(item, index) in MULTI_LEVEL_DEPT"
:key="index"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="configForm.candidateStrategy === CandidateStrategy.EXPRESSION"
label="流程表达式"
@@ -201,7 +252,8 @@ import {
CandidateStrategy,
NodeType,
CANDIDATE_STRATEGY,
FieldPermissionType
FieldPermissionType,
MULTI_LEVEL_DEPT
} from '../consts'
import {
useWatchNode,
@@ -221,6 +273,15 @@ const props = defineProps({
required: true
}
})
const deptLevelLabel = computed(() => {
let label = '部门负责人来源'
if (configForm.value.candidateStrategy == CandidateStrategy.MULTI_LEVEL_DEPT_LEADER) {
label = label + '(指定部门向上)'
} else {
label = label + '(发起人部门向上)'
}
return label
})
// 抽屉配置
const { settingVisible, closeDrawer, openDrawer } = useDrawer()
// 当前节点
@@ -230,9 +291,16 @@ const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(NodeType.COPY_
// 激活的 Tab 标签页
const activeTabName = ref('user')
// 表单字段权限配置
const { formType, fieldsPermissionConfig, getNodeConfigFormFields } = useFormFieldsPermission(
FieldPermissionType.READ
)
const { formType, fieldsPermissionConfig, formFieldOptions, getNodeConfigFormFields } =
useFormFieldsPermission(FieldPermissionType.READ)
// 表单内用户字段选项, 必须是必填和用户选择器
const userFieldOnFormOptions = computed(() => {
return formFieldOptions.filter((item) => item.type === 'UserSelect')
})
// 表单内部门字段选项, 必须是必填和部门选择器
const deptFieldOnFormOptions = computed(() => {
return formFieldOptions.filter((item) => item.type === 'DeptSelect')
})
// 抄送人表单配置
const formRef = ref() // 表单 Ref
// 表单校验规则
@@ -243,6 +311,8 @@ const formRules = reactive({
deptIds: [{ required: true, message: '部门不能为空', trigger: 'change' }],
userGroups: [{ required: true, message: '用户组不能为空', trigger: 'change' }],
postIds: [{ required: true, message: '岗位不能为空', trigger: 'change' }],
formUser: [{ required: true, message: '表单内用户字段不能为空', trigger: 'change' }],
formDept: [{ required: true, message: '表单内部门字段不能为空', trigger: 'change' }],
expression: [{ required: true, message: '流程表达式不能为空', trigger: 'blur' }]
})
@@ -260,11 +330,7 @@ const {
const configForm = tempConfigForm as Ref<CopyTaskFormType>
// 抄送人策略, 去掉发起人自选 和 发起人自己
const copyUserStrategies = computed(() => {
return CANDIDATE_STRATEGY.filter(
(item) =>
item.value !== CandidateStrategy.START_USER_SELECT &&
item.value !== CandidateStrategy.START_USER
)
return CANDIDATE_STRATEGY.filter((item) => item.value !== CandidateStrategy.START_USER)
})
// 改变抄送人设置策略
const changeCandidateStrategy = () => {
@@ -274,6 +340,7 @@ const changeCandidateStrategy = () => {
configForm.value.postIds = []
configForm.value.userGroups = []
configForm.value.deptLevel = 1
configForm.value.formUser = ''
}
// 保存配置
const saveConfig = async () => {
@@ -0,0 +1,190 @@
<template>
<el-drawer
:append-to-body="true"
v-model="settingVisible"
:show-close="false"
:size="550"
:before-close="saveConfig"
>
<template #header>
<div class="config-header">
<input
v-if="showInput"
type="text"
class="config-editable-input"
@blur="blurEvent()"
v-mountedFocus
v-model="nodeName"
:placeholder="nodeName"
/>
<div v-else class="node-name">
{{ nodeName }} <Icon class="ml-1" icon="ep:edit-pen" :size="16" @click="clickIcon()" />
</div>
<div class="divide-line"></div>
</div>
</template>
<div>
<el-form ref="formRef" :model="configForm" label-position="top" :rules="formRules">
<el-form-item label="延迟时间" prop="delayType">
<el-radio-group v-model="configForm.delayType">
<el-radio-button
v-for="item in DELAY_TYPE"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-radio-group>
</el-form-item>
<el-form-item v-if="configForm.delayType === DelayTypeEnum.FIXED_TIME_DURATION">
<el-form-item prop="timeDuration">
<el-input-number
class="mr-2"
:style="{ width: '100px' }"
v-model="configForm.timeDuration"
:min="1"
controls-position="right"
/>
</el-form-item>
<el-select v-model="configForm.timeUnit" class="mr-2" :style="{ width: '100px' }">
<el-option
v-for="item in TIME_UNIT_TYPES"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-text>后进入下一节点</el-text>
</el-form-item>
<el-form-item v-if="configForm.delayType === DelayTypeEnum.FIXED_DATE_TIME" prop="dateTime">
<el-date-picker
class="mr-2"
v-model="configForm.dateTime"
type="datetime"
placeholder="请选择日期和时间"
value-format="YYYY-MM-DDTHH:mm:ss"
/>
<el-text>后进入下一节点</el-text>
</el-form-item>
</el-form>
</div>
<template #footer>
<el-divider />
<div>
<el-button type="primary" @click="saveConfig">确 定</el-button>
<el-button @click="closeDrawer">取 消</el-button>
</div>
</template>
</el-drawer>
</template>
<script setup lang="ts">
import {
SimpleFlowNode,
NodeType,
TIME_UNIT_TYPES,
TimeUnitType,
DelayTypeEnum,
DELAY_TYPE
} from '../consts'
import { useWatchNode, useDrawer, useNodeName } from '../node'
import { convertTimeUnit } from '../utils'
defineOptions({
name: 'DelayTimerNodeConfig'
})
const props = defineProps({
flowNode: {
type: Object as () => SimpleFlowNode,
required: true
}
})
// 抽屉配置
const { settingVisible, closeDrawer, openDrawer } = useDrawer()
// 当前节点
const currentNode = useWatchNode(props)
// 节点名称
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(NodeType.DELAY_TIMER_NODE)
// 抄送人表单配置
const formRef = ref() // 表单 Ref
// 表单校验规则
const formRules = reactive({
delayType: [{ required: true, message: '延迟时间不能为空', trigger: 'change' }],
timeDuration: [{ required: true, message: '延迟时间不能为空', trigger: 'change' }],
dateTime: [{ required: true, message: '延迟时间不能为空', trigger: 'change' }]
})
// 配置表单数据
const configForm = ref({
delayType: DelayTypeEnum.FIXED_TIME_DURATION,
timeDuration: 1,
timeUnit: TimeUnitType.HOUR,
dateTime: ''
})
// 保存配置
const saveConfig = async () => {
if (!formRef) return false
const valid = await formRef.value.validate()
if (!valid) return false
const showText = getShowText()
if (!showText) return false
currentNode.value.name = nodeName.value!
currentNode.value.showText = showText
if (configForm.value.delayType === DelayTypeEnum.FIXED_TIME_DURATION) {
currentNode.value.delaySetting = {
delayType: configForm.value.delayType,
delayTime: getIsoTimeDuration()
}
}
if (configForm.value.delayType === DelayTypeEnum.FIXED_DATE_TIME) {
currentNode.value.delaySetting = {
delayType: configForm.value.delayType,
delayTime: configForm.value.dateTime
}
}
settingVisible.value = false
return true
}
const getShowText = (): string => {
let showText = ''
if (configForm.value.delayType === DelayTypeEnum.FIXED_TIME_DURATION) {
showText = `延迟${configForm.value.timeDuration}${TIME_UNIT_TYPES.find((item) => item.value === configForm.value.timeUnit).label}`
}
if (configForm.value.delayType === DelayTypeEnum.FIXED_DATE_TIME) {
showText = `延迟至${configForm.value.dateTime.replace('T', ' ')}`
}
return showText
}
const getIsoTimeDuration = () => {
let strTimeDuration = 'PT'
if (configForm.value.timeUnit === TimeUnitType.MINUTE) {
strTimeDuration += configForm.value.timeDuration + 'M'
}
if (configForm.value.timeUnit === TimeUnitType.HOUR) {
strTimeDuration += configForm.value.timeDuration + 'H'
}
if (configForm.value.timeUnit === TimeUnitType.DAY) {
strTimeDuration += configForm.value.timeDuration + 'D'
}
return strTimeDuration
}
// 显示延迟器节点配置, 由父组件传过来
const showDelayTimerNodeConfig = (node: SimpleFlowNode) => {
nodeName.value = node.name
if (node.delaySetting) {
configForm.value.delayType = node.delaySetting.delayType
// 固定时长
if (configForm.value.delayType === DelayTypeEnum.FIXED_TIME_DURATION) {
const strTimeDuration = node.delaySetting.delayTime
let parseTime = strTimeDuration.slice(2, strTimeDuration.length - 1)
let parseTimeUnit = strTimeDuration.slice(strTimeDuration.length - 1)
configForm.value.timeDuration = parseInt(parseTime)
configForm.value.timeUnit = convertTimeUnit(parseTimeUnit)
}
// 固定日期时间
if (configForm.value.delayType === DelayTypeEnum.FIXED_DATE_TIME) {
configForm.value.dateTime = node.delaySetting.delayTime
}
}
}
defineExpose({ openDrawer, showDelayTimerNodeConfig }) // 暴露方法给父组件
</script>
<style lang="scss" scoped></style>
@@ -0,0 +1,201 @@
<template>
<el-drawer
:append-to-body="true"
v-model="settingVisible"
:show-close="false"
:size="630"
:before-close="saveConfig"
>
<template #header>
<div class="config-header">
<input
v-if="showInput"
type="text"
class="config-editable-input"
@blur="blurEvent()"
v-mountedFocus
v-model="nodeName"
:placeholder="nodeName"
/>
<div v-else class="node-name">
{{ nodeName }} <Icon class="ml-1" icon="ep:edit-pen" :size="16" @click="clickIcon()" />
</div>
<div class="divide-line"></div>
</div>
</template>
<div>
<el-form label-position="top">
<el-card class="mb-15px" v-for="(item, index) in routerGroups" :key="index">
<template #header>
<div class="flex flex-items-center">
<el-text size="large">路由{{ index + 1 }}</el-text>
<el-select class="ml-15px" v-model="item.nodeId" style="width: 180px">
<el-option
v-for="node in nodeOptions"
:key="node.value"
:label="node.label"
:value="node.value"
/>
</el-select>
<el-button class="mla" type="danger" link @click="deleteRouterGroup(index)">
删除
</el-button>
</div>
</template>
<Condition
:ref="($event) => (conditionRef[index] = $event)"
v-model="routerGroups[index]"
/>
</el-card>
</el-form>
<el-button class="w-1/1" type="primary" :icon="Plus" @click="addRouterGroup">
新增路由分支
</el-button>
</div>
<template #footer>
<el-divider />
<div>
<el-button type="primary" @click="saveConfig">确 定</el-button>
<el-button @click="closeDrawer">取 消</el-button>
</div>
</template>
</el-drawer>
</template>
<script setup lang="ts">
import { Plus } from '@element-plus/icons-vue'
import { SimpleFlowNode, NodeType, ConditionType, RouterSetting } from '../consts'
import { useWatchNode, useDrawer, useNodeName } from '../node'
import Condition from './components/Condition.vue'
defineOptions({
name: 'RouterNodeConfig'
})
const message = useMessage() // 消息弹窗
const props = defineProps({
flowNode: {
type: Object as () => SimpleFlowNode,
required: true
}
})
const processNodeTree = inject<Ref<SimpleFlowNode>>('processNodeTree')
// 抽屉配置
const { settingVisible, closeDrawer, openDrawer } = useDrawer()
// 当前节点
const currentNode = useWatchNode(props)
// 节点名称
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(NodeType.ROUTER_BRANCH_NODE)
const routerGroups = ref<RouterSetting[]>([])
const nodeOptions = ref<any>([])
const conditionRef = ref([])
/** 保存配置 */
const saveConfig = async () => {
// 校验表单
let valid = true
for (const item of conditionRef.value) {
if (item && !(await item.validate())) {
valid = false
}
}
if (!valid) return false
const showText = getShowText()
if (!showText) return false
currentNode.value.name = nodeName.value!
currentNode.value.showText = showText
currentNode.value.routerGroups = routerGroups.value
settingVisible.value = false
return true
}
// 显示路由分支节点配置, 由父组件传过来
const showRouteNodeConfig = (node: SimpleFlowNode) => {
getRouterNode(processNodeTree?.value)
routerGroups.value = []
nodeName.value = node.name
if (node.routerGroups) {
routerGroups.value = node.routerGroups
}
}
const getShowText = () => {
if (!routerGroups.value || !Array.isArray(routerGroups.value) || routerGroups.value.length <= 0) {
message.warning('请配置路由!')
return ''
}
for (const route of routerGroups.value) {
if (!route.nodeId || !route.conditionType) {
message.warning('请完善路由配置项!')
return ''
}
if (route.conditionType === ConditionType.EXPRESSION && !route.conditionExpression) {
message.warning('请完善路由配置项!')
return ''
}
if (route.conditionType === ConditionType.RULE) {
for (const condition of route.conditionGroups.conditions) {
for (const rule of condition.rules) {
if (!rule.leftSide || !rule.rightSide) {
message.warning('请完善路由配置项!')
return ''
}
}
}
}
}
return `${routerGroups.value.length}条路由分支`
}
const addRouterGroup = () => {
routerGroups.value.push({
nodeId: '',
conditionType: ConditionType.RULE,
conditionExpression: '',
conditionGroups: {
and: true,
conditions: [
{
and: true,
rules: [
{
opCode: '==',
leftSide: '',
rightSide: ''
}
]
}
]
}
})
}
const deleteRouterGroup = (index: number) => {
routerGroups.value.splice(index, 1)
}
// 递归获取所有节点
const getRouterNode = (node) => {
// TODO 最好还需要满足以下要求
// 并行分支、包容分支内部节点不能跳转到外部节点
// 条件分支节点可以向上跳转到外部节点
while (true) {
if (!node) break
if (node.type !== NodeType.ROUTER_BRANCH_NODE && node.type !== NodeType.CONDITION_NODE) {
nodeOptions.value.push({
label: node.name,
value: node.id
})
}
if (!node.childNode || node.type === NodeType.END_EVENT_NODE) {
break
}
if (node.conditionNodes && node.conditionNodes.length) {
node.conditionNodes.forEach((item) => {
getRouterNode(item)
})
}
node = node.childNode
}
}
defineExpose({ openDrawer, showRouteNodeConfig }) // 暴露方法给父组件
</script>
@@ -25,7 +25,20 @@
</template>
<el-tabs type="border-card" v-model="activeTabName">
<el-tab-pane label="权限" name="user">
<div> 待实现 </div>
<el-text v-if="!startUserIds || startUserIds.length === 0"> 全部成员可以发起流程 </el-text>
<el-text v-else-if="startUserIds.length == 1">
{{ getUserNicknames(startUserIds) }} 可发起流程
</el-text>
<el-text v-else>
<el-tooltip
class="box-item"
effect="dark"
placement="top"
:content="getUserNicknames(startUserIds)"
>
{{ getUserNicknames(startUserIds.slice(0,2)) }} 等 {{ startUserIds.length }} 人可发起流程
</el-tooltip>
</el-text>
</el-tab-pane>
<el-tab-pane label="表单字段权限" name="fields" v-if="formType === 10">
<div class="field-setting-pane">
@@ -86,7 +99,7 @@
<script setup lang="ts">
import { SimpleFlowNode, NodeType, FieldPermissionType, START_USER_BUTTON_SETTING } from '../consts'
import { useWatchNode, useDrawer, useNodeName, useFormFieldsPermission } from '../node'
import * as UserApi from '@/api/system/user'
defineOptions({
name: 'StartUserNodeConfig'
})
@@ -96,6 +109,10 @@ const props = defineProps({
required: true
}
})
// 可发起流程的用户编号
const startUserIds = inject<Ref<any[]>>('startUserIds')
// 用户列表
const userOptions = inject<Ref<UserApi.UserVO[]>>('userList')
// 抽屉配置
const { settingVisible, closeDrawer, openDrawer } = useDrawer()
// 当前节点
@@ -108,18 +125,28 @@ const activeTabName = ref('user')
const { formType, fieldsPermissionConfig, getNodeConfigFormFields } = useFormFieldsPermission(
FieldPermissionType.WRITE
)
const getUserNicknames = (userIds: number[]): string => {
if (!userIds || userIds.length === 0) {
return ''
}
const nicknames: string[] = []
userIds.forEach((userId) => {
const found = userOptions?.value.find((item) => item.id === userId)
if (found && found.nickname) {
nicknames.push(found.nickname)
}
})
return nicknames.join(',')
}
// 保存配置
const saveConfig = async () => {
activeTabName.value = 'user'
currentNode.value.name = nodeName.value!
// TODO 暂时写死。后续可以显示谁有权限可以发起
currentNode.value.showText = '已设置'
// 设置表单权限
currentNode.value.fieldsPermission = fieldsPermissionConfig.value
// 设置发起人的按钮权限
currentNode.value.buttonsSetting = START_USER_BUTTON_SETTING
console.log('currentNode.value.buttonsSetting==>', currentNode.value.buttonsSetting)
settingVisible.value = false
return true
}
@@ -0,0 +1,334 @@
<template>
<el-drawer
:append-to-body="true"
v-model="settingVisible"
:show-close="false"
:size="550"
:before-close="saveConfig"
>
<template #header>
<div class="config-header">
<input
v-if="showInput"
type="text"
class="config-editable-input"
@blur="blurEvent()"
v-mountedFocus
v-model="nodeName"
:placeholder="nodeName"
/>
<div v-else class="node-name">
{{ nodeName }} <Icon class="ml-1" icon="ep:edit-pen" :size="16" @click="clickIcon()" />
</div>
<div class="divide-line"></div>
</div>
</template>
<div>
<el-form ref="formRef" :model="configForm" label-position="top" :rules="formRules">
<el-form-item label="触发器类型" prop="type">
<el-select v-model="configForm.type">
<el-option
v-for="(item, index) in TRIGGER_TYPES"
:key="index"
:value="item.value"
:label="item.label"
/>
</el-select>
</el-form-item>
<!-- HTTP 请求触发器 -->
<div
v-if="configForm.type === TriggerTypeEnum.HTTP_REQUEST && configForm.httpRequestSetting"
>
<el-form-item>
<el-alert
title="仅支持 POST 请求,以请求体方式接收参数"
type="warning"
show-icon
:closable="false"
/>
</el-form-item>
<!-- 请求地址-->
<el-form-item label="请求地址" prop="httpRequestSetting.url">
<el-input v-model="configForm.httpRequestSetting.url" />
</el-form-item>
<!-- 请求头,请求体设置-->
<HttpRequestParamSetting
:header="configForm.httpRequestSetting.header"
:body="configForm.httpRequestSetting.body"
:bind="'httpRequestSetting'"
/>
<!-- 返回值设置-->
<el-form-item label="返回值">
<el-alert
title="通过请求返回值, 可以修改流程表单的值"
type="warning"
show-icon
:closable="false"
/>
</el-form-item>
<el-form-item>
<div
class="flex pt-2"
v-for="(item, index) in configForm.httpRequestSetting.response"
:key="index"
>
<div class="mr-2">
<el-form-item
:prop="`httpRequestSetting.response.${index}.key`"
:rules="{
required: true,
message: '表单字段不能为空',
trigger: 'blur'
}"
>
<el-select class="w-160px!" v-model="item.key" placeholder="请选择表单字段">
<el-option
v-for="(field, fIdx) in formFields"
:key="fIdx"
:label="field.title"
:value="field.field"
:disabled="!field.required"
/>
</el-select>
</el-form-item>
</div>
<div class="mr-2">
<el-form-item
:prop="`httpRequestSetting.response.${index}.value`"
:rules="{
required: true,
message: '请求返回字段不能为空',
trigger: 'blur'
}"
>
<el-input class="w-160px" v-model="item.value" placeholder="请求返回字段" />
</el-form-item>
</div>
<div class="mr-1 pt-1 cursor-pointer">
<Icon
icon="ep:delete"
:size="18"
@click="deleteHttpResponseSetting(configForm.httpRequestSetting.response!, index)"
/>
</div>
</div>
<el-button
type="primary"
text
@click="addHttpResponseSetting(configForm.httpRequestSetting.response!)"
>
<Icon icon="ep:plus" class="mr-5px" />添加一行
</el-button>
</el-form-item>
</div>
<div
v-if="
configForm.type === TriggerTypeEnum.UPDATE_NORMAL_FORM && configForm.normalFormSetting
"
>
<el-divider content-position="left">修改表单设置</el-divider>
<div
class="flex items-center"
v-for="key in Object.keys(configForm.normalFormSetting.updateFormFields!)"
:key="key"
>
<div class="mr-2 flex items-center">
<el-form-item>
<el-select
class="w-160px!"
:model-value="key"
@update:model-value="(newKey) => updateFormFieldKey(key, newKey)"
placeholder="请选择表单字段"
:disabled="key !== ''"
>
<el-option
v-for="(field, fIdx) in optionalUpdateFormFields"
:key="fIdx"
:label="field.title"
:value="field.field"
:disabled="field.disabled"
/>
</el-select>
</el-form-item>
</div>
<div class="mx-2"><el-form-item>的值设置为</el-form-item></div>
<div class="mr-2">
<el-form-item
:prop="`normalFormSetting.updateFormFields.${key}`"
:rules="{
required: true,
message: '值不能为空',
trigger: 'blur'
}"
>
<el-input
class="w-160px"
v-model="configForm.normalFormSetting.updateFormFields![key]"
placeholder="请输入"
:disabled="!key"
/>
</el-form-item>
</div>
<div class="mr-1 pt-1 cursor-pointer">
<el-form-item>
<Icon icon="ep:delete" :size="18" @click="deleteFormFieldSetting(key)" />
</el-form-item>
</div>
</div>
<el-button type="primary" text @click="addFormFieldSetting()">
<Icon icon="ep:plus" class="mr-5px" />添加修改字段
</el-button>
</div>
</el-form>
</div>
<template #footer>
<el-divider />
<div>
<el-button type="primary" @click="saveConfig">确 定</el-button>
<el-button @click="closeDrawer">取 消</el-button>
</div>
</template>
</el-drawer>
</template>
<script setup lang="ts">
import { SimpleFlowNode, NodeType, TriggerSetting, TRIGGER_TYPES, TriggerTypeEnum } from '../consts'
import { useWatchNode, useDrawer, useNodeName, useFormFields } from '../node'
import HttpRequestParamSetting from './components/HttpRequestParamSetting.vue'
defineOptions({
name: 'TriggerNodeConfig'
})
const props = defineProps({
flowNode: {
type: Object as () => SimpleFlowNode,
required: true
}
})
const message = useMessage() // 消息弹窗
// 抽屉配置
const { settingVisible, closeDrawer, openDrawer } = useDrawer()
// 当前节点
const currentNode = useWatchNode(props)
// 节点名称
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(NodeType.TRIGGER_NODE)
// 触发器表单配置
const formRef = ref() // 表单 Ref
// 表单校验规则
const formRules = reactive({
type: [{ required: true, message: '触发器类型不能为空', trigger: 'change' }],
'httpRequestSetting.url': [{ required: true, message: '请求地址不能为空', trigger: 'blur' }]
})
// 触发器配置表单数据
const configForm = ref<TriggerSetting>({
type: TriggerTypeEnum.HTTP_REQUEST,
httpRequestSetting: {
url: '',
header: [],
body: [],
response: []
},
normalFormSetting: { updateFormFields: {} }
})
// 流程表单字段
const formFields = useFormFields()
// 可选的修改的表单字段
const optionalUpdateFormFields = computed(() => {
const usedFields = Object.keys(configForm.value.normalFormSetting?.updateFormFields || {})
return formFields.map((field) => ({
title: field.title,
field: field.field,
disabled: usedFields.includes(field.field)
}))
})
const updateFormFieldKey = (oldKey: string, newKey: string) => {
if (!configForm.value.normalFormSetting?.updateFormFields) return
const value = configForm.value.normalFormSetting.updateFormFields[oldKey]
delete configForm.value.normalFormSetting.updateFormFields[oldKey]
configForm.value.normalFormSetting.updateFormFields[newKey] = value
}
/** 添加 HTTP 请求返回值设置项*/
const addHttpResponseSetting = (responseSetting: Record<string, string>[]) => {
responseSetting.push({
key: '',
value: ''
})
}
/** 删除 HTTP 请求返回值设置项 */
const deleteHttpResponseSetting = (responseSetting: Record<string, string>[], index: number) => {
responseSetting.splice(index, 1)
}
/** 添加修改表单设置项 */
const addFormFieldSetting = () => {
if (configForm.value.normalFormSetting!.updateFormFields === undefined) {
configForm.value.normalFormSetting!.updateFormFields = {}
}
configForm.value.normalFormSetting!.updateFormFields[''] = undefined
}
/** 删除修改表单设置项 */
const deleteFormFieldSetting = (key: string) => {
if (!configForm.value.normalFormSetting?.updateFormFields) return
delete configForm.value.normalFormSetting.updateFormFields[key]
}
/** 保存配置 */
const saveConfig = async () => {
if (!formRef) return false
const valid = await formRef.value.validate()
if (!valid) return false
const showText = getShowText()
if (!showText) return false
currentNode.value.name = nodeName.value!
currentNode.value.showText = showText
if (configForm.value.type === TriggerTypeEnum.HTTP_REQUEST) {
configForm.value.normalFormSetting = undefined
}
if (configForm.value.type === TriggerTypeEnum.UPDATE_NORMAL_FORM) {
configForm.value.httpRequestSetting = undefined
}
currentNode.value.triggerSetting = configForm.value
settingVisible.value = false
return true
}
/** 获取节点展示内容 */
const getShowText = (): string => {
let showText = ''
if (configForm.value.type === TriggerTypeEnum.HTTP_REQUEST) {
showText = `${configForm.value.httpRequestSetting?.url}`
} else if (configForm.value.type === TriggerTypeEnum.UPDATE_NORMAL_FORM) {
const updatefields = Object.keys(configForm.value.normalFormSetting?.updateFormFields || {})
if (updatefields.length === 0) {
message.warning('请设置修改表单字段')
} else {
showText = '修改表单数据'
}
}
return showText
}
/** 显示触发器节点配置, 由父组件传过来 */
const showTriggerNodeConfig = (node: SimpleFlowNode) => {
nodeName.value = node.name
if (node.triggerSetting) {
configForm.value = {
type: node.triggerSetting.type,
httpRequestSetting: node.triggerSetting.httpRequestSetting || {
url: '',
header: [],
body: [],
response: []
},
normalFormSetting: node.triggerSetting.normalFormSetting || { updateFormFields: {} }
}
}
}
defineExpose({ openDrawer, showTriggerNodeConfig }) // 暴露方法给父组件
</script>
<style lang="scss" scoped></style>
@@ -3,7 +3,7 @@
:append-to-body="true"
v-model="settingVisible"
:show-close="false"
:size="550"
:size="580"
:before-close="saveConfig"
class="justify-start"
>
@@ -19,7 +19,8 @@
:placeholder="nodeName"
/>
<div v-else class="node-name">
{{ nodeName }} <Icon class="ml-1" icon="ep:edit-pen" :size="16" @click="clickIcon()" />
{{ nodeName }}
<Icon class="ml-1" icon="ep:edit-pen" :size="16" @click="clickIcon()" />
</div>
<div class="divide-line"></div>
</div>
@@ -46,23 +47,21 @@
v-model="configForm.candidateStrategy"
@change="changeCandidateStrategy"
>
<el-radio
v-for="(dict, index) in CANDIDATE_STRATEGY"
:key="index"
:value="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio>
<el-row>
<el-col v-for="(dict, index) in CANDIDATE_STRATEGY" :key="index" :span="8">
<el-radio :value="dict.value" :label="dict.value">
{{ dict.label }}
</el-radio>
</el-col>
</el-row>
</el-radio-group>
</el-form-item>
<el-form-item
v-if="configForm.candidateStrategy == CandidateStrategy.ROLE"
label="指定角色"
prop="roleIds"
>
<el-select v-model="configForm.roleIds" clearable multiple style="width: 100%">
<el-select filterable v-model="configForm.roleIds" clearable multiple style="width: 100%">
<el-option
v-for="item in roleOptions"
:key="item.id"
@@ -94,32 +93,13 @@
show-checkbox
/>
</el-form-item>
<el-form-item
v-if="
configForm.candidateStrategy == CandidateStrategy.MULTI_LEVEL_DEPT_LEADER ||
configForm.candidateStrategy == CandidateStrategy.START_USER_DEPT_LEADER ||
configForm.candidateStrategy == CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER
"
:label="deptLevelLabel!"
prop="deptLevel"
span="24"
>
<el-select v-model="configForm.deptLevel" clearable>
<el-option
v-for="(item, index) in MULTI_LEVEL_DEPT"
:key="index"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="configForm.candidateStrategy == CandidateStrategy.POST"
label="指定岗位"
prop="postIds"
span="24"
>
<el-select v-model="configForm.postIds" clearable multiple style="width: 100%">
<el-select filterable v-model="configForm.postIds" clearable multiple style="width: 100%">
<el-option
v-for="item in postOptions"
:key="item.id"
@@ -134,13 +114,7 @@
prop="userIds"
span="24"
>
<el-select
v-model="configForm.userIds"
clearable
multiple
style="width: 100%"
@change="changedCandidateUsers"
>
<el-select filterable v-model="configForm.userIds" clearable multiple style="width: 100%">
<el-option
v-for="item in userOptions"
:key="item.id"
@@ -154,7 +128,7 @@
label="指定用户组"
prop="userGroups"
>
<el-select v-model="configForm.userGroups" clearable multiple style="width: 100%">
<el-select filterable v-model="configForm.userGroups" clearable multiple style="width: 100%">
<el-option
v-for="item in userGroupOptions"
:key="item.id"
@@ -163,6 +137,57 @@
/>
</el-select>
</el-form-item>
<el-form-item
v-if="configForm.candidateStrategy === CandidateStrategy.FORM_USER"
label="表单内用户字段"
prop="formUser"
>
<el-select filterable v-model="configForm.formUser" clearable style="width: 100%">
<el-option
v-for="(item, idx) in userFieldOnFormOptions"
:key="idx"
:label="item.title"
:value="item.field"
:disabled="!item.required"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="configForm.candidateStrategy === CandidateStrategy.FORM_DEPT_LEADER"
label="表单内部门字段"
prop="formDept"
>
<el-select filterable v-model="configForm.formDept" clearable style="width: 100%">
<el-option
v-for="(item, idx) in deptFieldOnFormOptions"
:key="idx"
:label="item.title"
:value="item.field"
:disabled="!item.required"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="
configForm.candidateStrategy == CandidateStrategy.MULTI_LEVEL_DEPT_LEADER ||
configForm.candidateStrategy == CandidateStrategy.START_USER_DEPT_LEADER ||
configForm.candidateStrategy ==
CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER ||
configForm.candidateStrategy == CandidateStrategy.FORM_DEPT_LEADER
"
:label="deptLevelLabel!"
prop="deptLevel"
span="24"
>
<el-select filterable v-model="configForm.deptLevel" clearable>
<el-option
v-for="(item, index) in MULTI_LEVEL_DEPT"
:key="index"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<!-- TODO @jason:后续要支持选择已经存好的表达式 -->
<el-form-item
v-if="configForm.candidateStrategy === CandidateStrategy.EXPRESSION"
@@ -184,14 +209,7 @@
:key="index"
class="flex items-center"
>
<el-radio
:value="item.value"
:label="item.value"
:disabled="
item.value !== ApproveMethodType.RANDOM_SELECT_ONE_APPROVE &&
notAllowedMultiApprovers
"
>
<el-radio :value="item.value" :label="item.value">
{{ item.label }}
</el-radio>
<el-form-item prop="approveRatio">
@@ -227,7 +245,7 @@
label="驳回节点"
prop="returnNodeId"
>
<el-select v-model="configForm.returnNodeId" clearable style="width: 100%">
<el-select filterable v-model="configForm.returnNodeId" clearable style="width: 100%">
<el-option
v-for="item in returnTaskList"
:key="item.id"
@@ -275,6 +293,7 @@
/>
</el-form-item>
<el-select
filterable
v-model="timeUnit"
class="mr-2"
:style="{ width: '100px' }"
@@ -314,6 +333,7 @@
span="24"
>
<el-select
filterable
v-model="configForm.assignEmptyHandlerUserIds"
clearable
multiple
@@ -338,6 +358,16 @@
</div>
</el-radio-group>
</el-form-item>
<el-divider content-position="left">是否需要签名</el-divider>
<el-form-item prop="signEnable">
<el-switch v-model="configForm.signEnable" active-text="是" inactive-text="否" />
</el-form-item>
<el-divider content-position="left">审批意见</el-divider>
<el-form-item prop="reasonRequire">
<el-switch v-model="configForm.reasonRequire" active-text="必填" inactive-text="非必填" />
</el-form-item>
</el-form>
</div>
</el-tab-pane>
@@ -417,6 +447,9 @@
</div>
</div>
</el-tab-pane>
<el-tab-pane label="监听器" name="listener">
<UserTaskListener ref="userTaskListenerRef" v-model="configForm" :form-field-options="formFieldOptions" />
</el-tab-pane>
</el-tabs>
<template #footer>
<el-divider />
@@ -451,7 +484,8 @@ import {
TimeoutHandlerType,
ASSIGN_EMPTY_HANDLER_TYPES,
AssignEmptyHandlerType,
FieldPermissionType
FieldPermissionType,
ProcessVariableEnum
} from '../consts'
import {
@@ -465,6 +499,7 @@ import {
import { defaultProps } from '@/utils/tree'
import { cloneDeep } from 'lodash-es'
import { convertTimeUnit, getApproveTypeText } from '../utils'
import UserTaskListener from './components/UserTaskListener.vue'
defineOptions({
name: 'UserTaskNodeConfig'
})
@@ -481,6 +516,8 @@ const deptLevelLabel = computed(() => {
let label = '部门负责人来源'
if (configForm.value.candidateStrategy == CandidateStrategy.MULTI_LEVEL_DEPT_LEADER) {
label = label + '(指定部门向上)'
} else if (configForm.value.candidateStrategy == CandidateStrategy.FORM_DEPT_LEADER) {
label = label + '(表单内部门向上)'
} else {
label = label + '(发起人部门向上)'
}
@@ -495,9 +532,23 @@ const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(NodeType.USER_
// 激活的 Tab 标签页
const activeTabName = ref('user')
// 表单字段权限设置
const { formType, fieldsPermissionConfig, getNodeConfigFormFields } = useFormFieldsPermission(
FieldPermissionType.READ
)
const { formType, fieldsPermissionConfig, formFieldOptions, getNodeConfigFormFields } =
useFormFieldsPermission(FieldPermissionType.READ)
// 表单内用户字段选项, 必须是必填和用户选择器
const userFieldOnFormOptions = computed(() => {
// 固定添加发起人 ID 字段
formFieldOptions.unshift({
field: ProcessVariableEnum.START_USER_ID,
title: '发起人',
type: 'UserSelect',
required: true
})
return formFieldOptions.filter((item) => item.type === 'UserSelect')
})
// 表单内部门字段选项, 必须是必填和部门选择器
const deptFieldOnFormOptions = computed(() => {
return formFieldOptions.filter((item) => item.type === 'DeptSelect')
})
// 操作按钮设置
const { buttonsSetting, btnDisplayNameEdit, changeBtnDisplayName, btnDisplayNameBlurEvent } =
useButtonsSetting()
@@ -511,6 +562,8 @@ const formRules = reactive({
roleIds: [{ required: true, message: '角色不能为空', trigger: 'change' }],
deptIds: [{ required: true, message: '部门不能为空', trigger: 'change' }],
userGroups: [{ required: true, message: '用户组不能为空', trigger: 'change' }],
formUser: [{ required: true, message: '表单内用户字段不能为空', trigger: 'change' }],
formDept: [{ required: true, message: '表单内部门字段不能为空', trigger: 'change' }],
postIds: [{ required: true, message: '岗位不能为空', trigger: 'change' }],
expression: [{ required: true, message: '流程表达式不能为空', trigger: 'blur' }],
approveMethod: [{ required: true, message: '多人审批方式不能为空', trigger: 'change' }],
@@ -537,8 +590,7 @@ const {
getShowText
} = useNodeForm(NodeType.USER_TASK_NODE)
const configForm = tempConfigForm as Ref<UserTaskFormType>
// 不允许多人审批
const notAllowedMultiApprovers = ref(false)
// 改变审批人设置策略
const changeCandidateStrategy = () => {
configForm.value.userIds = []
@@ -547,30 +599,11 @@ const changeCandidateStrategy = () => {
configForm.value.postIds = []
configForm.value.userGroups = []
configForm.value.deptLevel = 1
configForm.value.formUser = ''
configForm.value.formDept = ''
configForm.value.approveMethod = ApproveMethodType.SEQUENTIAL_APPROVE
if (
configForm.value.candidateStrategy === CandidateStrategy.START_USER ||
configForm.value.candidateStrategy === CandidateStrategy.USER
) {
notAllowedMultiApprovers.value = true
} else {
notAllowedMultiApprovers.value = false
}
}
// 改变审批候选人
const changedCandidateUsers = () => {
if (
configForm.value.userIds &&
configForm.value.userIds?.length <= 1 &&
configForm.value.candidateStrategy === CandidateStrategy.USER
) {
configForm.value.approveMethod = ApproveMethodType.RANDOM_SELECT_ONE_APPROVE
configForm.value.rejectHandlerType = RejectHandlerType.FINISH_PROCESS
notAllowedMultiApprovers.value = true
} else {
notAllowedMultiApprovers.value = false
}
}
// 审批方式改变
const approveMethodChanged = () => {
configForm.value.rejectHandlerType = RejectHandlerType.FINISH_PROCESS
@@ -579,7 +612,7 @@ const approveMethodChanged = () => {
}
formRef.value.clearValidate('approveRatio')
}
// 审批拒绝 可回退的节点
// 审批拒绝 可退回的节点
const returnTaskList = ref<SimpleFlowNode[]>([])
// 审批人超时未处理设置
const {
@@ -592,9 +625,11 @@ const {
cTimeoutMaxRemindCount
} = useTimeoutHandler()
const userTaskListenerRef = ref()
// 保存配置
const saveConfig = async () => {
activeTabName.value = 'user'
// activeTabName.value = 'user'
// 设置审批节点名称
currentNode.value.name = nodeName.value!
// 设置审批类型
@@ -607,7 +642,8 @@ const saveConfig = async () => {
}
if (!formRef) return false
const valid = await formRef.value.validate()
if (!userTaskListenerRef) return false
const valid = (await formRef.value.validate()) && (await userTaskListenerRef.value.validate())
if (!valid) return false
const showText = getShowText()
if (!showText) return false
@@ -646,6 +682,31 @@ const saveConfig = async () => {
currentNode.value.fieldsPermission = fieldsPermissionConfig.value
// 设置按钮权限
currentNode.value.buttonsSetting = buttonsSetting.value
// 创建任务监听器
currentNode.value.taskCreateListener = {
enable: configForm.value.taskCreateListenerEnable ?? false,
path: configForm.value.taskCreateListenerPath,
header: configForm.value.taskCreateListener?.header,
body: configForm.value.taskCreateListener?.body
}
// 指派任务监听器
currentNode.value.taskAssignListener = {
enable: configForm.value.taskAssignListenerEnable ?? false,
path: configForm.value.taskAssignListenerPath,
header: configForm.value.taskAssignListener?.header,
body: configForm.value.taskAssignListener?.body
}
// 完成任务监听器
currentNode.value.taskCompleteListener = {
enable: configForm.value.taskCompleteListenerEnable ?? false,
path: configForm.value.taskCompleteListenerPath,
header: configForm.value.taskCompleteListener?.header,
body: configForm.value.taskCompleteListener?.body
}
// 签名
currentNode.value.signEnable = configForm.value.signEnable
// 审批意见
currentNode.value.reasonRequire = configForm.value.reasonRequire
currentNode.value.showText = showText
settingVisible.value = false
@@ -666,11 +727,6 @@ const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
configForm.value.candidateStrategy = node.candidateStrategy!
// 解析候选人参数
parseCandidateParam(node.candidateStrategy!, node?.candidateParam)
if (configForm.value.userIds && configForm.value.userIds.length > 1) {
notAllowedMultiApprovers.value = true
} else {
notAllowedMultiApprovers.value = false
}
// 2.2 设置审批方式
configForm.value.approveMethod = node.approveMethod!
if (node.approveMethod == ApproveMethodType.APPROVE_BY_RATIO) {
@@ -702,6 +758,32 @@ const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
buttonsSetting.value = cloneDeep(node.buttonsSetting) || DEFAULT_BUTTON_SETTING
// 4. 表单字段权限配置
getNodeConfigFormFields(node.fieldsPermission)
// 5. 监听器
// 5.1 创建任务
configForm.value.taskCreateListenerEnable = node.taskCreateListener?.enable
configForm.value.taskCreateListenerPath = node.taskCreateListener?.path
configForm.value.taskCreateListener = {
header: node.taskCreateListener?.header ?? [],
body: node.taskCreateListener?.body ?? []
}
// 5.2 指派任务
configForm.value.taskAssignListenerEnable = node.taskAssignListener?.enable
configForm.value.taskAssignListenerPath = node.taskAssignListener?.path
configForm.value.taskAssignListener = {
header: node.taskAssignListener?.header ?? [],
body: node.taskAssignListener?.body ?? []
}
// 5.3 完成任务
configForm.value.taskCompleteListenerEnable = node.taskCompleteListener?.enable
configForm.value.taskCompleteListenerPath = node.taskCompleteListener?.path
configForm.value.taskCompleteListener = {
header: node.taskCompleteListener?.header ?? [],
body: node.taskCompleteListener?.body ?? []
}
// 6. 签名
configForm.value.signEnable = node?.signEnable ?? false
// 7. 审批意见
configForm.value.reasonRequire = node?.reasonRequire ?? false
}
defineExpose({ openDrawer, showUserTaskNodeConfig }) // 暴露方法给父组件
@@ -0,0 +1,264 @@
<template>
<el-form ref="formRef" :model="condition" :rules="formRules" label-position="top">
<el-form-item label="配置方式" prop="conditionType">
<el-radio-group v-model="condition.conditionType" @change="changeConditionType">
<el-radio
v-for="(dict, indexConditionType) in conditionConfigTypes"
:key="indexConditionType"
:value="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="condition.conditionType === ConditionType.RULE && condition.conditionGroups" label="条件规则">
<div class="condition-group-tool">
<div class="flex items-center">
<div class="mr-4">条件组关系</div>
<el-switch
v-model="condition.conditionGroups.and"
inline-prompt
active-text="且"
inactive-text="或"
/>
</div>
</div>
<el-space direction="vertical" :spacer="condition.conditionGroups.and ? '且' : '或'">
<el-card
class="condition-group"
style="width: 530px"
v-for="(equation, cIdx) in condition.conditionGroups.conditions"
:key="cIdx"
>
<div
class="condition-group-delete"
v-if="condition.conditionGroups.conditions.length > 1"
>
<Icon
color="#0089ff"
icon="ep:circle-close-filled"
:size="18"
@click="deleteConditionGroup(condition.conditionGroups.conditions, cIdx)"
/>
</div>
<template #header>
<div class="flex items-center justify-between">
<div>条件组</div>
<div class="flex">
<div class="mr-4">规则关系</div>
<el-switch
v-model="equation.and"
inline-prompt
active-text="且"
inactive-text="或"
/>
</div>
</div>
</template>
<div class="flex pt-2" v-for="(rule, rIdx) in equation.rules" :key="rIdx">
<div class="mr-2">
<el-form-item
:prop="`conditionGroups.conditions.${cIdx}.rules.${rIdx}.leftSide`"
:rules="{
required: true,
message: '左值不能为空',
trigger: 'change'
}"
>
<el-select style="width: 160px" v-model="rule.leftSide">
<el-option
v-for="(field, fIdx) in fieldOptions"
:key="fIdx"
:label="field.title"
:value="field.field"
:disabled="!field.required"
/>
</el-select>
</el-form-item>
</div>
<div class="mr-2">
<el-select v-model="rule.opCode" style="width: 100px">
<el-option
v-for="operator in COMPARISON_OPERATORS"
:key="operator.value"
:label="operator.label"
:value="operator.value"
/>
</el-select>
</div>
<div class="mr-2">
<el-form-item
:prop="`conditionGroups.conditions.${cIdx}.rules.${rIdx}.rightSide`"
:rules="{
required: true,
message: '右值不能为空',
trigger: 'blur'
}"
>
<el-input v-model="rule.rightSide" style="width: 160px" />
</el-form-item>
</div>
<div class="mr-1 flex items-center" v-if="equation.rules.length > 1">
<Icon icon="ep:delete" :size="18" @click="deleteConditionRule(equation, rIdx)" />
</div>
<div class="flex items-center">
<Icon icon="ep:plus" :size="18" @click="addConditionRule(equation, rIdx)" />
</div>
</div>
</el-card>
</el-space>
<div title="添加条件组" class="mt-4 cursor-pointer">
<Icon
color="#0089ff"
icon="ep:plus"
:size="24"
@click="addConditionGroup(condition.conditionGroups?.conditions)"
/>
</div>
</el-form-item>
<el-form-item
v-if="condition.conditionType === ConditionType.EXPRESSION"
label="条件表达式"
prop="conditionExpression"
>
<el-input
type="textarea"
v-model="condition.conditionExpression"
clearable
style="width: 100%"
/>
</el-form-item>
</el-form>
</template>
<script setup lang="ts">
import {
COMPARISON_OPERATORS,
CONDITION_CONFIG_TYPES,
ConditionType,
DEFAULT_CONDITION_GROUP_VALUE
} from '../../consts'
import { BpmModelFormType } from '@/utils/constants'
import { useFormFieldsAndStartUser } from '../../node'
const props = defineProps({
modelValue: {
type: Object,
required: true
}
})
const emit = defineEmits(['update:modelValue'])
const condition = computed({
get() {
return props.modelValue
},
set(newValue) {
emit('update:modelValue', newValue)
}
})
const formType = inject<Ref<number>>('formType') // 表单类型
const conditionConfigTypes = computed(() => {
return CONDITION_CONFIG_TYPES.filter((item) => {
// 业务表单暂时去掉条件规则选项
if (formType?.value === BpmModelFormType.CUSTOM && item.value === ConditionType.RULE) {
return false
} else {
return true
}
})
})
/** 条件规则可选择的表单字段 */
const fieldOptions = useFormFieldsAndStartUser()
// 表单校验规则
const formRules = reactive({
conditionType: [{ required: true, message: '配置方式不能为空', trigger: 'blur' }],
conditionExpression: [{ required: true, message: '条件表达式不能为空', trigger: 'blur' }]
})
const formRef = ref() // 表单 Ref
/** 切换条件配置方式 */
const changeConditionType = () => {
if (condition.value.conditionType === ConditionType.RULE) {
if (!condition.value.conditionGroups) {
condition.value.conditionGroups = DEFAULT_CONDITION_GROUP_VALUE
}
}
}
const deleteConditionGroup = (conditions, index) => {
conditions.splice(index, 1)
}
const deleteConditionRule = (condition, index) => {
condition.rules.splice(index, 1)
}
const addConditionRule = (condition, index) => {
const rule = {
opCode: '==',
leftSide: '',
rightSide: ''
}
condition.rules.splice(index + 1, 0, rule)
}
const addConditionGroup = (conditions) => {
const condition = {
and: true,
rules: [
{
opCode: '==',
leftSide: '',
rightSide: ''
}
]
}
conditions.push(condition)
}
const validate = async () => {
if (!formRef) return false
return await formRef.value.validate()
}
defineExpose({ validate })
</script>
<style lang="scss" scoped>
.condition-group-tool {
display: flex;
justify-content: space-between;
width: 500px;
margin-bottom: 20px;
}
.condition-group {
position: relative;
&:hover {
border-color: #0089ff;
.condition-group-delete {
opacity: 1;
}
}
.condition-group-delete {
position: absolute;
top: 0;
left: 0;
display: flex;
cursor: pointer;
opacity: 0;
}
}
::v-deep(.el-card__header) {
padding: 8px var(--el-card-padding);
border-bottom: 1px solid var(--el-card-border-color);
box-sizing: border-box;
}
</style>
@@ -0,0 +1,184 @@
<template>
<el-form-item label="请求头">
<div class="flex pt-2" v-for="(item, index) in props.header" :key="index">
<div class="mr-2">
<el-form-item
:prop="`${bind}.header.${index}.key`"
:rules="{
required: true,
message: '参数名不能为空',
trigger: 'blur'
}"
>
<el-input class="w-160px" v-model="item.key" />
</el-form-item>
</div>
<div class="mr-2">
<el-select class="w-100px!" v-model="item.type">
<el-option
v-for="types in BPM_HTTP_REQUEST_PARAM_TYPES"
:key="types.value"
:label="types.label"
:value="types.value"
/>
</el-select>
</div>
<div class="mr-2">
<el-form-item
:prop="`${bind}.header.${index}.value`"
:rules="{
required: true,
message: '参数值不能为空',
trigger: 'blur'
}"
>
<el-input
v-if="item.type === BpmHttpRequestParamTypeEnum.FIXED_VALUE"
class="w-160px"
v-model="item.value"
/>
</el-form-item>
<el-form-item
:prop="`${bind}.header.${index}.value`"
:rules="{
required: true,
message: '参数值不能为空',
trigger: 'change'
}"
>
<el-select
v-if="item.type === BpmHttpRequestParamTypeEnum.FROM_FORM"
class="w-160px!"
v-model="item.value"
>
<el-option
v-for="(field, fIdx) in formFieldOptions"
:key="fIdx"
:label="field.title"
:value="field.field"
:disabled="!field.required"
/>
</el-select>
</el-form-item>
</div>
<div class="mr-1 flex items-center">
<Icon icon="ep:delete" :size="18" @click="deleteHttpRequestParam(props.header, index)" />
</div>
</div>
<el-button type="primary" text @click="addHttpRequestParam(props.header)">
<Icon icon="ep:plus" class="mr-5px" />添加一行
</el-button>
</el-form-item>
<el-form-item label="请求体">
<div class="flex pt-2" v-for="(item, index) in props.body" :key="index">
<div class="mr-2">
<el-form-item
:prop="`${bind}.body.${index}.key`"
:rules="{
required: true,
message: '参数名不能为空',
trigger: 'blur'
}"
>
<el-input class="w-160px" v-model="item.key" />
</el-form-item>
</div>
<div class="mr-2">
<el-select class="w-100px!" v-model="item.type">
<el-option
v-for="types in BPM_HTTP_REQUEST_PARAM_TYPES"
:key="types.value"
:label="types.label"
:value="types.value"
/>
</el-select>
</div>
<div class="mr-2">
<el-form-item
:prop="`${bind}.body.${index}.value`"
:rules="{
required: true,
message: '参数值不能为空',
trigger: 'blur'
}"
>
<el-input
v-if="item.type === BpmHttpRequestParamTypeEnum.FIXED_VALUE"
class="w-160px"
v-model="item.value"
/>
</el-form-item>
<el-form-item
:prop="`${bind}.body.${index}.value`"
:rules="{
required: true,
message: '参数值不能为空',
trigger: 'change'
}"
>
<el-select
v-if="item.type === BpmHttpRequestParamTypeEnum.FROM_FORM"
class="w-160px!"
v-model="item.value"
>
<el-option
v-for="(field, fIdx) in formFieldOptions"
:key="fIdx"
:label="field.title"
:value="field.field"
:disabled="!field.required"
/>
</el-select>
</el-form-item>
</div>
<div class="mr-1 flex items-center">
<Icon icon="ep:delete" :size="18" @click="deleteHttpRequestParam(props.body, index)" />
</div>
</div>
<el-button type="primary" text @click="addHttpRequestParam(props.body)">
<Icon icon="ep:plus" class="mr-5px" />添加一行
</el-button>
</el-form-item>
</template>
<script setup lang="ts">
import { HttpRequestParam, BPM_HTTP_REQUEST_PARAM_TYPES, BpmHttpRequestParamTypeEnum } from '../../consts'
import { useFormFieldsAndStartUser } from '../../node'
defineOptions({
name: 'HttpRequestParamSetting'
})
const props = defineProps({
header: {
type: Array as () => HttpRequestParam[],
required: false,
default: () => []
},
body: {
type: Array as () => HttpRequestParam[],
required: false,
default: () => []
},
bind: {
type: String,
required: true
}
})
// 流程表单字段,发起人字段
const formFieldOptions = useFormFieldsAndStartUser()
/** 添加请求配置项 */
const addHttpRequestParam = (arr: HttpRequestParam[]) => {
arr.push({
key: '',
type: BpmHttpRequestParamTypeEnum.FIXED_VALUE,
value: ''
})
}
/** 删除请求配置项 */
const deleteHttpRequestParam = (arr: HttpRequestParam[], index: number) => {
arr.splice(index, 1)
}
</script>
<style lang="scss" scoped></style>
@@ -0,0 +1,88 @@
<template>
<el-form ref="listenerFormRef" :model="configForm" label-position="top">
<div v-for="(listener, listenerIdx) in taskListener" :key="listenerIdx">
<el-divider content-position="left">
<el-text tag="b" size="large">{{ listener.name }}</el-text>
</el-divider>
<el-form-item>
<el-switch
v-model="configForm[`task${listener.type}ListenerEnable`]"
active-text="开启"
inactive-text="关闭"
/>
</el-form-item>
<div v-if="configForm[`task${listener.type}ListenerEnable`]">
<el-form-item>
<el-alert
title="仅支持 POST 请求,以请求体方式接收参数"
type="warning"
show-icon
:closable="false"
/>
</el-form-item>
<el-form-item
label="请求地址"
:prop="`task${listener.type}ListenerPath`"
:rules="{
required: true,
message: '请求地址不能为空',
trigger: 'blur'
}"
>
<el-input v-model="configForm[`task${listener.type}ListenerPath`]" />
</el-form-item>
<HttpRequestParamSetting
:header="configForm[`task${listener.type}Listener`].header"
:body="configForm[`task${listener.type}Listener`].body"
:bind="`task${listener.type}Listener`"
/>
</div>
</div>
</el-form>
</template>
<script setup lang="ts">
import HttpRequestParamSetting from './HttpRequestParamSetting.vue'
const props = defineProps({
modelValue: {
type: Object,
required: true
},
formFieldOptions: {
type: Object,
required: true
}
})
const emit = defineEmits(['update:modelValue'])
const listenerFormRef = ref()
const configForm = computed({
get() {
return props.modelValue
},
set(newValue) {
emit('update:modelValue', newValue)
}
})
const taskListener = ref([
{
name: '创建任务',
type: 'Create'
},
{
name: '指派任务执行人员',
type: 'Assign'
},
{
name: '完成任务',
type: 'Complete'
}
])
const validate = async () => {
if (!listenerFormRef) return false
return await listenerFormRef.value.validate()
}
defineExpose({ validate })
</script>
@@ -1,11 +1,17 @@
<template>
<div class="node-wrapper">
<div class="node-container">
<div class="node-box" :class="{ 'node-config-error': !currentNode.showText }">
<div
class="node-box"
:class="[
{ 'node-config-error': !currentNode.showText },
`${useTaskStatusClass(currentNode?.activityStatus)}`
]"
>
<div class="node-title-container">
<div class="node-title-icon copy-task"><span class="iconfont icon-copy"></span></div>
<input
v-if="showInput"
v-if="!readonly && showInput"
type="text"
class="editable-title-input"
@blur="blurEvent()"
@@ -24,9 +30,9 @@
<div class="node-text" v-else>
{{ NODE_DEFAULT_TEXT.get(NodeType.COPY_TASK_NODE) }}
</div>
<Icon icon="ep:arrow-right-bold" />
<Icon v-if="!readonly" icon="ep:arrow-right-bold" />
</div>
<div class="node-toolbar">
<div v-if="!readonly" class="node-toolbar">
<div class="toolbar-icon"
><Icon color="#0089ff" icon="ep:circle-close-filled" :size="18" @click="deleteNode"
/></div>
@@ -34,15 +40,23 @@
</div>
<!-- 传递子节点给添加节点组件。会在子节点前面添加节点 -->
<NodeHandler v-if="currentNode" v-model:child-node="currentNode.childNode" />
<NodeHandler
v-if="currentNode"
v-model:child-node="currentNode.childNode"
:current-node="currentNode"
/>
</div>
<CopyTaskNodeConfig v-if="currentNode" ref="nodeSetting" :flow-node="currentNode" />
<CopyTaskNodeConfig
v-if="!readonly && currentNode"
ref="nodeSetting"
:flow-node="currentNode"
/>
</div>
</template>
<script setup lang="ts">
import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from '../consts'
import NodeHandler from '../NodeHandler.vue'
import { useNodeName2, useWatchNode } from '../node'
import { useNodeName2, useWatchNode, useTaskStatusClass } from '../node'
import CopyTaskNodeConfig from '../nodes-config/CopyTaskNodeConfig.vue'
defineOptions({
name: 'CopyTaskNode'
@@ -57,7 +71,8 @@ const props = defineProps({
const emits = defineEmits<{
'update:flowNode': [node: SimpleFlowNode | undefined]
}>()
// 是否只读
const readonly = inject<Boolean>('readonly')
// 监控节点的变化
const currentNode = useWatchNode(props)
// 节点名称编辑
@@ -66,6 +81,9 @@ const { showInput, blurEvent, clickTitle } = useNodeName2(currentNode, NodeType.
const nodeSetting = ref()
// 打开节点配置
const openNodeConfig = () => {
if (readonly) {
return
}
nodeSetting.value.showCopyTaskNodeConfig(currentNode.value)
nodeSetting.value.openDrawer()
}
@@ -0,0 +1,97 @@
<template>
<div class="node-wrapper">
<div class="node-container">
<div
class="node-box"
:class="[
{ 'node-config-error': !currentNode.showText },
`${useTaskStatusClass(currentNode?.activityStatus)}`
]"
>
<div class="node-title-container">
<div class="node-title-icon delay-node"><span class="iconfont icon-delay"></span></div>
<input
v-if="!readonly && showInput"
type="text"
class="editable-title-input"
@blur="blurEvent()"
v-mountedFocus
v-model="currentNode.name"
:placeholder="currentNode.name"
/>
<div v-else class="node-title" @click="clickTitle">
{{ currentNode.name }}
</div>
</div>
<div class="node-content" @click="openNodeConfig">
<div class="node-text" :title="currentNode.showText" v-if="currentNode.showText">
{{ currentNode.showText }}
</div>
<div class="node-text" v-else>
{{ NODE_DEFAULT_TEXT.get(NodeType.DELAY_TIMER_NODE) }}
</div>
<Icon v-if="!readonly" icon="ep:arrow-right-bold" />
</div>
<div v-if="!readonly" class="node-toolbar">
<div class="toolbar-icon"
><Icon color="#0089ff" icon="ep:circle-close-filled" :size="18" @click="deleteNode"
/></div>
</div>
</div>
<!-- 传递子节点给添加节点组件。会在子节点前面添加节点 -->
<NodeHandler
v-if="currentNode"
v-model:child-node="currentNode.childNode"
:current-node="currentNode"
/>
</div>
<DelayTimerNodeConfig
v-if="!readonly && currentNode"
ref="nodeSetting"
:flow-node="currentNode"
/>
</div>
</template>
<script setup lang="ts">
import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from '../consts'
import NodeHandler from '../NodeHandler.vue'
import { useNodeName2, useWatchNode, useTaskStatusClass } from '../node'
import DelayTimerNodeConfig from '../nodes-config/DelayTimerNodeConfig.vue'
defineOptions({
name: 'DelayTimerNode'
})
const props = defineProps({
flowNode: {
type: Object as () => SimpleFlowNode,
required: true
}
})
// 定义事件,更新父组件。
const emits = defineEmits<{
'update:flowNode': [node: SimpleFlowNode | undefined]
}>()
// 是否只读
const readonly = inject<Boolean>('readonly')
// 监控节点的变化
const currentNode = useWatchNode(props)
// 节点名称编辑
const { showInput, blurEvent, clickTitle } = useNodeName2(currentNode, NodeType.DELAY_TIMER_NODE)
const nodeSetting = ref()
// 打开节点配置
const openNodeConfig = () => {
if (readonly) {
return
}
nodeSetting.value.showDelayTimerNodeConfig(currentNode.value)
nodeSetting.value.openDrawer()
}
// 删除节点。更新当前节点为孩子节点
const deleteNode = () => {
emits('update:flowNode', currentNode.value.childNode)
}
</script>
<style lang="scss" scoped></style>
@@ -1,13 +1,102 @@
<template>
<div class="end-node-wrapper">
<div class="end-node-box">
<div class="end-node-box cursor-pointer" :class="`${useTaskStatusClass(currentNode?.activityStatus)}`" @click="nodeClick">
<span class="node-fixed-name" title="结束">结束</span>
</div>
</div>
<el-dialog title="审批信息" v-model="dialogVisible" width="1000px" append-to-body>
<el-row>
<el-table
:data="processInstanceInfos"
size="small"
border
header-cell-class-name="table-header-gray"
>
<el-table-column
label="序号"
header-align="center"
align="center"
type="index"
width="50"
/>
<el-table-column
label="发起人"
prop="assigneeUser.nickname"
min-width="100"
align="center"
/>
<el-table-column label="部门" min-width="100" align="center">
<template #default="scope">
{{ scope.row.assigneeUser?.deptName || scope.row.ownerUser?.deptName }}
</template>
</el-table-column>
<el-table-column
:formatter="dateFormatter"
align="center"
label="开始时间"
prop="createTime"
min-width="140"
/>
<el-table-column
:formatter="dateFormatter"
align="center"
label="结束时间"
prop="endTime"
min-width="140"
/>
<el-table-column align="center" label="审批状态" prop="status" min-width="90">
<template #default="scope">
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column align="center" label="耗时" prop="durationInMillis" width="100">
<template #default="scope">
{{ formatPast2(scope.row.durationInMillis) }}
</template>
</el-table-column>
</el-table>
</el-row>
</el-dialog>
</template>
<script setup lang="ts">
import { SimpleFlowNode } from '../consts'
import { useWatchNode, useTaskStatusClass } from '../node'
import { dateFormatter, formatPast2 } from '@/utils/formatTime'
import { DICT_TYPE } from '@/utils/dict'
defineOptions({
name: 'EndEventNode'
})
const props = defineProps({
flowNode: {
type: Object as () => SimpleFlowNode,
default: () => null
}
})
// 监控节点变化
const currentNode = useWatchNode(props)
// 是否只读
const readonly = inject<Boolean>('readonly')
const processInstance = inject<Ref<any>>('processInstance', ref({}))
// 审批信息的弹窗显示,用于只读模式
const dialogVisible = ref(false) // 弹窗可见性
const processInstanceInfos = ref<any[]>([]) // 流程的审批信息
const nodeClick = () => {
if (readonly) {
if(processInstance && processInstance.value){
processInstanceInfos.value = [
{
assigneeUser: processInstance.value.startUser,
createTime: processInstance.value.startTime,
endTime: processInstance.value.endTime,
status: processInstance.value.status,
durationInMillis: processInstance.value.durationInMillis
}
]
dialogVisible.value = true
}
}
}
</script>
<style lang="scss" scoped></style>
@@ -1,7 +1,17 @@
<template>
<div class="branch-node-wrapper">
<div class="branch-node-container">
<div class="branch-node-add" @click="addCondition">添加条件</div>
<div
v-if="readonly"
class="branch-node-readonly"
:class="`${useTaskStatusClass(currentNode?.activityStatus)}`"
>
<span class="iconfont icon-exclusive icon-size condition"></span>
</div>
<el-button v-else class="branch-node-add" color="#67c23a" @click="addCondition" plain
>添加条件</el-button
>
<div
class="branch-node-item"
v-for="(item, index) in currentNode.conditionNodes"
@@ -17,9 +27,15 @@
</template>
<div class="node-wrapper">
<div class="node-container">
<div class="node-box" :class="{ 'node-config-error': !item.showText }">
<div
class="node-box"
:class="[
{ 'node-config-error': !item.showText },
`${useTaskStatusClass(item.activityStatus)}`
]"
>
<div class="branch-node-title-container">
<div v-if="showInputs[index]">
<div v-if="!readonly && showInputs[index]">
<input
type="text"
class="input-max-width editable-title-input"
@@ -39,7 +55,10 @@
{{ NODE_DEFAULT_TEXT.get(NodeType.CONDITION_NODE) }}
</div>
</div>
<div class="node-toolbar" v-if="index + 1 !== currentNode.conditionNodes?.length">
<div
class="node-toolbar"
v-if="!readonly && index + 1 !== currentNode.conditionNodes?.length"
>
<div class="toolbar-icon">
<Icon
color="#0089ff"
@@ -65,7 +84,7 @@
<Icon icon="ep:arrow-right" />
</div>
</div>
<NodeHandler v-model:child-node="item.childNode" />
<NodeHandler v-model:child-node="item.childNode" :current-node="item" />
</div>
</div>
<ConditionNodeConfig :node-index="index" :condition-node="item" :ref="item.id" />
@@ -78,15 +97,20 @@
/>
</div>
</div>
<NodeHandler v-if="currentNode" v-model:child-node="currentNode.childNode" />
<NodeHandler
v-if="currentNode"
v-model:child-node="currentNode.childNode"
:current-node="currentNode"
/>
</div>
</template>
<script setup lang="ts">
import NodeHandler from '../NodeHandler.vue'
import ProcessNodeTree from '../ProcessNodeTree.vue'
import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from '../consts'
import { SimpleFlowNode, NodeType, ConditionType, DEFAULT_CONDITION_GROUP_VALUE, NODE_DEFAULT_TEXT } from '../consts'
import { getDefaultConditionNodeName } from '../utils'
import { useTaskStatusClass } from '../node'
import { generateUUID } from '@/utils'
import ConditionNodeConfig from '../nodes-config/ConditionNodeConfig.vue'
const { proxy } = getCurrentInstance() as any
@@ -94,10 +118,6 @@ defineOptions({
name: 'ExclusiveNode'
})
const props = defineProps({
// parentNode : {
// type: Object as () => SimpleFlowNode,
// required: true
// },
flowNode: {
type: Object as () => SimpleFlowNode,
required: true
@@ -113,10 +133,9 @@ const emits = defineEmits<{
nodeType: number
]
}>()
// 是否只读
const readonly = inject<Boolean>('readonly')
const currentNode = ref<SimpleFlowNode>(props.flowNode)
// const conditionNodes = computed(() => currentNode.value.conditionNodes);
watch(
() => props.flowNode,
(newValue) => {
@@ -130,7 +149,7 @@ const blurEvent = (index: number) => {
showInputs.value[index] = false
const conditionNode = currentNode.value.conditionNodes?.at(index) as SimpleFlowNode
conditionNode.name =
conditionNode.name || getDefaultConditionNodeName(index, conditionNode.defaultFlow)
conditionNode.name || getDefaultConditionNodeName(index, conditionNode.conditionSetting?.defaultFlow)
}
// 点击条件名称
@@ -139,6 +158,9 @@ const clickEvent = (index: number) => {
}
const conditionNodeConfig = (nodeId: string) => {
if (readonly) {
return
}
const conditionNode = proxy.$refs[nodeId][0]
conditionNode.open()
}
@@ -156,8 +178,11 @@ const addCondition = () => {
type: NodeType.CONDITION_NODE,
childNode: undefined,
conditionNodes: [],
conditionType: 1,
defaultFlow: false
conditionSetting: {
defaultFlow: false,
conditionType: ConditionType.RULE,
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE
}
}
conditionNodes.splice(lastIndex, 0, conditionData)
}
@@ -193,7 +218,7 @@ const recursiveFindParentNode = (
node: SimpleFlowNode,
nodeType: number
) => {
if (!node || node.type === NodeType.START_EVENT_NODE) {
if (!node || node.type === NodeType.START_USER_NODE) {
return
}
if (node.type === nodeType) {
@@ -0,0 +1,236 @@
<template>
<div class="branch-node-wrapper">
<div class="branch-node-container">
<div
v-if="readonly"
class="branch-node-readonly"
:class="`${useTaskStatusClass(currentNode?.activityStatus)}`"
>
<span class="iconfont icon-inclusive icon-size inclusive"></span>
</div>
<el-button v-else class="branch-node-add" color="#345da2" @click="addCondition" plain
>添加条件</el-button
>
<div
class="branch-node-item"
v-for="(item, index) in currentNode.conditionNodes"
:key="index"
>
<template v-if="index == 0">
<div class="branch-line-first-top"> </div>
<div class="branch-line-first-bottom"></div>
</template>
<template v-if="index + 1 == currentNode.conditionNodes?.length">
<div class="branch-line-last-top"></div>
<div class="branch-line-last-bottom"></div>
</template>
<div class="node-wrapper">
<div class="node-container">
<div
class="node-box"
:class="[
{ 'node-config-error': !item.showText },
`${useTaskStatusClass(item.activityStatus)}`
]"
>
<div class="branch-node-title-container">
<div v-if="!readonly && showInputs[index]">
<input
type="text"
class="editable-title-input"
@blur="blurEvent(index)"
v-mountedFocus
v-model="item.name"
/>
</div>
<div v-else class="branch-title" @click="clickEvent(index)"> {{ item.name }} </div>
</div>
<div class="branch-node-content" @click="conditionNodeConfig(item.id)">
<div class="branch-node-text" :title="item.showText" v-if="item.showText">
{{ item.showText }}
</div>
<div class="branch-node-text" v-else>
{{ NODE_DEFAULT_TEXT.get(NodeType.CONDITION_NODE) }}
</div>
</div>
<div
class="node-toolbar"
v-if="!readonly && index + 1 !== currentNode.conditionNodes?.length"
>
<div class="toolbar-icon">
<Icon
color="#0089ff"
icon="ep:circle-close-filled"
:size="18"
@click="deleteCondition(index)"
/>
</div>
</div>
<div
class="branch-node-move move-node-left"
v-if="!readonly && index != 0 && index + 1 !== currentNode.conditionNodes?.length"
@click="moveNode(index, -1)"
>
<Icon icon="ep:arrow-left" />
</div>
<div
class="branch-node-move move-node-right"
v-if="
!readonly &&
currentNode.conditionNodes &&
index < currentNode.conditionNodes.length - 2
"
@click="moveNode(index, 1)"
>
<Icon icon="ep:arrow-right" />
</div>
</div>
<NodeHandler v-model:child-node="item.childNode" :current-node="item" />
</div>
</div>
<ConditionNodeConfig :node-index="index" :condition-node="item" :ref="item.id" />
<!-- 递归显示子节点 -->
<ProcessNodeTree
v-if="item && item.childNode"
:parent-node="item"
v-model:flow-node="item.childNode"
@find:recursive-find-parent-node="recursiveFindParentNode"
/>
</div>
</div>
<NodeHandler
v-if="currentNode"
v-model:child-node="currentNode.childNode"
:current-node="currentNode"
/>
</div>
</template>
<script setup lang="ts">
import NodeHandler from '../NodeHandler.vue'
import ProcessNodeTree from '../ProcessNodeTree.vue'
import { SimpleFlowNode, NodeType, ConditionType, DEFAULT_CONDITION_GROUP_VALUE, NODE_DEFAULT_TEXT } from '../consts'
import { useTaskStatusClass } from '../node'
import { getDefaultInclusiveConditionNodeName } from '../utils'
import { generateUUID } from '@/utils'
import ConditionNodeConfig from '../nodes-config/ConditionNodeConfig.vue'
const { proxy } = getCurrentInstance() as any
defineOptions({
name: 'InclusiveNode'
})
const props = defineProps({
flowNode: {
type: Object as () => SimpleFlowNode,
required: true
}
})
// 定义事件,更新父组件
const emits = defineEmits<{
'update:modelValue': [node: SimpleFlowNode | undefined]
'find:parentNode': [nodeList: SimpleFlowNode[], nodeType: number]
'find:recursiveFindParentNode': [
nodeList: SimpleFlowNode[],
curentNode: SimpleFlowNode,
nodeType: number
]
}>()
// 是否只读
const readonly = inject<Boolean>('readonly')
const currentNode = ref<SimpleFlowNode>(props.flowNode)
watch(
() => props.flowNode,
(newValue) => {
currentNode.value = newValue
}
)
const showInputs = ref<boolean[]>([])
// 失去焦点
const blurEvent = (index: number) => {
showInputs.value[index] = false
const conditionNode = currentNode.value.conditionNodes?.at(index) as SimpleFlowNode
conditionNode.name =
conditionNode.name || getDefaultInclusiveConditionNodeName(index, conditionNode.conditionSetting?.defaultFlow)
}
// 点击条件名称
const clickEvent = (index: number) => {
showInputs.value[index] = true
}
const conditionNodeConfig = (nodeId: string) => {
if (readonly) {
return
}
const conditionNode = proxy.$refs[nodeId][0]
conditionNode.open()
}
// 新增条件
const addCondition = () => {
const conditionNodes = currentNode.value.conditionNodes
if (conditionNodes) {
const len = conditionNodes.length
let lastIndex = len - 1
const conditionData: SimpleFlowNode = {
id: 'Flow_' + generateUUID(),
name: '包容条件' + len,
showText: '',
type: NodeType.CONDITION_NODE,
childNode: undefined,
conditionNodes: [],
conditionSetting: {
defaultFlow: false,
conditionType: ConditionType.RULE,
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE
}
}
conditionNodes.splice(lastIndex, 0, conditionData)
}
}
// 删除条件
const deleteCondition = (index: number) => {
const conditionNodes = currentNode.value.conditionNodes
if (conditionNodes) {
conditionNodes.splice(index, 1)
if (conditionNodes.length == 1) {
const childNode = currentNode.value.childNode
// 更新此节点为后续孩子节点
emits('update:modelValue', childNode)
}
}
}
// 移动节点
const moveNode = (index: number, to: number) => {
// -1 :向左 1: 向右
if (currentNode.value.conditionNodes) {
currentNode.value.conditionNodes[index] = currentNode.value.conditionNodes.splice(
index + to,
1,
currentNode.value.conditionNodes[index]
)[0]
}
}
// 递归从父节点中查询匹配的节点
const recursiveFindParentNode = (
nodeList: SimpleFlowNode[],
node: SimpleFlowNode,
nodeType: number
) => {
if (!node || node.type === NodeType.START_USER_NODE) {
return
}
if (node.type === nodeType) {
nodeList.push(node)
}
// 条件节点 (NodeType.CONDITION_NODE) 比较特殊。需要调用其父节点条件分支节点(NodeType.INCLUSIVE_BRANCH_NODE) 继续查找
emits('find:parentNode', nodeList, nodeType)
}
</script>
<style lang="scss" scoped></style>
Loaded 100 of 260 files, more files were not shown because too many files have changed in this diff. Show more