Browse Source

删除多余功能

master
xsx 1 year ago
parent
commit
2f099710ca
  1. 229
      db/im-admin.sql
  2. 14
      im-admin-ui/.env.development
  3. 12
      im-admin-ui/bin/build.bat
  4. 12
      im-admin-ui/bin/package.bat
  5. 12
      im-admin-ui/bin/run-web.bat
  6. 12
      im-admin-ui/package.json
  7. BIN
      im-admin-ui/public/favicon.ico
  8. 62
      im-admin-ui/src/api/demo/demo/index.ts
  9. 90
      im-admin-ui/src/api/demo/demo/types.ts
  10. 62
      im-admin-ui/src/api/demo/tree/index.ts
  11. 80
      im-admin-ui/src/api/demo/tree/types.ts
  12. 63
      im-admin-ui/src/api/workflow/category/index.ts
  13. 67
      im-admin-ui/src/api/workflow/category/types.ts
  14. 49
      im-admin-ui/src/api/workflow/definitionConfig/index.ts
  15. 102
      im-admin-ui/src/api/workflow/definitionConfig/types.ts
  16. 76
      im-admin-ui/src/api/workflow/formManage/index.ts
  17. 69
      im-admin-ui/src/api/workflow/formManage/types.ts
  18. 63
      im-admin-ui/src/api/workflow/leave/index.ts
  19. 24
      im-admin-ui/src/api/workflow/leave/types.ts
  20. 104
      im-admin-ui/src/api/workflow/model/index.ts
  21. 66
      im-admin-ui/src/api/workflow/model/types.ts
  22. 38
      im-admin-ui/src/api/workflow/nodeConfig/types.ts
  23. 114
      im-admin-ui/src/api/workflow/processDefinition/index.ts
  24. 24
      im-admin-ui/src/api/workflow/processDefinition/types.ts
  25. 136
      im-admin-ui/src/api/workflow/processInstance/index.ts
  26. 27
      im-admin-ui/src/api/workflow/processInstance/types.ts
  27. 264
      im-admin-ui/src/api/workflow/task/index.ts
  28. 49
      im-admin-ui/src/api/workflow/task/types.ts
  29. 29
      im-admin-ui/src/api/workflow/workflowCommon/index.ts
  30. 16
      im-admin-ui/src/api/workflow/workflowCommon/types.ts
  31. BIN
      im-admin-ui/src/assets/logo/logo.png
  32. 23
      im-admin-ui/src/bpmn/assets/defaultXML.ts
  33. 126
      im-admin-ui/src/bpmn/assets/lang/zh.ts
  34. 1250
      im-admin-ui/src/bpmn/assets/moddle/flowable.ts
  35. 138
      im-admin-ui/src/bpmn/assets/module/ContextPad/CustomContextPadProvider.ts
  36. 109
      im-admin-ui/src/bpmn/assets/module/Palette/CustomPaletteProvider.ts
  37. 56
      im-admin-ui/src/bpmn/assets/module/Renderer/CustomRenderer.ts
  38. 15
      im-admin-ui/src/bpmn/assets/module/Translate/index.ts
  39. 17
      im-admin-ui/src/bpmn/assets/module/index.ts
  40. 50
      im-admin-ui/src/bpmn/assets/showConfig.ts
  41. 284
      im-admin-ui/src/bpmn/assets/style/index.scss
  42. 145
      im-admin-ui/src/bpmn/hooks/usePanel.ts
  43. 34
      im-admin-ui/src/bpmn/hooks/useParseElement.ts
  44. 496
      im-admin-ui/src/bpmn/index.vue
  45. 68
      im-admin-ui/src/bpmn/panel/GatewayPanel.vue
  46. 68
      im-admin-ui/src/bpmn/panel/ParticipantPanel.vue
  47. 71
      im-admin-ui/src/bpmn/panel/ProcessPanel.vue
  48. 95
      im-admin-ui/src/bpmn/panel/SequenceFlowPanel.vue
  49. 67
      im-admin-ui/src/bpmn/panel/StartEndPanel.vue
  50. 193
      im-admin-ui/src/bpmn/panel/SubProcessPanel.vue
  51. 491
      im-admin-ui/src/bpmn/panel/TaskPanel.vue
  52. 110
      im-admin-ui/src/bpmn/panel/index.vue
  53. 252
      im-admin-ui/src/bpmn/panel/property/DueDate.vue
  54. 308
      im-admin-ui/src/bpmn/panel/property/ExecutionListener.vue
  55. 121
      im-admin-ui/src/bpmn/panel/property/ListenerParam.vue
  56. 310
      im-admin-ui/src/bpmn/panel/property/TaskListener.vue
  57. 71
      im-admin-ui/src/components/BpmnDesign/index.vue
  58. 411
      im-admin-ui/src/components/BpmnView/index.vue
  59. 13
      im-admin-ui/src/components/RuoYiDoc/index.vue
  60. 13
      im-admin-ui/src/components/RuoYiGit/index.vue
  61. 8
      im-admin-ui/src/layout/components/Navbar.vue
  62. 14
      im-admin-ui/src/router/index.ts
  63. 254
      im-admin-ui/src/views/demo/demo/index.vue
  64. 258
      im-admin-ui/src/views/demo/tree/index.vue
  65. 38
      im-admin-ui/src/views/login.vue
  66. 9
      im-admin-ui/src/views/monitor/admin/index.vue
  67. 9
      im-admin-ui/src/views/monitor/snailjob/index.vue
  68. 263
      im-admin-ui/src/views/workflow/category/index.vue
  69. 243
      im-admin-ui/src/views/workflow/formManage/index.vue
  70. 242
      im-admin-ui/src/views/workflow/leave/index.vue
  71. 252
      im-admin-ui/src/views/workflow/leave/leaveEdit.vue
  72. 383
      im-admin-ui/src/views/workflow/model/index.vue
  73. 45
      im-admin-ui/src/views/workflow/processDefinition/components/processPreview.vue
  74. 517
      im-admin-ui/src/views/workflow/processDefinition/index.vue
  75. 364
      im-admin-ui/src/views/workflow/processInstance/index.vue
  76. 286
      im-admin-ui/src/views/workflow/task/allTaskWaiting.vue
  77. 255
      im-admin-ui/src/views/workflow/task/myDocument.vue
  78. 150
      im-admin-ui/src/views/workflow/task/taskCopyList.vue
  79. 136
      im-admin-ui/src/views/workflow/task/taskFinish.vue
  80. 149
      im-admin-ui/src/views/workflow/task/taskWaiting.vue
  81. 2
      im-admin-ui/vite.config.ts
  82. 18
      im-admin/.editorconfig
  83. 48
      im-admin/.gitignore
  84. 182
      im-admin/README.md
  85. 26
      im-admin/pom.xml
  86. 23
      im-admin/ruoyi-admin/src/main/java/org/dromara/DromaraApplication.java
  87. 18
      im-admin/ruoyi-admin/src/main/java/org/dromara/DromaraServletInitializer.java
  88. 15
      im-admin/ruoyi-admin/src/main/resources/application.yml

229
db/im-admin.sql

@ -65,13 +65,6 @@ create table sys_tenant
) engine=innodb comment = '租户表';
-- ----------------------------
-- 初始化-租户表数据
-- ----------------------------
insert into sys_tenant values(1, '000000', '管理组', '15888888888', 'XXX有限公司', null, null, '多租户通用后台管理管理系统', null, null, null, null, -1, '0', '0', 103, 1, sysdate(), null, null);
-- ----------------------------
-- 租户套餐表
-- ----------------------------
@ -121,16 +114,7 @@ create table sys_dept (
-- ----------------------------
insert into sys_dept values(100, '000000', 0, '0', 'XXX科技', null,0, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
insert into sys_dept values(101, '000000', 100, '0,100', '深圳总公司', null,1, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
insert into sys_dept values(102, '000000', 100, '0,100', '长沙分公司', null,2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
insert into sys_dept values(103, '000000', 101, '0,100,101', '研发部门', null,1, 1, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
insert into sys_dept values(104, '000000', 101, '0,100,101', '市场部门', null,2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
insert into sys_dept values(105, '000000', 101, '0,100,101', '测试部门', null,3, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
insert into sys_dept values(106, '000000', 101, '0,100,101', '财务部门', null,4, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
insert into sys_dept values(107, '000000', 101, '0,100,101', '运维部门', null,5, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
insert into sys_dept values(108, '000000', 102, '0,100,102', '市场部门', null,1, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
insert into sys_dept values(109, '000000', 102, '0,100,102', '财务部门', null,2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
insert into sys_dept values(100, '000000', 0, '0', '盒子科技', null,0, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
-- ----------------------------
@ -164,9 +148,7 @@ create table sys_user (
-- ----------------------------
-- 初始化-用户信息表数据
-- ----------------------------
insert into sys_user values(1, '000000', 103, 'admin', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@163.com', '15888888888', '1', null, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate(), 103, 1, sysdate(), null, null, '管理员');
insert into sys_user values(3, '000000', 108, 'test', '本部门及以下 密码666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate(), 103, 1, sysdate(), 3, sysdate(), null);
insert into sys_user values(4, '000000', 102, 'test1', '仅本人 密码666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate(), 103, 1, sysdate(), 4, sysdate(), null);
insert into sys_user values(1, '000000', 100, 'admin', 'admin', 'sys_user', 'xxx@163.com', '15888888888', '1', null, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate(), 103, 1, sysdate(), null, null, '管理员');
-- ----------------------------
-- 3、岗位信息表
@ -190,13 +172,6 @@ create table sys_post
primary key (post_id)
) engine=innodb comment = '岗位信息表';
-- ----------------------------
-- 初始化-岗位信息表数据
-- ----------------------------
insert into sys_post values(1, '000000', 103, 'ceo', null, '董事长', 1, '0', 103, 1, sysdate(), null, null, '');
insert into sys_post values(2, '000000', 100, 'se', null, '项目经理', 2, '0', 103, 1, sysdate(), null, null, '');
insert into sys_post values(3, '000000', 100, 'hr', null, '人力资源', 3, '0', 103, 1, sysdate(), null, null, '');
insert into sys_post values(4, '000000', 100, 'user', null, '普通员工', 4, '0', 103, 1, sysdate(), null, null, '');
-- ----------------------------
@ -226,8 +201,6 @@ create table sys_role (
-- 初始化-角色信息表数据
-- ----------------------------
insert into sys_role values(1, '000000', '超级管理员', 'superadmin', 1, 1, 1, 1, '0', '0', 103, 1, sysdate(), null, null, '超级管理员');
insert into sys_role values(3, '000000', '本部门及以下', 'test1', 3, 4, 1, 1, '0', '0', 103, 1, sysdate(), null, null, '');
insert into sys_role values(4, '000000', '仅本人', 'test2', 4, 5, 1, 1, '0', '0', 103, 1, sysdate(), null, null, '');
-- ----------------------------
-- 5、菜单权限表
@ -261,11 +234,9 @@ create table sys_menu (
-- ----------------------------
-- 一级菜单
insert into sys_menu values('1', '系统管理', '0', '1', 'system', null, '', 1, 0, 'M', '0', '0', '', 'system', 103, 1, sysdate(), null, null, '系统管理目录');
insert into sys_menu values('6', '租户管理', '0', '2', 'tenant', null, '', 1, 0, 'M', '0', '0', '', 'chart', 103, 1, sysdate(), null, null, '租户管理目录');
insert into sys_menu values('2', '系统监控', '0', '3', 'monitor', null, '', 1, 0, 'M', '0', '0', '', 'monitor', 103, 1, sysdate(), null, null, '系统监控目录');
insert into sys_menu values('3', '系统工具', '0', '4', 'tool', null, '', 1, 0, 'M', '0', '0', '', 'tool', 103, 1, sysdate(), null, null, '系统工具目录');
insert into sys_menu values('4', 'PLUS官网', '0', '5', 'https://gitee.com/dromara/RuoYi-Vue-Plus', null, '', 0, 0, 'M', '0', '0', '', 'guide', 103, 1, sysdate(), null, null, 'RuoYi-Vue-Plus官网地址');
insert into sys_menu values('5', '测试菜单', '0', '5', 'demo', null, '', 1, 0, 'M', '0', '0', '', 'star', 103, 1, sysdate(), null, null, '测试菜单');
-- 二级菜单
insert into sys_menu values('100', '用户管理', '1', '1', 'user', 'system/user/index', '', 1, 0, 'C', '0', '0', 'system:user:list', 'user', 103, 1, sysdate(), null, null, '用户管理菜单');
insert into sys_menu values('101', '角色管理', '1', '2', 'role', 'system/role/index', '', 1, 0, 'C', '0', '0', 'system:role:list', 'peoples', 103, 1, sysdate(), null, null, '角色管理菜单');
@ -283,12 +254,8 @@ insert into sys_menu values('121', '租户管理', '6', '1', 'tenant',
insert into sys_menu values('122', '租户套餐管理', '6', '2', 'tenantPackage', 'system/tenantPackage/index', '', 1, 0, 'C', '0', '0', 'system:tenantPackage:list', 'form', 103, 1, sysdate(), null, null, '租户套餐管理菜单');
insert into sys_menu values('123', '客户端管理', '1', '11', 'client', 'system/client/index', '', 1, 0, 'C', '0', '0', 'system:client:list', 'international', 103, 1, sysdate(), null, null, '客户端管理菜单');
-- springboot-admin监控
insert into sys_menu values('117', 'Admin监控', '2', '5', 'Admin', 'monitor/admin/index', '', 1, 0, 'C', '0', '0', 'monitor:admin:list', 'dashboard', 103, 1, sysdate(), null, null, 'Admin监控菜单');
-- oss菜单
insert into sys_menu values('118', '文件管理', '1', '10', 'oss', 'system/oss/index', '', 1, 0, 'C', '0', '0', 'system:oss:list', 'upload', 103, 1, sysdate(), null, null, '文件管理菜单');
-- snail-job server控制台
insert into sys_menu values('120', '任务调度中心', '2', '6', 'snailjob', 'monitor/snailjob/index', '', 1, 0, 'C', '0', '0', 'monitor:snailjob:list', 'job', 103, 1, sysdate(), null, null, 'SnailJob控制台菜单');
-- 三级菜单
insert into sys_menu values('500', '操作日志', '108', '1', 'operlog', 'monitor/operlog/index', '', 1, 0, 'C', '0', '0', 'monitor:operlog:list', 'form', 103, 1, sysdate(), null, null, '操作日志菜单');
@ -370,37 +337,13 @@ insert into sys_menu values('1621', '配置添加', '118', '6', '#', '', '', 1,
insert into sys_menu values('1622', '配置编辑', '118', '6', '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:edit', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('1623', '配置删除', '118', '6', '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:remove', '#', 103, 1, sysdate(), null, null, '');
-- 租户管理相关按钮
insert into sys_menu values ('1606', '租户查询', '121', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:query', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values ('1607', '租户新增', '121', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:add', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values ('1608', '租户修改', '121', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:edit', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values ('1609', '租户删除', '121', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:remove', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values ('1610', '租户导出', '121', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:export', '#', 103, 1, sysdate(), null, null, '');
-- 租户套餐管理相关按钮
insert into sys_menu values ('1611', '租户套餐查询', '122', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:query', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values ('1612', '租户套餐新增', '122', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:add', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values ('1613', '租户套餐修改', '122', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:edit', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values ('1614', '租户套餐删除', '122', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:remove', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values ('1615', '租户套餐导出', '122', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:export', '#', 103, 1, sysdate(), null, null, '');
-- 客户端管理按钮
insert into sys_menu values('1061', '客户端管理查询', '123', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:client:query', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('1062', '客户端管理新增', '123', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:client:add', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('1063', '客户端管理修改', '123', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:client:edit', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('1064', '客户端管理删除', '123', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:client:remove', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('1065', '客户端管理导出', '123', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:client:export', '#', 103, 1, sysdate(), null, null, '');
-- 测试菜单
insert into sys_menu values('1500', '测试单表', '5', '1', 'demo', 'demo/demo/index', '', 1, 0, 'C', '0', '0', 'demo:demo:list', '#', 103, 1, sysdate(), null, null, '测试单表菜单');
insert into sys_menu values('1501', '测试单表查询', '1500', '1', '#', '', '', 1, 0, 'F', '0', '0', 'demo:demo:query', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('1502', '测试单表新增', '1500', '2', '#', '', '', 1, 0, 'F', '0', '0', 'demo:demo:add', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('1503', '测试单表修改', '1500', '3', '#', '', '', 1, 0, 'F', '0', '0', 'demo:demo:edit', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('1504', '测试单表删除', '1500', '4', '#', '', '', 1, 0, 'F', '0', '0', 'demo:demo:remove', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('1505', '测试单表导出', '1500', '5', '#', '', '', 1, 0, 'F', '0', '0', 'demo:demo:export', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('1506', '测试树表', '5', '1', 'tree', 'demo/tree/index', '', 1, 0, 'C', '0', '0', 'demo:tree:list', '#', 103, 1, sysdate(), null, null, '测试树表菜单');
insert into sys_menu values('1507', '测试树表查询', '1506', '1', '#', '', '', 1, 0, 'F', '0', '0', 'demo:tree:query', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('1508', '测试树表新增', '1506', '2', '#', '', '', 1, 0, 'F', '0', '0', 'demo:tree:add', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('1509', '测试树表修改', '1506', '3', '#', '', '', 1, 0, 'F', '0', '0', 'demo:tree:edit', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('1510', '测试树表删除', '1506', '4', '#', '', '', 1, 0, 'F', '0', '0', 'demo:tree:remove', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('1511', '测试树表导出', '1506', '5', '#', '', '', 1, 0, 'F', '0', '0', 'demo:tree:export', '#', 103, 1, sysdate(), null, null, '');
-- ----------------------------
-- 6、用户和角色关联表 用户N-1角色
@ -415,8 +358,6 @@ create table sys_user_role (
-- 初始化-用户和角色关联表数据
-- ----------------------------
insert into sys_user_role values ('1', '1');
insert into sys_user_role values ('3', '3');
insert into sys_user_role values ('4', '4');
-- ----------------------------
-- 7、角色和菜单关联表 角色1-N菜单
@ -427,93 +368,6 @@ create table sys_role_menu (
primary key(role_id, menu_id)
) engine=innodb comment = '角色和菜单关联表';
-- ----------------------------
-- 初始化-角色和菜单关联表数据
-- ----------------------------
insert into sys_role_menu values ('3', '1');
insert into sys_role_menu values ('3', '5');
insert into sys_role_menu values ('3', '100');
insert into sys_role_menu values ('3', '101');
insert into sys_role_menu values ('3', '102');
insert into sys_role_menu values ('3', '103');
insert into sys_role_menu values ('3', '104');
insert into sys_role_menu values ('3', '105');
insert into sys_role_menu values ('3', '106');
insert into sys_role_menu values ('3', '107');
insert into sys_role_menu values ('3', '108');
insert into sys_role_menu values ('3', '500');
insert into sys_role_menu values ('3', '501');
insert into sys_role_menu values ('3', '1001');
insert into sys_role_menu values ('3', '1002');
insert into sys_role_menu values ('3', '1003');
insert into sys_role_menu values ('3', '1004');
insert into sys_role_menu values ('3', '1005');
insert into sys_role_menu values ('3', '1006');
insert into sys_role_menu values ('3', '1007');
insert into sys_role_menu values ('3', '1008');
insert into sys_role_menu values ('3', '1009');
insert into sys_role_menu values ('3', '1010');
insert into sys_role_menu values ('3', '1011');
insert into sys_role_menu values ('3', '1012');
insert into sys_role_menu values ('3', '1013');
insert into sys_role_menu values ('3', '1014');
insert into sys_role_menu values ('3', '1015');
insert into sys_role_menu values ('3', '1016');
insert into sys_role_menu values ('3', '1017');
insert into sys_role_menu values ('3', '1018');
insert into sys_role_menu values ('3', '1019');
insert into sys_role_menu values ('3', '1020');
insert into sys_role_menu values ('3', '1021');
insert into sys_role_menu values ('3', '1022');
insert into sys_role_menu values ('3', '1023');
insert into sys_role_menu values ('3', '1024');
insert into sys_role_menu values ('3', '1025');
insert into sys_role_menu values ('3', '1026');
insert into sys_role_menu values ('3', '1027');
insert into sys_role_menu values ('3', '1028');
insert into sys_role_menu values ('3', '1029');
insert into sys_role_menu values ('3', '1030');
insert into sys_role_menu values ('3', '1031');
insert into sys_role_menu values ('3', '1032');
insert into sys_role_menu values ('3', '1033');
insert into sys_role_menu values ('3', '1034');
insert into sys_role_menu values ('3', '1035');
insert into sys_role_menu values ('3', '1036');
insert into sys_role_menu values ('3', '1037');
insert into sys_role_menu values ('3', '1038');
insert into sys_role_menu values ('3', '1039');
insert into sys_role_menu values ('3', '1040');
insert into sys_role_menu values ('3', '1041');
insert into sys_role_menu values ('3', '1042');
insert into sys_role_menu values ('3', '1043');
insert into sys_role_menu values ('3', '1044');
insert into sys_role_menu values ('3', '1045');
insert into sys_role_menu values ('3', '1500');
insert into sys_role_menu values ('3', '1501');
insert into sys_role_menu values ('3', '1502');
insert into sys_role_menu values ('3', '1503');
insert into sys_role_menu values ('3', '1504');
insert into sys_role_menu values ('3', '1505');
insert into sys_role_menu values ('3', '1506');
insert into sys_role_menu values ('3', '1507');
insert into sys_role_menu values ('3', '1508');
insert into sys_role_menu values ('3', '1509');
insert into sys_role_menu values ('3', '1510');
insert into sys_role_menu values ('3', '1511');
insert into sys_role_menu values ('4', '5');
insert into sys_role_menu values ('4', '1500');
insert into sys_role_menu values ('4', '1501');
insert into sys_role_menu values ('4', '1502');
insert into sys_role_menu values ('4', '1503');
insert into sys_role_menu values ('4', '1504');
insert into sys_role_menu values ('4', '1505');
insert into sys_role_menu values ('4', '1506');
insert into sys_role_menu values ('4', '1507');
insert into sys_role_menu values ('4', '1508');
insert into sys_role_menu values ('4', '1509');
insert into sys_role_menu values ('4', '1510');
insert into sys_role_menu values ('4', '1511');
-- ----------------------------
-- 8、角色和部门关联表 角色1-N部门
-- ----------------------------
@ -724,12 +578,6 @@ create table sys_notice (
primary key (notice_id)
) engine=innodb comment = '通知公告表';
-- ----------------------------
-- 初始化-公告信息表数据
-- ----------------------------
insert into sys_notice values('1', '000000', '温馨提醒:2018-07-01 新版本发布啦', '2', '新版本内容', '0', 103, 1, sysdate(), null, null, '管理员');
insert into sys_notice values('2', '000000', '维护通知:2018-07-01 系统凌晨维护', '1', '维护内容', '0', 103, 1, sysdate(), null, null, '管理员');
-- ----------------------------
-- 18、代码生成业务表
@ -838,10 +686,6 @@ create table sys_oss_config (
) engine=innodb comment='对象存储配置表';
insert into sys_oss_config values (1, '000000', 'minio', 'ruoyi', 'ruoyi123', 'ruoyi', '', '127.0.0.1:9000', '','N', '', '1' ,'0', '', 103, 1, sysdate(), 1, sysdate(), null);
insert into sys_oss_config values (2, '000000', 'qiniu', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi', '', 's3-cn-north-1.qiniucs.com', '','N', '', '1' ,'1', '', 103, 1, sysdate(), 1, sysdate(), null);
insert into sys_oss_config values (3, '000000', 'aliyun', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi', '', 'oss-cn-beijing.aliyuncs.com', '','N', '', '1' ,'1', '', 103, 1, sysdate(), 1, sysdate(), null);
insert into sys_oss_config values (4, '000000', 'qcloud', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi-1250000000', '', 'cos.ap-beijing.myqcloud.com', '','N', 'ap-beijing', '1' ,'1', '', 103, 1, sysdate(), 1, sysdate(), null);
insert into sys_oss_config values (5, '000000', 'image', 'ruoyi', 'ruoyi123', 'ruoyi', 'image', '127.0.0.1:9000', '','N', '', '1' ,'1', '', 103, 1, sysdate(), 1, sysdate(), null);
-- ----------------------------
-- 系统授权表
@ -869,67 +713,4 @@ insert into sys_client values (1, 'e5cd7e4891bf95d1d19206ce24a7b32e', 'pc', 'pc1
insert into sys_client values (2, '428a8310cd442757ae699df5d894f051', 'app', 'app123', 'password,sms,social', 'android', 1800, 604800, 0, 0, 103, 1, sysdate(), 1, sysdate());
CREATE TABLE test_demo
(
id bigint(0) NOT NULL COMMENT '主键',
tenant_id varchar(20) NULL DEFAULT '000000' COMMENT '租户编号',
dept_id bigint(0) NULL DEFAULT NULL COMMENT '部门id',
user_id bigint(0) NULL DEFAULT NULL COMMENT '用户id',
order_num int(0) NULL DEFAULT 0 COMMENT '排序号',
test_key varchar(255) NULL DEFAULT NULL COMMENT 'key键',
value varchar(255) NULL DEFAULT NULL COMMENT '',
version int(0) NULL DEFAULT 0 COMMENT '版本',
create_dept bigint(0) NULL DEFAULT NULL COMMENT '创建部门',
create_time datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
create_by bigint(0) NULL DEFAULT NULL COMMENT '创建人',
update_time datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
update_by bigint(0) NULL DEFAULT NULL COMMENT '更新人',
del_flag int(0) NULL DEFAULT 0 COMMENT '删除标志',
PRIMARY KEY (id) USING BTREE
) ENGINE = InnoDB COMMENT = '测试单表';
CREATE TABLE test_tree
(
id bigint(0) NOT NULL COMMENT '主键',
tenant_id varchar(20) NULL DEFAULT '000000' COMMENT '租户编号',
parent_id bigint(0) NULL DEFAULT 0 COMMENT '父id',
dept_id bigint(0) NULL DEFAULT NULL COMMENT '部门id',
user_id bigint(0) NULL DEFAULT NULL COMMENT '用户id',
tree_name varchar(255) NULL DEFAULT NULL COMMENT '',
version int(0) NULL DEFAULT 0 COMMENT '版本',
create_dept bigint(0) NULL DEFAULT NULL COMMENT '创建部门',
create_time datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
create_by bigint(0) NULL DEFAULT NULL COMMENT '创建人',
update_time datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
update_by bigint(0) NULL DEFAULT NULL COMMENT '更新人',
del_flag int(0) NULL DEFAULT 0 COMMENT '删除标志',
PRIMARY KEY (id) USING BTREE
) ENGINE = InnoDB COMMENT = '测试树表';
INSERT INTO test_demo VALUES (1, '000000', 102, 4, 1, '测试数据权限', '测试', 0, 103, sysdate(), 1, NULL, NULL, 0);
INSERT INTO test_demo VALUES (2, '000000', 102, 3, 2, '子节点1', '111', 0, 103, sysdate(), 1, NULL, NULL, 0);
INSERT INTO test_demo VALUES (3, '000000', 102, 3, 3, '子节点2', '222', 0, 103, sysdate(), 1, NULL, NULL, 0);
INSERT INTO test_demo VALUES (4, '000000', 108, 4, 4, '测试数据', 'demo', 0, 103, sysdate(), 1, NULL, NULL, 0);
INSERT INTO test_demo VALUES (5, '000000', 108, 3, 13, '子节点11', '1111', 0, 103, sysdate(), 1, NULL, NULL, 0);
INSERT INTO test_demo VALUES (6, '000000', 108, 3, 12, '子节点22', '2222', 0, 103, sysdate(), 1, NULL, NULL, 0);
INSERT INTO test_demo VALUES (7, '000000', 108, 3, 11, '子节点33', '3333', 0, 103, sysdate(), 1, NULL, NULL, 0);
INSERT INTO test_demo VALUES (8, '000000', 108, 3, 10, '子节点44', '4444', 0, 103, sysdate(), 1, NULL, NULL, 0);
INSERT INTO test_demo VALUES (9, '000000', 108, 3, 9, '子节点55', '5555', 0, 103, sysdate(), 1, NULL, NULL, 0);
INSERT INTO test_demo VALUES (10, '000000', 108, 3, 8, '子节点66', '6666', 0, 103, sysdate(), 1, NULL, NULL, 0);
INSERT INTO test_demo VALUES (11, '000000', 108, 3, 7, '子节点77', '7777', 0, 103, sysdate(), 1, NULL, NULL, 0);
INSERT INTO test_demo VALUES (12, '000000', 108, 3, 6, '子节点88', '8888', 0, 103, sysdate(), 1, NULL, NULL, 0);
INSERT INTO test_demo VALUES (13, '000000', 108, 3, 5, '子节点99', '9999', 0, 103, sysdate(), 1, NULL, NULL, 0);
INSERT INTO test_tree VALUES (1, '000000', 0, 102, 4, '测试数据权限', 0, 103, sysdate(), 1, NULL, NULL, 0);
INSERT INTO test_tree VALUES (2, '000000', 1, 102, 3, '子节点1', 0, 103, sysdate(), 1, NULL, NULL, 0);
INSERT INTO test_tree VALUES (3, '000000', 2, 102, 3, '子节点2', 0, 103, sysdate(), 1, NULL, NULL, 0);
INSERT INTO test_tree VALUES (4, '000000', 0, 108, 4, '测试树1', 0, 103, sysdate(), 1, NULL, NULL, 0);
INSERT INTO test_tree VALUES (5, '000000', 4, 108, 3, '子节点11', 0, 103, sysdate(), 1, NULL, NULL, 0);
INSERT INTO test_tree VALUES (6, '000000', 4, 108, 3, '子节点22', 0, 103, sysdate(), 1, NULL, NULL, 0);
INSERT INTO test_tree VALUES (7, '000000', 4, 108, 3, '子节点33', 0, 103, sysdate(), 1, NULL, NULL, 0);
INSERT INTO test_tree VALUES (8, '000000', 5, 108, 3, '子节点44', 0, 103, sysdate(), 1, NULL, NULL, 0);
INSERT INTO test_tree VALUES (9, '000000', 6, 108, 3, '子节点55', 0, 103, sysdate(), 1, NULL, NULL, 0);
INSERT INTO test_tree VALUES (10, '000000', 7, 108, 3, '子节点66', 0, 103, sysdate(), 1, NULL, NULL, 0);
INSERT INTO test_tree VALUES (11, '000000', 7, 108, 3, '子节点77', 0, 103, sysdate(), 1, NULL, NULL, 0);
INSERT INTO test_tree VALUES (12, '000000', 10, 108, 3, '子节点88', 0, 103, sysdate(), 1, NULL, NULL, 0);
INSERT INTO test_tree VALUES (13, '000000', 10, 108, 3, '子节点99', 0, 103, sysdate(), 1, NULL, NULL, 0);

14
im-admin-ui/.env.development

@ -1,5 +1,5 @@
# 页面标题
VITE_APP_TITLE = RuoYi-Vue-Plus多租户管理系统
VITE_APP_TITLE = 盒子IM后台管理系统
# 开发环境配置
VITE_APP_ENV = 'development'
@ -10,20 +10,8 @@ VITE_APP_BASE_API = '/dev-api'
# 应用访问路径 例如使用前缀 /admin/
VITE_APP_CONTEXT_PATH = '/'
# 监控地址
VITE_APP_MONITOR_ADMIN = 'http://localhost:9090/admin/applications'
# SnailJob 控制台地址
VITE_APP_SNAILJOB_ADMIN = 'http://localhost:8800/snail-job'
VITE_APP_PORT = 80
# 接口加密功能开关(如需关闭 后端也必须对应关闭)
VITE_APP_ENCRYPT = true
# 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换
VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
# 接口响应解密 RSA 私钥与后端加密公钥对应 如更换需前后端一同更换
VITE_APP_RSA_PRIVATE_KEY = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE='
# 客户端id
VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e'

12
im-admin-ui/bin/build.bat

@ -1,12 +0,0 @@
@echo off
echo.
echo [信息] 打包Web工程,生成dist文件。
echo.
%~d0
cd %~dp0
cd ..
yarn build:prod
pause

12
im-admin-ui/bin/package.bat

@ -1,12 +0,0 @@
@echo off
echo.
echo [信息] 安装Web工程,生成node_modules文件。
echo.
%~d0
cd %~dp0
cd ..
yarn --registry=https://registry.npmmirror.com
pause

12
im-admin-ui/bin/run-web.bat

@ -1,12 +0,0 @@
@echo off
echo.
echo [信息] 使用 Vite 命令运行 Web 工程。
echo.
%~d0
cd %~dp0
cd ..
yarn dev
pause

12
im-admin-ui/package.json

@ -1,8 +1,8 @@
{
"name": "ruoyi-vue-plus",
"version": "5.2.3",
"description": "RuoYi-Vue-Plus多租户管理系统",
"author": "LionLi",
"name": "盒子IM",
"version": "3.0",
"description": "盒子IM后台管理系统",
"author": "Blue",
"license": "MIT",
"type": "module",
"scripts": {
@ -13,10 +13,6 @@
"lint:eslint": "eslint --fix --ext .ts,.js,.vue ./src ",
"prettier": "prettier --write ."
},
"repository": {
"type": "git",
"url": "https://gitee.com/JavaLionLi/plus-ui.git"
},
"dependencies": {
"@element-plus/icons-vue": "2.3.1",
"@highlightjs/vue-plugin": "2.1.0",

BIN
im-admin-ui/public/favicon.ico

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 34 KiB

62
im-admin-ui/src/api/demo/demo/index.ts

@ -1,62 +0,0 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { DemoVO, DemoForm, DemoQuery } from '@/api/demo/demo/types';
/**
*
* @param query
* @returns {*}
*/
export const listDemo = (query?: DemoQuery): AxiosPromise<DemoVO[]> => {
return request({
url: '/demo/demo/list',
method: 'get',
params: query
});
};
/**
*
* @param id
*/
export const getDemo = (id: string | number): AxiosPromise<DemoVO> => {
return request({
url: '/demo/demo/' + id,
method: 'get'
});
};
/**
*
* @param data
*/
export const addDemo = (data: DemoForm) => {
return request({
url: '/demo/demo',
method: 'post',
data: data
});
};
/**
*
* @param data
*/
export const updateDemo = (data: DemoForm) => {
return request({
url: '/demo/demo',
method: 'put',
data: data
});
};
/**
*
* @param id
*/
export const delDemo = (id: string | number | Array<string | number>) => {
return request({
url: '/demo/demo/' + id,
method: 'delete'
});
};

90
im-admin-ui/src/api/demo/demo/types.ts

@ -1,90 +0,0 @@
export interface DemoVO {
/**
*
*/
id: string | number;
/**
* id
*/
deptId: string | number;
/**
* id
*/
userId: string | number;
/**
*
*/
orderNum: number;
/**
* key键
*/
testKey: string;
/**
*
*/
value: string;
}
export interface DemoForm extends BaseEntity {
/**
*
*/
id?: string | number;
/**
* id
*/
deptId?: string | number;
/**
* id
*/
userId?: string | number;
/**
*
*/
orderNum?: number;
/**
* key键
*/
testKey?: string;
/**
*
*/
value?: string;
}
export interface DemoQuery extends PageQuery {
/**
* id
*/
deptId?: string | number;
/**
* id
*/
userId?: string | number;
/**
*
*/
orderNum?: number;
/**
* key键
*/
testKey?: string;
/**
*
*/
value?: string;
}

62
im-admin-ui/src/api/demo/tree/index.ts

@ -1,62 +0,0 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { TreeVO, TreeForm, TreeQuery } from '@/api/demo/tree/types';
/**
*
* @param query
* @returns {*}
*/
export const listTree = (query?: TreeQuery): AxiosPromise<TreeVO[]> => {
return request({
url: '/demo/tree/list',
method: 'get',
params: query
});
};
/**
*
* @param id
*/
export const getTree = (id: string | number): AxiosPromise<TreeVO> => {
return request({
url: '/demo/tree/' + id,
method: 'get'
});
};
/**
*
* @param data
*/
export const addTree = (data: TreeForm) => {
return request({
url: '/demo/tree',
method: 'post',
data: data
});
};
/**
*
* @param data
*/
export const updateTree = (data: TreeForm) => {
return request({
url: '/demo/tree',
method: 'put',
data: data
});
};
/**
*
* @param id
*/
export const delTree = (id: string | number | Array<string | number>) => {
return request({
url: '/demo/tree/' + id,
method: 'delete'
});
};

80
im-admin-ui/src/api/demo/tree/types.ts

@ -1,80 +0,0 @@
export interface TreeVO {
/**
*
*/
id: string | number;
/**
* id
*/
parentId: string | number;
/**
* id
*/
deptId: string | number;
/**
* id
*/
userId: string | number;
/**
*
*/
treeName: string;
/**
*
*/
children: TreeVO[];
}
export interface TreeForm extends BaseEntity {
/**
*
*/
id?: string | number;
/**
* id
*/
parentId?: string | number;
/**
* id
*/
deptId?: string | number;
/**
* id
*/
userId?: string | number;
/**
*
*/
treeName?: string;
}
export interface TreeQuery {
/**
* id
*/
parentId?: string | number;
/**
* id
*/
deptId?: string | number;
/**
* id
*/
userId?: string | number;
/**
*
*/
treeName?: string;
}

63
im-admin-ui/src/api/workflow/category/index.ts

@ -1,63 +0,0 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { CategoryVO, CategoryForm, CategoryQuery } from '@/api/workflow/category/types';
/**
*
* @param query
* @returns {*}
*/
export const listCategory = (query?: CategoryQuery): AxiosPromise<CategoryVO[]> => {
return request({
url: '/workflow/category/list',
method: 'get',
params: query
});
};
/**
*
* @param id
*/
export const getCategory = (id: string | number): AxiosPromise<CategoryVO> => {
return request({
url: '/workflow/category/' + id,
method: 'get'
});
};
/**
*
* @param data
*/
export const addCategory = (data: CategoryForm) => {
return request({
url: '/workflow/category',
method: 'post',
data: data
});
};
/**
*
* @param data
*/
export const updateCategory = (data: CategoryForm) => {
return request({
url: '/workflow/category',
method: 'put',
data: data
});
};
/**
*
* @param id
*/
export const delCategory = (id: string | number | Array<string | number>) => {
return request({
url: '/workflow/category/' + id,
method: 'delete'
});
};

67
im-admin-ui/src/api/workflow/category/types.ts

@ -1,67 +0,0 @@
export interface CategoryVO {
/**
*
*/
id: string;
/**
*
*/
categoryName: string;
/**
*
*/
categoryCode: string;
/**
* id
*/
parentId: string | number;
/**
*
*/
sortNum: number;
children?: CategoryVO[];
}
export interface CategoryForm extends BaseEntity {
/**
*
*/
id?: string | number;
/**
*
*/
categoryName?: string;
/**
*
*/
categoryCode?: string;
/**
* id
*/
parentId?: string | number;
/**
*
*/
sortNum?: number;
}
export interface CategoryQuery extends PageQuery {
/**
*
*/
categoryName?: string;
/**
*
*/
categoryCode?: string;
}

49
im-admin-ui/src/api/workflow/definitionConfig/index.ts

@ -1,49 +0,0 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { DefinitionConfigVO, DefinitionConfigForm } from '@/api/workflow/definitionConfig/types';
/**
*
* @param definitionId
*/
export const getByDefId = (definitionId: string | number): AxiosPromise<DefinitionConfigVO> => {
return request({
url: '/workflow/definitionConfig/getByDefId/' + definitionId,
method: 'get'
});
};
/**
*
* @param data
*/
export const saveOrUpdate = (data: DefinitionConfigForm) => {
return request({
url: '/workflow/definitionConfig/saveOrUpdate',
method: 'post',
data: data
});
};
/**
*
* @param id
*/
export const deldefinitionConfig = (id: string | number | Array<string | number>) => {
return request({
url: '/workflow/definitionConfig/' + id,
method: 'delete'
});
};
/**
*
* @param tableName
* @param definitionId
*/
export const getByTableNameNotDefId = (tableName: string, definitionId: string | number) => {
return request({
url: `/workflow/definitionConfig/getByTableNameNotDefId/${tableName}/${definitionId}`,
method: 'get'
});
};

102
im-admin-ui/src/api/workflow/definitionConfig/types.ts

@ -1,102 +0,0 @@
import { FormManageVO } from '@/api/workflow/formManage/types';
export interface DefinitionConfigVO {
/**
*
*/
id: string | number;
/**
*
*/
tableName?: string;
/**
* ID
*/
definitionId: string | number;
/**
* KEY
*/
processKey: string;
/**
*
*/
version?: string | number;
/**
*
*/
remark: string;
/**
*
*/
wfFormManageVo: FormManageVO;
}
export interface DefinitionConfigForm extends BaseEntity {
/**
*
*/
id?: string | number;
/**
*
*/
tableName?: string;
/**
* ID
*/
definitionId?: string | number;
/**
* KEY
*/
processKey?: string;
/**
*
*/
version?: string | number;
/**
*
*/
remark?: string;
/**
*
*/
wfFormManageVo?: FormManageVO;
}
export interface DefinitionConfigQuery extends PageQuery {
/**
*
*/
tableName?: string;
/**
* ID
*/
definitionId?: string | number;
/**
* KEY
*/
processKey?: string;
/**
*
*/
version?: string | number;
/**
*
*/
wfFormManageVo: FormManageVO;
}

76
im-admin-ui/src/api/workflow/formManage/index.ts

@ -1,76 +0,0 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { FormManageVO, FormManageForm, FormManageQuery } from '@/api/workflow/formManage/types';
/**
*
* @param query
* @returns {*}
*/
export const listFormManage = (query?: FormManageQuery): AxiosPromise<FormManageVO[]> => {
return request({
url: '/workflow/formManage/list',
method: 'get',
params: query
});
};
/**
*
* @param query
* @returns {*}
*/
export const selectListFormManage = (): AxiosPromise<FormManageVO[]> => {
return request({
url: '/workflow/formManage/list/selectList',
method: 'get'
});
};
/**
*
* @param id
*/
export const getFormManage = (id: string | number): AxiosPromise<FormManageVO> => {
return request({
url: '/workflow/formManage/' + id,
method: 'get'
});
};
/**
*
* @param data
*/
export const addFormManage = (data: FormManageForm) => {
return request({
url: '/workflow/formManage',
method: 'post',
data: data
});
};
/**
*
* @param data
*/
export const updateFormManage = (data: FormManageForm) => {
return request({
url: '/workflow/formManage',
method: 'put',
data: data
});
};
/**
*
* @param id
*/
export const delFormManage = (id: string | number | Array<string | number>) => {
return request({
url: '/workflow/formManage/' + id,
method: 'delete'
});
};

69
im-admin-ui/src/api/workflow/formManage/types.ts

@ -1,69 +0,0 @@
export interface FormManageVO {
/**
*
*/
id: string | number;
/**
*
*/
formName: string;
/**
*
*/
formType: string;
/**
*
*/
formTypeName: string;
/**
* /ID
*/
router: string;
/**
*
*/
remark: string;
}
export interface FormManageForm extends BaseEntity {
/**
*
*/
id?: string | number;
/**
*
*/
formName?: string;
/**
*
*/
formType?: string;
/**
* /ID
*/
router?: string;
/**
*
*/
remark?: string;
}
export interface FormManageQuery extends PageQuery {
/**
*
*/
formName?: string;
/**
*
*/
formType?: string;
}

63
im-admin-ui/src/api/workflow/leave/index.ts

@ -1,63 +0,0 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { LeaveVO, LeaveQuery, LeaveForm } from '@/api/workflow/leave/types';
/**
*
* @param query
* @returns {*}
*/
export const listLeave = (query?: LeaveQuery): AxiosPromise<LeaveVO[]> => {
return request({
url: '/workflow/leave/list',
method: 'get',
params: query
});
};
/**
*
* @param id
*/
export const getLeave = (id: string | number): AxiosPromise<LeaveVO> => {
return request({
url: '/workflow/leave/' + id,
method: 'get'
});
};
/**
*
* @param data
*/
export const addLeave = (data: LeaveForm): AxiosPromise<LeaveVO> => {
return request({
url: '/workflow/leave',
method: 'post',
data: data
});
};
/**
*
* @param data
*/
export const updateLeave = (data: LeaveForm): AxiosPromise<LeaveVO> => {
return request({
url: '/workflow/leave',
method: 'put',
data: data
});
};
/**
*
* @param id
*/
export const delLeave = (id: string | number | Array<string | number>) => {
return request({
url: '/workflow/leave/' + id,
method: 'delete'
});
};

24
im-admin-ui/src/api/workflow/leave/types.ts

@ -1,24 +0,0 @@
export interface LeaveVO {
id: string | number;
leaveType: string;
startDate: string;
endDate: string;
leaveDays: number;
remark: string;
status?: string;
}
export interface LeaveForm extends BaseEntity {
id?: string | number;
leaveType?: string;
startDate?: string;
endDate?: string;
leaveDays?: number;
remark?: string;
status?: string;
}
export interface LeaveQuery extends PageQuery {
startLeaveDays?: number;
endLeaveDays?: number;
}

104
im-admin-ui/src/api/workflow/model/index.ts

@ -1,104 +0,0 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { ModelForm, ModelQuery, ModelVO } from '@/api/workflow/model/types';
/**
*
* @param query
* @returns {*}
*/
export const listModel = (query: ModelQuery): AxiosPromise<ModelVO[]> => {
return request({
url: '/workflow/model/list',
method: 'get',
params: query
});
};
/**
*
* @param query
* @returns {*}
*/
export const getInfo = (id: string): AxiosPromise<ModelForm> => {
return request({
url: '/workflow/model/getInfo/' + id,
method: 'get'
});
};
/**
*
* @param data
* @returns {*}
*/
export const addModel = (data: ModelForm): AxiosPromise<void> => {
return request({
url: '/workflow/model/save',
method: 'post',
data: data
});
};
/**
*
* @param data
* @returns {*}
*/
export function update(data: ModelForm): AxiosPromise<void> {
return request({
url: '/workflow/model/update',
method: 'put',
data: data
});
}
/**
*
* @param data
* @returns {*}
*/
export function editModelXml(data: ModelForm): AxiosPromise<void> {
return request({
url: '/workflow/model/editModelXml',
method: 'put',
data: data
});
}
/**
* id删除模型
* @returns {*}
* @param id id
*/
export function delModel(id: string | string[]): AxiosPromise<void> {
return request({
url: '/workflow/model/' + id,
method: 'delete'
});
}
/**
*
* @returns {*}
* @param id id
*/
export const modelDeploy = (id: string): AxiosPromise<void> => {
return request({
url: `/workflow/model/modelDeploy/${id}`,
method: 'post'
});
};
/**
*
* @param data
* @returns {*}
*/
export const copyModel = (data: ModelForm): AxiosPromise<void> => {
return request({
url: '/workflow/model/copyModel',
method: 'post',
data: data
});
};

66
im-admin-ui/src/api/workflow/model/types.ts

@ -1,66 +0,0 @@
export interface ModelForm {
id: string;
name: string;
key: string;
categoryCode: string;
xml: string;
svg: string;
description: string;
}
export interface ModelQuery extends PageQuery {
name?: string;
key?: string;
categoryCode?: string;
}
export interface OriginalPersistentState {
metaInfo: string;
editorSourceValueId: string;
createTime: string;
deploymentId?: string;
name: string;
tenantId: string;
category?: string;
version: number;
editorSourceExtraValueId?: string;
key: string;
lastUpdateTime: string;
}
export interface PersistentState {
metaInfo: string;
editorSourceValueId: string;
createTime: string;
deploymentId?: string;
name: string;
tenantId: string;
category?: string;
version: number;
editorSourceExtraValueId?: string;
key: string;
lastUpdateTime: string;
}
export interface ModelVO {
id: string;
revision: number;
originalPersistentState: OriginalPersistentState;
name: string;
key: string;
category?: string;
createTime: string;
lastUpdateTime: string;
version: number;
metaInfo: string;
deploymentId?: string;
editorSourceValueId: string;
editorSourceExtraValueId?: string;
tenantId: string;
persistentState: PersistentState;
revisionNext: number;
idPrefix: string;
inserted: boolean;
updated: boolean;
deleted: boolean;
}

38
im-admin-ui/src/api/workflow/nodeConfig/types.ts

@ -1,38 +0,0 @@
import { FormManageVO } from '@/api/workflow/formManage/types';
export interface NodeConfigVO {
/**
*
*/
id: string | number;
/**
* id
*/
formId: string | number;
/**
*
*/
formType: string;
/**
*
*/
nodeName: string;
/**
* id
*/
nodeId: string | number;
/**
* id
*/
definitionId: string | number;
/**
*
*/
wfFormManageVo: FormManageVO;
}

114
im-admin-ui/src/api/workflow/processDefinition/index.ts

@ -1,114 +0,0 @@
import request from '@/utils/request';
import { ProcessDefinitionQuery, ProcessDefinitionVO, definitionXmlVO } from '@/api/workflow/processDefinition/types';
import { AxiosPromise } from 'axios';
/**
*
* @param query id
* @returns
*/
export const listProcessDefinition = (query: ProcessDefinitionQuery): AxiosPromise<ProcessDefinitionVO[]> => {
return request({
url: `/workflow/processDefinition/list`,
method: 'get',
params: query
});
};
/**
* key获取流程定义
* @param processInstanceId id
* @returns
*/
export const getListByKey = (key: string) => {
return request({
url: `/workflow/processDefinition/getListByKey/${key}`,
method: 'get'
});
};
/**
* id获取流程图
*/
export const definitionImage = (processDefinitionId: string): AxiosPromise<any> => {
return request({
url: `/workflow/processDefinition/definitionImage/${processDefinitionId}` + '?t' + Math.random(),
method: 'get'
});
};
/**
* id获取xml
* @param processDefinitionId id
* @returns
*/
export const definitionXml = (processDefinitionId: string): AxiosPromise<definitionXmlVO> => {
return request({
url: `/workflow/processDefinition/definitionXml/${processDefinitionId}`,
method: 'get'
});
};
/**
*
* @param deploymentId id
* @param processDefinitionId id
* @returns
*/
export const deleteProcessDefinition = (deploymentId: string | string[], processDefinitionId: string | string[]) => {
return request({
url: `/workflow/processDefinition/${deploymentId}/${processDefinitionId}`,
method: 'delete'
});
};
/**
* /
* @param processDefinitionId id
* @returns
*/
export const updateDefinitionState = (processDefinitionId: string) => {
return request({
url: `/workflow/processDefinition/updateDefinitionState/${processDefinitionId}`,
method: 'put'
});
};
/**
*
* @param processDefinitionId id
* @returns
*/
export const convertToModel = (processDefinitionId: string) => {
return request({
url: `/workflow/processDefinition/convertToModel/${processDefinitionId}`,
method: 'put'
});
};
/**
* zip或xml部署流程定义
* @returns
*/
export function deployProcessFile(data: any) {
return request({
url: '/workflow/processDefinition/deployByFile',
method: 'post',
data: data,
headers: {
repeatSubmit: false
}
});
}
/**
*
* @param currentProcessDefinitionId
* @param fromProcessDefinitionId
* @returns
*/
export const migrationDefinition = (currentProcessDefinitionId: string, fromProcessDefinitionId: string) => {
return request({
url: `/workflow/processDefinition/migrationDefinition/${currentProcessDefinitionId}/${fromProcessDefinitionId}`,
method: 'put'
});
};

24
im-admin-ui/src/api/workflow/processDefinition/types.ts

@ -1,24 +0,0 @@
import { DefinitionConfigVO } from '@/api/workflow/definitionConfig/types';
export interface ProcessDefinitionQuery extends PageQuery {
key?: string;
name?: string;
categoryCode?: string;
}
export interface ProcessDefinitionVO extends BaseEntity {
id: string;
name: string;
key: string;
version: number;
suspensionState: number;
resourceName: string;
diagramResourceName: string;
deploymentId: string;
deploymentTime: string;
wfDefinitionConfigVo: DefinitionConfigVO;
}
export interface definitionXmlVO {
xml: string[];
xmlStr: string;
}

136
im-admin-ui/src/api/workflow/processInstance/index.ts

@ -1,136 +0,0 @@
import request from '@/utils/request';
import { ProcessInstanceQuery, ProcessInstanceVO } from '@/api/workflow/processInstance/types';
import { AxiosPromise } from 'axios';
/**
*
* @param query
* @returns {*}
*/
export const getPageByRunning = (query: ProcessInstanceQuery): AxiosPromise<ProcessInstanceVO[]> => {
return request({
url: '/workflow/processInstance/getPageByRunning',
method: 'get',
params: query
});
};
/**
*
* @param query
* @returns {*}
*/
export const getPageByFinish = (query: ProcessInstanceQuery): AxiosPromise<ProcessInstanceVO[]> => {
return request({
url: '/workflow/processInstance/getPageByFinish',
method: 'get',
params: query
});
};
/**
* id获取历史流程图
*/
export const getHistoryImage = (businessKey: string) => {
return request({
url: `/workflow/processInstance/getHistoryImage/${businessKey}` + '?t' + Math.random(),
method: 'get'
});
};
/**
* id获取历史流程图运行中
*/
export const getHistoryList = (businessKey: string): AxiosPromise<Record<string, any>> => {
return request({
url: `/workflow/processInstance/getHistoryList/${businessKey}` + '?t' + Math.random(),
method: 'get'
});
};
/**
*
* @param businessKey id
* @returns
*/
export const getHistoryRecord = (businessKey: string | number) => {
return request({
url: `/workflow/processInstance/getHistoryRecord/${businessKey}`,
method: 'get'
});
};
/**
*
* @param data
* @returns
*/
export const deleteRunInstance = (data: object) => {
return request({
url: `/workflow/processInstance/deleteRunInstance`,
method: 'post',
data: data
});
};
/**
*
* @param businessKey id
* @returns
*/
export const deleteRunAndHisInstance = (businessKey: string | string[]) => {
return request({
url: `/workflow/processInstance/deleteRunAndHisInstance/${businessKey}`,
method: 'delete'
});
};
/**
*
* @param businessKey id
* @returns
*/
export const deleteFinishAndHisInstance = (businessKey: string | string[]) => {
return request({
url: `/workflow/processInstance/deleteFinishAndHisInstance/${businessKey}`,
method: 'delete'
});
};
/**
*
* @param query
* @returns {*}
*/
export const getPageByCurrent = (query: ProcessInstanceQuery): AxiosPromise<ProcessInstanceVO[]> => {
return request({
url: '/workflow/processInstance/getPageByCurrent',
method: 'get',
params: query
});
};
/**
*
* @param businessKey id
* @returns
*/
export const cancelProcessApply = (businessKey: string) => {
return request({
url: `/workflow/processInstance/cancelProcessApply/${businessKey}`,
method: 'post'
});
};
export default {
getPageByRunning,
getPageByFinish,
getHistoryImage,
getHistoryList,
getHistoryRecord,
deleteRunInstance,
deleteRunAndHisInstance,
deleteFinishAndHisInstance,
getPageByCurrent,
cancelProcessApply
};

27
im-admin-ui/src/api/workflow/processInstance/types.ts

@ -1,27 +0,0 @@
import { TaskVO } from '@/api/workflow/task/types';
export interface ProcessInstanceQuery extends PageQuery {
categoryCode?: string;
name?: string;
key?: string;
startUserId?: string;
businessKey?: string;
}
export interface ProcessInstanceVO extends BaseEntity {
id: string;
processDefinitionId: string;
processDefinitionName: string;
processDefinitionKey: string;
processDefinitionVersion: string;
deploymentId: string;
businessKey: string;
isSuspended?: any;
tenantId: string;
startTime: string;
endTime?: string;
startUserId: string;
businessStatus: string;
businessStatusName: string;
taskVoList: TaskVO[];
}

264
im-admin-ui/src/api/workflow/task/index.ts

@ -1,264 +0,0 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { TaskQuery, TaskVO } from '@/api/workflow/task/types';
/**
*
* @param query
* @returns {*}
*/
export const getPageByTaskWait = (query: TaskQuery): AxiosPromise<TaskVO[]> => {
return request({
url: '/workflow/task/getPageByTaskWait',
method: 'get',
params: query
});
};
/**
*
* @param query
* @returns {*}
*/
export const getPageByTaskFinish = (query: TaskQuery): AxiosPromise<TaskVO[]> => {
return request({
url: '/workflow/task/getPageByTaskFinish',
method: 'get',
params: query
});
};
/**
*
* @param query
* @returns {*}
*/
export const getPageByTaskCopy = (query: TaskQuery): AxiosPromise<TaskVO[]> => {
return request({
url: '/workflow/task/getPageByTaskCopy',
method: 'get',
params: query
});
};
/**
*
* @param query
* @returns {*}
*/
export const getPageByAllTaskWait = (query: TaskQuery): AxiosPromise<TaskVO[]> => {
return request({
url: '/workflow/task/getPageByAllTaskWait',
method: 'get',
params: query
});
};
/**
*
* @param query
* @returns {*}
*/
export const getPageByAllTaskFinish = (query: TaskQuery): AxiosPromise<TaskVO[]> => {
return request({
url: '/workflow/task/getPageByAllTaskFinish',
method: 'get',
params: query
});
};
/**
*
* @param data
* @returns {*}
*/
export const startWorkFlow = (data: object): any => {
return request({
url: '/workflow/task/startWorkFlow',
method: 'post',
data: data
});
};
/**
*
* @param data
* @returns {*}
*/
export const completeTask = (data: object) => {
return request({
url: '/workflow/task/completeTask',
method: 'post',
data: data
});
};
/**
*
* @param taskId
* @returns {*}
*/
export const claim = (taskId: string): any => {
return request({
url: '/workflow/task/claim/' + taskId,
method: 'post'
});
};
/**
*
* @param taskId
* @returns {*}
*/
export const returnTask = (taskId: string): any => {
return request({
url: '/workflow/task/returnTask/' + taskId,
method: 'post'
});
};
/**
*
* @param data
* @returns {*}
*/
export const backProcess = (data: any): any => {
return request({
url: '/workflow/task/backProcess',
method: 'post',
data: data
});
};
/**
*
* @param taskId
* @returns
*/
export const getTaskById = (taskId: string) => {
return request({
url: '/workflow/task/getTaskById/' + taskId,
method: 'get'
});
};
/**
*
* @param data
* @returns
*/
export const addMultiInstanceExecution = (data: any) => {
return request({
url: '/workflow/task/addMultiInstanceExecution',
method: 'post',
data: data
});
};
/**
*
* @param data
* @returns
*/
export const deleteMultiInstanceExecution = (data: any) => {
return request({
url: '/workflow/task/deleteMultiInstanceExecution',
method: 'post',
data: data
});
};
/**
*
* @param taskIds
* @param userId
* @returns
*/
export const updateAssignee = (taskIds: Array<string>, userId: string) => {
return request({
url: `/workflow/task/updateAssignee/${taskIds}/${userId}`,
method: 'put'
});
};
/**
*
* @returns
*/
export const transferTask = (data: any) => {
return request({
url: `/workflow/task/transferTask`,
method: 'post',
data: data
});
};
/**
*
* @returns
*/
export const terminationTask = (data: any) => {
return request({
url: `/workflow/task/terminationTask`,
method: 'post',
data: data
});
};
/**
*
* @returns
*/
export const getInstanceVariable = (taskId: string) => {
return request({
url: `/workflow/task/getInstanceVariable/${taskId}`,
method: 'get'
});
};
/**
*
* @returns
*/
export const getTaskNodeList = (processInstanceId: string) => {
return request({
url: `/workflow/task/getTaskNodeList/${processInstanceId}`,
method: 'get'
});
};
/**
*
* @returns
*/
export const delegateTask = (data: any) => {
return request({
url: `/workflow/task/delegateTask`,
method: 'post',
data: data
});
};
/**
*
* @param taskId
* @returns {*}
*/
export const getTaskUserIdsByAddMultiInstance = (taskId: string) => {
return request({
url: '/workflow/task/getTaskUserIdsByAddMultiInstance/' + taskId,
method: 'get'
});
};
/**
*
* @param taskId
* @returns {*}
*/
export const getListByDeleteMultiInstance = (taskId: string) => {
return request({
url: '/workflow/task/getListByDeleteMultiInstance/' + taskId,
method: 'get'
});
};

49
im-admin-ui/src/api/workflow/task/types.ts

@ -1,49 +0,0 @@
import { NodeConfigVO } from '@/api/workflow/nodeConfig/types';
import { DefinitionConfigVO } from '@/api/workflow/definitionConfig/types';
export interface TaskQuery extends PageQuery {
name?: string;
processDefinitionKey?: string;
processDefinitionName?: string;
}
export interface ParticipantVo {
groupIds?: string[] | number[];
candidate: string[] | number[];
candidateName: string[];
claim: boolean;
}
export interface TaskVO extends BaseEntity {
id: string;
name: string;
description?: string;
priority: number;
owner?: string;
assignee?: string | number;
assigneeName?: string;
processInstanceId: string;
executionId: string;
taskDefinitionId?: any;
processDefinitionId: string;
endTime?: string;
taskDefinitionKey: string;
dueDate?: string;
category?: any;
parentTaskId?: any;
tenantId: string;
claimTime?: string;
businessStatus?: string;
businessStatusName?: string;
processDefinitionName?: string;
processDefinitionKey?: string;
participantVo?: ParticipantVo;
multiInstance?: boolean;
businessKey?: string;
wfNodeConfigVo?: NodeConfigVO;
wfDefinitionConfigVo?: DefinitionConfigVO;
}
export interface VariableVo {
key: string;
value: string;
}

29
im-admin-ui/src/api/workflow/workflowCommon/index.ts

@ -1,29 +0,0 @@
import { RouterJumpVo } from '@/api/workflow/workflowCommon/types';
export default {
routerJump(routerJumpVo: RouterJumpVo, proxy) {
if (routerJumpVo.wfNodeConfigVo && routerJumpVo.wfNodeConfigVo.formType === 'static' && routerJumpVo.wfNodeConfigVo.wfFormManageVo) {
proxy.$tab.closePage(proxy.$route);
proxy.$router.push({
path: `${routerJumpVo.wfNodeConfigVo.wfFormManageVo.router}`,
query: {
id: routerJumpVo.businessKey,
type: routerJumpVo.type,
taskId: routerJumpVo.taskId
}
});
} else if (routerJumpVo.wfNodeConfigVo && routerJumpVo.wfNodeConfigVo.formType === 'dynamic' && routerJumpVo.wfNodeConfigVo.wfFormManageVo) {
proxy.$tab.closePage(proxy.$route);
proxy.$router.push({
path: `${routerJumpVo.wfNodeConfigVo.wfFormManageVo.router}`,
query: {
id: routerJumpVo.businessKey,
type: routerJumpVo.type,
taskId: routerJumpVo.taskId
}
});
} else {
proxy?.$modal.msgError('请到模型配置菜单!');
}
}
};

16
im-admin-ui/src/api/workflow/workflowCommon/types.ts

@ -1,16 +0,0 @@
import { NodeConfigVO } from '@/api/workflow/nodeConfig/types';
import { DefinitionConfigVO } from '@/api/workflow/definitionConfig/types';
export interface RouterJumpVo {
wfNodeConfigVo: NodeConfigVO;
wfDefinitionConfigVo: DefinitionConfigVO;
businessKey: string;
taskId: string;
type: string;
}
export interface StartProcessBo {
businessKey: string | number;
tableName: string;
variables: any;
}

BIN
im-admin-ui/src/assets/logo/logo.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 34 KiB

23
im-admin-ui/src/bpmn/assets/defaultXML.ts

@ -1,23 +0,0 @@
function generateRandomValue() {
// 生成一个随机数
const randomValue = Math.random().toString(36).slice(2, 12);
return `Process_${randomValue}`;
}
const cartage: string = 'default';
export default `<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:bioc="http://bpmn.io/schema/bpmn/biocolor/1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:flowable="http://flowable.org/bpmn" targetNamespace="http://www.flowable.org/processdef">
<process id="process_${generateRandomValue()}" name="name_${generateRandomValue()}">
<startEvent id="startNode1" name="开始" />
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_flow">
<bpmndi:BPMNPlane id="BPMNPlane_flow" bpmnElement="T-2d89e7a3-ba79-4abd-9f64-ea59621c258c">
<bpmndi:BPMNShape id="BPMNShape_startNode1" bpmnElement="startNode1" bioc:stroke="">
<omgdc:Bounds x="240" y="200" width="30" height="30" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="242" y="237" width="23" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>`;

126
im-admin-ui/src/bpmn/assets/lang/zh.ts

@ -1,126 +0,0 @@
export const NodeName = {
'bpmn:Process': '流程',
'bpmn:StartEvent': '开始事件',
'bpmn:IntermediateThrowEvent': '中间事件',
'bpmn:Task': '任务',
'bpmn:SendTask': '发送任务',
'bpmn:ReceiveTask': '接收任务',
'bpmn:UserTask': '用户任务',
'bpmn:ManualTask': '手工任务',
'bpmn:BusinessRuleTask': '业务规则任务',
'bpmn:ServiceTask': '服务任务',
'bpmn:ScriptTask': '脚本任务',
'bpmn:EndEvent': '结束事件',
'bpmn:SequenceFlow': '流程线',
'bpmn:ExclusiveGateway': '互斥网关',
'bpmn:ParallelGateway': '并行网关',
'bpmn:InclusiveGateway': '相容网关',
'bpmn:ComplexGateway': '复杂网关',
'bpmn:EventBasedGateway': '事件网关',
'bpmn:Participant': '池/参与者',
'bpmn:SubProcess': '子流程',
'bpmn:DataObjectReference': '数据对象引用',
'bpmn:DataStoreReference': '数据存储引用',
'bpmn:Group': '组'
};
export default {
'Activate hand tool': '启动手动工具',
'Activate lasso tool': '启动 Lasso 工具',
'Activate create/remove space tool': '启动创建/删除空间工具',
'Activate global connect tool': '启动全局连接工具',
'Ad-hoc': 'Ad-hoc',
'Add lane above': '在上方添加泳道',
'Add lane below': '在下方添加泳道',
'Business rule task': '规则任务',
'Call activity': '引用流程',
'Compensation end event': '结束补偿事件',
'Compensation intermediate throw event': '中间补偿抛出事件',
'Complex gateway': '复杂网关',
'Conditional intermediate catch event': '中间条件捕获事件',
'Conditional start event (non-interrupting)': '条件启动事件 (非中断)',
'Conditional start event': '条件启动事件',
'Connect using association': '文本关联',
'Connect using sequence/message flow or association': '消息关联',
'Change element': '更改元素',
'Change type': '更改类型',
'Create data object reference': '创建数据对象引用',
'Create data store reference': '创建数据存储引用',
'Create expanded sub-process': '创建可折叠子流程',
'Create pool/participant': '创建池/参与者',
'Collection': '集合',
'Connect using data input association': '数据输入关联',
'Data store reference': '数据存储引用',
'Data object reference': '数据对象引用',
'Divide into two lanes': '分成两个泳道',
'Divide into three lanes': '分成三个泳道',
'End event': '结束事件',
'Error end event': '结束错误事件',
'Escalation end event': '结束升级事件',
'Escalation intermediate throw event': '中间升级抛出事件',
'Event sub-process': '事件子流程',
'Event-based gateway': '事件网关',
'Exclusive gateway': '互斥网关',
'Empty pool/participant (removes content)': '清空池/参与者 (删除内容)',
'Empty pool/participant': '清空池/参与者',
'Expanded pool/participant': '展开池/参与者',
'Inclusive gateway': '相容网关',
'Intermediate throw event': '中间抛出事件',
'Loop': '循环',
'Link intermediate catch event': '中间链接捕获事件',
'Link intermediate throw event': '中间链接抛出事件',
'Manual task': '手动任务',
'Message end event': '结束消息事件',
'Message intermediate catch event': '中间消息捕获事件',
'Message intermediate throw event': '中间消息抛出事件',
'Message start event': '消息启动事件',
'Parallel gateway': '并行网关',
'Parallel multi-instance': '并行多实例',
'Participant multiplicity': '参与者多重性',
'Receive task': '接受任务',
'Remove': '移除',
'Script task': '脚本任务',
'Send task': '发送任务',
'Sequential multi-instance': '串行多实例',
'Service task': '服务任务',
'Signal end event': '结束信号事件',
'Signal intermediate catch event': '中间信号捕获事件',
'Signal intermediate throw event': '中间信号抛出事件',
'Signal start event (non-interrupting)': '信号启动事件 (非中断)',
'Signal start event': '信号启动事件',
'Start event': '开始事件',
'Sub-process (collapsed)': '可折叠子流程',
'Sub-process (expanded)': '可展开子流程',
'Sub rocess': '子流程',
'Task': '任务',
'Transaction': '事务',
'Terminate end event': '终止边界事件',
'Timer intermediate catch event': '中间定时捕获事件',
'Timer start event (non-interrupting)': '定时启动事件 (非中断)',
'Timer start event': '定时启动事件',
'User task': '用户任务',
'Create start event': '创建开始事件',
'Create gateway': '创建网关',
'Create intermediate/boundary event': '创建中间/边界事件',
'Create end event': '创建结束事件',
'Create group': '创建组',
'Create startEvent': '开始节点',
'Create endEvent': '结束节点',
'Create exclusiveGateway': '互斥网关',
'Create parallelGateway': '并行网关',
'Create task': '任务节点',
'Create userTask': '用户任务节点',
'Condition type': '条件类型',
'Append end event': '追加结束事件节点',
'Append gateway': '追加网关节点',
'Append task': '追加任务',
'Append user task': '追加用户任务节点',
'Append text annotation': '追加文本注释',
'Append intermediate/boundary event': '追加中间或边界事件',
'Append receive task': '追加接收任务节点',
'Append message intermediate catch event': '追加中间消息捕获事件',
'Append timer intermediate catch event': '追加中间定时捕获事件',
'Append conditional intermediate catch event': '追加中间条件捕获事件',
'Append signal intermediate catch event': '追加中间信号捕获事件',
'flow elements must be children of pools/participants': '流程元素必须是池/参与者的子元素'
};

1250
im-admin-ui/src/bpmn/assets/moddle/flowable.ts

File diff suppressed because it is too large

138
im-admin-ui/src/bpmn/assets/module/ContextPad/CustomContextPadProvider.ts

@ -1,138 +0,0 @@
import ContextPadProvider from 'bpmn-js/lib/features/context-pad/ContextPadProvider';
import { Injector } from 'didi';
import EventBus from 'diagram-js/lib/core/EventBus';
import ContextPad from 'diagram-js/lib/features/context-pad/ContextPad';
import Modeling from 'bpmn-js/lib/features/modeling/Modeling.js';
import ElementFactory from 'bpmn-js/lib/features/modeling/ElementFactory';
import Connect from 'diagram-js/lib/features/connect/Connect';
import Create from 'diagram-js/lib/features/create/Create';
import PopupMenu from 'diagram-js/lib/features/popup-menu/PopupMenu';
import Canvas from 'diagram-js/lib/core/Canvas';
import Rules from 'diagram-js/lib/features/rules/Rules';
import { Element, Shape } from 'diagram-js/lib/model/Types';
import BpmnFactory from 'bpmn-js/lib/features/modeling/BpmnFactory';
import modeler from '@/store/modules/modeler';
// @Description: 增强元素连线事件
class CustomContextPadProvider extends ContextPadProvider {
private _contextPad: ContextPad;
private _modeling: Modeling;
private _elementFactory: ElementFactory;
private _autoPlace: any;
private _connect: Connect;
private _create: Create;
private _popupMenu: PopupMenu;
private _canvas: Canvas;
private _rules: Rules;
constructor(
config: any,
injector: Injector,
eventBus: EventBus,
contextPad: ContextPad,
modeling: Modeling,
elementFactory: ElementFactory,
connect: Connect,
create: Create,
popupMenu: PopupMenu,
canvas: Canvas,
rules: Rules,
translate
) {
// @ts-expect-error 忽略异常
super(config, injector, eventBus, contextPad, modeling, elementFactory, connect, create, popupMenu, canvas, rules, translate);
this._contextPad = contextPad;
this._modeling = modeling;
this._elementFactory = elementFactory;
this._connect = connect;
this._create = create;
this._popupMenu = popupMenu;
this._canvas = canvas;
this._rules = rules;
this._autoPlace = injector.get('autoPlace', false);
}
getContextPadEntries(element: Element) {
const actions: Record<string, any> = {};
const appendUserTask = (event: Event, element: Shape) => {
const shape = this._elementFactory.createShape({ type: 'bpmn:UserTask' });
this._create.start(event, shape, {
source: element
});
};
const appendMultiInstanceUserTask = (event: Event, element: Shape) => {
const store = modeler();
const bpmnFactory = store.getModeler().get('bpmnFactory') as BpmnFactory;
const businessObject = bpmnFactory.create('bpmn:UserTask', {
// name: '多实例用户任务',
isForCompensation: false
});
businessObject.loopCharacteristics = bpmnFactory.create('bpmn:MultiInstanceLoopCharacteristics');
// 创建 Shape
const shape = this._elementFactory.createShape({
type: 'bpmn:UserTask',
businessObject: businessObject
});
this._create.start(event, shape, { source: element });
};
const appendTask = this._autoPlace
? (event, element) => {
const bpmnFactory: BpmnFactory | undefined = modeler().getModeler().get('bpmnFactory');
const businessObject = bpmnFactory.create('bpmn:UserTask', {
// name: '多实例用户任务',// 右键创建显示
isForCompensation: false
});
// 创建多实例属性并分配给用户任务的 loopCharacteristics
businessObject.loopCharacteristics = bpmnFactory.create('bpmn:MultiInstanceLoopCharacteristics');
// 创建 Shape
const shape = this._elementFactory.createShape({
type: 'bpmn:UserTask',
businessObject: businessObject
});
this._autoPlace.append(element, shape);
}
: appendMultiInstanceUserTask;
const append = this._autoPlace
? (event: Event, element: Shape) => {
const shape = this._elementFactory.createShape({ type: 'bpmn:UserTask' });
this._autoPlace.append(element, shape);
}
: appendUserTask;
// // 添加创建用户任务按钮
actions['append.append-user-task'] = {
group: 'model',
className: 'bpmn-icon-user-task',
title: '用户任务',
action: {
dragstart: appendUserTask,
click: append
}
};
// 添加创建多实例用户任务按钮
actions['append.append-multi-instance-user-task'] = {
group: 'model',
className: 'bpmn-icon-user', // 你可以使用多实例用户任务的图标 bpmn-icon-user bpmn-icon-user-task
title: '多实例用户任务',
action: {
dragstart: appendMultiInstanceUserTask,
click: appendTask
}
};
return actions;
}
}
export default CustomContextPadProvider;

109
im-admin-ui/src/bpmn/assets/module/Palette/CustomPaletteProvider.ts

@ -1,109 +0,0 @@
import { assign } from 'min-dash';
import PaletteProvider from 'bpmn-js/lib/features/palette/PaletteProvider';
import ElementFactory from 'bpmn-js/lib/features/modeling/ElementFactory';
import Create from 'diagram-js/lib/features/create/Create';
import SpaceTool from 'diagram-js/lib/features/space-tool/SpaceTool';
import LassoTool from 'diagram-js/lib/features/lasso-tool/LassoTool';
import HandTool from 'diagram-js/lib/features/hand-tool/HandTool';
import GlobalConnect from 'diagram-js/lib/features/global-connect/GlobalConnect';
import Palette from 'diagram-js/lib/features/palette/Palette';
import modeler from '@/store/modules/modeler';
import BpmnFactory from 'bpmn-js/lib/features/modeling/BpmnFactory';
// @Description: 增强左侧面板
class CustomPaletteProvider extends PaletteProvider {
private readonly _palette: Palette;
private readonly _create: Create;
private readonly _elementFactory: ElementFactory;
private readonly _spaceTool: SpaceTool;
private readonly _lassoTool: LassoTool;
private readonly _handTool: HandTool;
private readonly _globalConnect: GlobalConnect;
private readonly _translate: any;
constructor(palette, create, elementFactory, spaceTool, lassoTool, handTool, globalConnect, translate) {
super(palette, create, elementFactory, spaceTool, lassoTool, handTool, globalConnect, translate);
this._palette = palette;
this._create = create;
this._elementFactory = elementFactory;
this._spaceTool = spaceTool;
this._lassoTool = lassoTool;
this._handTool = handTool;
this._globalConnect = globalConnect;
this._translate = translate;
}
getPaletteEntries() {
const actions = {},
create = this._create,
elementFactory = this._elementFactory,
translate = this._translate;
function createAction(type: string, group: string, className: string, title: string, options?: object) {
function createListener(event) {
const shape = elementFactory.createShape(assign({ type: type }, options));
if (options) {
!shape.businessObject.di && (shape.businessObject.di = {});
shape.businessObject.di.isExpanded = (options as { [key: string]: any }).isExpanded;
}
create.start(event, shape, null);
}
const shortType = type.replace(/^bpmn:/, '');
return {
group: group,
className: className,
title: title || translate('Create {type}', { type: shortType }),
action: {
dragstart: createListener,
click: createListener
}
};
}
function createMultiInstanceUserTask(event) {
const bpmnFactory: BpmnFactory | undefined = modeler().getBpmnFactory();
// 创建一个 bpmn:UserTask
const userTask = bpmnFactory.create('bpmn:UserTask', {
// name: '多实例用户任务', // 在画板中显示字段
isForCompensation: false
});
// 将多实例属性分配给 bpmn:UserTask 的 loopCharacteristics
userTask.loopCharacteristics = bpmnFactory.create('bpmn:MultiInstanceLoopCharacteristics');
const customUserTask = elementFactory.createShape({
type: 'bpmn:UserTask',
businessObject: userTask // 分配创建的 userTask 到 businessObject
});
create.start(event, customUserTask, {});
}
assign(actions, {
'create.parallel-gateway': createAction('bpmn:ParallelGateway', 'gateway', 'bpmn-icon-gateway-parallel', '并行网关'),
'create.event-base-gateway': createAction('bpmn:EventBasedGateway', 'gateway', 'bpmn-icon-gateway-eventbased', '事件网关'),
// 分组线
'gateway-separator': {
group: 'gateway',
separator: true
},
'create.user-task': createAction('bpmn:UserTask', 'activity', 'bpmn-icon-user-task', '创建用户任务'),
'create.multi-instance-user-task': {
group: 'activity',
type: 'bpmn:UserTask',
className: 'bpmn-icon-user task-multi-instance',
title: '创建多实例用户任务',
action: {
click: createMultiInstanceUserTask,
dragstart: createMultiInstanceUserTask
}
},
'task-separator': {
group: 'activity',
separator: true
}
});
return actions;
}
}
CustomPaletteProvider['$inject'] = ['palette', 'create', 'elementFactory', 'spaceTool', 'lassoTool', 'handTool', 'globalConnect', 'translate'];
export default CustomPaletteProvider;

56
im-admin-ui/src/bpmn/assets/module/Renderer/CustomRenderer.ts

@ -1,56 +0,0 @@
import BaseRenderer from 'diagram-js/lib/draw/BaseRenderer';
import {
append as svgAppend,
attr as svgAttr,
create as svgCreate,
select as svgSelect,
selectAll as svgSelectAll,
clone as svgClone,
clear as svgClear,
remove as svgRemove
} from 'tiny-svg';
const HIGH_PRIORITY = 1500;
export default class CustomRenderer extends BaseRenderer {
bpmnRenderer: BaseRenderer;
modeling: any;
constructor(eventBus, bpmnRenderer, modeling) {
super(eventBus, HIGH_PRIORITY);
this.bpmnRenderer = bpmnRenderer;
this.modeling = modeling;
}
canRender(element) {
// ignore labels
return !element.labelTarget;
}
/**
*
* @param {*} parentNode svgNode
* @param {*} element
* @returns
*/
drawShape(parentNode, element) {
const shape = this.bpmnRenderer.drawShape(parentNode, element);
const { type, width, height } = element;
// 开始 填充绿色
if (type === 'bpmn:StartEvent') {
svgAttr(shape, { fill: '#77DF6D' });
return shape;
}
if (type === 'bpmn:EndEvent') {
svgAttr(shape, { fill: '#EE7B77' });
return shape;
}
if (type === 'bpmn:UserTask') {
svgAttr(shape, { fill: '#A9C4F8' });
return shape;
}
return shape;
}
getShapePath(shape) {
return this.bpmnRenderer.getShapePath(shape);
}
}
CustomRenderer['$inject'] = ['eventBus', 'bpmnRenderer'];

15
im-admin-ui/src/bpmn/assets/module/Translate/index.ts

@ -1,15 +0,0 @@
import zh from '../../lang/zh';
const customTranslate = (template: any, replacements: any) => {
replacements = replacements || {};
template = zh[template] || template;
return template.replace(/{([^}]+)}/g, function (_: any, key: any) {
return replacements[key] || '{' + key + '}';
});
};
export const translateModule = {
translate: ['value', customTranslate]
};
export default translateModule;

17
im-admin-ui/src/bpmn/assets/module/index.ts

@ -1,17 +0,0 @@
// 翻译模块
import TranslationModule from './Translate';
import { ModuleDeclaration } from 'didi';
import CustomPaletteProvider from './Palette/CustomPaletteProvider';
import CustomRenderer from './Renderer/CustomRenderer';
import CustomContextPadProvider from './ContextPad/CustomContextPadProvider';
const Module: ModuleDeclaration[] = [
{
__init__: ['customPaletteProvider', 'customContextPadProvider', 'customRenderer'],
customPaletteProvider: ['type', CustomPaletteProvider],
customRenderer: ['type', CustomRenderer],
customContextPadProvider: ['type', CustomContextPadProvider]
},
TranslationModule
];
export default Module;

50
im-admin-ui/src/bpmn/assets/showConfig.ts

@ -1,50 +0,0 @@
export default {
'bpmn:EndEvent': {},
'bpmn:StartEvent': {
initiator: true,
formKey: true
},
'bpmn:UserTask': {
allocationType: true,
specifyDesc: true,
multipleUserAuditType: true,
async: true,
priority: true,
skipExpression: true,
dueDate: true,
taskListener: true,
executionListener: true
},
'bpmn:ServiceTask': {
async: true,
skipExpression: true,
isForCompensation: true,
triggerable: true,
class: true
},
'bpmn:ScriptTask': {
async: true,
isForCompensation: true,
autoStoreVariables: true
},
'bpmn:ManualTask': {
async: true,
isForCompensation: true
},
'bpmn:ReceiveTask': {
async: true,
isForCompensation: true
},
'bpmn:SendTask': {
async: true,
isForCompensation: true
},
'bpmn:BusinessRuleTask': {
async: true,
isForCompensation: true,
ruleVariablesInput: true,
rules: true,
resultVariable: true,
exclude: true
}
};

284
im-admin-ui/src/bpmn/assets/style/index.scss

@ -1,284 +0,0 @@
.djs-palette {
width: 300px;
.bpmn-icon-hand-tool:hover {
&:after {
content: '启动手动工具';
position: absolute;
left: 45px;
width: 120px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-lasso-tool:hover {
&:after {
content: '启动套索工具';
position: absolute;
left: 100px;
width: 120px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-space-tool:hover {
&:after {
content: '启动创建/删除空间工具';
position: absolute;
left: 45px;
width: 170px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-connection-multi:hover {
&:after {
content: '启动全局连接工具';
position: absolute;
left: 100px;
width: 140px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-start-event-none:hover {
&:after {
content: '创建开始事件';
position: absolute;
left: 45px;
width: 120px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-intermediate-event-none:hover {
&:after {
content: '创建中间/边界事件';
position: absolute;
left: 100px;
width: 140px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-end-event-none:hover {
&:after {
content: '创建结束事件';
position: absolute;
left: 45px;
width: 120px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-gateway-none:hover {
&:after {
content: '创建网关';
position: absolute;
left: 100px;
width: 90px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-gateway-parallel:hover {
&:after {
content: '创建并行网关';
position: absolute;
left: 45px;
width: 120px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-gateway-eventbased:hover {
&:after {
content: '创建事件网关';
position: absolute;
left: 100px;
width: 120px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-task:hover {
&:after {
content: '创建任务';
position: absolute;
left: 45px;
width: 80px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-subprocess-expanded:hover {
&:after {
content: '创建可折叠子流程';
position: absolute;
left: 100px;
width: 140px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-user-task:hover {
&:after {
content: '创建用户任务';
position: absolute;
left: 45px;
width: 120px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.task-multi-instance:hover {
&:after {
content: '创建多实例用户任务';
position: absolute;
left: 100px;
width: 160px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-participant:hover {
&:after {
content: '创建泳池/泳道';
position: absolute;
left: 45px;
width: 120px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-data-object {
display: none;
&:hover {
&:after {
content: '创建数据对象';
position: absolute;
left: 45px;
width: 120px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
}
.bpmn-icon-data-store {
display: none;
&:hover {
&:after {
content: '创建数据存储';
position: absolute;
left: 100px;
width: 120px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
}
.bpmn-icon-group {
display: none;
&:hover {
&:after {
content: '创建分组';
position: absolute;
left: 100px;
width: 100px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
}
}

145
im-admin-ui/src/bpmn/hooks/usePanel.ts

@ -1,145 +0,0 @@
import showConfig from '../assets/showConfig';
import type { ModdleElement } from 'bpmn';
import useModelerStore from '@/store/modules/modeler';
import { MultiInstanceTypeEnum } from '@/enums/bpmn/IndexEnums';
interface Options {
element: ModdleElement;
}
export default (ops: Options) => {
const { element } = ops;
const { getModeling, getModdle } = useModelerStore();
const modeling = getModeling();
const moddle = getModdle();
/**
*
*/
const elementType = computed(() => {
const bizObj = element.businessObject;
return bizObj.eventDefinitions ? bizObj.eventDefinitions[0].$type : bizObj.$type;
});
/**
*
*/
const config = computed(() => showConfig[elementType.value] || {});
/**
*
* @param elementType
* @param properties
* @param parent
*/
const createModdleElement = (elementType: string, properties: any, parent: ModdleElement) => {
const element = moddle.create(elementType, properties);
parent && (element.$parent = parent);
return element;
};
/**
*
*/
const getExtensionElements = (create = true) => {
let extensionElements = element.businessObject.get<ModdleElement>('extensionElements');
if (!extensionElements && create) {
extensionElements = createModdleElement('bpmn:ExtensionElements', { values: [] }, element.businessObject);
modeling.updateModdleProperties(element, element.businessObject, { extensionElements });
}
return extensionElements;
};
/**
* extensionElements下的properties
* @param extensionElements Element下的extensionElements下的Properties
*/
const getPropertiesElements = (extensionElements?: ModdleElement) => {
if (!extensionElements) {
extensionElements = getExtensionElements();
}
let propertiesElements = extensionElements.values.find((item) => item.$type === 'flowable:properties');
if (!propertiesElements) {
propertiesElements = createModdleElement('flowable:properties', { values: [] }, extensionElements);
modeling.updateModdleProperties(element, extensionElements, {
values: [...extensionElements.get<[]>('values'), propertiesElements]
});
}
return propertiesElements;
};
/**
*
* @param properties
*/
const updateProperties = (properties: any) => {
modeling.updateProperties(element, properties);
};
/**
*
* @param updateElement
* @param properties
*/
const updateModdleProperties = (updateElement, properties: any) => {
modeling.updateModdleProperties(element, updateElement, properties);
};
/**
* Property属性
* @param name key值
* @param value
*/
const updateProperty = (name: string, value: string) => {
const propertiesElements = getPropertiesElements();
let propertyElements = propertiesElements.values.find((item) => item.name === name);
if (!propertyElements) {
propertyElements = createModdleElement('flowable:property', { name: name, value: value }, propertiesElements);
modeling.updateModdleProperties(element, propertiesElements, {
values: [...propertiesElements.get('values'), propertyElements]
});
} else {
propertyElements.name = name;
propertyElements.value = value;
}
return propertyElements;
};
const idChange = (newVal: string) => {
if (newVal) {
updateProperties({ id: newVal });
}
};
const nameChange = (newVal: string) => {
if (newVal) {
updateProperties({ name: newVal });
}
};
const formKeyChange = (newVal: string) => {
updateProperties({ formKey: newVal });
};
const constant = {
MultiInstanceType: [
{ id: '373d4b81-a0d1-4eb8-8685-0d2fb1b468e2', label: '无', value: MultiInstanceTypeEnum.NONE },
{ id: 'b5acea7c-b7e5-46b0-8778-390db091bdab', label: '串行', value: MultiInstanceTypeEnum.SERIAL },
{ id: 'b4f0c683-1ccc-43c4-8380-e1b998986caf', label: '并行', value: MultiInstanceTypeEnum.PARALLEL }
]
};
return {
elementType,
constant,
showConfig: config,
updateProperties,
updateProperty,
updateModdleProperties,
createModdleElement,
idChange,
nameChange,
formKeyChange,
getExtensionElements,
getPropertiesElements
};
};

34
im-admin-ui/src/bpmn/hooks/useParseElement.ts

@ -1,34 +0,0 @@
import type { ModdleElement } from 'bpmn';
interface Options {
element: ModdleElement;
}
interface Data {
id: string;
}
export default (ops: Options) => {
const { element } = ops;
const parseData = <T>(): T => {
const result = {
...element.businessObject,
...element.businessObject.$attrs
};
// 移除flowable前缀,格式化数组
for (const key in result) {
if (key.indexOf('flowable:') === 0) {
const newKey = key.replace('flowable:', '');
result[newKey] = result[key];
delete result[key];
}
}
return { ...result } as T;
};
return {
parseData
};
};

496
im-admin-ui/src/bpmn/index.vue

@ -1,496 +0,0 @@
<template>
<div class="containers-bpmn">
<!-- dark模式下 连接线的箭头样式 -->
<svg width="0" height="0" style="position: absolute">
<defs>
<marker id="markerArrow-dark-mode" viewBox="0 0 20 20" refX="11" refY="10" markerWidth="10" markerHeight="10" orient="auto">
<path d="M 1 5 L 11 10 L 1 15 Z" class="arrow-dark" />
</marker>
</defs>
</svg>
<div v-loading="loading" class="app-containers-bpmn">
<el-container class="h-full">
<el-container style="align-items: stretch">
<el-header>
<div class="process-toolbar">
<el-space wrap :size="10">
<el-tooltip effect="dark" content="自适应屏幕" placement="bottom">
<el-button size="small" icon="Rank" @click="fitViewport" />
</el-tooltip>
<el-tooltip effect="dark" content="放大" placement="bottom">
<el-button size="small" icon="ZoomIn" @click="zoomViewport(true)" />
</el-tooltip>
<el-tooltip effect="dark" content="缩小" placement="bottom">
<el-button size="small" icon="ZoomOut" @click="zoomViewport(false)" />
</el-tooltip>
<el-tooltip effect="dark" content="后退" placement="bottom">
<el-button size="small" icon="Back" @click="bpmnModeler.get('commandStack').undo()" />
</el-tooltip>
<el-tooltip effect="dark" content="前进" placement="bottom">
<el-button size="small" icon="Right" @click="bpmnModeler.get('commandStack').redo()" />
</el-tooltip>
</el-space>
<el-space wrap :size="10" style="float: right; padding-right: 10px">
<el-button size="small" type="primary" @click="saveXml"> </el-button>
<el-dropdown size="small">
<el-button size="small" type="primary"> </el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item icon="Document" @click="previewXML">XML预览</el-dropdown-item>
<el-dropdown-item icon="View" @click="previewSVG"> SVG预览</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-dropdown size="small">
<el-button size="small" type="primary"> </el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item icon="Download" @click="downloadXML">下载XML</el-dropdown-item>
<el-dropdown-item icon="Download" @click="downloadSVG"> 下载SVG</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-space>
</div>
</el-header>
<div ref="canvas" class="canvas" />
</el-container>
<div :class="{ 'process-panel': true, 'hide': panelFlag }">
<div class="process-panel-bar" @click="panelBarClick">
<div class="open-bar">
<el-link type="default" :underline="false">
<svg-icon class-name="open-bar" :icon-class="panelFlag ? 'caret-back' : 'caret-forward'"></svg-icon>
</el-link>
</div>
</div>
<transition enter-active-class="animate__animated animate__fadeIn">
<div v-show="showPanel" v-if="bpmnModeler" class="panel-content">
<PropertyPanel :modeler="bpmnModeler" />
</div>
</transition>
</div>
</el-container>
</div>
</div>
<div>
<el-dialog v-model="perviewXMLShow" title="XML预览" width="80%" append-to-body>
<highlightjs :code="xmlStr" language="XML" />
</el-dialog>
</div>
<div>
<el-dialog v-model="perviewSVGShow" title="SVG预览" width="80%" append-to-body>
<div style="text-align: center" v-html="svgData" />
</el-dialog>
</div>
</template>
<script lang="ts" setup name="BpmnDesign">
import 'bpmn-js/dist/assets/diagram-js.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css';
import './assets/style/index.scss';
import type { Canvas, Modeler } from 'bpmn';
import PropertyPanel from './panel/index.vue';
import BpmnModeler from 'bpmn-js/lib/Modeler.js';
import defaultXML from './assets/defaultXML';
import flowableModdle from './assets/moddle/flowable';
import Modules from './assets/module/index';
import useModelerStore from '@/store/modules/modeler';
import useDialog from '@/hooks/useDialog';
const emit = defineEmits(['closeCallBack', 'saveCallBack']);
const { visible, title, openDialog, closeDialog } = useDialog({
title: '编辑流程'
});
const modelerStore = useModelerStore();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const panelFlag = ref(false);
const showPanel = ref(true);
const canvas = ref<HTMLDivElement>();
const panel = ref<HTMLDivElement>();
const bpmnModeler = ref<Modeler>();
const zoom = ref(1);
const perviewXMLShow = ref(false);
const perviewSVGShow = ref(false);
const xmlStr = ref('');
const svgData = ref('');
const loading = ref(false);
const panelBarClick = () => {
//
panelFlag.value = !panelFlag.value;
setTimeout(() => {
showPanel.value = !panelFlag.value;
}, 100);
};
/**
* 初始化Canvas
*/
const initCanvas = () => {
bpmnModeler.value = new BpmnModeler({
container: canvas.value,
//
keyboard: {
bindTo: window // window
},
propertiesPanel: {
parent: panel.value
},
additionalModules: Modules,
moddleExtensions: {
flowable: flowableModdle
}
});
};
/**
* 初始化Model
*/
const initModel = () => {
if (modelerStore.getModeler()) {
modelerStore.getModeler().destroy();
modelerStore.setModeler(undefined);
}
modelerStore.setModeler(bpmnModeler.value);
};
/**
* 新建
*/
const newDiagram = async () => {
await proxy?.$modal.confirm('是否确认新建');
initDiagram();
};
/**
* 初始化
*/
const initDiagram = (xml?: string) => {
if (!xml) xml = defaultXML;
bpmnModeler.value.importXML(xml);
};
/**
* 自适应屏幕
*/
const fitViewport = () => {
zoom.value = bpmnModeler.value.get<Canvas>('canvas').zoom('fit-viewport');
const bbox = document.querySelector<SVGGElement>('.app-containers-bpmn .viewport').getBBox();
const currentViewBox = bpmnModeler.value.get<Canvas>('canvas').viewbox();
const elementMid = {
x: bbox.x + bbox.width / 2 - 65,
y: bbox.y + bbox.height / 2
};
bpmnModeler.value.get<Canvas>('canvas').viewbox({
x: elementMid.x - currentViewBox.width / 2,
y: elementMid.y - currentViewBox.height / 2,
width: currentViewBox.width,
height: currentViewBox.height
});
zoom.value = (bbox.width / currentViewBox.width) * 1.8;
};
/**
* 放大或者缩小
* @param zoomIn true 放大 | false 缩小
*/
const zoomViewport = (zoomIn = true) => {
zoom.value = bpmnModeler.value.get<Canvas>('canvas').zoom();
zoom.value += zoomIn ? 0.1 : -0.1;
bpmnModeler.value.get<Canvas>('canvas').zoom(zoom.value);
};
/**
* 下载XML
*/
const downloadXML = async () => {
try {
const { xml } = await bpmnModeler.value.saveXML({ format: true });
downloadFile(`${getProcessElement().name}.bpmn20.xml`, xml, 'application/xml');
} catch (e) {
proxy?.$modal.msgError(e);
}
};
/**
* 下载SVG
*/
const downloadSVG = async () => {
try {
const { svg } = await bpmnModeler.value.saveSVG();
downloadFile(getProcessElement().name, svg, 'image/svg+xml');
} catch (e) {
proxy?.$modal.msgError(e);
}
};
/**
* XML预览
*/
const previewXML = async () => {
try {
const { xml } = await bpmnModeler.value.saveXML({ format: true });
xmlStr.value = xml;
perviewXMLShow.value = true;
} catch (e) {
proxy?.$modal.msgError(e);
}
};
/**
* SVG预览
*/
const previewSVG = async () => {
try {
const { svg } = await bpmnModeler.value.saveSVG();
svgData.value = svg;
perviewSVGShow.value = true;
} catch (e) {
proxy?.$modal.msgError(e);
}
};
const curNodeInfo = reactive({
curType: '', //
curNode: '',
expValue: '' //
});
const downloadFile = (fileName: string, data: any, type: string) => {
const a = document.createElement('a');
const url = window.URL.createObjectURL(new Blob([data], { type: type }));
a.href = url;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
};
const getProcessElement = () => {
const rootElements = bpmnModeler.value?.getDefinitions().rootElements;
for (let i = 0; i < rootElements.length; i++) {
if (rootElements[i].$type === 'bpmn:Process') return rootElements[i];
}
};
const getProcess = () => {
const element = getProcessElement();
return {
id: element.id,
name: element.name
};
};
const saveXml = async () => {
const { xml } = await bpmnModeler.value.saveXML({ format: true });
const { svg } = await bpmnModeler.value.saveSVG();
const process = getProcess();
let data = {
xml: xml,
svg: svg,
key: process.id,
name: process.name,
loading: loading
};
emit('saveCallBack', data);
};
const open = (xml?: string) => {
openDialog();
nextTick(() => {
initDiagram(xml);
});
};
const close = () => {
closeDialog();
};
onMounted(() => {
nextTick(() => {
initCanvas();
initModel();
});
});
/**
* 对外暴露子组件方法
*/
defineExpose({
initDiagram,
saveXml,
open,
close
});
</script>
<style lang="scss">
/** 夜间模式 线条的颜色 */
$stroke-color-dark: white;
$bpmn-font-size: 12px;
/** 日间模式 字体颜色 */
$bpmn-font-color-dark: white;
/** 夜间模式 字体颜色 */
$bpmn-font-color-light: #222;
/* 背景网格 */
@mixin djs-container {
background-image: linear-gradient(90deg, hsl(0deg 0% 78.4% / 15%) 10%, transparent 0), linear-gradient(hsl(0deg 0% 78.4% / 15%) 10%, transparent 0) !important;
background-size: 10px 10px !important;
}
html[class='light'] {
/** 从左侧拖动时的背景图 */
svg.new-parent {
@include djs-container;
}
/** 双击编辑元素时样式保持一致 */
div.djs-direct-editing-parent {
border-radius: 10px;
background-color: transparent !important;
color: $bpmn-font-color-light;
}
g.djs-visual {
.djs-label {
fill: $bpmn-font-color-light !important;
font-size: $bpmn-font-size !important;
}
}
}
html[class='dark'] {
/** dark模式下 连接线的箭头样式 */
.arrow-dark {
stroke-width: 1px;
stroke-linecap: round;
stroke: $stroke-color-dark;
fill: $stroke-color-dark;
stroke-linejoin: round;
}
/** 从左侧拖动时的背景图 */
svg.new-parent {
background-color: black !important;
@include djs-container;
}
/** 双击编辑元素时样式保持一致 */
div.djs-direct-editing-parent {
border-radius: 10px;
background-color: transparent !important;
color: $bpmn-font-color-dark;
}
/** 元素相关设置 */
g.djs-visual {
/** 元素边框 需要去除文字(.djs-label) */
& > *:first-child:not(.djs-label) {
stroke: $stroke-color-dark !important;
}
/** 字体颜色 */
.djs-label {
fill: $bpmn-font-color-dark !important;
font-size: $bpmn-font-size !important;
}
/* 连接线样式 */
path[data-corner-radius] {
stroke: $stroke-color-dark !important;
marker-end: url('#markerArrow-dark-mode') !important;
}
}
}
.containers-bpmn {
height: 100%;
.app-containers-bpmn {
width: 100%;
height: 100%;
.canvas {
width: 100%;
height: 100%;
@include djs-container;
}
.el-header {
height: 35px;
padding: 0;
}
.process-panel {
transition: width 0.25s ease-in;
.process-panel-bar {
width: 34px;
height: 40px;
.open-bar {
width: 34px;
line-height: 40px;
}
}
//
&.hide {
width: 34px;
overflow: hidden;
padding: 0;
.process-panel-bar {
width: 34px;
height: 100%;
box-sizing: border-box;
display: block;
text-align: left;
line-height: 34px;
}
.process-panel-bar:hover {
background-color: var(--bpmn-panel-bar-background-color);
}
}
}
}
}
pre {
margin: 0;
height: 100%;
max-height: calc(80vh - 32px);
overflow-x: hidden;
overflow-y: auto;
.hljs {
word-break: break-word;
white-space: pre-wrap;
padding: 0.5em;
}
}
.open-bar {
font-size: 20px;
cursor: pointer;
text-align: center;
}
.process-panel {
box-sizing: border-box;
padding: 0 8px 0 8px;
border-left: 1px solid var(--bpmn-panel-border);
box-shadow: var(--bpmn-panel-box-shadow) 0 0 8px;
max-height: 100%;
width: 25%;
height: calc(100vh - 100px);
.el-collapse {
height: calc(100vh - 182px);
overflow: auto;
}
}
//
//:deep(.djs-palette) {
// opacity: 0.3;
// transition: all 1s;
//}
//
//:deep(.djs-palette:hover) {
// opacity: 1;
// transition: all 1s;
//}
</style>

68
im-admin-ui/src/bpmn/panel/GatewayPanel.vue

@ -1,68 +0,0 @@
<template>
<div>
<el-collapse v-model="currentCollapseItem">
<el-collapse-item name="1">
<template #title>
<div class="collapse__title">
<el-icon>
<InfoFilled />
</el-icon>
常规
</div>
</template>
<div>
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="80px">
<el-form-item prop="id" label="节点 ID">
<el-input v-model="formData.id" @change="idChange"> </el-input>
</el-form-item>
<el-form-item prop="name" label="节点名称">
<el-input v-model="formData.name" @change="nameChange"> </el-input>
</el-form-item>
</el-form>
</div>
</el-collapse-item>
<el-collapse-item name="2">
<template #title>
<div class="collapse__title">
<el-icon>
<BellFilled />
</el-icon>
执行监听器
</div>
</template>
<div>
<ExecutionListener :element="element"></ExecutionListener>
</div>
</el-collapse-item>
</el-collapse>
</div>
</template>
<script setup lang="ts">
import useParseElement from '../hooks/useParseElement';
import usePanel from '../hooks/usePanel';
import type { Modeler, ModdleElement } from 'bpmn';
import type { GatewayPanel } from 'bpmnDesign';
import ExecutionListener from './property/ExecutionListener.vue';
interface PropType {
element: ModdleElement;
}
const props = withDefaults(defineProps<PropType>(), {});
const { nameChange, idChange } = usePanel({
element: toRaw(props.element)
});
const { parseData } = useParseElement({
element: toRaw(props.element)
});
const currentCollapseItem = ref(['1', '2']);
const formData = ref(parseData<GatewayPanel>());
const formRules = ref<ElFormRules>({
processCategory: [{ required: true, message: '请选择', trigger: 'blur' }],
id: [{ required: true, message: '请输入', trigger: 'blur' }],
name: [{ required: true, message: '请输入', trigger: 'blur' }]
});
</script>
<style lang="scss" scoped></style>

68
im-admin-ui/src/bpmn/panel/ParticipantPanel.vue

@ -1,68 +0,0 @@
<template>
<div>
<el-collapse v-model="currentCollapseItem">
<el-collapse-item name="1">
<template #title>
<div class="collapse__title">
<el-icon>
<InfoFilled />
</el-icon>
常规
</div>
</template>
<div>
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="90px">
<el-form-item prop="id" label="节点 ID">
<el-input v-model="formData.id" @change="idChange"></el-input>
</el-form-item>
<el-form-item prop="name" label="节点名称">
<el-input v-model="formData.name" @change="nameChange"></el-input>
</el-form-item>
</el-form>
</div>
</el-collapse-item>
<el-collapse-item name="2">
<template #title>
<div class="collapse__title">
<el-icon>
<BellFilled />
</el-icon>
执行监听器
</div>
</template>
<div>
<ExecutionListener :element="element"></ExecutionListener>
</div>
</el-collapse-item>
</el-collapse>
</div>
</template>
<script setup lang="ts">
import useParseElement from '../hooks/useParseElement';
import usePanel from '../hooks/usePanel';
import ExecutionListener from './property/ExecutionListener.vue';
import type { ModdleElement } from 'bpmn';
import type { ParticipantPanel } from 'bpmnDesign';
interface PropType {
element: ModdleElement;
}
const props = withDefaults(defineProps<PropType>(), {});
const { nameChange, idChange } = usePanel({
element: toRaw(props.element)
});
const { parseData } = useParseElement({
element: toRaw(props.element)
});
const formData = ref(parseData<ParticipantPanel>());
const currentCollapseItem = ref(['1', '2']);
const formRules = ref<ElFormRules>({
id: [{ required: true, message: '请输入', trigger: 'blur' }],
name: [{ required: true, message: '请输入', trigger: 'blur' }]
});
</script>
<style lang="scss" scoped></style>

71
im-admin-ui/src/bpmn/panel/ProcessPanel.vue

@ -1,71 +0,0 @@
<template>
<div>
<el-collapse v-model="currentCollapseItem">
<el-collapse-item name="1">
<template #title>
<div class="collapse__title">
<el-icon>
<InfoFilled />
</el-icon>
常规
</div>
</template>
<div>
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="80px">
<el-form-item label="流程标识" prop="id">
<el-input v-model="formData.id" @change="idChange"></el-input>
</el-form-item>
<el-form-item label="流程名称" prop="name">
<el-input v-model="formData.name" @change="nameChange"></el-input>
</el-form-item>
</el-form>
</div>
</el-collapse-item>
<el-collapse-item name="2">
<template #title>
<div class="collapse__title">
<el-icon>
<BellFilled />
</el-icon>
执行监听器
</div>
</template>
<div>
<ExecutionListener :element="element"></ExecutionListener>
</div>
</el-collapse-item>
</el-collapse>
</div>
</template>
<script setup lang="ts">
import ExecutionListener from './property/ExecutionListener.vue';
import useParseElement from '../hooks/useParseElement';
import usePanel from '../hooks/usePanel';
import type { Modeler, ModdleElement } from 'bpmn';
import type { ProcessPanel } from 'bpmnDesign';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
interface PropType {
element: ModdleElement;
}
const props = withDefaults(defineProps<PropType>(), {});
const { parseData } = useParseElement({
element: toRaw(props.element)
});
const { idChange, nameChange } = usePanel({
element: toRaw(props.element)
});
const currentCollapseItem = ref(['1', '2']);
const formData = ref<ProcessPanel>(parseData<ProcessPanel>());
const formRules = ref<ElFormRules>({
id: [{ required: true, message: '请输入', trigger: 'blur' }],
name: [{ required: true, message: '请输入', trigger: 'blur' }]
});
</script>
<style scoped lang="scss"></style>

95
im-admin-ui/src/bpmn/panel/SequenceFlowPanel.vue

@ -1,95 +0,0 @@
<template>
<div>
<el-collapse v-model="currentCollapseItem">
<el-collapse-item name="1">
<template #title>
<div class="collapse__title">
<el-icon>
<InfoFilled />
</el-icon>
常规
</div>
</template>
<div>
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="90px">
<el-form-item prop="id" label="节点 ID">
<el-input v-model="formData.id" @change="idChange"> </el-input>
</el-form-item>
<el-form-item prop="name" label="节点名称">
<el-input v-model="formData.name" @change="nameChange"> </el-input>
</el-form-item>
<el-form-item prop="conditionExpression" label="跳转条件">
<el-input v-model="formData.conditionExpressionValue" @change="conditionExpressionChange"> </el-input>
</el-form-item>
<el-form-item prop="skipExpression" label="跳过表达式">
<el-input v-model="formData.skipExpression" @change="skipExpressionChange"> </el-input>
</el-form-item>
</el-form>
</div>
</el-collapse-item>
<el-collapse-item name="2">
<template #title>
<div class="collapse__title">
<el-icon>
<BellFilled />
</el-icon>
执行监听器
</div>
</template>
<div>
<ExecutionListener :element="element"></ExecutionListener>
</div>
</el-collapse-item>
</el-collapse>
</div>
</template>
<script setup lang="ts">
import useParseElement from '../hooks/useParseElement';
import useModelerStore from '@/store/modules/modeler';
import usePanel from '../hooks/usePanel';
import ExecutionListener from './property/ExecutionListener.vue';
import type { Modeler, ModdleElement } from 'bpmn';
import type { SequenceFlowPanel } from 'bpmnDesign';
interface PropType {
element: ModdleElement;
}
const props = withDefaults(defineProps<PropType>(), {});
const { nameChange, idChange, updateProperties } = usePanel({
element: toRaw(props.element)
});
const { parseData } = useParseElement({
element: toRaw(props.element)
});
const moddle = useModelerStore().getModdle();
const currentCollapseItem = ref(['1', '2']);
const formData = ref(parseData<SequenceFlowPanel>());
const formRules = ref<ElFormRules>({
processCategory: [{ required: true, message: '请选择', trigger: 'blur' }],
id: [{ required: true, message: '请输入', trigger: 'blur' }],
name: [{ required: true, message: '请输入', trigger: 'blur' }]
});
const conditionExpressionChange = (val: string) => {
if (val) {
const newCondition = moddle.create('bpmn:FormalExpression', { body: val });
updateProperties({ conditionExpression: newCondition });
} else {
updateProperties({ conditionExpression: null });
}
};
const skipExpressionChange = (val: string) => {
updateProperties({ 'flowable:skipExpression': val });
};
onBeforeMount(() => {
if (formData.value.conditionExpression) {
formData.value.conditionExpressionValue = formData.value.conditionExpression.body;
}
});
</script>
<style lang="scss" scoped></style>

67
im-admin-ui/src/bpmn/panel/StartEndPanel.vue

@ -1,67 +0,0 @@
<template>
<div>
<el-collapse v-model="currentCollapseItem">
<el-collapse-item name="1">
<template #title>
<div class="collapse__title">
<el-icon>
<InfoFilled />
</el-icon>
常规
</div>
</template>
<div>
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="90px">
<el-form-item prop="id" label="节点 ID">
<el-input v-model="formData.id" @change="idChange"> </el-input>
</el-form-item>
<el-form-item prop="name" label="节点名称">
<el-input v-model="formData.name" @change="nameChange"> </el-input>
</el-form-item>
</el-form>
</div>
</el-collapse-item>
<el-collapse-item name="2">
<template #title>
<div class="collapse__title">
<el-icon>
<BellFilled />
</el-icon>
执行监听器
</div>
</template>
<div>
<ExecutionListener :element="element"></ExecutionListener>
</div>
</el-collapse-item>
</el-collapse>
</div>
</template>
<script setup lang="ts">
import ExecutionListener from './property/ExecutionListener.vue';
import useParseElement from '../hooks/useParseElement';
import usePanel from '../hooks/usePanel';
import type { Modeler, ModdleElement } from 'bpmn';
import type { StartEndPanel } from 'bpmnDesign';
interface PropType {
element: ModdleElement;
}
const props = withDefaults(defineProps<PropType>(), {});
const { nameChange, idChange } = usePanel({
element: toRaw(props.element)
});
const { parseData } = useParseElement({
element: toRaw(props.element)
});
const formData = ref(parseData<StartEndPanel>());
const currentCollapseItem = ref(['1', '2']);
const formRules = ref<ElFormRules>({
id: [{ required: true, message: '请输入', trigger: 'blur' }],
name: [{ required: true, message: '请输入', trigger: 'blur' }]
});
</script>
<style lang="scss" scoped></style>

193
im-admin-ui/src/bpmn/panel/SubProcessPanel.vue

@ -1,193 +0,0 @@
<template>
<div>
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="90px">
<el-collapse v-model="currentCollapseItem">
<el-collapse-item name="1">
<template #title>
<div class="collapse__title">
<el-icon>
<InfoFilled />
</el-icon>
常规
</div>
</template>
<div>
<el-form-item prop="id" label="节点 ID">
<el-input v-model="formData.id" @change="idChange"> </el-input>
</el-form-item>
<el-form-item prop="name" label="节点名称">
<el-input v-model="formData.name" @change="nameChange"> </el-input>
</el-form-item>
</div>
</el-collapse-item>
<el-collapse-item name="2">
<template #title>
<div class="collapse__title">
<el-icon>
<BellFilled />
</el-icon>
执行监听器
</div>
</template>
<div>
<ExecutionListener :element="element"></ExecutionListener>
</div>
</el-collapse-item>
<el-collapse-item name="3">
<template #title>
<div class="collapse__title">
<el-icon>
<HelpFilled />
</el-icon>
多实例
</div>
</template>
<div>
<el-form-item label="多实例类型">
<el-select v-model="formData.multiInstanceType" @change="multiInstanceTypeChange">
<el-option v-for="item in constant.MultiInstanceType" :key="item.id" :value="item.value" :label="item.label"> </el-option>
</el-select>
</el-form-item>
<div v-if="formData.multiInstanceType !== MultiInstanceTypeEnum.NONE">
<el-form-item label="集合">
<template #label>
<span>
集合
<el-tooltip placement="top">
<el-icon><QuestionFilled /></el-icon>
<template #content>
属性会作为表达式进行解析如果表达式解析为字符串而不是一个集合<br />
不论是因为本身配置的就是静态字符串值还是表达式计算结果为字符串<br />
这个字符串都会被当做变量名并从流程变量中用于获取实际的集合
</template>
</el-tooltip>
</span>
</template>
<el-input v-model="formData.collection" @change="collectionChange"></el-input>
</el-form-item>
<el-form-item label="元素变量">
<template #label>
<span>
元素变量
<el-tooltip placement="top">
<el-icon><QuestionFilled /></el-icon>
<template #content>
每创建一个用户任务前先以该元素变量为label集合中的一项为value<br />
创建局部流程变量该局部流程变量被用于指派用户任务<br />
一般来说该字符串应与指定人员变量相同
</template>
</el-tooltip>
</span>
</template>
<el-input v-model="formData.elementVariable" @change="elementVariableChange"> </el-input>
</el-form-item>
<el-form-item label="完成条件">
<template #label>
<span>
完成条件
<el-tooltip placement="top">
<el-icon><QuestionFilled /></el-icon>
<template #content>
多实例活动在所有实例都完成时结束然而也可以指定一个表达式在每个实例<br />
结束时进行计算当表达式计算为true时将销毁所有剩余的实例并结束多实例<br />
活动继续执行流程例如 ${nrOfCompletedInstances/nrOfInstances >= 0.6 }<br />
表示当任务完成60%该节点就算完成
</template>
</el-tooltip>
</span>
</template>
<el-input v-model="formData.completionCondition" @change="completionConditionChange"> </el-input>
</el-form-item>
</div>
</div>
</el-collapse-item>
</el-collapse>
</el-form>
</div>
</template>
<script setup lang="ts">
import ExecutionListener from './property/ExecutionListener.vue';
import useParseElement from '../hooks/useParseElement';
import usePanel from '../hooks/usePanel';
import type { ModdleElement } from 'bpmn';
import type { SubProcessPanel } from 'bpmnDesign';
import { MultiInstanceTypeEnum } from '@/enums/bpmn/IndexEnums';
interface PropType {
element: ModdleElement;
}
const props = withDefaults(defineProps<PropType>(), {});
const { nameChange, idChange, updateProperties, createModdleElement, constant } = usePanel({
element: toRaw(props.element)
});
const { parseData } = useParseElement({
element: toRaw(props.element)
});
const formData = ref(parseData<SubProcessPanel>());
const currentCollapseItem = ref(['1', '2', '3']);
const multiInstanceTypeChange = (newVal) => {
if (newVal !== MultiInstanceTypeEnum.NONE) {
let loopCharacteristics = props.element.businessObject.get('loopCharacteristics');
if (!loopCharacteristics) {
loopCharacteristics = createModdleElement('bpmn:MultiInstanceLoopCharacteristics', {}, props.element.businessObject);
}
loopCharacteristics.isSequential = newVal === MultiInstanceTypeEnum.SERIAL;
updateProperties({ loopCharacteristics: loopCharacteristics });
} else {
updateProperties({ loopCharacteristics: undefined });
}
};
const collectionChange = (newVal) => {
let loopCharacteristics = props.element.businessObject.get('loopCharacteristics');
if (!loopCharacteristics) {
loopCharacteristics = createModdleElement('bpmn:MultiInstanceLoopCharacteristics', {}, props.element.businessObject);
}
loopCharacteristics.collection = newVal && newVal.length > 0 ? newVal : undefined;
updateProperties({ loopCharacteristics: loopCharacteristics });
};
const elementVariableChange = (newVal) => {
let loopCharacteristics = props.element.businessObject.get('loopCharacteristics');
if (!loopCharacteristics) {
loopCharacteristics = createModdleElement('bpmn:MultiInstanceLoopCharacteristics', {}, props.element.businessObject);
}
loopCharacteristics.elementVariable = newVal && newVal.length > 0 ? newVal : undefined;
updateProperties({ loopCharacteristics: loopCharacteristics });
};
const completionConditionChange = (newVal) => {
let loopCharacteristics = props.element.businessObject.get<ModdleElement>('loopCharacteristics');
if (!loopCharacteristics) {
loopCharacteristics = createModdleElement('bpmn:MultiInstanceLoopCharacteristics', {}, props.element.businessObject);
}
if (newVal && newVal.length > 0) {
if (!loopCharacteristics.completionCondition) {
loopCharacteristics.completionCondition = createModdleElement('bpmn:Expression', { body: newVal }, loopCharacteristics);
} else {
loopCharacteristics.completionCondition.body = newVal;
}
} else {
loopCharacteristics.completionCondition = undefined;
}
updateProperties({ loopCharacteristics: loopCharacteristics });
};
onBeforeMount(() => {
if (formData.value.loopCharacteristics) {
const loopCharacteristics = formData.value.loopCharacteristics;
formData.value.collection = loopCharacteristics.collection || '';
formData.value.elementVariable = loopCharacteristics.elementVariable || '';
formData.value.completionCondition = loopCharacteristics.completionCondition?.body || '';
formData.value.multiInstanceType = loopCharacteristics.isSequential ? MultiInstanceTypeEnum.SERIAL : MultiInstanceTypeEnum.PARALLEL;
}
});
const formRules = ref<ElFormRules>({
id: [{ required: true, message: '请输入', trigger: 'blur' }],
name: [{ required: true, message: '请输入', trigger: 'blur' }]
});
</script>
<style lang="scss" scoped></style>

491
im-admin-ui/src/bpmn/panel/TaskPanel.vue

@ -1,491 +0,0 @@
<template>
<div>
<el-form ref="formRef" size="default" :model="formData" :rules="formRules" label-width="100px">
<el-collapse v-model="currentCollapseItem">
<el-collapse-item name="1">
<template #title>
<div class="collapse__title">
<el-icon>
<InfoFilled />
</el-icon>
常规
</div>
</template>
<div>
<el-form-item prop="id" label="节点 ID">
<el-input v-model="formData.id" @change="idChange"> </el-input>
</el-form-item>
<el-form-item prop="name" label="节点名称">
<el-input v-model="formData.name" @change="nameChange"> </el-input>
</el-form-item>
<el-form-item v-if="showConfig.skipExpression" prop="skipExpression" label="跳过表达式">
<el-input v-model="formData.skipExpression" @change="skipExpressionChange"> </el-input>
</el-form-item>
<el-form-item v-loading="formManageListLoading" prop="formKey" label="表单地址">
<el-select v-model="formData.formKey" clearable filterable placeholder="请选择表单" style="width: 260px" @change="formKeyChange">
<el-option
v-for="item in formManageList"
:key="item.id"
:label="item.formTypeName + ':' + item.formName"
:value="item.formType + ':' + item.id"
/>
</el-select>
</el-form-item>
</div>
</el-collapse-item>
<el-collapse-item name="2">
<template #title>
<div class="collapse__title">
<el-icon>
<Checked />
</el-icon>
任务
</div>
</template>
<div>
<el-form-item v-if="showConfig.async" prop="sync" label="是否异步">
<el-switch v-model="formData.async" inline-prompt active-text="" inactive-text="" @change="syncChange" />
</el-form-item>
<el-tabs tab-position="left" class="demo-tabs">
<el-tab-pane label="身份存储">
<el-form-item label="分配人员">
<el-input v-model="formData.assignee" @blur="blurAssignee(formData.assignee)">
<template #append>
<el-button icon="Search" type="primary" @click="openSingleUserSelect" />
</template>
</el-input>
</el-form-item>
<el-form-item label="候选人员">
<el-badge :value="selectUserLength" :max="99">
<el-button size="small" type="primary" @click="openUserSelect">选择人员</el-button>
</el-badge>
</el-form-item>
<el-form-item label="候选组">
<el-badge :value="selectRoleLength" :max="99">
<el-button size="small" type="primary" @click="openRoleSelect">选择组</el-button>
</el-badge>
</el-form-item>
</el-tab-pane>
<!-- <el-tab-pane label="固定值">
<el-form-item prop="auditUserType" label="分配类型">
<el-select v-model="formData.allocationType">
<el-option v-for="item in AllocationTypeSelect" :key="item.id" :value="item.value" :label="item.label"> </el-option>
</el-select>
</el-form-item>
<el-form-item v-if="formData.allocationType === AllocationTypeEnum.USER" label="分配人员">
<el-input v-model="formData.assignee">
<template #append>
<el-button icon="Search" type="primary" @click="openSingleUserSelect" />
</template>
</el-input>
</el-form-item>
<div v-if="formData.allocationType === AllocationTypeEnum.CANDIDATE">
<el-form-item label="候选人员">
<el-badge :value="selectUserLength" :max="99">
<el-button size="small" type="primary" @click="openUserSelect">选择人员</el-button>
</el-badge>
</el-form-item>
<el-form-item label="候选组">
<el-badge :value="selectRoleLength" :max="99">
<el-button size="small" type="primary" @click="openRoleSelect">选择组</el-button>
</el-badge>
</el-form-item>
</div>
<el-form-item v-if="formData.allocationType === AllocationTypeEnum.SPECIFY && showConfig.specifyDesc" style="">
<el-radio-group v-model="formData.specifyDesc" class="ml-4">
<el-radio v-for="item in SpecifyDesc" :key="item.id" :value="item.value" size="large">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
</el-tab-pane> -->
</el-tabs>
<el-form-item v-if="showConfig.dueDate" prop="dueDate" label="到期时间">
<el-input v-model="formData.dueDate" clearable @change="dueDateChange" @click="openDueDate">
<template #append>
<el-button icon="Search" type="primary" @click="openDueDate" />
</template>
</el-input>
</el-form-item>
<el-form-item v-if="showConfig.priority" prop="priority" label="优先级">
<el-input-number v-model="formData.priority" :min="0" @change="priorityChange"> </el-input-number>
</el-form-item>
</div>
</el-collapse-item>
<el-collapse-item name="3">
<template #title>
<div class="collapse__title">
<el-icon>
<HelpFilled />
</el-icon>
多实例
</div>
</template>
<div>
<el-form-item label="多实例类型">
<el-select v-model="formData.multiInstanceType" @change="multiInstanceTypeChange">
<el-option v-for="item in constant.MultiInstanceType" :key="item.id" :value="item.value" :label="item.label"> </el-option>
</el-select>
</el-form-item>
<div v-if="formData.multiInstanceType !== MultiInstanceTypeEnum.NONE">
<el-form-item label="集合">
<template #label>
<span>
集合
<el-tooltip placement="top">
<el-icon><QuestionFilled /></el-icon>
<template #content>
属性会作为表达式进行解析如果表达式解析为字符串而不是一个集合<br />
不论是因为本身配置的就是静态字符串值还是表达式计算结果为字符串<br />
这个字符串都会被当做变量名并从流程变量中用于获取实际的集合
</template>
</el-tooltip>
</span>
</template>
<el-input v-model="formData.collection" @change="collectionChange"></el-input>
</el-form-item>
<el-form-item label="元素变量">
<template #label>
<span>
元素变量
<el-tooltip placement="top">
<el-icon><QuestionFilled /></el-icon>
<template #content>
每创建一个用户任务前先以该元素变量为label集合中的一项为value<br />
创建局部流程变量该局部流程变量被用于指派用户任务<br />
一般来说该字符串应与指定人员变量相同
</template>
</el-tooltip>
</span>
</template>
<el-input v-model="formData.elementVariable" @change="elementVariableChange"> </el-input>
</el-form-item>
<el-form-item label="完成条件">
<template #label>
<span>
完成条件
<el-tooltip placement="top">
<el-icon><QuestionFilled /></el-icon>
<template #content>
多实例活动在所有实例都完成时结束然而也可以指定一个表达式在每个实例<br />
结束时进行计算当表达式计算为true时将销毁所有剩余的实例并结束多实例<br />
活动继续执行流程例如 ${nrOfCompletedInstances/nrOfInstances >= 0.6 }<br />
表示当任务完成60%该节点就算完成
</template>
</el-tooltip>
</span>
</template>
<el-input v-model="formData.completionCondition" @change="completionConditionChange"> </el-input>
</el-form-item>
</div>
</div>
</el-collapse-item>
<el-collapse-item v-if="showConfig.taskListener" name="4">
<template #title>
<div class="collapse__title">
<el-icon>
<BellFilled />
</el-icon>
任务监听器
</div>
</template>
<div>
<TaskListener v-if="showConfig.taskListener" :element="element"></TaskListener>
</div>
</el-collapse-item>
<el-collapse-item v-if="showConfig.executionListener" name="5">
<template #title>
<div class="collapse__title">
<el-icon>
<BellFilled />
</el-icon>
执行监听器
</div>
</template>
<div>
<ExecutionListener v-if="showConfig.executionListener" :element="element"></ExecutionListener>
</div>
</el-collapse-item>
<el-form-item v-if="showConfig.isForCompensation" prop="isForCompensation" label="是否为补偿">
<el-switch v-model="formData.isForCompensation" inline-prompt active-text="" inactive-text="" />
</el-form-item>
<el-form-item v-if="showConfig.triggerServiceTask" prop="triggerServiceTask" label="服务任务可触发">
<el-switch v-model="formData.triggerServiceTask" inline-prompt active-text="" inactive-text="" />
</el-form-item>
<el-form-item v-if="showConfig.autoStoreVariables" prop="autoStoreVariables" label="自动存储变量">
<el-switch v-model="formData.autoStoreVariables" inline-prompt active-text="" inactive-text="" />
</el-form-item>
<el-form-item v-if="showConfig.ruleVariablesInput" prop="skipExpression" label="输入变量">
<el-input v-model="formData.ruleVariablesInput"> </el-input>
</el-form-item>
<el-form-item v-if="showConfig.exclude" prop="exclude" label="排除">
<el-switch v-model="formData.exclude" inline-prompt active-text="" inactive-text="" />
</el-form-item>
<el-form-item v-if="showConfig.class" prop="class" label="类">
<el-input v-model="formData.class"> </el-input>
</el-form-item>
</el-collapse>
</el-form>
<UserSelect ref="userSelectRef" :data="formData.candidateUsers" @confirm-call-back="userSelectCallBack"></UserSelect>
<UserSelect ref="singleUserSelectRef" :data="formData.assignee" :multiple="false" @confirm-call-back="singleUserSelectCallBack"></UserSelect>
<RoleSelect ref="roleSelectRef" :data="formData.candidateGroups" @confirm-call-back="roleSelectCallBack"></RoleSelect>
<DueDate ref="dueDateRef" v-model="formData.dueDate" :data="formData.dueDate" @confirm-call-back="dueDateCallBack"></DueDate>
</div>
</template>
<script setup lang="ts">
import useParseElement from '../hooks/useParseElement';
import usePanel from '../hooks/usePanel';
import UserSelect from '@/components/UserSelect';
import RoleSelect from '@/components/RoleSelect';
import ExecutionListener from './property/ExecutionListener.vue';
import TaskListener from './property/TaskListener.vue';
import DueDate from './property/DueDate.vue';
import type { ModdleElement } from 'bpmn';
import type { TaskPanel } from 'bpmnDesign';
import { AllocationTypeEnum, MultiInstanceTypeEnum, SpecifyDescEnum } from '@/enums/bpmn/IndexEnums';
import { UserVO } from '@/api/system/user/types';
import { RoleVO } from '@/api/system/role/types';
import { selectListFormManage } from '@/api/workflow/formManage';
import { FormManageVO } from '@/api/workflow/formManage/types';
const formManageList = ref<FormManageVO[]>([]);
const formManageListLoading = ref(false);
interface PropType {
element: ModdleElement;
}
const props = withDefaults(defineProps<PropType>(), {});
const { showConfig, nameChange, formKeyChange, idChange, updateProperties, getExtensionElements, createModdleElement, constant } = usePanel({
element: toRaw(props.element)
});
const { parseData } = useParseElement({
element: toRaw(props.element)
});
const initFormData = {
id: '',
name: '',
dueDate: '',
multiInstanceType: MultiInstanceTypeEnum.NONE,
allocationType: AllocationTypeEnum.USER,
specifyDesc: SpecifyDescEnum.SPECIFY_SINGLE
};
const formData = ref({ ...initFormData, ...parseData<TaskPanel>() });
const assignee = ref<Partial<UserVO>>({
userName: ''
});
const currentCollapseItem = ref(['1', '2']);
const userSelectRef = ref<InstanceType<typeof UserSelect>>();
const singleUserSelectRef = ref<InstanceType<typeof UserSelect>>();
const roleSelectRef = ref<InstanceType<typeof RoleSelect>>();
const dueDateRef = ref<InstanceType<typeof DueDate>>();
const openUserSelect = () => {
userSelectRef.value.open();
};
const openSingleUserSelect = () => {
if (formData.value.assignee?.includes('$')) {
formData.value.assignee = '';
}
singleUserSelectRef.value.open();
};
const openRoleSelect = () => {
roleSelectRef.value.open();
};
const openDueDate = (e) => {
dueDateRef.value.openDialog();
};
const blurAssignee = (assignee) => {
updateProperties({ 'flowable:assignee': assignee ? assignee : undefined });
};
const singleUserSelectCallBack = (data: UserVO[]) => {
const user: UserVO = data.length !== 0 ? data[0] : undefined;
updateProperties({ 'flowable:assignee': user?.userId });
assignee.value = user ? user : { userName: '' };
formData.value.assignee = String(user?.userId);
let extensionElements = getExtensionElements();
extensionElements.values = extensionElements.get('values').filter((item) => item.$type !== 'flowable:extAssignee');
if (user) {
const extAssigneeElement = createModdleElement('flowable:extAssignee', { body: '' }, extensionElements);
extensionElements.get('values').push(extAssigneeElement);
extAssigneeElement.body = JSON.stringify({ userName: user.userName, userId: user.userId });
}
if (extensionElements.values.length === 0) {
extensionElements = undefined;
}
updateProperties({ extensionElements: extensionElements });
};
const userSelectCallBack = (data: UserVO[]) => {
let extensionElements = getExtensionElements();
extensionElements.values = extensionElements.values.filter((item) => item.$type !== 'flowable:extCandidateUsers');
if (data.length === 0) {
formData.value.candidateUsers = undefined;
updateProperties({ 'flowable:candidateUsers': undefined });
} else {
const userIds = data.map((item) => item.userId).join(',');
formData.value.candidateUsers = userIds;
updateProperties({ 'flowable:candidateUsers': userIds });
const extCandidateUsersElement = createModdleElement('flowable:extCandidateUsers', { body: '' }, extensionElements);
extensionElements.values.push(extCandidateUsersElement);
const users = data.map((item) => {
return {
userId: item.userId,
userName: item.userName
};
});
extCandidateUsersElement.body = JSON.stringify(users);
}
if (extensionElements.values.length === 0) {
extensionElements = undefined;
}
updateProperties({ extensionElements: extensionElements });
};
const roleSelectCallBack = (data: RoleVO[]) => {
if (data.length === 0) {
formData.value.candidateGroups = '';
updateProperties({ 'flowable:candidateGroups': undefined });
} else {
const roleIds = data.map((item) => item.roleId).join(',');
formData.value.candidateGroups = roleIds;
updateProperties({ 'flowable:candidateGroups': roleIds });
}
};
const dueDateCallBack = (data: string) => {
updateProperties({ 'flowable:dueDate': data });
};
const taskTabClick = (e) => {
formData.value.candidateGroups = '';
formData.value.candidateUsers = '';
formData.value.assignee = '';
// formData.value.fixedAssignee = '';
assignee.value = {};
};
const syncChange = (newVal) => {
updateProperties({ 'flowable:async': newVal });
};
const skipExpressionChange = (newVal) => {
updateProperties({ 'flowable:skipExpression': newVal && newVal.length > 0 ? newVal : undefined });
};
const priorityChange = (newVal) => {
updateProperties({ 'flowable:priority': newVal });
};
const fixedAssigneeChange = (newVal) => {
updateProperties({ 'flowable:assignee': newVal && newVal.length > 0 ? newVal : undefined });
};
const multiInstanceTypeChange = (newVal) => {
if (newVal !== MultiInstanceTypeEnum.NONE) {
let loopCharacteristics = props.element.businessObject.get('loopCharacteristics');
if (!loopCharacteristics) {
loopCharacteristics = createModdleElement('bpmn:MultiInstanceLoopCharacteristics', {}, props.element.businessObject);
}
loopCharacteristics.isSequential = newVal === MultiInstanceTypeEnum.SERIAL;
updateProperties({ loopCharacteristics: loopCharacteristics });
} else {
updateProperties({ loopCharacteristics: undefined });
}
};
const collectionChange = (newVal) => {
let loopCharacteristics = props.element.businessObject.get('loopCharacteristics');
if (!loopCharacteristics) {
loopCharacteristics = createModdleElement('bpmn:MultiInstanceLoopCharacteristics', {}, props.element.businessObject);
}
loopCharacteristics.collection = newVal && newVal.length > 0 ? newVal : undefined;
updateProperties({ loopCharacteristics: loopCharacteristics });
};
const elementVariableChange = (newVal) => {
let loopCharacteristics = props.element.businessObject.get('loopCharacteristics');
if (!loopCharacteristics) {
loopCharacteristics = createModdleElement('bpmn:MultiInstanceLoopCharacteristics', {}, props.element.businessObject);
}
loopCharacteristics.elementVariable = newVal && newVal.length > 0 ? newVal : undefined;
updateProperties({ loopCharacteristics: loopCharacteristics });
};
const completionConditionChange = (newVal) => {
let loopCharacteristics = props.element.businessObject.get<ModdleElement>('loopCharacteristics');
if (!loopCharacteristics) {
loopCharacteristics = createModdleElement('bpmn:MultiInstanceLoopCharacteristics', {}, props.element.businessObject);
}
if (newVal && newVal.length > 0) {
if (!loopCharacteristics.completionCondition) {
loopCharacteristics.completionCondition = createModdleElement('bpmn:Expression', { body: newVal }, loopCharacteristics);
} else {
loopCharacteristics.completionCondition.body = newVal;
}
} else {
loopCharacteristics.completionCondition = undefined;
}
updateProperties({ loopCharacteristics: loopCharacteristics });
};
const dueDateChange = (newVal) => {
updateProperties({ 'flowable:dueDate': newVal && newVal.length > 0 ? newVal : undefined });
};
const selectUserLength = computed(() => {
if (formData.value.candidateUsers) {
return formData.value.candidateUsers.split(',').length;
} else {
return 0;
}
});
const selectRoleLength = computed(() => {
if (formData.value.candidateGroups) {
return formData.value.candidateGroups.split(',').length;
} else {
return 0;
}
});
onBeforeMount(() => {
const extensionElements = getExtensionElements(false);
if (extensionElements && extensionElements.get('values')) {
let extAssigneeElement = extensionElements.get('values').find((item) => item.$type === 'flowable:extAssignee');
if (extAssigneeElement) {
assignee.value = JSON.parse(extAssigneeElement.body);
}
}
if (formData.value.loopCharacteristics) {
const loopCharacteristics = formData.value.loopCharacteristics;
formData.value.collection = loopCharacteristics.collection || '';
formData.value.elementVariable = loopCharacteristics.elementVariable || '';
formData.value.completionCondition = loopCharacteristics.completionCondition?.body || '';
formData.value.multiInstanceType = loopCharacteristics.isSequential ? MultiInstanceTypeEnum.SERIAL : MultiInstanceTypeEnum.PARALLEL;
}
if (formData.value.assignee) {
formData.value.fixedAssignee = formData.value.assignee;
}
});
const formRules = ref<ElFormRules>({
id: [{ required: true, message: '请输入', trigger: 'blur' }],
name: [{ required: true, message: '请输入', trigger: 'blur' }]
});
const AllocationTypeSelect = [
{ id: 'b9cdf970-dd91-47c0-819f-42a7010ca2a6', label: '指定人员', value: AllocationTypeEnum.USER },
{ id: '3f7ccbcd-c464-4602-bb9d-e96649d10585', label: '候选人员', value: AllocationTypeEnum.CANDIDATE },
{ id: 'c49065e0-7f2d-4c09-aedb-ab2d47d9a454', label: '发起人自己', value: AllocationTypeEnum.YOURSELF },
{ id: '6ef40a03-7e9a-4898-89b2-c88fe9064542', label: '发起人指定', value: AllocationTypeEnum.SPECIFY }
];
const SpecifyDesc = [
{ id: 'fa253b34-4335-458c-b1bc-b039e2a2b7a6', label: '指定一个人', value: 'specifySingle' },
{ id: '7365ff54-2e05-4312-9bfb-0b8edd779c5b', label: '指定多个人', value: 'specifyMultiple' }
];
const listFormManage = async () => {
formManageListLoading.value = true;
const res = await selectListFormManage();
formManageList.value = res.data;
formManageListLoading.value = false;
};
onMounted(() => {
nextTick(() => {
listFormManage();
});
});
</script>
<style lang="scss" scoped></style>

110
im-admin-ui/src/bpmn/panel/index.vue

@ -1,110 +0,0 @@
<template>
<div ref="propertyPanel">
<div v-if="nodeName" class="node-name">{{ nodeName }}</div>
<component :is="component" v-if="element" :element="element" />
</div>
</template>
<script setup lang="ts" name="PropertyPanel">
import { NodeName } from '../assets/lang/zh';
import TaskPanel from './TaskPanel.vue';
import ProcessPanel from './ProcessPanel.vue';
import StartEndPanel from './StartEndPanel.vue';
import GatewayPanel from './GatewayPanel.vue';
import SequenceFlowPanel from './SequenceFlowPanel.vue';
import ParticipantPanel from './ParticipantPanel.vue';
import SubProcessPanel from './SubProcessPanel.vue';
import type { Modeler, ModdleElement } from 'bpmn';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
interface propsType {
modeler: Modeler;
}
const props = withDefaults(defineProps<propsType>(), {});
const element = ref<ModdleElement>();
const processElement = ref<ModdleElement>();
const startEndType = ['bpmn:IntermediateThrowEvent', 'bpmn:StartEvent', 'bpmn:EndEvent'];
const taskType = [
'bpmn:UserTask',
'bpmn:Task',
'bpmn:SendTask',
'bpmn:ReceiveTask',
'bpmn:ManualTask',
'bpmn:BusinessRuleTask',
'bpmn:ServiceTask',
'bpmn:ScriptTask'
];
const sequenceType = ['bpmn:SequenceFlow'];
const gatewayType = ['bpmn:InclusiveGateway', 'bpmn:ExclusiveGateway', 'bpmn:ParallelGateway', 'bpmn:EventBasedGateway', 'bpmn:ComplexGateway'];
const processType = ['bpmn:Process'];
//
const component = computed(() => {
if (!element.value) return null;
const type = element.value.type;
if (startEndType.includes(type)) return StartEndPanel;
if (taskType.includes(type)) return TaskPanel;
if (sequenceType.includes(type)) return SequenceFlowPanel;
if (gatewayType.includes(type)) return GatewayPanel;
if (processType.includes(type)) return ProcessPanel;
if (type === 'bpmn:Participant') return ParticipantPanel;
if (type === 'bpmn:SubProcess') return SubProcessPanel;
//return proxy?.$modal.msgWarning('....');
return undefined;
});
const nodeName = computed(() => {
if (element.value) {
const bizObj = element.value.businessObject;
const type = bizObj?.eventDefinitions && bizObj?.eventDefinitions.length > 0 ? bizObj.eventDefinitions[0].$type : bizObj.$type;
return NodeName[type] || type;
}
return '';
});
const handleModeler = () => {
props.modeler.on('root.added', (e: any) => {
element.value = null;
if (e.element.type === 'bpmn:Process') {
nextTick(() => {
element.value = e.element;
processElement.value = e.element;
});
}
});
props.modeler.on('element.click', (e: any) => {
if (e.element.type === 'bpmn:Process') {
nextTick(() => {
element.value = e.element;
processElement.value = e.element;
});
}
});
props.modeler.on('selection.changed', (e: any) => {
// nullvue
element.value = null;
const newElement = e.newSelection[0];
if (newElement) {
nextTick(() => {
element.value = newElement;
});
} else {
nextTick(() => {
element.value = processElement.value;
});
}
});
};
onMounted(() => {
handleModeler();
});
</script>
<style scoped lang="scss">
.node-name {
font-size: 16px;
font-weight: bold;
padding: 10px;
}
</style>

252
im-admin-ui/src/bpmn/panel/property/DueDate.vue

@ -1,252 +0,0 @@
<template>
<div>
<el-dialog v-model="visible" :title="title" width="600px" append-to-body>
<el-form label-width="100px">
<el-form-item label="小时">
<el-radio-group v-model="hourValue" @change="hourChange">
<el-radio-button label="4" value="4" />
<el-radio-button label="8" value="8" />
<el-radio-button label="12" value="12" />
<el-radio-button label="24" value="24" />
<el-radio-button label="自定义" value="自定义" />
<el-input-number v-show="hourValue === '自定义'" v-model="customHourValue" :min="1" @change="customHourValueChange"></el-input-number>
</el-radio-group>
</el-form-item>
<el-form-item label="天">
<el-radio-group v-model="dayValue" @change="dayChange">
<el-radio-button label="1" value="1" />
<el-radio-button label="2" value="2" />
<el-radio-button label="3" value="3" />
<el-radio-button label="4" value="4" />
<el-radio-button label="自定义" value="自定义" />
<el-input-number v-show="dayValue === '自定义'" v-model="customDayValue" :min="1" @change="customDayValueChange"></el-input-number>
</el-radio-group>
</el-form-item>
<el-form-item label="周">
<el-radio-group v-model="weekValue" @change="weekChange">
<el-radio-button label="1" value="1" />
<el-radio-button label="2" value="2" />
<el-radio-button label="3" value="3" />
<el-radio-button label="4" value="4" />
<el-radio-button label="自定义" value="自定义" />
<el-input-number v-show="weekValue === '自定义'" v-model="customWeekValue" :min="1" @change="customWeekValueChange"></el-input-number>
</el-radio-group>
</el-form-item>
<el-form-item label="月">
<el-radio-group v-model="monthValue" @change="monthChange">
<el-radio-button label="1" value="1" />
<el-radio-button label="2" value="2" />
<el-radio-button label="3" value="3" />
<el-radio-button label="4" value="4" />
<el-radio-button label="自定义" value="自定义" />
<el-input-number v-show="monthValue === '自定义'" v-model="customMonthValue" :min="1" @change="customMonthValueChange"></el-input-number>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
<div>
<el-button @click="closeDialog">取消</el-button>
<el-button type="primary" @click="confirm">确定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import useDialog from '@/hooks/useDialog';
interface PropType {
modelValue?: string;
data?: string;
}
const prop = withDefaults(defineProps<PropType>(), {
modelValue: '',
data: ''
});
const emit = defineEmits(['update:modelValue', 'confirmCallBack']);
const { title, visible, openDialog, closeDialog } = useDialog({
title: '设置任务到期时间'
});
const formValue = ref();
const valueType = ref();
const hourValue = ref('');
const dayValue = ref('');
const weekValue = ref('');
const monthValue = ref('');
const customHourValue = ref(1);
const customDayValue = ref(1);
const customWeekValue = ref(1);
const customMonthValue = ref(1);
const hourValueConst = ['4', '8', '12', '24'];
const dayAndWeekAndMonthValueConst = ['1', '2', '3', '4'];
const initValue = () => {
formValue.value = prop.data;
if (prop.data) {
const lastStr = prop.data.substring(prop.data.length - 1);
if (lastStr === 'H') {
const hourValueValue = prop.data.substring(2, prop.data.length - 1);
if (hourValueConst.includes(hourValueValue)) {
hourValue.value = hourValueValue;
} else {
hourValue.value = '自定义';
customHourValue.value = Number(hourValueValue);
}
}
const dayAndWeekAndMonthValue = prop.data.substring(1, prop.data.length - 1);
if (lastStr === 'D') {
if (dayAndWeekAndMonthValueConst.includes(dayAndWeekAndMonthValue)) {
dayValue.value = dayAndWeekAndMonthValue;
} else {
dayValue.value = '自定义';
customDayValue.value = Number(dayAndWeekAndMonthValue);
}
}
if (lastStr === 'W') {
if (dayAndWeekAndMonthValueConst.includes(dayAndWeekAndMonthValue)) {
weekValue.value = dayAndWeekAndMonthValue;
} else {
weekValue.value = '自定义';
customWeekValue.value = Number(dayAndWeekAndMonthValue);
}
}
if (lastStr === 'M') {
if (dayAndWeekAndMonthValueConst.includes(dayAndWeekAndMonthValue)) {
monthValue.value = dayAndWeekAndMonthValue;
} else {
monthValue.value = '自定义';
customMonthValue.value = Number(dayAndWeekAndMonthValue);
}
}
}
};
const confirm = () => {
emit('update:modelValue', formValue.value);
emit('confirmCallBack', formValue.value);
closeDialog();
};
const customHourValueChange = (customHourValue) => {
formValue.value = `PT${customHourValue}H`;
dayValue.value = '';
weekValue.value = '';
monthValue.value = '';
customDayValue.value = 1;
customWeekValue.value = 1;
customMonthValue.value = 1;
};
const customDayValueChange = (customDayValue) => {
formValue.value = `P${customDayValue}D`;
hourValue.value = '';
weekValue.value = '';
monthValue.value = '';
customHourValue.value = 1;
customWeekValue.value = 1;
customMonthValue.value = 1;
};
const customWeekValueChange = (customWeekValue) => {
formValue.value = `P${customWeekValue}W`;
hourValue.value = '';
dayValue.value = '';
monthValue.value = '';
customHourValue.value = 1;
customDayValue.value = 1;
customMonthValue.value = 1;
};
const customMonthValueChange = (customMonthValue) => {
formValue.value = `P${customMonthValue}M`;
hourValue.value = '';
dayValue.value = '';
weekValue.value = '';
customHourValue.value = 1;
customDayValue.value = 1;
customWeekValue.value = 1;
};
const hourChange = (hourValue) => {
if (hourValue === '自定义') {
formValue.value = `PT${customHourValue.value}H`;
} else {
formValue.value = `PT${hourValue}H`;
}
dayValue.value = '';
weekValue.value = '';
monthValue.value = '';
customDayValue.value = 1;
customWeekValue.value = 1;
customMonthValue.value = 1;
};
const dayChange = (dayValue) => {
if (dayValue === '自定义') {
formValue.value = `P${customDayValue.value}D`;
} else {
formValue.value = `P${dayValue}D`;
}
hourValue.value = '';
weekValue.value = '';
monthValue.value = '';
customHourValue.value = 1;
customWeekValue.value = 1;
customMonthValue.value = 1;
};
const weekChange = (weekValue) => {
if (weekValue === '自定义') {
formValue.value = `P${customWeekValue.value}W`;
} else {
formValue.value = `P${weekValue}W`;
}
hourValue.value = '';
dayValue.value = '';
monthValue.value = '';
customHourValue.value = 1;
customDayValue.value = 1;
customMonthValue.value = 1;
};
const monthChange = (monthValue) => {
if (monthValue === '自定义') {
formValue.value = `P${customMonthValue.value}M`;
} else {
formValue.value = `P${monthValue}M`;
}
hourValue.value = '';
dayValue.value = '';
weekValue.value = '';
customHourValue.value = 1;
customDayValue.value = 1;
customWeekValue.value = 1;
};
watch(
() => visible.value,
() => {
if (visible.value) {
initValue();
}
}
);
defineExpose({
openDialog,
closeDialog
});
</script>

308
im-admin-ui/src/bpmn/panel/property/ExecutionListener.vue

@ -1,308 +0,0 @@
<template>
<div>
<vxe-toolbar>
<template #buttons>
<el-button type="primary" link size="small" @click="insertEvent">新增</el-button>
<el-button type="primary" link size="small" @click="removeSelectRowEvent">删除</el-button>
</template>
</vxe-toolbar>
<vxe-table
ref="tableRef"
size="mini"
height="100px"
border
show-overflow
keep-source
:data="tableData"
:menu-config="menuConfig"
@cell-dblclick="cellDBLClickEvent"
@menu-click="contextMenuClickEvent"
>
<vxe-column type="checkbox" width="40"></vxe-column>
<vxe-column type="seq" width="40"></vxe-column>
<vxe-column field="event" title="事件" min-width="100px">
<template #default="slotParams">
<span>{{ eventSelect.find((e) => e.value === slotParams.row.event)?.label }}</span>
</template>
</vxe-column>
<vxe-column field="type" title="类型" min-width="100px">
<template #default="slotParams">
<span>{{ typeSelect.find((e) => e.value === slotParams.row.type)?.label }}</span>
</template>
</vxe-column>
<vxe-column field="className" title="Java 类名" min-width="100px"> </vxe-column>
</vxe-table>
<el-dialog
v-model="formDialog.visible.value"
:title="formDialog.title.value"
width="600px"
:close-on-click-modal="false"
:close-on-press-escape="false"
:show-close="false"
append-to-body
>
<el-form ref="formRef" :model="formData" :rules="tableRules" label-width="100px">
<el-form-item label="事件" prop="event">
<el-select v-model="formData.event">
<el-option v-for="item in eventSelect" :key="item.id" :value="item.value" :label="item.label"></el-option>
</el-select>
</el-form-item>
<el-form-item label="类型" prop="type">
<template #label>
<span>
类型
<el-tooltip placement="top">
<el-icon><QuestionFilled /></el-icon>
<template #content>
示例 com.company.MyCustomListener自定义类必须实现 org.flowable.engine.delegate.TaskListener 接口<br />
表达式示例 ${myObject.callMethod(task, task.eventName)}<br />
委托表达式示例 ${myListenerSpringBean} springBean 需要实现 org.flowable.engine.delegate.TaskListener 接口
</template>
</el-tooltip>
</span>
</template>
<el-select v-model="formData.type">
<el-option v-for="item in typeSelect" :key="item.id" :value="item.value" :label="item.label"></el-option>
</el-select>
</el-form-item>
<el-form-item
:label="typeSelect.filter((e) => e.value === formData.type)[0] ? typeSelect.filter((e) => e.value === formData.type)[0]?.label : '表达式'"
prop="className"
>
<el-input v-model="formData.className" type="text"></el-input>
</el-form-item>
</el-form>
<el-tabs type="border-card">
<el-tab-pane label="参数">
<ListenerParam ref="listenerParamRef" :table-data="formData.params" />
</el-tab-pane>
</el-tabs>
<template #footer>
<div class="dialog-footer">
<el-button @click="formDialog.closeDialog"> </el-button>
<el-button type="primary" @click="submitEvent"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import ListenerParam from './ListenerParam.vue';
import { VxeTableEvents, VxeTableInstance, VxeTablePropTypes } from 'vxe-table';
import type { ExecutionListenerVO } from 'bpmnDesign';
import type { Moddle, Modeler, ModdleElement } from 'bpmn';
import usePanel from '../../hooks/usePanel';
import useDialog from '@/hooks/useDialog';
import useModelerStore from '@/store/modules/modeler';
const emit = defineEmits(['close']);
interface PropType {
element: ModdleElement;
}
const props = withDefaults(defineProps<PropType>(), {});
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const selectRow = ref<ExecutionListenerVO | null>();
const formDialog = useDialog({
title: selectRow.value ? '编辑&保存' : '新增&保存'
});
const { showConfig, elementType, updateProperties } = usePanel({
element: toRaw(props.element)
});
const { getModdle } = useModelerStore();
const moddle = getModdle();
const listenerParamRef = ref<InstanceType<typeof ListenerParam>>();
const tableRef = ref<VxeTableInstance<ExecutionListenerVO>>();
const formRef = ref<ElFormInstance>();
const initData: ExecutionListenerVO = {
event: '',
type: '',
className: '',
params: []
};
const formData = ref<ExecutionListenerVO>({ ...initData });
const tableData = ref<ExecutionListenerVO[]>([]);
const tableRules = ref<ElFormRules>({
event: [{ required: true, message: '请选择', trigger: 'blur' }],
type: [{ required: true, message: '请选择', trigger: 'blur' }],
className: [{ required: true, message: '请输入', trigger: 'blur' }]
});
const submitEvent = async () => {
const error = await listenerParamRef.value.validate();
await formRef.value.validate((validate) => {
if (validate && !error) {
const $table = tableRef.value;
if ($table) {
formData.value.params = listenerParamRef.value.getTableData();
if (selectRow.value) {
Object.assign(selectRow.value, formData.value);
} else {
$table.insertAt({ ...formData.value }, -1);
}
updateElement();
formDialog.closeDialog();
}
}
});
};
const removeSelectRowEvent = async () => {
const $table = tableRef.value;
if ($table) {
const selectCount = $table.getCheckboxRecords().length;
if (selectCount === 0) {
proxy?.$modal.msgWarning('请选择行');
} else {
await $table.removeCheckboxRow();
updateElement();
}
}
};
const insertEvent = async () => {
Object.assign(formData.value, initData);
selectRow.value = null;
formDialog.openDialog();
};
const editEvent = (row: ExecutionListenerVO) => {
Object.assign(formData.value, row);
selectRow.value = row;
formDialog.openDialog();
};
const removeEvent = async (row: ExecutionListenerVO) => {
await proxy?.$modal.confirm('您确定要删除该数据?');
const $table = tableRef.value;
if ($table) {
await $table.remove(row);
updateElement();
}
};
const updateElement = () => {
const $table = tableRef.value;
const data = $table.getTableData().fullData;
if (data.length) {
let extensionElements = props.element.businessObject.get('extensionElements');
if (!extensionElements) {
extensionElements = moddle.create('bpmn:ExtensionElements');
}
//
extensionElements.values = extensionElements.values?.filter((item) => item.$type !== 'flowable:ExecutionListener') ?? [];
data.forEach((item) => {
const executionListener = moddle.create('flowable:ExecutionListener');
executionListener['event'] = item.event;
executionListener[item.type] = item.className;
if (item.params && item.params.length) {
item.params.forEach((field) => {
const fieldElement = moddle.create('flowable:Field');
fieldElement['name'] = field.name;
fieldElement[field.type] = field.value;
executionListener.get('fields').push(fieldElement);
});
}
extensionElements.get('values').push(executionListener);
});
updateProperties({ extensionElements: extensionElements });
} else {
const extensionElements = props.element.businessObject[`extensionElements`];
if (extensionElements) {
extensionElements.values = extensionElements.values?.filter((item) => item.$type !== 'flowable:ExecutionListener') ?? [];
}
}
};
const cellDBLClickEvent: VxeTableEvents.CellDblclick<ExecutionListenerVO> = ({ row }) => {
editEvent(row);
};
const menuConfig = reactive<VxeTablePropTypes.MenuConfig<ExecutionListenerVO>>({
body: {
options: [
[
{ code: 'edit', name: '编辑', prefixIcon: 'vxe-icon-edit', disabled: false },
{ code: 'remove', name: '删除', prefixIcon: 'vxe-icon-delete', disabled: false }
]
]
},
visibleMethod({ options, column }) {
const isDisabled = !column;
options.forEach((list) => {
list.forEach((item) => {
item.disabled = isDisabled;
});
});
return true;
}
});
const contextMenuClickEvent: VxeTableEvents.MenuClick<ExecutionListenerVO> = ({ menu, row, column }) => {
const $table = tableRef.value;
if ($table) {
switch (menu.code) {
case 'edit':
editEvent(row);
break;
case 'remove':
removeEvent(row);
break;
}
}
};
const initTableData = () => {
tableData.value =
props.element.businessObject.extensionElements?.values
.filter((item) => item.$type === 'flowable:ExecutionListener')
.map((item) => {
let type;
if ('class' in item) type = 'class';
if ('expression' in item) type = 'expression';
if ('delegateExpression' in item) type = 'delegateExpression';
return {
event: item.event,
type: type,
className: item[type],
params:
item.fields?.map((field) => {
let fieldType;
if ('stringValue' in field) fieldType = 'stringValue';
if ('expression' in field) fieldType = 'expression';
return {
name: field.name,
type: fieldType,
value: field[fieldType]
};
}) ?? []
};
}) ?? [];
};
onMounted(() => {
initTableData();
});
const typeSelect = [
{ id: '742fdeb7-23b4-416b-ac66-cd4ec8b901b7', label: '类', value: 'class' },
{ id: '660c9c46-8fae-4bae-91a0-0335420019dc', label: '表达式', value: 'expression' },
{ id: '4b8135ab-6bc3-4a0f-80be-22f58bc6c5fd', label: '委托表达式', value: 'delegateExpression' }
];
const eventSelect = [
{ id: 'e6e0a51a-2d5d-4dc4-b847-b5c14f43a6ab', label: '开始', value: 'start' },
{ id: '6da97c1e-15fc-4445-8943-75d09f49778e', label: '结束', value: 'end' },
{ id: '6a2cbcec-e026-4f11-bef7-fff0b5c871e2', label: '启用', value: 'take' }
];
</script>
<style scoped lang="scss">
.el-badge {
:deep(.el-badge__content) {
top: 10px;
}
}
</style>

121
im-admin-ui/src/bpmn/panel/property/ListenerParam.vue

@ -1,121 +0,0 @@
<template>
<vxe-toolbar>
<template #buttons>
<el-button icon="Plus" @click="insertRow">新增</el-button>
</template>
</vxe-toolbar>
<vxe-table
ref="tableRef"
:height="height"
border
show-overflow
keep-source
:data="tableData"
:edit-rules="tableRules"
:edit-config="{ trigger: 'click', mode: 'row', showStatus: true }"
>
<vxe-column type="seq" width="40"></vxe-column>
<vxe-column field="type" title="类型" :edit-render="{}">
<template #default="slotParams">
<span>{{ typeSelect.find((e) => e.value === slotParams.row.type)?.label }}</span>
</template>
<template #edit="slotParams">
<vxe-select v-model="slotParams.row.type">
<vxe-option v-for="item in typeSelect" :key="item.id" :value="item.value" :label="item.label"></vxe-option>
</vxe-select>
</template>
</vxe-column>
<vxe-column field="name" title="名称" :edit-render="{}">
<template #edit="slotParams">
<vxe-input v-model="slotParams.row.name" type="text"></vxe-input>
</template>
</vxe-column>
<vxe-column field="value" title="值" :edit-render="{}">
<template #edit="slotParams">
<vxe-input v-model="slotParams.row.value" type="text"></vxe-input>
</template>
</vxe-column>
<vxe-column title="操作" width="100" show-overflow align="center">
<template #default="slotParams">
<el-tooltip content="删除" placement="top">
<el-button link type="danger" icon="Delete" @click="removeRow(slotParams.row)"></el-button>
</el-tooltip>
</template>
</vxe-column>
</vxe-table>
</template>
<script setup lang="ts">
import { VXETable, VxeTableInstance, VxeTablePropTypes } from 'vxe-table';
import type { ParamVO } from 'bpmnDesign';
import useDialog from '@/hooks/useDialog';
interface PropType {
height?: string;
tableData?: ParamVO[];
}
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const props = withDefaults(defineProps<PropType>(), {
height: '200px',
tableData: () => []
});
const tableRules = ref<VxeTablePropTypes.EditRules>({
type: [{ required: true, message: '请选择', trigger: 'blur' }],
name: [{ required: true, message: '请输入', trigger: 'blur' }],
value: [{ required: true, message: '请输入', trigger: 'blur' }]
});
const { title, visible, openDialog, closeDialog } = useDialog({
title: '监听器参数'
});
const typeSelect = [
{ id: '742fdeb7-23b4-416b-ac66-cd4ec8b901b7', label: '字符串', value: 'stringValue' },
{ id: '660c9c46-8fae-4bae-91a0-0335420019dc', label: '表达式', value: 'expression' }
];
const tableRef = ref<VxeTableInstance<ParamVO>>();
const getTableData = () => {
const $table = tableRef.value;
if ($table) {
return $table.getTableData().fullData;
}
return [];
};
const insertRow = async () => {
const $table = tableRef.value;
if ($table) {
const { row: newRow } = await $table.insertAt({}, -1);
//
await $table.validate(newRow);
}
};
const removeRow = async (row: ParamVO) => {
await proxy?.$modal.confirm('您确定要删除该数据?');
const $table = tableRef.value;
if ($table) {
await $table.remove(row);
}
};
const validate = async () => {
const $table = tableRef.value;
if ($table) {
return await $table.validate(true);
}
};
defineExpose({
closeDialog,
openDialog,
validate,
getTableData
});
</script>
<style scoped lang="scss"></style>

310
im-admin-ui/src/bpmn/panel/property/TaskListener.vue

@ -1,310 +0,0 @@
<template>
<div>
<vxe-toolbar>
<template #buttons>
<el-button type="primary" link size="small" @click="insertEvent">新增</el-button>
<el-button type="primary" link size="small" @click="removeSelectRowEvent">删除</el-button>
</template>
</vxe-toolbar>
<vxe-table
ref="tableRef"
size="mini"
height="100px"
border
show-overflow
keep-source
:data="tableData"
:menu-config="menuConfig"
@cell-dblclick="cellDBLClickEvent"
@menu-click="contextMenuClickEvent"
>
<vxe-column type="checkbox" width="40"></vxe-column>
<vxe-column type="seq" width="40"></vxe-column>
<vxe-column field="event" title="事件" min-width="100px">
<template #default="slotParams">
<span>{{ eventSelect.find((e) => e.value === slotParams.row.event)?.label }}</span>
</template>
</vxe-column>
<vxe-column field="type" title="类型" min-width="100px">
<template #default="slotParams">
<span>{{ typeSelect.find((e) => e.value === slotParams.row.type)?.label }}</span>
</template>
</vxe-column>
<vxe-column field="className" title="Java 类名" min-width="100px"> </vxe-column>
</vxe-table>
<el-dialog
v-model="formDialog.visible.value"
:title="formDialog.title.value"
width="600px"
:close-on-click-modal="false"
:close-on-press-escape="false"
:show-close="false"
append-to-body
>
<el-form ref="formRef" :model="formData" :rules="tableRules" label-width="100px">
<el-form-item label="事件" prop="event">
<template #label>
<span>
事件
<el-tooltip placement="top">
<el-icon><QuestionFilled /></el-icon>
<template #content>
create创建当任务已经创建并且所有任务参数都已经设置时触发<br />
assignment指派当任务已经指派给某人时触发请注意当流程执行到达用户任务时在触发create事件之前会首先触发assignment事件<br />
complete完成当任务已经完成从运行时数据中删除前触发<br />
delete删除在任务即将被删除前触发请注意任务由completeTask正常完成时也会触发
</template>
</el-tooltip>
</span>
</template>
<el-select v-model="formData.event">
<el-option v-for="item in eventSelect" :key="item.id" :value="item.value" :label="item.label"></el-option>
</el-select>
</el-form-item>
<el-form-item label="类型" prop="type">
<el-select v-model="formData.type">
<el-option v-for="item in typeSelect" :key="item.id" :value="item.value" :label="item.label"></el-option>
</el-select>
</el-form-item>
<el-form-item
:label="typeSelect.filter((e) => e.value === formData.type)[0] ? typeSelect.filter((e) => e.value === formData.type)[0]?.label : '表达式'"
prop="className"
>
<el-input v-model="formData.className" type="text"></el-input>
</el-form-item>
</el-form>
<el-tabs type="border-card">
<el-tab-pane label="参数">
<ListenerParam ref="listenerParamRef" :table-data="formData.params" />
</el-tab-pane>
</el-tabs>
<template #footer>
<div class="dialog-footer">
<el-button @click="formDialog.closeDialog"> </el-button>
<el-button type="primary" @click="submitEvent"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import ListenerParam from './ListenerParam.vue';
import { VxeTableEvents, VxeTableInstance, VxeTablePropTypes } from 'vxe-table';
import type { TaskListenerVO } from 'bpmnDesign';
import type { ModdleElement } from 'bpmn';
import usePanel from '../../hooks/usePanel';
import useDialog from '@/hooks/useDialog';
import useModelerStore from '@/store/modules/modeler';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
interface PropType {
element: ModdleElement;
}
const props = withDefaults(defineProps<PropType>(), {});
const selectRow = ref<TaskListenerVO | null>();
const formDialog = useDialog({
title: selectRow.value ? '编辑&保存' : '新增&保存'
});
const { showConfig, elementType, updateProperties } = usePanel({
element: toRaw(props.element)
});
const { getModdle } = useModelerStore();
const moddle = getModdle();
const listenerParamRef = ref<InstanceType<typeof ListenerParam>>();
const tableRef = ref<VxeTableInstance<TaskListenerVO>>();
const formRef = ref<ElFormInstance>();
const initData: TaskListenerVO = {
event: '',
type: '',
className: '',
name: '',
params: []
};
const formData = ref<TaskListenerVO>({ ...initData });
const currentIndex = ref(0);
const tableData = ref<TaskListenerVO[]>([]);
const tableRules = ref<VxeTablePropTypes.EditRules>({
event: [{ required: true, message: '请选择', trigger: 'blur' }],
type: [{ required: true, message: '请选择', trigger: 'blur' }],
name: [{ required: true, message: '请输入', trigger: 'blur' }],
className: [{ required: true, message: '请输入', trigger: 'blur' }]
});
const submitEvent = async () => {
const error = await listenerParamRef.value.validate();
await formRef.value.validate((validate) => {
if (validate && !error) {
const $table = tableRef.value;
if ($table) {
formData.value.params = listenerParamRef.value.getTableData();
if (selectRow.value) {
Object.assign(selectRow.value, formData.value);
} else {
$table.insertAt({ ...formData.value }, -1);
}
updateElement();
formDialog.closeDialog();
}
}
});
};
const insertEvent = async () => {
Object.assign(formData.value, initData);
selectRow.value = null;
formDialog.openDialog();
};
const editEvent = (row: TaskListenerVO) => {
Object.assign(formData.value, row);
selectRow.value = row;
formDialog.openDialog();
};
const removeEvent = async (row: TaskListenerVO) => {
await proxy?.$modal.confirm('您确定要删除该数据?');
const $table = tableRef.value;
if ($table) {
await $table.remove(row);
updateElement();
}
};
const removeSelectRowEvent = async () => {
const $table = tableRef.value;
if ($table) {
const selectCount = $table.getCheckboxRecords().length;
if (selectCount === 0) {
proxy?.$modal.msgWarning('请选择行');
} else {
await $table.removeCheckboxRow();
updateElement();
}
}
};
const updateElement = () => {
const $table = tableRef.value;
const data = $table.getTableData().fullData;
if (data.length) {
let extensionElements = props.element.businessObject.get('extensionElements');
if (!extensionElements) {
extensionElements = moddle.create('bpmn:ExtensionElements');
}
//
extensionElements.values = extensionElements.values?.filter((item) => item.$type !== 'flowable:TaskListener') ?? [];
data.forEach((item) => {
const taskListener = moddle.create('flowable:TaskListener');
taskListener['event'] = item.event;
taskListener[item.type] = item.className;
if (item.params && item.params.length) {
item.params.forEach((field) => {
const fieldElement = moddle.create('flowable:Field');
fieldElement['name'] = field.name;
fieldElement[field.type] = field.value;
taskListener.get('fields').push(fieldElement);
});
}
extensionElements.get('values').push(taskListener);
});
updateProperties({ extensionElements: extensionElements });
} else {
const extensionElements = props.element.businessObject[`extensionElements`];
if (extensionElements) {
extensionElements.values = extensionElements.values?.filter((item) => item.$type !== 'flowable:TaskListener') ?? [];
}
}
};
const cellDBLClickEvent: VxeTableEvents.CellDblclick<TaskListenerVO> = ({ row }) => {
editEvent(row);
};
const menuConfig = reactive<VxeTablePropTypes.MenuConfig<TaskListenerVO>>({
body: {
options: [
[
{ code: 'edit', name: '编辑', prefixIcon: 'vxe-icon-edit', disabled: false },
{ code: 'remove', name: '删除', prefixIcon: 'vxe-icon-delete', disabled: false }
]
]
},
visibleMethod({ options, column }) {
const isDisabled = !column;
options.forEach((list) => {
list.forEach((item) => {
item.disabled = isDisabled;
});
});
return true;
}
});
const contextMenuClickEvent: VxeTableEvents.MenuClick<TaskListenerVO> = ({ menu, row, column }) => {
const $table = tableRef.value;
if ($table) {
switch (menu.code) {
case 'edit':
editEvent(row);
break;
case 'remove':
removeEvent(row);
break;
}
}
};
const initTableData = () => {
tableData.value =
props.element.businessObject.extensionElements?.values
.filter((item) => item.$type === 'flowable:TaskListener')
.map((item) => {
let type;
if ('class' in item) type = 'class';
if ('expression' in item) type = 'expression';
if ('delegateExpression' in item) type = 'delegateExpression';
return {
event: item.event,
type: type,
className: item[type],
params:
item.fields?.map((field) => {
let fieldType;
if ('stringValue' in field) fieldType = 'stringValue';
if ('expression' in field) fieldType = 'expression';
return {
name: field.name,
type: fieldType,
value: field[fieldType]
};
}) ?? []
};
}) ?? [];
};
onMounted(() => {
initTableData();
});
const typeSelect = [
{ id: '742fdeb7-23b4-416b-ac66-cd4ec8b901b7', label: '类', value: 'class' },
{ id: '660c9c46-8fae-4bae-91a0-0335420019dc', label: '表达式', value: 'expression' },
{ id: '4b8135ab-6bc3-4a0f-80be-22f58bc6c5fd', label: '委托表达式', value: 'delegateExpression' }
];
const eventSelect = [
{ id: 'e6e0a51a-2d5d-4dc4-b847-b5c14f43a6ab', label: '创建', value: 'create' },
{ id: '6da97c1e-15fc-4445-8943-75d09f49778e', label: '指派', value: 'assignment' },
{ id: '6a2cbcec-e026-4f11-bef7-fff0b5c871e2', label: '完成', value: 'complete' },
{ id: '68801972-85f1-482f-bd86-1fad015c26ed', label: '删除', value: 'delete' }
];
</script>
<style scoped lang="scss">
.el-badge {
:deep(.el-badge__content) {
top: 10px;
}
}
</style>

71
im-admin-ui/src/components/BpmnDesign/index.vue

@ -1,71 +0,0 @@
<template>
<div class="design">
<el-dialog v-model="visible" width="100%" fullscreen :title="title">
<div class="modeler">
<bpmn-design ref="bpmnDesignRef" @save-call-back="saveCallBack"></bpmn-design>
</div>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="Design">
import { getInfo, editModelXml } from '@/api/workflow/model';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
import { ModelForm } from '@/api/workflow/model/types';
import BpmnDesign from '@/bpmn/index.vue';
import useDialog from '@/hooks/useDialog';
const bpmnDesignRef = ref<InstanceType<typeof BpmnDesign>>();
const modelForm = ref<ModelForm>();
const emit = defineEmits(['closeCallBack']);
const { visible, title } = useDialog({
title: '编辑流程'
});
const modelId = ref('');
const open = async (id) => {
visible.value = true;
modelId.value = id;
const { data } = await getInfo(id);
modelForm.value = data;
bpmnDesignRef.value.initDiagram(modelForm.value.xml);
};
//
const saveCallBack = async (data) => {
await proxy?.$modal.confirm('是否确认保存?');
data.loading.value = true;
modelForm.value.id = modelId.value;
modelForm.value.xml = data.xml;
modelForm.value.svg = data.svg;
modelForm.value.key = data.key;
modelForm.value.name = data.name;
editModelXml(modelForm.value).then((res) => {
if (res.code === 200) {
visible.value = false;
proxy?.$modal.msgSuccess('保存成功');
emit('closeCallBack', data);
}
});
data.loading.value = false;
};
/**
* 对外暴露子组件方法
*/
defineExpose({
open
});
</script>
<style lang="scss" scoped>
.design {
:deep(.el-dialog .el-dialog__body) {
max-height: 100% !important;
min-height: calc(100vh - 80px);
padding: 10px 0 10px 0 !important;
}
:deep(.el-dialog__header) {
padding: 0 0 5px 0 !important;
}
}
</style>

411
im-admin-ui/src/components/BpmnView/index.vue

@ -1,411 +0,0 @@
<template>
<div v-loading="loading" class="bpmnDialogContainers">
<el-header style="border-bottom: 1px solid rgb(218 218 218); height: auto">
<div class="header-div">
<div>
<el-tooltip effect="dark" content="自适应屏幕" placement="bottom">
<el-button size="small" icon="Rank" @click="fitViewport" />
</el-tooltip>
<el-tooltip effect="dark" content="放大" placement="bottom">
<el-button size="small" icon="ZoomIn" @click="zoomViewport(true)" />
</el-tooltip>
<el-tooltip effect="dark" content="缩小" placement="bottom">
<el-button size="small" icon="ZoomOut" @click="zoomViewport(false)" />
</el-tooltip>
</div>
<div>
<div class="tips-label">
<div class="un-complete">未完成</div>
<div class="in-progress">进行中</div>
<div class="complete">已完成</div>
</div>
</div>
</div>
</el-header>
<div class="flow-containers">
<el-container class="bpmn-el-container" style="align-items: stretch">
<el-main style="padding: 0">
<div ref="canvas" class="canvas" />
</el-main>
</el-container>
</div>
</div>
</template>
<script lang="ts" setup>
import BpmnViewer from 'bpmn-js/lib/Viewer';
import MoveCanvasModule from 'diagram-js/lib/navigation/movecanvas';
import ZoomScrollModule from 'diagram-js/lib/navigation/zoomscroll';
import { ModuleDeclaration } from 'didi';
import type { Canvas, ModdleElement } from 'bpmn';
import EventBus from 'diagram-js/lib/core/EventBus';
import Overlays from 'diagram-js/lib/features/overlays/Overlays';
import processApi from '@/api/workflow/processInstance/index';
const canvas = ref<HTMLElement>();
const modeler = ref<BpmnViewer>();
const taskList = ref([]);
const zoom = ref(1);
const xml = ref('');
const loading = ref(false);
const bpmnVisible = ref(true);
const historyList = ref([]);
const init = (businessKey) => {
loading.value = true;
bpmnVisible.value = true;
nextTick(async () => {
if (modeler.value) modeler.value.destroy();
modeler.value = new BpmnViewer({
container: canvas.value,
additionalModules: [
{
//
zoomScroll: ['value', '']
},
ZoomScrollModule,
MoveCanvasModule
] as ModuleDeclaration[]
});
const resp = await processApi.getHistoryList(businessKey);
xml.value = resp.data.xml;
taskList.value = resp.data.taskList;
historyList.value = resp.data.historyList;
await createDiagram(xml.value);
loading.value = false;
});
};
const initXml = (xmlStr: string) => {
loading.value = true;
bpmnVisible.value = true;
nextTick(async () => {
if (modeler.value) modeler.value.destroy();
modeler.value = new BpmnViewer({
container: canvas.value,
additionalModules: [
{
//
zoomScroll: ['value', '']
},
ZoomScrollModule,
MoveCanvasModule
] as ModuleDeclaration[]
});
xml.value = xmlStr;
await createDiagram(xml.value);
loading.value = false;
});
};
const createDiagram = async (data) => {
try {
await modeler.value.importXML(data);
fitViewport();
fillColor();
loading.value = false;
addEventBusListener();
} catch (err) {
console.log(err);
}
};
const addEventBusListener = () => {
const eventBus = modeler.value.get<EventBus>('eventBus');
const overlays = modeler.value.get<Overlays>('overlays');
eventBus.on<ModdleElement>('element.hover', (e) => {
let data = historyList.value.find((t) => t.taskDefinitionKey === e.element.id);
if (e.element.type === 'bpmn:UserTask' && data) {
setTimeout(() => {
genNodeDetailBox(e, overlays, data);
}, 10);
}
});
eventBus.on('element.out', (e) => {
overlays.clear();
});
};
const genNodeDetailBox = (e, overlays, data) => {
overlays.add(e.element.id, {
position: { top: e.element.height, left: 0 },
html: `<div class="verlays">
<p>审批人员: ${data.nickName || ''}<p/>
<p>节点状态${data.status || ''}</p>
<p>开始时间${data.startTime || ''}</p>
<p>结束时间${data.endTime || ''}</p>
<p>审批耗时${data.runDuration || ''}</p>
<p>流程版本v${data.version || ''}</p>
</div>`
});
};
//
const fitViewport = () => {
zoom.value = modeler.value.get<Canvas>('canvas').zoom('fit-viewport');
const bbox = document.querySelector<SVGGElement>('.flow-containers .viewport').getBBox();
const currentViewBox = modeler.value.get('canvas').viewbox();
const elementMid = {
x: bbox.x + bbox.width / 2 - 65,
y: bbox.y + bbox.height / 2
};
modeler.value.get<Canvas>('canvas').viewbox({
x: elementMid.x - currentViewBox.width / 2,
y: elementMid.y - currentViewBox.height / 2,
width: currentViewBox.width,
height: currentViewBox.height
});
zoom.value = (bbox.width / currentViewBox.width) * 1.8;
};
//
const zoomViewport = (zoomIn = true) => {
zoom.value = modeler.value.get<Canvas>('canvas').zoom();
zoom.value += zoomIn ? 0.1 : -0.1;
modeler.value.get<Canvas>('canvas').zoom(zoom.value);
};
//
const fillColor = () => {
const canvas = modeler.value.get<Canvas>('canvas');
bpmnNodeList(modeler.value._definitions.rootElements[0].flowElements, canvas);
};
//
const bpmnNodeList = (flowElements, canvas) => {
flowElements.forEach((n) => {
if (n.$type === 'bpmn:UserTask') {
const completeTask = taskList.value.find((m) => m.key === n.id);
if (completeTask) {
canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo');
n.outgoing?.forEach((nn) => {
const targetTask = taskList.value.find((m) => m.key === nn.targetRef.id);
if (targetTask) {
canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo');
} else if (nn.targetRef.$type === 'bpmn:ExclusiveGateway') {
canvas.addMarker(nn.id, completeTask.completed ? 'highlight' : 'highlight-todo');
canvas.addMarker(nn.targetRef.id, completeTask.completed ? 'highlight' : 'highlight-todo');
nn.targetRef.outgoing.forEach((e) => {
gateway(e.id, e.targetRef.$type, e.targetRef.id, canvas, completeTask.completed);
});
} else if (nn.targetRef.$type === 'bpmn:ParallelGateway') {
canvas.addMarker(nn.id, completeTask.completed ? 'highlight' : 'highlight-todo');
canvas.addMarker(nn.targetRef.id, completeTask.completed ? 'highlight' : 'highlight-todo');
nn.targetRef.outgoing.forEach((e) => {
gateway(e.id, e.targetRef.$type, e.targetRef.id, canvas, completeTask.completed);
});
} else if (nn.targetRef.$type === 'bpmn:InclusiveGateway') {
canvas.addMarker(nn.id, completeTask.completed ? 'highlight' : 'highlight-todo');
canvas.addMarker(nn.targetRef.id, completeTask.completed ? 'highlight' : 'highlight-todo');
nn.targetRef.outgoing.forEach((e) => {
gateway(e.id, e.targetRef.$type, e.targetRef.id, canvas, completeTask.completed);
});
}
});
}
} else if (n.$type === 'bpmn:ExclusiveGateway') {
n.outgoing.forEach((nn) => {
const targetTask = taskList.value.find((m) => m.key === nn.targetRef.id);
if (targetTask) {
canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo');
}
});
} else if (n.$type === 'bpmn:ParallelGateway') {
n.outgoing.forEach((nn) => {
const targetTask = taskList.value.find((m) => m.key === nn.targetRef.id);
if (targetTask) {
canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo');
}
});
} else if (n.$type === 'bpmn:InclusiveGateway') {
n.outgoing.forEach((nn) => {
const targetTask = taskList.value.find((m) => m.key === nn.targetRef.id);
if (targetTask) {
canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo');
}
});
} else if (n.$type === 'bpmn:SubProcess') {
const completeTask = taskList.value.find((m) => m.key === n.id);
if (completeTask) {
canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo');
}
bpmnNodeList(n.flowElements, canvas);
} else if (n.$type === 'bpmn:StartEvent') {
canvas.addMarker(n.id, 'startEvent');
if (n.outgoing) {
n.outgoing.forEach((nn) => {
const completeTask = taskList.value.find((m) => m.key === nn.targetRef.id);
if (completeTask) {
canvas.addMarker(nn.id, 'highlight');
canvas.addMarker(n.id, 'highlight');
}
});
}
} else if (n.$type === 'bpmn:EndEvent') {
canvas.addMarker(n.id, 'endEvent');
const completeTask = taskList.value.find((m) => m.key === n.id);
if (completeTask) {
canvas.addMarker(completeTask.key, 'highlight');
canvas.addMarker(n.id, 'highlight');
return;
}
}
});
};
const gateway = (id, targetRefType, targetRefId, canvas, completed) => {
if (targetRefType === 'bpmn:ExclusiveGateway') {
canvas.addMarker(id, completed ? 'highlight' : 'highlight-todo');
canvas.addMarker(targetRefId, completed ? 'highlight' : 'highlight-todo');
}
if (targetRefType === 'bpmn:ParallelGateway') {
canvas.addMarker(id, completed ? 'highlight' : 'highlight-todo');
canvas.addMarker(targetRefId, completed ? 'highlight' : 'highlight-todo');
}
if (targetRefType === 'bpmn:InclusiveGateway') {
canvas.addMarker(id, completed ? 'highlight' : 'highlight-todo');
canvas.addMarker(targetRefId, completed ? 'highlight' : 'highlight-todo');
}
};
defineExpose({
init,
initXml
});
</script>
<style lang="scss" scoped>
.canvas {
width: 100%;
height: 100%;
}
.header-div {
display: flex;
padding: 10px 0;
justify-content: space-between;
.tips-label {
display: flex;
div {
margin-right: 10px;
padding: 5px;
font-size: 12px;
}
.un-complete {
border: 1px solid #000;
}
.in-progress {
background-color: rgb(255, 237, 204);
border: 1px dashed orange;
}
.complete {
background-color: rgb(204, 230, 204);
border: 1px solid green;
}
}
}
.view-mode {
.el-header,
.el-aside,
.djs-palette,
.bjs-powered-by {
display: none;
}
.el-loading-mask {
background-color: initial;
}
.el-loading-spinner {
display: none;
}
}
.bpmn-el-container {
height: calc(100vh - 350px);
}
.flow-containers {
width: 100%;
height: 100%;
overflow-y: auto;
.canvas {
width: 100%;
height: 100%;
}
.load {
margin-right: 10px;
}
:deep(.el-form-item__label) {
font-size: 13px;
}
:deep(.djs-palette) {
left: 0 !important;
top: 0;
border-top: none;
}
:deep(.djs-container svg) {
min-height: 650px;
}
:deep(.startEvent.djs-shape .djs-visual > :nth-child(1)) {
fill: #77df6d !important;
}
:deep(.endEvent.djs-shape .djs-visual > :nth-child(1)) {
fill: #ee7b77 !important;
}
:deep(.highlight.djs-shape .djs-visual > :nth-child(1)) {
fill: green !important;
stroke: green !important;
fill-opacity: 0.2 !important;
}
:deep(.highlight.djs-shape .djs-visual > :nth-child(2)) {
fill: green !important;
}
:deep(.highlight.djs-shape .djs-visual > path) {
fill: green !important;
fill-opacity: 0.2 !important;
stroke: green !important;
}
:deep(.highlight.djs-connection > .djs-visual > path) {
stroke: green !important;
}
//
@keyframes path-animation {
from {
stroke-dashoffset: 100%;
}
to {
stroke-dashoffset: 0%;
}
}
:deep(.highlight-todo.djs-connection > .djs-visual > path) {
animation: path-animation 60s;
animation-timing-function: linear;
animation-iteration-count: infinite;
stroke-dasharray: 4px !important;
stroke: orange !important;
fill-opacity: 0.2 !important;
marker-end: url('#sequenceflow-end-_E7DFDF-_E7DFDF-803g1kf6zwzmcig1y2ulm5egr');
}
:deep(.highlight-todo.djs-shape .djs-visual > :nth-child(1)) {
animation: path-animation 60s;
animation-timing-function: linear;
animation-iteration-count: infinite;
stroke-dasharray: 4px !important;
stroke: orange !important;
fill: orange !important;
fill-opacity: 0.2 !important;
}
}
:deep(.verlays) {
width: 250px;
background: rgb(102, 102, 102);
border-radius: 4px;
border: 1px solid #ebeef5;
color: #fff;
padding: 15px 10px;
p {
line-height: 28px;
margin: 0;
padding: 0;
}
cursor: pointer;
}
</style>

13
im-admin-ui/src/components/RuoYiDoc/index.vue

@ -1,13 +0,0 @@
<template>
<div>
<svg-icon icon-class="question" @click="goto" />
</div>
</template>
<script setup>
const url = ref('https://plus-doc.dromara.org/');
function goto() {
window.open(url.value);
}
</script>

13
im-admin-ui/src/components/RuoYiGit/index.vue

@ -1,13 +0,0 @@
<template>
<div>
<svg-icon icon-class="github" @click="goto" />
</div>
</template>
<script setup>
const url = ref('https://gitee.com/dromara/RuoYi-Vue-Plus');
function goto() {
window.open(url.value);
}
</script>

8
im-admin-ui/src/layout/components/Navbar.vue

@ -43,14 +43,6 @@
</el-popover>
</div>
</el-tooltip>
<el-tooltip content="Github" effect="dark" placement="bottom">
<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
</el-tooltip>
<el-tooltip :content="$t('navbar.document')" effect="dark" placement="bottom">
<ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" />
</el-tooltip>
<el-tooltip :content="$t('navbar.full')" effect="dark" placement="bottom">
<screenfull id="screenfull" class="right-menu-item hover-effect" />
</el-tooltip>

14
im-admin-ui/src/router/index.ts

@ -162,20 +162,6 @@ export const dynamicRoutes: RouteRecordRaw[] = [
meta: { title: '修改生成配置', activeMenu: '/tool/gen', icon: '', noCache: true }
}
]
},
{
path: '/workflow/leaveEdit',
component: Layout,
hidden: true,
permissions: ['workflow:leave:edit'],
children: [
{
path: 'index',
component: () => import('@/views/workflow/leave/leaveEdit.vue'),
name: 'leaveEdit',
meta: { title: '请假申请', activeMenu: '/workflow/leave', noCache: true }
}
]
}
];

254
im-admin-ui/src/views/demo/demo/index.vue

@ -1,254 +0,0 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="部门id" prop="deptId">
<el-input v-model="queryParams.deptId" placeholder="请输入部门id" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="用户id" prop="userId">
<el-input v-model="queryParams.userId" placeholder="请输入用户id" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="排序号" prop="orderNum">
<el-input v-model="queryParams.orderNum" placeholder="请输入排序号" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="key键" prop="testKey">
<el-input v-model="queryParams.testKey" placeholder="请输入key键" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="值" prop="value">
<el-input v-model="queryParams.value" placeholder="请输入值" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button v-hasPermi="['demo:demo:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['demo:demo:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['demo:demo:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()"
>删除</el-button
>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['demo:demo:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="demoList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column v-if="true" label="主键" align="center" prop="id" />
<el-table-column label="部门id" align="center" prop="deptId" />
<el-table-column label="用户id" align="center" prop="userId" />
<el-table-column label="排序号" align="center" prop="orderNum" />
<el-table-column label="key键" align="center" prop="testKey" />
<el-table-column label="值" align="center" prop="value" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button v-hasPermi="['demo:demo:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button v-hasPermi="['demo:demo:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card>
<!-- 添加或修改测试单对话框 -->
<el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
<el-form ref="demoFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="部门id" prop="deptId">
<el-input v-model="form.deptId" placeholder="请输入部门id" />
</el-form-item>
<el-form-item label="用户id" prop="userId">
<el-input v-model="form.userId" placeholder="请输入用户id" />
</el-form-item>
<el-form-item label="排序号" prop="orderNum">
<el-input v-model="form.orderNum" placeholder="请输入排序号" />
</el-form-item>
<el-form-item label="key键" prop="testKey">
<el-input v-model="form.testKey" placeholder="请输入key键" />
</el-form-item>
<el-form-item label="值" prop="value">
<el-input v-model="form.value" placeholder="请输入值" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="Demo" lang="ts">
import { listDemo, getDemo, delDemo, addDemo, updateDemo } from '@/api/demo/demo';
import { DemoVO, DemoQuery, DemoForm } from '@/api/demo/demo/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const demoList = ref<DemoVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const demoFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: DemoForm = {
id: undefined,
deptId: undefined,
userId: undefined,
orderNum: undefined,
testKey: undefined,
value: undefined
};
const data = reactive<PageData<DemoForm, DemoQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
deptId: undefined,
userId: undefined,
orderNum: undefined,
testKey: undefined,
value: undefined
},
rules: {
id: [{ required: true, message: '主键不能为空', trigger: 'blur' }],
deptId: [{ required: true, message: '部门id不能为空', trigger: 'blur' }],
userId: [{ required: true, message: '用户id不能为空', trigger: 'blur' }],
orderNum: [{ required: true, message: '排序号不能为空', trigger: 'blur' }],
testKey: [{ required: true, message: 'key键不能为空', trigger: 'blur' }],
value: [{ required: true, message: '值不能为空', trigger: 'blur' }]
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询测试单列表 */
const getList = async () => {
loading.value = true;
const res = await listDemo(queryParams.value);
demoList.value = res.rows;
total.value = res.total;
loading.value = false;
};
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
demoFormRef.value?.resetFields();
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: DemoVO[]) => {
ids.value = selection.map((item) => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
};
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = '添加测试单';
};
/** 修改按钮操作 */
const handleUpdate = async (row?: DemoVO) => {
reset();
const _id = row?.id || ids.value[0];
const res = await getDemo(_id);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = '修改测试单';
};
/** 提交按钮 */
const submitForm = () => {
demoFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if (form.value.id) {
await updateDemo(form.value).finally(() => (buttonLoading.value = false));
} else {
await addDemo(form.value).finally(() => (buttonLoading.value = false));
}
proxy?.$modal.msgSuccess('修改成功');
dialog.visible = false;
await getList();
}
});
};
/** 删除按钮操作 */
const handleDelete = async (row?: DemoVO) => {
const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除测试单编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
await delDemo(_ids);
proxy?.$modal.msgSuccess('删除成功');
await getList();
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download(
'demo/demo/export',
{
...queryParams.value
},
`demo_${new Date().getTime()}.xlsx`
);
};
onMounted(() => {
getList();
});
</script>

258
im-admin-ui/src/views/demo/tree/index.vue

@ -1,258 +0,0 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="树节点名" prop="treeName">
<el-input v-model="queryParams.treeName" placeholder="请输入树节点名" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button v-hasPermi="['demo:tree:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">展开/折叠</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table
ref="treeTableRef"
v-loading="loading"
:data="treeList"
row-key="id"
:default-expand-all="isExpandAll"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
<el-table-column label="父id" align="center" prop="parentId" />
<el-table-column label="部门id" align="center" prop="deptId" />
<el-table-column label="用户id" align="center" prop="userId" />
<el-table-column label="树节点名" align="center" prop="treeName" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button v-hasPermi="['demo:tree:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)" />
</el-tooltip>
<el-tooltip content="新增" placement="top">
<el-button v-hasPermi="['demo:tree:add']" link type="primary" icon="Plus" @click="handleAdd(scope.row)" />
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button v-hasPermi="['demo:tree:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)" />
</el-tooltip>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 添加或修改测试树对话框 -->
<el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
<el-form ref="treeFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="父id" prop="parentId">
<el-tree-select
v-model="form.parentId"
:data="treeOptions"
:props="{ value: 'id', label: 'treeName', children: 'children' }"
value-key="id"
placeholder="请选择父id"
check-strictly
/>
</el-form-item>
<el-form-item label="部门id" prop="deptId">
<el-input v-model="form.deptId" placeholder="请输入部门id" />
</el-form-item>
<el-form-item label="用户id" prop="userId">
<el-input v-model="form.userId" placeholder="请输入用户id" />
</el-form-item>
<el-form-item label="值" prop="treeName">
<el-input v-model="form.treeName" placeholder="请输入值" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="Tree" lang="ts">
import { listTree, getTree, delTree, addTree, updateTree } from '@/api/demo/tree';
import { TreeVO, TreeQuery, TreeForm } from '@/api/demo/tree/types';
type TreeOption = {
id: number;
treeName: string;
children?: TreeOption[];
};
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const treeList = ref<TreeVO[]>([]);
const treeOptions = ref<TreeOption[]>([]);
const buttonLoading = ref(false);
const showSearch = ref(true);
const isExpandAll = ref(true);
const loading = ref(false);
const queryFormRef = ref<ElFormInstance>();
const treeFormRef = ref<ElFormInstance>();
const treeTableRef = ref<ElTableInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: TreeForm = {
id: undefined,
parentId: undefined,
deptId: undefined,
userId: undefined,
treeName: undefined
};
const data = reactive<PageData<TreeForm, TreeQuery>>({
form: { ...initFormData },
queryParams: {
parentId: undefined,
deptId: undefined,
userId: undefined,
treeName: undefined
},
rules: {
id: [{ required: true, message: '主键不能为空', trigger: 'blur' }],
parentId: [{ required: true, message: '父id不能为空', trigger: 'blur' }],
deptId: [{ required: true, message: '部门id不能为空', trigger: 'blur' }],
userId: [{ required: true, message: '用户id不能为空', trigger: 'blur' }],
treeName: [{ required: true, message: '值不能为空', trigger: 'blur' }]
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询测试树列表 */
const getList = async () => {
loading.value = true;
const res = await listTree(queryParams.value);
const data = proxy?.handleTree<TreeVO>(res.data, 'id', 'parentId');
if (data) {
treeList.value = data;
loading.value = false;
}
};
/** 查询测试树下拉树结构 */
const getTreeselect = async () => {
const res = await listTree();
treeOptions.value = [];
const data: TreeOption = { id: 0, treeName: '顶级节点', children: [] };
data.children = proxy?.handleTree<TreeOption>(res.data, 'id', 'parentId');
treeOptions.value.push(data);
};
//
const cancel = () => {
reset();
dialog.visible = false;
};
//
const reset = () => {
form.value = { ...initFormData };
treeFormRef.value?.resetFields();
};
/** 搜索按钮操作 */
const handleQuery = () => {
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
};
/** 新增按钮操作 */
const handleAdd = (row?: TreeVO) => {
reset();
getTreeselect();
if (row && row.id) {
form.value.parentId = row.id;
} else {
form.value.parentId = 0;
}
dialog.visible = true;
dialog.title = '添加测试树';
};
/** 展开/折叠操作 */
const handleToggleExpandAll = () => {
isExpandAll.value = !isExpandAll.value;
toggleExpandAll(treeList.value, isExpandAll.value);
};
/** 展开/折叠操作 */
const toggleExpandAll = (data: TreeVO[], status: boolean) => {
data.forEach((item) => {
treeTableRef.value?.toggleRowExpansion(item, status);
if (item.children && item.children.length > 0) toggleExpandAll(item.children, status);
});
};
/** 修改按钮操作 */
const handleUpdate = async (row: TreeVO) => {
reset();
await getTreeselect();
if (row) {
form.value.parentId = row.id;
}
const res = await getTree(row.id);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = '修改测试树';
};
/** 提交按钮 */
const submitForm = () => {
treeFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if (form.value.id) {
await updateTree(form.value).finally(() => (buttonLoading.value = false));
} else {
await addTree(form.value).finally(() => (buttonLoading.value = false));
}
proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false;
await getList();
}
});
};
/** 删除按钮操作 */
const handleDelete = async (row: TreeVO) => {
await proxy?.$modal.confirm('是否确认删除测试树编号为"' + row.id + '"的数据项?');
loading.value = true;
await delTree(row.id).finally(() => (loading.value = false));
await getList();
proxy?.$modal.msgSuccess('删除成功');
};
onMounted(() => {
getList();
});
</script>

38
im-admin-ui/src/views/login.vue

@ -1,7 +1,7 @@
<template>
<div class="login">
<el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
<h3 class="title">RuoYi-Vue-Plus多租户管理系统</h3>
<h3 class="title">盒子IM后台管理</h3>
<el-form-item v-if="tenantEnabled" prop="tenantId">
<el-select v-model="loginForm.tenantId" filterable placeholder="请选择/输入公司名称" style="width: 100%">
<el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName" :value="item.tenantId"></el-option>
@ -27,23 +27,6 @@
</div>
</el-form-item>
<el-checkbox v-model="loginForm.rememberMe" style="margin: 0 0 25px 0">记住密码</el-checkbox>
<el-form-item style="float: right">
<el-button circle title="微信登录" @click="doSocialLogin('wechat')">
<svg-icon icon-class="wechat" />
</el-button>
<el-button circle title="MaxKey登录" @click="doSocialLogin('maxkey')">
<svg-icon icon-class="maxkey" />
</el-button>
<el-button circle title="TopIam登录" @click="doSocialLogin('topiam')">
<svg-icon icon-class="topiam" />
</el-button>
<el-button circle title="Gitee登录" @click="doSocialLogin('gitee')">
<svg-icon icon-class="gitee" />
</el-button>
<el-button circle title="Github登录" @click="doSocialLogin('github')">
<svg-icon icon-class="github" />
</el-button>
</el-form-item>
<el-form-item style="width: 100%">
<el-button :loading="loading" size="large" type="primary" style="width: 100%" @click.prevent="handleLogin">
<span v-if="!loading"> </span>
@ -54,10 +37,6 @@
</div>
</el-form-item>
</el-form>
<!-- 底部 -->
<div class="el-login-footer">
<span>Copyright © 2018-2024 疯狂的狮子Li All Rights Reserved.</span>
</div>
</div>
</template>
@ -186,20 +165,7 @@ const initTenantList = async () => {
}
};
/**
* 第三方登录
* @param type
*/
const doSocialLogin = (type: string) => {
authBinding(type, loginForm.value.tenantId).then((res: any) => {
if (res.code === HttpStatus.SUCCESS) {
//
window.location.href = res.data;
} else {
ElMessage.error(res.msg);
}
});
};
onMounted(() => {
getCode();

9
im-admin-ui/src/views/monitor/admin/index.vue

@ -1,9 +0,0 @@
<template>
<div>
<i-frame v-model:src="url"></i-frame>
</div>
</template>
<script setup lang="ts">
const url = ref(import.meta.env.VITE_APP_MONITOR_ADMIN);
</script>

9
im-admin-ui/src/views/monitor/snailjob/index.vue

@ -1,9 +0,0 @@
<template>
<div>
<i-frame v-model:src="url"></i-frame>
</div>
</template>
<script setup lang="ts">
const url = ref(import.meta.env.VITE_APP_SNAILJOB_ADMIN);
</script>

263
im-admin-ui/src/views/workflow/category/index.vue

@ -1,263 +0,0 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="search">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="分类名称" prop="categoryName">
<el-input v-model="queryParams.categoryName" placeholder="请输入分类名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="分类编码" prop="categoryCode">
<el-input v-model="queryParams.categoryCode" placeholder="请输入分类编码" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</div>
</transition>
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button v-hasPermi="['workflow:category:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">展开/折叠</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table
ref="categoryTableRef"
v-loading="loading"
:data="categoryList"
row-key="id"
:default-expand-all="isExpandAll"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
<el-table-column label="分类名称" prop="categoryName" />
<el-table-column label="分类编码" align="center" prop="categoryCode" />
<el-table-column label="排序" align="center" prop="sortNum" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button v-hasPermi="['workflow:category:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)" />
</el-tooltip>
<el-tooltip content="新增" placement="top">
<el-button v-hasPermi="['workflow:category:add']" link type="primary" icon="Plus" @click="handleAdd(scope.row)" />
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button v-hasPermi="['workflow:category:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)" />
</el-tooltip>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 添加或修改流程分类对话框 -->
<el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
<el-form ref="categoryFormRef" v-loading="loading" :model="form" :rules="rules" label-width="80px">
<el-form-item label="父级分类" prop="parentId">
<el-tree-select
v-model="form.parentId"
:data="categoryOptions"
:props="{ value: 'id', label: 'categoryName', children: 'children' }"
value-key="id"
placeholder="请选择父级id"
check-strictly
/>
</el-form-item>
<el-form-item label="分类名称" prop="categoryName">
<el-input v-model="form.categoryName" placeholder="请输入分类名称" />
</el-form-item>
<el-form-item label="分类编码" prop="categoryCode">
<el-input v-model="form.categoryCode" placeholder="请输入分类编码" />
</el-form-item>
<el-form-item label="排序" prop="sortNum">
<el-input-number v-model="form.sortNum" placeholder="请输入排序" controls-position="right" :min="0" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="Category" lang="ts">
import { listCategory, getCategory, delCategory, addCategory, updateCategory } from '@/api/workflow/category';
import { CategoryVO, CategoryQuery, CategoryForm } from '@/api/workflow/category/types';
type CategoryOption = {
id: number;
categoryName: string;
children?: CategoryOption[];
};
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const categoryList = ref<CategoryVO[]>([]);
const categoryOptions = ref<CategoryOption[]>([]);
const buttonLoading = ref(false);
const showSearch = ref(true);
const isExpandAll = ref(true);
const loading = ref(false);
const queryFormRef = ref<ElFormInstance>();
const categoryFormRef = ref<ElFormInstance>();
const categoryTableRef = ref<ElTableInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: CategoryForm = {
id: undefined,
categoryName: undefined,
categoryCode: undefined,
parentId: undefined,
sortNum: 0
};
const data = reactive<PageData<CategoryForm, CategoryQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
categoryName: undefined,
categoryCode: undefined
},
rules: {
id: [{ required: true, message: '主键不能为空', trigger: 'blur' }],
categoryName: [{ required: true, message: '分类名称不能为空', trigger: 'blur' }],
categoryCode: [{ required: true, message: '分类编码不能为空', trigger: 'blur' }],
parentId: [{ required: true, message: '父级id不能为空', trigger: 'blur' }]
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询流程分类列表 */
const getList = async () => {
loading.value = true;
const res = await listCategory(queryParams.value);
const data = proxy?.handleTree<CategoryVO>(res.data, 'id', 'parentId');
if (data) {
categoryList.value = data;
loading.value = false;
}
};
/** 查询流程分类下拉树结构 */
const getTreeselect = async () => {
const res = await listCategory();
categoryOptions.value = [];
const data: CategoryOption = { id: 0, categoryName: '顶级节点', children: [] };
data.children = proxy?.handleTree<CategoryOption>(res.data, 'id', 'parentId');
categoryOptions.value.push(data);
};
//
const cancel = () => {
reset();
dialog.visible = false;
};
//
const reset = () => {
form.value = { ...initFormData };
categoryFormRef.value?.resetFields();
};
/** 搜索按钮操作 */
const handleQuery = () => {
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
};
/** 新增按钮操作 */
const handleAdd = (row?: CategoryVO) => {
dialog.visible = true;
dialog.title = '添加流程分类';
nextTick(() => {
reset();
getTreeselect();
if (row != null && row.id) {
form.value.parentId = row.id;
} else {
form.value.parentId = 0;
}
});
};
/** 展开/折叠操作 */
const handleToggleExpandAll = () => {
isExpandAll.value = !isExpandAll.value;
toggleExpandAll(categoryList.value, isExpandAll.value);
};
/** 展开/折叠操作 */
const toggleExpandAll = (data: CategoryVO[], status: boolean) => {
data.forEach((item) => {
categoryTableRef.value?.toggleRowExpansion(item, status);
if (item.children && item.children.length > 0) toggleExpandAll(item.children, status);
});
};
/** 修改按钮操作 */
const handleUpdate = (row: CategoryVO) => {
loading.value = true;
dialog.visible = true;
dialog.title = '修改流程分类';
nextTick(async () => {
reset();
await getTreeselect();
if (row != null) {
form.value.parentId = row.id;
}
const res = await getCategory(row.id);
loading.value = false;
Object.assign(form.value, res.data);
});
};
/** 提交按钮 */
const submitForm = () => {
categoryFormRef.value.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if (form.value.id) {
await updateCategory(form.value).finally(() => (buttonLoading.value = false));
} else {
await addCategory(form.value).finally(() => (buttonLoading.value = false));
}
proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false;
await getList();
}
});
};
/** 删除按钮操作 */
const handleDelete = async (row: CategoryVO) => {
await proxy?.$modal.confirm('是否确认删除流程分类编号为"' + row.id + '"的数据项?');
loading.value = true;
await delCategory(row.id).finally(() => (loading.value = false));
await getList();
proxy?.$modal.msgSuccess('删除成功');
};
onMounted(() => {
getList();
});
</script>

243
im-admin-ui/src/views/workflow/formManage/index.vue

@ -1,243 +0,0 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="search">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="表单名称" prop="formName">
<el-input v-model="queryParams.formName" placeholder="请输入表单名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</div>
</transition>
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button v-hasPermi="['workflow:formManage:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['workflow:formManage:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()"
>修改</el-button
>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['workflow:formManage:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()"
>删除</el-button
>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['workflow:formManage:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="formManageList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="表单名称" align="center" prop="formName" />
<el-table-column label="表单类型" align="center">
<template #default="scope">
<dict-tag :options="wf_form_type" :value="scope.row.formType"></dict-tag>
</template>
</el-table-column>
<el-table-column label="地址" align="center" prop="router" />
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button v-hasPermi="['workflow:formManage:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button v-hasPermi="['workflow:formManage:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card>
<!-- 添加或修改表单管理对话框 -->
<el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
<el-form ref="formManageFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="表单名称" prop="formName">
<el-input v-model="form.formName" placeholder="请输入表单名称" />
</el-form-item>
<el-form-item label="表单类型" prop="formType">
<el-radio-group v-model="form.formType" @change="form.router = ''">
<el-radio v-for="dict in wf_form_type" :key="dict.value" border :value="dict.value">{{ dict.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.formType === 'static'" label="路由地址" prop="router">
<el-input v-model="form.router" placeholder="请输入路由地址" />
</el-form-item>
<el-form-item v-else label="表单" prop="router">
<el-input v-model="form.router" disabled placeholder="请选择表单">
<template #append>
<el-button icon="Search" />
</template>
</el-input>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="FormManage" lang="ts">
import { listFormManage, getFormManage, delFormManage, addFormManage, updateFormManage } from '@/api/workflow/formManage';
import { FormManageVO, FormManageQuery, FormManageForm } from '@/api/workflow/formManage/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { wf_form_type } = toRefs<any>(proxy?.useDict('wf_form_type'));
const formManageList = ref<FormManageVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const formManageFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: FormManageForm = {
id: undefined,
formName: undefined,
formType: 'static',
remark: undefined
};
const data = reactive<PageData<FormManageForm, FormManageQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
formName: undefined,
formType: undefined
},
rules: {
id: [{ required: true, message: '主键不能为空', trigger: 'blur' }],
formName: [{ required: true, message: '表单名称不能为空', trigger: 'blur' }],
formType: [{ required: true, message: '表单类型不能为空', trigger: 'change' }],
router: [{ required: true, message: '不能为空', trigger: 'blur' }]
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询表单管理列表 */
const getList = async () => {
loading.value = true;
const res = await listFormManage(queryParams.value);
formManageList.value = res.rows;
total.value = res.total;
loading.value = false;
};
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
formManageFormRef.value?.resetFields();
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: FormManageVO[]) => {
ids.value = selection.map((item) => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
};
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = '添加表单管理';
};
/** 修改按钮操作 */
const handleUpdate = async (row?: FormManageVO) => {
reset();
const _id = row?.id || ids.value[0];
const res = await getFormManage(_id);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = '修改表单管理';
};
/** 提交按钮 */
const submitForm = () => {
formManageFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if (form.value.id) {
await updateFormManage(form.value).finally(() => (buttonLoading.value = false));
} else {
await addFormManage(form.value).finally(() => (buttonLoading.value = false));
}
proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false;
await getList();
}
});
};
/** 删除按钮操作 */
const handleDelete = async (row?: FormManageVO) => {
const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除表单管理编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
await delFormManage(_ids);
proxy?.$modal.msgSuccess('删除成功');
await getList();
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download(
'workflow/formManage/export',
{
...queryParams.value
},
`formManage_${new Date().getTime()}.xlsx`
);
};
onMounted(() => {
getList();
});
</script>

242
im-admin-ui/src/views/workflow/leave/index.vue

@ -1,242 +0,0 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="search">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="请假天数" prop="startLeaveDays">
<el-input v-model="queryParams.startLeaveDays" placeholder="请输入请假天数" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item prop="endLeaveDays"> </el-form-item>
<el-form-item prop="endLeaveDays">
<el-input v-model="queryParams.endLeaveDays" placeholder="请输入请假天数" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</div>
</transition>
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button v-hasPermi="['workflow:leave:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['workflow:leave:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="leaveList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column v-if="false" label="主键" align="center" prop="id" />
<el-table-column label="请假类型" align="center">
<template #default="scope">
<el-tag>{{ options.find((e) => e.value === scope.row.leaveType)?.label }}</el-tag>
</template>
</el-table-column>
<el-table-column label="开始时间" align="center" prop="startDate">
<template #default="scope">
<span>{{ parseTime(scope.row.startDate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="结束时间" align="center" prop="endDate">
<template #default="scope">
<span>{{ parseTime(scope.row.endDate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="请假天数" align="center" prop="leaveDays" />
<el-table-column label="请假原因" align="center" prop="remark" />
<el-table-column align="center" label="流程状态" min-width="70">
<template #default="scope">
<dict-tag :options="wf_business_status" :value="scope.row.status"></dict-tag>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button
v-if="scope.row.status === 'draft' || scope.row.status === 'cancel' || scope.row.status === 'back'"
v-hasPermi="['workflow:leave:edit']"
size="small"
link
type="primary"
icon="Edit"
@click="handleUpdate(scope.row)"
>修改</el-button
>
<el-button
v-if="scope.row.status === 'draft' || scope.row.status === 'cancel' || scope.row.status === 'back'"
v-hasPermi="['workflow:leave:remove']"
size="small"
link
type="primary"
icon="Delete"
@click="handleDelete(scope.row)"
>删除</el-button
>
<el-button link type="primary" size="small" icon="View" @click="handleView(scope.row)">查看</el-button>
<el-button
v-if="scope.row.status === 'waiting'"
link
size="small"
type="primary"
icon="Notification"
@click="handleCancelProcessApply(scope.row.id)"
>撤销</el-button
>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card>
</div>
</template>
<script setup name="Leave" lang="ts">
import { delLeave, listLeave } from '@/api/workflow/leave';
import { cancelProcessApply } from '@/api/workflow/processInstance';
import { LeaveForm, LeaveQuery, LeaveVO } from '@/api/workflow/leave/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { wf_business_status } = toRefs<any>(proxy?.useDict('wf_business_status'));
const leaveList = ref<LeaveVO[]>([]);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const options = [
{
value: '1',
label: '事假'
},
{
value: '2',
label: '调休'
},
{
value: '3',
label: '病假'
},
{
value: '4',
label: '婚假'
}
];
const queryFormRef = ref<ElFormInstance>();
const data = reactive<PageData<LeaveForm, LeaveQuery>>({
form: {},
queryParams: {
pageNum: 1,
pageSize: 10,
startLeaveDays: undefined,
endLeaveDays: undefined
},
rules: {}
});
const { queryParams } = toRefs(data);
/** 查询请假列表 */
const getList = async () => {
loading.value = true;
const res = await listLeave(queryParams.value);
leaveList.value = res.rows;
total.value = res.total;
loading.value = false;
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: LeaveVO[]) => {
ids.value = selection.map((item) => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
};
/** 新增按钮操作 */
const handleAdd = () => {
proxy.$tab.closePage(proxy.$route);
proxy.$router.push({
path: `/workflow/leaveEdit/index`,
query: {
type: 'add'
}
});
};
/** 修改按钮操作 */
const handleUpdate = (row?: LeaveVO) => {
proxy.$tab.closePage(proxy.$route);
proxy.$router.push({
path: `/workflow/leaveEdit/index`,
query: {
id: row.id,
type: 'update'
}
});
};
/** 查看按钮操作 */
const handleView = (row?: LeaveVO) => {
proxy.$tab.closePage(proxy.$route);
proxy.$router.push({
path: `/workflow/leaveEdit/index`,
query: {
id: row.id,
type: 'view'
}
});
};
/** 删除按钮操作 */
const handleDelete = async (row?: LeaveVO) => {
const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除请假编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
await delLeave(_ids);
proxy?.$modal.msgSuccess('删除成功');
await getList();
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download(
'workflow/leave/export',
{
...queryParams.value
},
`leave_${new Date().getTime()}.xlsx`
);
};
/** 撤销按钮操作 */
const handleCancelProcessApply = async (id: string) => {
await proxy?.$modal.confirm('是否确认撤销当前单据?');
loading.value = true;
await cancelProcessApply(id).finally(() => (loading.value = false));
await getList();
proxy?.$modal.msgSuccess('撤销成功');
};
onMounted(() => {
getList();
});
</script>

252
im-admin-ui/src/views/workflow/leave/leaveEdit.vue

@ -1,252 +0,0 @@
<template>
<div class="p-2">
<el-card shadow="never">
<div style="display: flex; justify-content: space-between">
<div>
<el-button v-if="submitButtonShow" :loading="buttonLoading" type="info" @click="submitForm('draft')">暂存</el-button>
<el-button v-if="submitButtonShow" :loading="buttonLoading" type="primary" @click="submitForm('submit')"> </el-button>
<el-button v-if="approvalButtonShow" :loading="buttonLoading" type="primary" @click="approvalVerifyOpen">审批</el-button>
<el-button v-if="form && form.id && form.status !== 'draft'" type="primary" @click="handleApprovalRecord">流程进度</el-button>
</div>
<div>
<el-button style="float: right" @click="goBack()">返回</el-button>
</div>
</div>
</el-card>
<el-card shadow="never" style="height: 78vh; overflow-y: auto">
<el-form ref="leaveFormRef" v-loading="loading" :disabled="routeParams.type === 'view'" :model="form" :rules="rules" label-width="80px">
<el-form-item label="请假类型" prop="leaveType">
<el-select v-model="form.leaveType" placeholder="请选择请假类型" style="width: 100%">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="请假时间">
<el-date-picker
v-model="leaveTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
range-separator="To"
start-placeholder="开始时间"
end-placeholder="结束时间"
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
@change="changeLeaveTime()"
/>
</el-form-item>
<el-form-item label="请假天数" prop="leaveDays">
<el-input v-model="form.leaveDays" disabled type="number" placeholder="请输入请假天数" />
</el-form-item>
<el-form-item label="请假原因" prop="remark">
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入请假原因" />
</el-form-item>
</el-form>
</el-card>
<!-- 提交组件 -->
<submitVerify ref="submitVerifyRef" :task-variables="taskVariables" @submit-callback="submitCallback" />
<!-- 审批记录 -->
<approvalRecord ref="approvalRecordRef" />
</div>
</template>
<script setup name="Leave" lang="ts">
import { addLeave, getLeave, updateLeave } from '@/api/workflow/leave';
import { LeaveForm, LeaveQuery, LeaveVO } from '@/api/workflow/leave/types';
import { startWorkFlow } from '@/api/workflow/task';
import SubmitVerify from '@/components/Process/submitVerify.vue';
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
import { AxiosResponse } from 'axios';
import { StartProcessBo } from '@/api/workflow/workflowCommon/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const buttonLoading = ref(false);
const loading = ref(true);
const leaveTime = ref<Array<string>>([]);
//
const routeParams = ref<Record<string, any>>({});
const options = [
{
value: '1',
label: '事假'
},
{
value: '2',
label: '调休'
},
{
value: '3',
label: '病假'
},
{
value: '4',
label: '婚假'
}
];
//
const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>();
//
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
const leaveFormRef = ref<ElFormInstance>();
const submitFormData = ref<StartProcessBo>({
businessKey: '',
tableName: '',
variables: {}
});
const taskVariables = ref<Record<string, any>>({});
const initFormData: LeaveForm = {
id: undefined,
leaveType: undefined,
startDate: undefined,
endDate: undefined,
leaveDays: undefined,
remark: undefined,
status: undefined
};
const data = reactive<PageData<LeaveForm, LeaveQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
startLeaveDays: undefined,
endLeaveDays: undefined
},
rules: {
id: [{ required: true, message: '主键不能为空', trigger: 'blur' }],
leaveType: [{ required: true, message: '请假类型不能为空', trigger: 'blur' }],
leaveTime: [{ required: true, message: '请假时间不能为空', trigger: 'blur' }],
leaveDays: [{ required: true, message: '请假天数不能为空', trigger: 'blur' }]
}
});
const { form, rules } = toRefs(data);
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
leaveTime.value = [];
leaveFormRef.value?.resetFields();
};
const changeLeaveTime = () => {
const startDate = new Date(leaveTime.value[0]).getTime();
const endDate = new Date(leaveTime.value[1]).getTime();
const diffInMilliseconds = endDate - startDate;
form.value.leaveDays = Math.floor(diffInMilliseconds / (1000 * 60 * 60 * 24)) + 1;
};
/** 获取详情 */
const getInfo = () => {
loading.value = true;
buttonLoading.value = false;
nextTick(async () => {
const res = await getLeave(routeParams.value.id);
Object.assign(form.value, res.data);
leaveTime.value = [];
leaveTime.value.push(form.value.startDate);
leaveTime.value.push(form.value.endDate);
loading.value = false;
buttonLoading.value = false;
});
};
/** 提交按钮 */
const submitForm = (status: string) => {
if (leaveTime.value.length === 0) {
proxy?.$modal.msgError('请假时间不能为空');
return;
}
try {
leaveFormRef.value?.validate(async (valid: boolean) => {
form.value.startDate = leaveTime.value[0];
form.value.endDate = leaveTime.value[1];
if (valid) {
buttonLoading.value = true;
let res: AxiosResponse<LeaveVO>;
if (form.value.id) {
res = await updateLeave(form.value);
} else {
res = await addLeave(form.value);
}
form.value = res.data;
if (status === 'draft') {
buttonLoading.value = false;
proxy?.$modal.msgSuccess('暂存成功');
proxy.$tab.closePage(proxy.$route);
proxy.$router.go(-1);
} else {
await handleStartWorkFlow(res.data);
}
}
});
} finally {
buttonLoading.value = false;
}
};
//
const handleStartWorkFlow = async (data: LeaveVO) => {
try {
submitFormData.value.tableName = 'test_leave';
submitFormData.value.businessKey = data.id;
//
taskVariables.value = {
entity: data,
leaveDays: data.leaveDays,
userList: ["1", "3"],
userList2: ["1", "3"]
};
submitFormData.value.variables = taskVariables.value;
const resp = await startWorkFlow(submitFormData.value);
if (submitVerifyRef.value) {
buttonLoading.value = false;
submitVerifyRef.value.openDialog(resp.data.taskId);
}
} finally {
buttonLoading.value = false;
}
};
//
const handleApprovalRecord = () => {
approvalRecordRef.value.init(form.value.id);
};
//
const submitCallback = async () => {
await proxy.$tab.closePage(proxy.$route);
proxy.$router.go(-1);
};
//
const goBack = () => {
proxy.$tab.closePage(proxy.$route);
proxy.$router.go(-1);
};
//
const approvalVerifyOpen = async () => {
submitVerifyRef.value.openDialog(routeParams.value.taskId);
};
//
const submitButtonShow = computed(() => {
return (
routeParams.value.type === 'add' ||
(routeParams.value.type === 'update' &&
form.value.status &&
(form.value.status === 'draft' || form.value.status === 'cancel' || form.value.status === 'back'))
);
});
//
const approvalButtonShow = computed(() => {
return routeParams.value.type === 'approval' && form.value.status && form.value.status === 'waiting';
});
onMounted(() => {
nextTick(async () => {
routeParams.value = proxy.$route.query;
reset();
loading.value = false;
if (routeParams.value.type === 'update' || routeParams.value.type === 'view' || routeParams.value.type === 'approval') {
getInfo();
}
});
});
</script>

383
im-admin-ui/src/views/workflow/model/index.vue

@ -1,383 +0,0 @@
<template>
<div class="p-2">
<el-row :gutter="20">
<!-- 流程分类树 -->
<el-col :lg="4" :xs="24" style="">
<el-card shadow="hover">
<el-input v-model="categoryName" placeholder="请输入流程分类名" prefix-icon="Search" clearable />
<el-tree
ref="categoryTreeRef"
class="mt-2"
node-key="id"
:data="categoryOptions"
:props="{ label: 'categoryName', children: 'children' }"
:expand-on-click-node="false"
:filter-node-method="filterNode"
highlight-current
default-expand-all
@node-click="handleNodeClick"
></el-tree>
</el-card>
</el-col>
<el-col :lg="20" :xs="24">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="模型名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入模型名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="模型KEY" prop="key">
<el-input v-model="queryParams.key" placeholder="请输入模型KEY" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="primary" plain :disabled="multiple" icon="Download" @click="clickExportZip()">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" border :data="modelList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column fixed align="center" type="index" label="序号" width="80"></el-table-column>
<el-table-column align="center" :show-overflow-tooltip="true" prop="name" label="模型名称" width="200"></el-table-column>
<el-table-column align="center" prop="key" label="模型KEY"></el-table-column>
<el-table-column align="center" prop="version" label="版本号" width="90">
<template #default="scope"> v{{ scope.row.version }}.0</template>
</el-table-column>
<el-table-column align="center" prop="metaInfo" label="备注说明" min-width="130"></el-table-column>
<el-table-column align="center" :show-overflow-tooltip="true" prop="createTime" label="创建时间" width="160"></el-table-column>
<el-table-column align="center" :show-overflow-tooltip="true" prop="lastUpdateTime" label="更新时间" width="160"></el-table-column>
<el-table-column fixed="right" label="操作" align="center" width="170" class-name="small-padding fixed-width">
<template #default="scope">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button link type="primary" size="small" icon="Pointer" @click="clickDesign(scope.row.id)">设计流程</el-button>
</el-col>
<el-col :span="1.5">
<el-button link type="primary" size="small" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
</el-col>
</el-row>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button link type="primary" size="small" icon="ScaleToOriginal" @click="clickDeploy(scope.row.id, scope.row.key)">
流程部署
</el-button>
</el-col>
<el-col :span="1.5">
<el-button link type="primary" size="small" icon="CopyDocument" @click="handleCopy(scope.row)"> 复制模型 </el-button>
</el-col>
</el-row>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
:total="total"
@pagination="getList"
/>
</el-card>
</el-col>
</el-row>
<!-- 设计流程开始 -->
<Design ref="designRef" @close-call-back="handleQuery"></Design>
<!-- 设计流程结束 -->
<!-- 添加模型对话框 -->
<el-dialog v-model="dialog.visible" :title="dialog.title" width="650px" append-to-body :close-on-click-modal="false">
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
<el-form-item label="模型名称:" prop="name">
<el-input v-model="form.name" :disabled="ids && ids.length > 0 && billType === 'update'" maxlength="20" show-word-limit />
</el-form-item>
<el-form-item label="模型KEY:" prop="key">
<el-input v-model="form.key" :disabled="ids && ids.length > 0 && billType === 'update'" maxlength="20" show-word-limit />
</el-form-item>
<el-form-item label="流程分类" prop="categoryCode">
<el-tree-select
v-model="form.categoryCode"
:data="categoryOptions"
:props="{ value: 'categoryCode', label: 'categoryName', children: 'children' }"
value-key="categoryCode"
placeholder="请选择流程分类"
check-strictly
/>
</el-form-item>
<el-form-item label="备注:" prop="description">
<el-input v-model="form.description" type="textarea" maxlength="200" show-word-limit></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="Model">
import Design from '../../../components/BpmnDesign/index.vue';
import { listModel, addModel, delModel, modelDeploy, getInfo, update } from '@/api/workflow/model';
import { ModelQuery, ModelForm, ModelVO } from '@/api/workflow/model/types';
import { listCategory } from '@/api/workflow/category';
import { copyModel } from '@/api/workflow/model';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const formRef = ref<ElFormInstance>();
const queryFormRef = ref<ElFormInstance>();
const categoryTreeRef = ref<ElTreeInstance>();
const designRef = ref<InstanceType<typeof Design>>();
type CategoryOption = {
categoryCode: string;
categoryName: string;
children?: CategoryOption[];
};
const buttonLoading = ref(false);
const loading = ref(true);
const ids = ref<string[]>([]);
const single = ref(true);
const multiple = ref(true);
const showSearch = ref(true);
const total = ref(0);
const modelList = ref<ModelVO[]>([]);
const categoryOptions = ref<CategoryOption[]>([]);
const categoryName = ref('');
const billType = ref<string>('');
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: ModelForm = {
id: '',
name: '',
key: '',
categoryCode: '',
xml: '',
svg: '',
description: ''
};
const data = reactive<PageData<ModelForm, ModelQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
name: '',
key: '',
categoryCode: ''
},
rules: {
name: [{ required: true, message: '模型不能为空', trigger: 'blur' }],
key: [{ required: true, message: '模型KEY不能为空', trigger: 'blur' }],
categoryCode: [{ required: true, message: '流程分类不能为空', trigger: 'blur' }]
}
});
const { queryParams, form, rules } = toRefs(data);
onMounted(() => {
getList();
getTreeselect();
});
/** 节点单击事件 */
const handleNodeClick = (data: ModelForm) => {
queryParams.value.categoryCode = data.categoryCode;
if (data.categoryCode === 'ALL') {
queryParams.value.categoryCode = '';
}
handleQuery();
};
/** 通过条件过滤节点 */
const filterNode = (value: string, data: any) => {
if (!value) return true;
return data.categoryName.indexOf(value) !== -1;
};
/** 根据名称筛选部门树 */
watchEffect(
() => {
categoryTreeRef.value?.filter(categoryName.value);
},
{
flush: 'post' // watchEffectDOMDOM
}
);
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
queryParams.value.categoryCode = '';
queryParams.value.pageNum = 1;
queryParams.value.pageSize = 10;
handleQuery();
};
//
const handleSelectionChange = (selection: ModelVO[]) => {
ids.value = selection.map((item: ModelVO) => item.id);
single.value = selection.length !== 1;
multiple.value = !selection.length;
};
//
const getList = async () => {
loading.value = true;
const resp = await listModel(queryParams.value);
modelList.value = resp.rows;
total.value = resp.total;
loading.value = false;
};
/** 删除按钮操作 */
const handleDelete = async (row?: ModelVO) => {
const id = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除模型id为【' + id + '】的数据项?');
loading.value = true;
await delModel(id).finally(() => (loading.value = false));
await getList();
proxy?.$modal.msgSuccess('删除成功');
};
//
const clickDeploy = async (id: string, key: string) => {
await proxy?.$modal.confirm('是否部署模型key为【' + key + '】流程?');
loading.value = true;
await modelDeploy(id).finally(() => (loading.value = false));
await getList();
proxy?.$modal.msgSuccess('部署成功');
};
//
const handleAdd = () => {
billType.value = 'add';
ids.value = [];
getTreeselect();
form.value = { ...initFormData };
dialog.visible = true;
dialog.title = '新增模型';
};
//
const handleUpdate = () => {
billType.value = 'update';
dialog.title = '修改模型';
nextTick(async () => {
await getTreeselect();
const _id = ids.value[0];
const res = await getInfo(_id);
Object.assign(form.value, res.data);
dialog.visible = true;
});
};
//
const handleCopy = (row?: ModelVO) => {
billType.value = 'copy';
dialog.title = '复制模型';
nextTick(async () => {
await getTreeselect();
form.value = { ...initFormData };
form.value.id = row.id;
dialog.visible = true;
});
};
/** 提交按钮 */
const submitForm = () => {
formRef.value.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if ('copy' === billType.value) {
await copyModel(form.value);
proxy?.$modal.msgSuccess('操作成功');
} else if (ids.value && ids.value.length > 0 && 'update' === billType.value) {
form.value.id = ids.value[0];
await update(form.value);
proxy?.$modal.msgSuccess('操作成功');
} else {
initXml(form.value.key, form.value.name);
form.value.xml = xml.value;
await addModel(form.value);
proxy?.$modal.msgSuccess('操作成功');
}
dialog.visible = false;
await getList();
}
});
};
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
formRef.value.resetFields();
};
//
const clickDesign = async (id: string) => {
await designRef.value.open(id);
};
//
const clickExportZip = () => {
proxy?.$download.zip('/workflow/model/export/zip/' + ids.value, '模型');
};
/** 查询流程分类下拉树结构 */
const getTreeselect = async () => {
const res = await listCategory();
categoryOptions.value = [];
const data: CategoryOption = { categoryCode: 'ALL', categoryName: '顶级节点', children: [] };
data.children = proxy?.handleTree<CategoryOption>(res.data, 'id', 'parentId');
categoryOptions.value.push(data);
};
const xml = ref<string>('');
const initXml = async (key: string, name: string) => {
xml.value = `<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:bioc="http://bpmn.io/schema/bpmn/biocolor/1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:flowable="http://flowable.org/bpmn" targetNamespace="http://www.flowable.org/processdef">
<process id="${key}" name="${name}">
<startEvent id="startNode1" name="开始" />
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_flow">
<bpmndi:BPMNPlane id="BPMNPlane_flow" bpmnElement="T-2d89e7a3-ba79-4abd-9f64-ea59621c258c">
<bpmndi:BPMNShape id="BPMNShape_startNode1" bpmnElement="startNode1" bioc:stroke="">
<omgdc:Bounds x="240" y="200" width="30" height="30" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="242" y="237" width="23" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>`;
return xml;
};
</script>

45
im-admin-ui/src/views/workflow/processDefinition/components/processPreview.vue

@ -1,45 +0,0 @@
<template>
<el-dialog v-model="data.visible" title="预览" width="70%" append-to-body destroy-on-close>
<div v-if="data.type === 'bpmn' && data.xmlStr">
<BpmnViewer ref="bpmnViewerRef"></BpmnViewer>
</div>
<div v-if="data.type === 'xml' && data.xmlStr">
<highlightjs language="xml" :code="data.xmlStr" />
</div>
<template #footer>
<span v-if="data.type === 'xml'" class="dialog-footer"> </span>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import BpmnViewer from '@/components/BpmnView/index.vue';
const data = reactive({
visible: false,
type: '',
xmlStr: ''
});
const bpmnViewerRef = ref<InstanceType<typeof BpmnViewer>>();
type PreviewType = 'xml' | 'bpmn';
//
const openDialog = (xmlStr: string, type: PreviewType) => {
data.visible = true;
data.xmlStr = xmlStr;
data.type = type;
/** 流程图 */
if (type === 'bpmn') {
/** 必须放在nextTick 否则第一次打开为空 */
nextTick(() => {
bpmnViewerRef.value?.initXml(data.xmlStr);
});
}
};
/**
* 对外暴露子组件方法
*/
defineExpose({
openDialog
});
</script>

517
im-admin-ui/src/views/workflow/processDefinition/index.vue

@ -1,517 +0,0 @@
<template>
<div class="p-2">
<el-row :gutter="20">
<!-- 流程分类树 -->
<el-col :lg="4" :xs="24" style="">
<el-card shadow="hover">
<el-input v-model="categoryName" placeholder="请输入流程分类名" prefix-icon="Search" clearable />
<el-tree
ref="categoryTreeRef"
class="mt-2"
node-key="id"
:data="categoryOptions"
:props="{ label: 'categoryName', children: 'children' }"
:expand-on-click-node="false"
:filter-node-method="filterNode"
highlight-current
default-expand-all
@node-click="handleNodeClick"
></el-tree>
</el-card>
</el-col>
<el-col :lg="20" :xs="24">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true" label-width="120px">
<el-form-item label="流程定义名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入流程定义名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程定义KEY" prop="key">
<el-input v-model="queryParams.key" placeholder="请输入流程定义KEY" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="danger" icon="Delete" :disabled="multiple" @click="handleDelete()">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="primary" icon="UploadFilled" @click="uploadDialog.visible = true">部署流程文件</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" border :data="processDefinitionList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column align="center" type="index" label="序号" width="60"></el-table-column>
<el-table-column align="center" prop="name" label="流程定义名称" :show-overflow-tooltip="true"></el-table-column>
<el-table-column align="center" prop="key" label="标识KEY" width="80"></el-table-column>
<el-table-column align="center" prop="version" label="版本号" width="80">
<template #default="scope"> v{{ scope.row.version }}.0</template>
</el-table-column>
<el-table-column align="center" prop="resourceName" label="流程XML" width="100" :show-overflow-tooltip="true">
<template #default="scope">
<el-link type="primary" @click="clickPreview(scope.row.id, 'xml')">{{ scope.row.resourceName }}</el-link>
</template>
</el-table-column>
<el-table-column align="center" prop="diagramResourceName" label="流程图片" width="100" :show-overflow-tooltip="true">
<template #default="scope">
<el-link type="primary" @click="clickPreview(scope.row.id, 'bpmn')">{{ scope.row.diagramResourceName }}</el-link>
</template>
</el-table-column>
<el-table-column align="center" prop="suspensionState" label="状态" width="80">
<template #default="scope">
<el-tag v-if="scope.row.suspensionState == 1" type="success">激活</el-tag>
<el-tag v-else type="danger">挂起</el-tag>
</template>
</el-table-column>
<el-table-column align="center" prop="deploymentTime" label="部署时间" width="120" :show-overflow-tooltip="true"></el-table-column>
<el-table-column align="center" label="表名/表单KEY" width="120" :show-overflow-tooltip="true">
<template #default="scope">
<span v-if="scope.row.wfDefinitionConfigVo">
{{ scope.row.wfDefinitionConfigVo.tableName }}
</span>
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" align="center" width="220" class-name="small-padding fixed-width">
<template #default="scope">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
link
type="primary"
size="small"
:icon="scope.row.suspensionState === 1 ? 'Lock' : 'Unlock'"
@click="handleProcessDefState(scope.row)"
>
{{ scope.row.suspensionState === 1 ? '挂起流程' : '激活流程' }}
</el-button>
</el-col>
<el-col :span="1.5">
<el-button link type="primary" size="small" icon="Document" @click="getProcessDefinitionHitoryList(scope.row.id, scope.row.key)">
历史版本
</el-button>
</el-col>
<el-col :span="1.5">
<el-button link type="primary" size="small" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
</el-col>
</el-row>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button link type="primary" size="small" icon="Sort" @click="handleConvertToModel(scope.row)"> 转换模型 </el-button>
</el-col>
<el-col :span="1.5">
<el-button link type="primary" size="small" icon="Tickets" @click="handleDefinitionConfigOpen(scope.row)">绑定业务</el-button>
</el-col>
</el-row>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
:total="total"
@pagination="getList"
/>
</el-card>
</el-col>
</el-row>
<!-- 预览图片或xml -->
<process-preview ref="previewRef" />
<!-- 部署文件 -->
<el-dialog v-if="uploadDialog.visible" v-model="uploadDialog.visible" :title="uploadDialog.title" width="30%">
<div v-loading="uploadDialogLoading">
<div class="mb5">
<el-text class="mx-1" size="large"><span class="text-danger">*</span>请选择部署流程分类</el-text>
<el-tree-select
v-model="selectCategory"
:data="categoryOptions"
:props="{ value: 'categoryCode', label: 'categoryName', children: 'children' }"
filterable
value-key="categoryCode"
:render-after-expand="false"
check-strictly
style="width: 240px"
/>
</div>
<el-upload
class="upload-demo"
drag
multiple
accept="application/zip,application/xml,.bpmn"
:before-upload="handlerBeforeUpload"
:http-request="handerDeployProcessFile"
>
<el-icon class="UploadFilled"><upload-filled /></el-icon>
<div class="el-upload__text"><em>点击上传选择BPMN流程文件</em></div>
<div class="el-upload__text">仅支持 .zip.bpmn20.xmlbpmn 格式文件</div>
<div class="el-upload__text">PS:如若部署请部署从本项目模型管理导出的数据</div>
</el-upload>
</div>
</el-dialog>
<!-- 历史版本 -->
<el-dialog v-if="processDefinitionDialog.visible" v-model="processDefinitionDialog.visible" :title="processDefinitionDialog.title" width="70%">
<el-table v-loading="loading" :data="processDefinitionHistoryList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column align="center" type="index" label="序号" width="60"></el-table-column>
<el-table-column align="center" prop="name" label="流程定义名称" :show-overflow-tooltip="true" min-width="80"></el-table-column>
<el-table-column align="center" prop="key" label="标识KEY"></el-table-column>
<el-table-column align="center" prop="version" label="版本号" width="90">
<template #default="scope"> v{{ scope.row.version }}.0</template>
</el-table-column>
<el-table-column align="center" prop="resourceName" label="流程XML" min-width="80" :show-overflow-tooltip="true">
<template #default="scope">
<el-link type="primary" @click="clickPreviewXML(scope.row.id)">{{ scope.row.resourceName }}</el-link>
</template>
</el-table-column>
<el-table-column align="center" prop="diagramResourceName" label="流程图片" min-width="80" :show-overflow-tooltip="true">
<template #default="scope">
<el-link type="primary" @click="clickPreviewImg(scope.row.id)">{{ scope.row.diagramResourceName }}</el-link>
</template>
</el-table-column>
<el-table-column align="center" prop="suspensionState" label="状态" min-width="70">
<template #default="scope">
<el-tag v-if="scope.row.suspensionState == 1" type="success">激活</el-tag>
<el-tag v-else type="danger">挂起</el-tag>
</template>
</el-table-column>
<el-table-column align="center" prop="deploymentTime" label="部署时间" :show-overflow-tooltip="true"></el-table-column>
<el-table-column fixed="right" label="操作" align="center" width="200" class-name="small-padding fixed-width">
<template #default="scope">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
link
type="primary"
size="small"
:icon="scope.row.suspensionState === 1 ? 'Lock' : 'Unlock'"
@click="handleProcessDefState(scope.row)"
>
{{ scope.row.suspensionState === 1 ? '挂起流程' : '激活流程' }}
</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="text" size="small" icon="Tickets" @click="handleDefinitionConfigOpen(scope.row)">绑定业务</el-button>
</el-col>
</el-row>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button link type="primary" icon="Sort" size="small" @click="handleConvertToModel(scope.row)"> 转换模型 </el-button>
</el-col>
<el-col :span="1.5">
<el-button link type="primary" icon="Delete" size="small" @click="handleDelete(scope.row)">删除</el-button>
</el-col>
</el-row>
</template>
</el-table-column>
</el-table>
</el-dialog>
<!-- 表单配置 -->
<el-dialog
v-model="definitionConfigDialog.visible"
:title="definitionConfigDialog.title"
width="650px"
append-to-body
:close-on-click-modal="false"
>
<el-form :model="definitionConfigForm" label-width="auto">
<el-form-item label="流程KEY">
<el-input v-model="definitionConfigForm.processKey" disabled />
</el-form-item>
<el-form-item label="表名" prop="formId">
<el-input v-model="definitionConfigForm.tableName" placeholder="示例:test_leave" />
</el-form-item>
<el-form-item label="备注">
<el-input v-model="definitionConfigForm.remark" type="textarea" resize="none" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="definitionConfigDialog.visible = false">取消</el-button>
<el-button type="primary" @click="handlerSaveForm">保存</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="processDefinition">
import {
listProcessDefinition,
definitionImage,
definitionXml,
deleteProcessDefinition,
updateDefinitionState,
convertToModel,
deployProcessFile,
getListByKey
} from '@/api/workflow/processDefinition';
import { getByTableNameNotDefId, getByDefId, saveOrUpdate } from '@/api/workflow/definitionConfig';
import ProcessPreview from './components/processPreview.vue';
import { listCategory } from '@/api/workflow/category';
import { CategoryVO } from '@/api/workflow/category/types';
import { ProcessDefinitionQuery, ProcessDefinitionVO } from '@/api/workflow/processDefinition/types';
import { DefinitionConfigForm } from '@/api/workflow/definitionConfig/types';
import { UploadRequestOptions, ElMessage, ElMessageBox } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const previewRef = ref<InstanceType<typeof ProcessPreview>>();
const queryFormRef = ref<ElFormInstance>();
const categoryTreeRef = ref<ElTreeInstance>();
const definitionConfigForm = ref<DefinitionConfigForm>({});
type CategoryOption = {
categoryCode: string;
categoryName: string;
children?: CategoryOption[];
};
const loading = ref(true);
const ids = ref<Array<any>>([]);
const deploymentIds = ref<Array<any>>([]);
const keys = ref<Array<any>>([]);
const single = ref(true);
const multiple = ref(true);
const showSearch = ref(true);
const total = ref(0);
const uploadDialogLoading = ref(false);
const processDefinitionList = ref<ProcessDefinitionVO[]>([]);
const processDefinitionHistoryList = ref<ProcessDefinitionVO[]>([]);
const categoryOptions = ref<CategoryOption[]>([]);
const categoryName = ref('');
/** 部署文件分类选择 */
const selectCategory = ref();
const uploadDialog = reactive<DialogOption>({
visible: false,
title: '部署流程文件'
});
const processDefinitionDialog = reactive<DialogOption>({
visible: false,
title: '历史版本'
});
const definitionConfigDialog = reactive<DialogOption>({
visible: false,
title: '流程定义配置'
});
//
const queryParams = ref<ProcessDefinitionQuery>({
pageNum: 1,
pageSize: 10,
name: undefined,
key: undefined,
categoryCode: undefined
});
onMounted(() => {
getList();
getTreeselect();
});
/** 节点单击事件 */
const handleNodeClick = (data: CategoryVO) => {
queryParams.value.categoryCode = data.categoryCode;
if (data.categoryCode === 'ALL') {
queryParams.value.categoryCode = '';
}
handleQuery();
};
/** 通过条件过滤节点 */
const filterNode = (value: string, data: any) => {
if (!value) return true;
return data.categoryName.indexOf(value) !== -1;
};
/** 根据名称筛选部门树 */
watchEffect(
() => {
categoryTreeRef.value.filter(categoryName.value);
},
{
flush: 'post' // watchEffectDOMDOM
}
);
/** 查询流程分类下拉树结构 */
const getTreeselect = async () => {
const res = await listCategory();
categoryOptions.value = [];
const data: CategoryOption = { categoryCode: 'ALL', categoryName: '顶级节点', children: [] };
data.children = proxy?.handleTree<CategoryOption>(res.data, 'id', 'parentId');
categoryOptions.value.push(data);
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
queryParams.value.categoryCode = '';
queryParams.value.pageNum = 1;
queryParams.value.pageSize = 10;
handleQuery();
};
//
const handleSelectionChange = (selection: any) => {
ids.value = selection.map((item: any) => item.id);
deploymentIds.value = selection.map((item: any) => item.deploymentId);
keys.value = selection.map((item: any) => item.key);
single.value = selection.length !== 1;
multiple.value = !selection.length;
};
//
const getList = async () => {
loading.value = true;
const resp = await listProcessDefinition(queryParams.value);
processDefinitionList.value = resp.rows;
total.value = resp.total;
loading.value = false;
};
//
const getProcessDefinitionHitoryList = async (id: string, key: string) => {
processDefinitionDialog.visible = true;
loading.value = true;
const resp = await getListByKey(key);
if (resp.data && resp.data.length > 0) {
processDefinitionHistoryList.value = resp.data.filter((item: any) => item.id !== id);
}
loading.value = false;
};
type PreviewType = 'xml' | 'bpmn';
//
const clickPreview = async (id: string, type: PreviewType) => {
loading.value = true;
const resp = await definitionXml(id);
if (previewRef.value) {
const xmlStr = resp.data.xmlStr;
loading.value = false;
previewRef.value.openDialog(xmlStr, type);
}
};
/** 删除按钮操作 */
const handleDelete = async (row?: ProcessDefinitionVO) => {
const id = row?.id || ids.value;
const deployIds = row?.deploymentId || deploymentIds.value;
const defKeys = row?.key || keys.value;
await proxy?.$modal.confirm('是否确认删除流程定义KEY为【' + defKeys + '】的数据项?');
loading.value = true;
await deleteProcessDefinition(deployIds, id).finally(() => (loading.value = false));
await getList();
proxy?.$modal.msgSuccess('删除成功');
};
/** 挂起/激活 */
const handleProcessDefState = async (row: ProcessDefinitionVO) => {
let msg: string;
if (row.suspensionState === 1) {
msg = `暂停后,此流程下的所有任务都不允许往后流转,您确定挂起【${row.name || row.key}】吗?`;
} else {
msg = `启动后,此流程下的所有任务都允许往后流转,您确定激活【${row.name || row.key}】吗?`;
}
await proxy?.$modal.confirm(msg);
loading.value = true;
await updateDefinitionState(row.id).finally(() => (loading.value = false));
await getList();
proxy?.$modal.msgSuccess('操作成功');
};
/** 流程定义转换为模型 */
const handleConvertToModel = async (row: ProcessDefinitionVO) => {
await proxy?.$modal.confirm('是否确认转换流程定义key为【' + row.key + '】的数据项?');
await convertToModel(row.id).finally(() => (loading.value = false));
getList();
proxy?.$modal.msgSuccess('操作成功');
};
//
const handlerBeforeUpload = () => {
if (selectCategory.value === 'ALL') {
proxy?.$modal.msgError('顶级节点不可作为分类!');
return false;
}
if (!selectCategory.value) {
proxy?.$modal.msgError('请选择左侧要上传的分类!');
return false;
}
};
//
const handerDeployProcessFile = (data: UploadRequestOptions): XMLHttpRequest => {
let formData = new FormData();
uploadDialogLoading.value = true;
formData.append('file', data.file);
formData.append('categoryCode', selectCategory.value);
deployProcessFile(formData)
.then(() => {
uploadDialog.visible = false;
proxy?.$modal.msgSuccess('部署成功');
handleQuery();
})
.finally(() => {
uploadDialogLoading.value = false;
});
return;
};
//
const handleDefinitionConfigOpen = async (row: ProcessDefinitionVO) => {
definitionConfigDialog.visible = true;
definitionConfigForm.value.processKey = row.key;
definitionConfigForm.value.definitionId = row.id;
definitionConfigForm.value.version = row.version;
const resp = await getByDefId(row.id);
if (resp.data) {
definitionConfigForm.value = resp.data;
} else {
definitionConfigForm.value.tableName = undefined;
definitionConfigForm.value.remark = undefined;
}
};
//
const handlerSaveForm = async () => {
getByTableNameNotDefId(definitionConfigForm.value.tableName, definitionConfigForm.value.definitionId).then((res) => {
if (res.data && res.data.length > 0) {
ElMessageBox.confirm('表名已被【' + res.data[0].processKey + '】版本v' + res.data[0].version + '.0绑定确认后将会删除绑定的流程KEY!', '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
saveOrUpdate(definitionConfigForm.value).then((resp) => {
if (resp.code === 200) {
proxy?.$modal.msgSuccess('操作成功');
definitionConfigDialog.visible = false;
getList();
}
});
});
} else {
saveOrUpdate(definitionConfigForm.value).then((resp) => {
if (resp.code === 200) {
proxy?.$modal.msgSuccess('操作成功');
definitionConfigDialog.visible = false;
getList();
}
});
}
});
};
</script>

364
im-admin-ui/src/views/workflow/processInstance/index.vue

@ -1,364 +0,0 @@
<template>
<div class="p-2">
<el-row :gutter="20">
<!-- 流程分类树 -->
<el-col :lg="4" :xs="24" style="">
<el-card shadow="hover">
<el-input v-model="categoryName" placeholder="请输入流程分类名" prefix-icon="Search" clearable />
<el-tree
ref="categoryTreeRef"
class="mt-2"
node-key="id"
:data="categoryOptions"
:props="{ label: 'categoryName', children: 'children' }"
:expand-on-click-node="false"
:filter-node-method="filterNode"
highlight-current
default-expand-all
@node-click="handleNodeClick"
></el-tree>
</el-card>
</el-col>
<el-col :lg="20" :xs="24">
<div class="mb-[10px]">
<el-card shadow="hover" class="text-center">
<el-radio-group v-model="tab" @change="changeTab(tab)">
<el-radio-button value="running">运行中</el-radio-button>
<el-radio-button value="finish">已完成</el-radio-button>
</el-radio-group>
</el-card>
</div>
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true" label-width="120px">
<el-form-item label="流程定义名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入流程定义名称" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程定义KEY" prop="key">
<el-input v-model="queryParams.key" placeholder="请输入流程定义KEY" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete">删除</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @query-table="handleQuery"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" border :data="processInstanceList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column align="center" type="index" label="序号" width="60"></el-table-column>
<el-table-column :show-overflow-tooltip="true" align="center" label="流程定义名称">
<template #default="scope">
<span>{{ scope.row.processDefinitionName }}v{{ scope.row.processDefinitionVersion }}.0</span>
</template>
</el-table-column>
<el-table-column align="center" prop="processDefinitionKey" label="流程定义KEY"></el-table-column>
<el-table-column align="center" prop="processDefinitionVersion" label="版本号" width="90">
<template #default="scope"> v{{ scope.row.processDefinitionVersion }}.0</template>
</el-table-column>
<el-table-column v-if="tab === 'running'" align="center" prop="isSuspended" label="状态" min-width="70">
<template #default="scope">
<el-tag v-if="!scope.row.isSuspended" type="success">激活</el-tag>
<el-tag v-else type="danger">挂起</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="流程状态" min-width="70">
<template #default="scope">
<dict-tag :options="wf_business_status" :value="scope.row.businessStatus"></dict-tag>
</template>
</el-table-column>
<el-table-column align="center" prop="startTime" label="启动时间" width="160"></el-table-column>
<el-table-column v-if="tab === 'finish'" align="center" prop="endTime" label="结束时间" width="160"></el-table-column>
<el-table-column label="操作" align="center" :width="130">
<template #default="scope">
<el-row v-if="tab === 'running'" :gutter="10" class="mb8">
<el-col :span="1.5">
<el-popover :ref="`popoverRef${scope.$index}`" trigger="click" placement="left" :width="300">
<el-input v-model="deleteReason" resize="none" :rows="3" type="textarea" placeholder="请输入作废原因" />
<div style="text-align: right; margin: 5px 0px 0px 0px">
<el-button size="small" text @click="cancelPopover(scope.$index)">取消</el-button>
<el-button size="small" type="primary" @click="handleInvalid(scope.row)">确认</el-button>
</div>
<template #reference>
<el-button link type="primary" size="small" icon="CircleClose">作废</el-button>
</template>
</el-popover>
</el-col>
<el-col :span="1.5">
<el-button link type="primary" size="small" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
</el-col>
</el-row>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button link type="primary" size="small" icon="View" @click="handleView(scope.row)">查看</el-button>
</el-col>
</el-row>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
:total="total"
@pagination="handleQuery"
/>
</el-card>
</el-col>
</el-row>
<el-dialog v-if="processDefinitionDialog.visible" v-model="processDefinitionDialog.visible" :title="processDefinitionDialog.title" width="70%">
<el-table v-loading="loading" :data="processDefinitionHistoryList">
<el-table-column fixed align="center" type="index" label="序号" width="60"></el-table-column>
<el-table-column fixed align="center" prop="name" label="流程定义名称"></el-table-column>
<el-table-column align="center" prop="key" label="标识Key"></el-table-column>
<el-table-column align="center" prop="version" label="版本号" width="90">
<template #default="scope"> v{{ scope.row.version }}.0</template>
</el-table-column>
<el-table-column align="center" prop="suspensionState" label="状态" min-width="70">
<template #default="scope">
<el-tag v-if="scope.row.suspensionState == 1" type="success">激活</el-tag>
<el-tag v-else type="danger">挂起</el-tag>
</template>
</el-table-column>
<el-table-column align="center" prop="deploymentTime" label="部署时间" :show-overflow-tooltip="true"></el-table-column>
<el-table-column fixed="right" label="操作" align="center" width="200" class-name="small-padding fixed-width">
<template #default="scope">
<el-button link type="primary" size="small" icon="Sort" @click="handleChange(scope.row.id)">切换</el-button>
</template>
</el-table-column>
</el-table>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import {
getPageByRunning,
getPageByFinish,
deleteRunAndHisInstance,
deleteFinishAndHisInstance,
deleteRunInstance
} from '@/api/workflow/processInstance';
import { getListByKey, migrationDefinition } from '@/api/workflow/processDefinition';
import { listCategory } from '@/api/workflow/category';
import { CategoryVO } from '@/api/workflow/category/types';
import { ProcessInstanceQuery, ProcessInstanceVO } from '@/api/workflow/processInstance/types';
import workflowCommon from '@/api/workflow/workflowCommon';
import { RouterJumpVo } from '@/api/workflow/workflowCommon/types';
//
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { wf_business_status } = toRefs<any>(proxy?.useDict('wf_business_status'));
const queryFormRef = ref<ElFormInstance>();
const categoryTreeRef = ref<ElTreeInstance>();
//
const loading = ref(true);
//
const ids = ref<Array<any>>([]);
// id
const businessKeys = ref<Array<any>>([]);
//
const single = ref(true);
//
const multiple = ref(true);
//
const showSearch = ref(true);
//
const total = ref(0);
// id
const processDefinitionId = ref<string>('');
//
const processInstanceList = ref<ProcessInstanceVO[]>([]);
const processDefinitionHistoryList = ref<Array<any>>([]);
const categoryOptions = ref<CategoryOption[]>([]);
const categoryName = ref('');
const processDefinitionDialog = reactive<DialogOption>({
visible: false,
title: '流程定义'
});
type CategoryOption = {
categoryCode: string;
categoryName: string;
children?: CategoryOption[];
};
const tab = ref('running');
//
const deleteReason = ref('');
//
const queryParams = ref<ProcessInstanceQuery>({
pageNum: 1,
pageSize: 10,
name: undefined,
key: undefined,
categoryCode: undefined
});
/** 节点单击事件 */
const handleNodeClick = (data: CategoryVO) => {
queryParams.value.categoryCode = data.categoryCode;
if (data.categoryCode === 'ALL') {
queryParams.value.categoryCode = '';
}
handleQuery();
};
/** 通过条件过滤节点 */
const filterNode = (value: string, data: any) => {
if (!value) return true;
return data.categoryName.indexOf(value) !== -1;
};
/** 根据名称筛选部门树 */
watchEffect(
() => {
categoryTreeRef.value.filter(categoryName.value);
},
{
flush: 'post' // watchEffectDOMDOM
}
);
/** 查询流程分类下拉树结构 */
const getTreeselect = async () => {
const res = await listCategory();
categoryOptions.value = [];
const data: CategoryOption = { categoryCode: 'ALL', categoryName: '顶级节点', children: [] };
data.children = proxy?.handleTree<CategoryOption>(res.data, 'id', 'parentId');
categoryOptions.value.push(data);
};
/** 搜索按钮操作 */
const handleQuery = () => {
if ('running' === tab.value) {
getProcessInstanceRunningList();
} else {
getProcessInstanceFinishList();
}
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
queryParams.value.categoryCode = '';
queryParams.value.pageNum = 1;
queryParams.value.pageSize = 10;
handleQuery();
};
//
const handleSelectionChange = (selection: ProcessInstanceVO[]) => {
ids.value = selection.map((item: any) => item.id);
businessKeys.value = selection.map((item: any) => item.businessKey);
single.value = selection.length !== 1;
multiple.value = !selection.length;
};
//
const getProcessInstanceRunningList = () => {
loading.value = true;
getPageByRunning(queryParams.value).then((resp) => {
processInstanceList.value = resp.rows;
total.value = resp.total;
loading.value = false;
});
};
//
const getProcessInstanceFinishList = () => {
loading.value = true;
getPageByFinish(queryParams.value).then((resp) => {
processInstanceList.value = resp.rows;
total.value = resp.total;
loading.value = false;
});
};
/** 删除按钮操作 */
const handleDelete = async (row: any) => {
const businessKey = row.businessKey || businessKeys.value;
await proxy?.$modal.confirm('是否确认删除业务id为【' + businessKey + '】的数据项?');
loading.value = true;
if ('running' === tab.value) {
await deleteRunAndHisInstance(businessKey).finally(() => (loading.value = false));
getProcessInstanceRunningList();
} else {
await deleteFinishAndHisInstance(businessKey).finally(() => (loading.value = false));
getProcessInstanceFinishList();
}
proxy?.$modal.msgSuccess('删除成功');
};
const changeTab = async (data: string) => {
processInstanceList.value = [];
queryParams.value.pageNum = 1;
if ('running' === data) {
getProcessInstanceRunningList();
} else {
getProcessInstanceFinishList();
}
};
/** 作废按钮操作 */
const handleInvalid = async (row: ProcessInstanceVO) => {
await proxy?.$modal.confirm('是否确认作废业务id为【' + row.businessKey + '】的数据项?');
loading.value = true;
if ('running' === tab.value) {
let param = {
businessKey: row.businessKey,
deleteReason: deleteReason.value
};
await deleteRunInstance(param).finally(() => (loading.value = false));
getProcessInstanceRunningList();
proxy?.$modal.msgSuccess('操作成功');
}
};
const cancelPopover = async (index: any) => {
(proxy?.$refs[`popoverRef${index}`] as any).hide(); //
};
//
const getProcessDefinitionHitoryList = (id: string, key: string) => {
processDefinitionDialog.visible = true;
processDefinitionId.value = id;
loading.value = true;
getListByKey(key).then((resp) => {
if (resp.data && resp.data.length > 0) {
processDefinitionHistoryList.value = resp.data.filter((item: any) => item.id !== id);
}
loading.value = false;
});
};
//
const handleChange = async (id: string) => {
await proxy?.$modal.confirm('是否确认切换?');
loading.value = true;
migrationDefinition(processDefinitionId.value, id).then((resp) => {
proxy?.$modal.msgSuccess('操作成功');
getProcessInstanceRunningList();
processDefinitionDialog.visible = false;
loading.value = false;
});
};
/** 查看按钮操作 */
const handleView = (row) => {
const routerJumpVo = reactive<RouterJumpVo>({
wfDefinitionConfigVo: row.wfDefinitionConfigVo,
wfNodeConfigVo: row.wfNodeConfigVo,
businessKey: row.businessKey,
taskId: row.id,
type: 'view'
});
workflowCommon.routerJump(routerJumpVo, proxy);
};
onMounted(() => {
getProcessInstanceRunningList();
getTreeselect();
});
</script>

286
im-admin-ui/src/views/workflow/task/allTaskWaiting.vue

@ -1,286 +0,0 @@
<template>
<div class="p-2">
<div class="mb-[10px]">
<el-card shadow="hover" class="text-center">
<el-radio-group v-model="tab" @change="changeTab(tab)">
<el-radio-button value="waiting">待办任务</el-radio-button>
<el-radio-button value="finish">已办任务</el-radio-button>
</el-radio-group>
</el-card>
</div>
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="任务名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入任务名称" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程定义名称" label-width="100" prop="processDefinitionName">
<el-input v-model="queryParams.processDefinitionName" placeholder="请输入流程定义名称" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程定义KEY" label-width="100" prop="processDefinitionKey">
<el-input v-model="queryParams.processDefinitionKey" placeholder="请输入流程定义KEY" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Edit" :disabled="multiple" @click="handleUpdate">修改办理人</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @query-table="handleQuery"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" border :data="taskList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column align="center" type="index" label="序号" width="60"></el-table-column>
<el-table-column :show-overflow-tooltip="true" align="center" label="流程定义名称">
<template #default="scope">
<span>{{ scope.row.processDefinitionName }}v{{ scope.row.processDefinitionVersion }}.0</span>
</template>
</el-table-column>
<el-table-column align="center" prop="processDefinitionKey" label="流程定义KEY"></el-table-column>
<el-table-column align="center" prop="name" label="任务名称"></el-table-column>
<el-table-column align="center" prop="assigneeName" label="办理人">
<template v-if="tab === 'waiting'" #default="scope">
<template v-if="scope.row.participantVo && scope.row.assignee === null">
<el-tag v-for="(item, index) in scope.row.participantVo.candidateName" :key="index" type="success">
{{ item }}
</el-tag>
</template>
<template v-else>
<el-tag type="success">
{{ scope.row.assigneeName || '无' }}
</el-tag>
</template>
</template>
<template v-else-if="tab === 'finish'" #default="scope">
<el-tag type="success">
{{ scope.row.assigneeName || '无' }}
</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="流程状态" min-width="70">
<template #default="scope">
<dict-tag v-if="tab === 'waiting'" :options="wf_business_status" :value="scope.row.businessStatus"></dict-tag>
<el-tag v-else type="success">已完成</el-tag>
</template>
</el-table-column>
<el-table-column v-if="tab === 'waiting'" align="center" prop="createTime" label="创建时间" width="160"></el-table-column>
<el-table-column v-if="tab === 'finish'" align="center" prop="startTime" label="创建时间" width="160"></el-table-column>
<el-table-column label="操作" align="center" :width="tab === 'finish' ? '80' : '151'">
<template #default="scope">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button link type="primary" size="small" icon="View" @click="handleView(scope.row)">查看</el-button>
</el-col>
<el-col v-if="tab === 'waiting'" :span="1.5">
<el-button link type="primary" size="small" icon="Document" @click="handleInstanceVariable(scope.row)">流程变量</el-button>
</el-col>
</el-row>
<el-row v-if="scope.row.multiInstance" :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button link type="primary" size="small" icon="Remove" @click="deleteMultiInstanceUser(scope.row)">减签</el-button>
</el-col>
<el-col :span="1.5">
<el-button link type="primary" size="small" icon="CirclePlus" @click="addMultiInstanceUser(scope.row)">加签</el-button>
</el-col>
</el-row>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
:total="total"
@pagination="handleQuery"
/>
</el-card>
<!-- 加签组件 -->
<multiInstanceUser ref="multiInstanceUserRef" :title="title" @submit-callback="handleQuery" />
<!-- 选人组件 -->
<UserSelect ref="userSelectRef" :multiple="false" @confirm-call-back="submitCallback"></UserSelect>
<!-- 流程变量开始 -->
<el-dialog v-model="variableVisible" draggable title="流程变量" width="60%" :close-on-click-modal="false">
<el-card v-loading="variableLoading" class="box-card">
<template #header>
<div class="clearfix">
<span
>流程定义名称<el-tag>{{ processDefinitionName }}</el-tag></span
>
</div>
</template>
<div v-for="(v, index) in variableList" :key="index">
<el-form v-if="v.key !== '_FLOWABLE_SKIP_EXPRESSION_ENABLED'" :label-position="'right'" label-width="150px">
<el-form-item :label="v.key + ':'">
{{ v.value }}
</el-form-item>
</el-form>
</div>
</el-card>
</el-dialog>
<!-- 流程变量结束 -->
</div>
</template>
<script lang="ts" setup>
import { getPageByAllTaskWait, getPageByAllTaskFinish, updateAssignee, getInstanceVariable } from '@/api/workflow/task';
import MultiInstanceUser from '@/components/Process/multiInstanceUser.vue';
import UserSelect from '@/components/UserSelect';
import { TaskQuery, TaskVO, VariableVo } from '@/api/workflow/task/types';
import workflowCommon from '@/api/workflow/workflowCommon';
import { RouterJumpVo } from '@/api/workflow/workflowCommon/types';
//
//
const multiInstanceUserRef = ref<InstanceType<typeof MultiInstanceUser>>();
//
const userSelectRef = ref<InstanceType<typeof UserSelect>>();
const queryFormRef = ref<ElFormInstance>();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { wf_business_status } = toRefs<any>(proxy?.useDict('wf_business_status'));
//
const loading = ref(true);
//
const ids = ref<Array<any>>([]);
//
const single = ref(true);
//
const multiple = ref(true);
//
const showSearch = ref(true);
//
const total = ref(0);
//
const taskList = ref([]);
const title = ref('');
//
const variableVisible = ref(false);
const variableLoading = ref(true);
//
const variableList = ref<VariableVo>({
key: '',
value: ''
});
//
const processDefinitionName = ref();
//
const queryParams = ref<TaskQuery>({
pageNum: 1,
pageSize: 10,
name: undefined,
processDefinitionName: undefined,
processDefinitionKey: undefined
});
const tab = ref('waiting');
//
const addMultiInstanceUser = (row: TaskVO) => {
if (multiInstanceUserRef.value) {
title.value = '加签人员';
multiInstanceUserRef.value.getAddMultiInstanceList(row.id, []);
}
};
//
const deleteMultiInstanceUser = (row: TaskVO) => {
if (multiInstanceUserRef.value) {
title.value = '减签人员';
multiInstanceUserRef.value.getDeleteMultiInstanceList(row.id);
}
};
/** 搜索按钮操作 */
const handleQuery = () => {
if ('waiting' === tab.value) {
getWaitingList();
} else {
getFinishList();
}
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
queryParams.value.pageNum = 1;
queryParams.value.pageSize = 10;
handleQuery();
};
//
const handleSelectionChange = (selection: any) => {
ids.value = selection.map((item: any) => item.id);
single.value = selection.length !== 1;
multiple.value = !selection.length;
};
const changeTab = async (data: string) => {
taskList.value = [];
queryParams.value.pageNum = 1;
if ('waiting' === data) {
getWaitingList();
} else {
getFinishList();
}
};
//
const getWaitingList = () => {
loading.value = true;
getPageByAllTaskWait(queryParams.value).then((resp) => {
taskList.value = resp.rows;
total.value = resp.total;
loading.value = false;
});
};
const getFinishList = () => {
loading.value = true;
getPageByAllTaskFinish(queryParams.value).then((resp) => {
taskList.value = resp.rows;
total.value = resp.total;
loading.value = false;
});
};
//
const handleUpdate = () => {
userSelectRef.value.open();
};
//
const submitCallback = async (data) => {
if (data && data.length > 0) {
await proxy?.$modal.confirm('是否确认提交?');
loading.value = true;
await updateAssignee(ids.value, data[0].userId);
handleQuery();
proxy?.$modal.msgSuccess('操作成功');
} else {
proxy?.$modal.msgWarning('请选择用户!');
}
};
//
const handleInstanceVariable = async (row: TaskVO) => {
variableLoading.value = true;
variableVisible.value = true;
processDefinitionName.value = row.processDefinitionName;
let data = await getInstanceVariable(row.id);
variableList.value = data.data;
variableLoading.value = false;
};
/** 查看按钮操作 */
const handleView = (row) => {
const routerJumpVo = reactive<RouterJumpVo>({
wfDefinitionConfigVo: row.wfDefinitionConfigVo,
wfNodeConfigVo: row.wfNodeConfigVo,
businessKey: row.businessKey,
taskId: row.id,
type: 'view'
});
workflowCommon.routerJump(routerJumpVo, proxy);
};
onMounted(() => {
getWaitingList();
});
</script>

255
im-admin-ui/src/views/workflow/task/myDocument.vue

@ -1,255 +0,0 @@
<template>
<div class="p-2">
<el-row :gutter="20">
<!-- 流程分类树 -->
<el-col :lg="4" :xs="24" style="">
<el-card shadow="hover">
<el-input v-model="categoryName" placeholder="请输入流程分类名" prefix-icon="Search" clearable />
<el-tree
ref="categoryTreeRef"
class="mt-2"
node-key="id"
:data="categoryOptions"
:props="{ label: 'categoryName', children: 'children' }"
:expand-on-click-node="false"
:filter-node-method="filterNode"
highlight-current
default-expand-all
@node-click="handleNodeClick"
></el-tree>
</el-card>
</el-col>
<el-col :lg="20" :xs="24">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true" label-width="120px">
<el-form-item label="流程定义名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入流程定义名称" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<right-toolbar v-model:showSearch="showSearch" @query-table="handleQuery"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" border :data="processInstanceList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column align="center" type="index" label="序号" width="60"></el-table-column>
<el-table-column v-if="false" align="center" prop="id" label="id"></el-table-column>
<el-table-column :show-overflow-tooltip="true" align="center" label="流程定义名称">
<template #default="scope">
<span>{{ scope.row.processDefinitionName }}v{{ scope.row.processDefinitionVersion }}.0</span>
</template>
</el-table-column>
<el-table-column align="center" prop="processDefinitionKey" label="流程定义KEY"></el-table-column>
<el-table-column align="center" prop="processDefinitionVersion" label="版本号" width="90">
<template #default="scope"> v{{ scope.row.processDefinitionVersion }}.0</template>
</el-table-column>
<el-table-column v-if="tab === 'running'" align="center" prop="isSuspended" label="状态" min-width="70">
<template #default="scope">
<el-tag v-if="!scope.row.isSuspended" type="success">激活</el-tag>
<el-tag v-else type="danger">挂起</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="流程状态" min-width="70">
<template #default="scope">
<dict-tag :options="wf_business_status" :value="scope.row.businessStatus"></dict-tag>
</template>
</el-table-column>
<el-table-column align="center" prop="startTime" label="启动时间" width="160"></el-table-column>
<el-table-column v-if="tab === 'finish'" align="center" prop="endTime" label="结束时间" width="160"></el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip
v-if="scope.row.businessStatus === 'draft' || scope.row.businessStatus === 'cancel' || scope.row.businessStatus === 'back'"
content="修改"
placement="top"
>
<el-button link type="primary" icon="Edit" @click="handleOpen(scope.row, 'update')"></el-button>
</el-tooltip>
<el-tooltip
v-if="scope.row.businessStatus === 'draft' || scope.row.businessStatus === 'cancel' || scope.row.businessStatus === 'back'"
content="删除"
placement="top"
>
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip>
<el-tooltip placement="top" content="查看">
<el-button link type="primary" icon="View" @click="handleOpen(scope.row, 'view')"></el-button>
</el-tooltip>
<el-tooltip v-if="scope.row.businessStatus === 'waiting'" content="撤销" placement="top">
<el-button link type="primary" icon="Notification" @click="handleCancelProcessApply(scope.row.businessKey)"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
:total="total"
@pagination="getList"
/>
</el-card>
</el-col>
</el-row>
<!-- 提交组件 -->
<submitVerify ref="submitVerifyRef" @submit-callback="getList" />
</div>
</template>
<script lang="ts" setup>
import { getPageByCurrent, deleteRunAndHisInstance, cancelProcessApply } from '@/api/workflow/processInstance';
import { listCategory } from '@/api/workflow/category';
import { CategoryVO } from '@/api/workflow/category/types';
import { ProcessInstanceQuery, ProcessInstanceVO } from '@/api/workflow/processInstance/types';
import workflowCommon from '@/api/workflow/workflowCommon';
import { RouterJumpVo } from '@/api/workflow/workflowCommon/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { wf_business_status } = toRefs<any>(proxy?.useDict('wf_business_status'));
const queryFormRef = ref<ElFormInstance>();
const categoryTreeRef = ref<ElTreeInstance>();
//
const loading = ref(true);
//
const businessKeys = ref<Array<any>>([]);
//
const single = ref(true);
//
const multiple = ref(true);
//
const showSearch = ref(true);
//
const total = ref(0);
//
const processInstanceList = ref<ProcessInstanceVO[]>([]);
const categoryOptions = ref<CategoryOption[]>([]);
const categoryName = ref('');
interface CategoryOption {
categoryCode: string;
categoryName: string;
children?: CategoryOption[];
}
const tab = ref('running');
//
const queryParams = ref<ProcessInstanceQuery>({
pageNum: 1,
pageSize: 10,
name: undefined,
categoryCode: undefined
});
onMounted(() => {
getList();
getTreeselect();
});
/** 节点单击事件 */
const handleNodeClick = (data: CategoryVO) => {
queryParams.value.categoryCode = data.categoryCode;
if (data.categoryCode === 'ALL') {
queryParams.value.categoryCode = '';
}
handleQuery();
};
/** 通过条件过滤节点 */
const filterNode = (value: string, data: any) => {
if (!value) return true;
return data.categoryName.indexOf(value) !== -1;
};
/** 根据名称筛选部门树 */
watchEffect(
() => {
categoryTreeRef.value.filter(categoryName.value);
},
{
flush: 'post' // watchEffectDOMDOM
}
);
/** 查询流程分类下拉树结构 */
const getTreeselect = async () => {
const res = await listCategory();
categoryOptions.value = [];
const data: CategoryOption = { categoryCode: 'ALL', categoryName: '顶级节点', children: [] };
data.children = proxy?.handleTree<CategoryOption>(res.data, 'id', 'parentId');
categoryOptions.value.push(data);
};
/** 搜索按钮操作 */
const handleQuery = () => {
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
queryParams.value.categoryCode = '';
queryParams.value.pageNum = 1;
queryParams.value.pageSize = 10;
handleQuery();
};
//
const handleSelectionChange = (selection: ProcessInstanceVO[]) => {
businessKeys.value = selection.map((item: any) => item.businessKey);
single.value = selection.length !== 1;
multiple.value = !selection.length;
};
//
const getList = () => {
loading.value = true;
getPageByCurrent(queryParams.value).then((resp) => {
processInstanceList.value = resp.rows;
total.value = resp.total;
loading.value = false;
});
};
/** 删除按钮操作 */
const handleDelete = async (row: ProcessInstanceVO) => {
const businessKey = row.businessKey || businessKeys.value;
await proxy?.$modal.confirm('是否确认删除业务id为【' + businessKey + '】的数据项?');
loading.value = true;
if ('running' === tab.value) {
await deleteRunAndHisInstance(businessKey).finally(() => (loading.value = false));
getList();
}
proxy?.$modal.msgSuccess('删除成功');
};
/** 撤销按钮操作 */
const handleCancelProcessApply = async (businessKey: string) => {
await proxy?.$modal.confirm('是否确认撤销当前单据?');
loading.value = true;
if ('running' === tab.value) {
await cancelProcessApply(businessKey).finally(() => (loading.value = false));
getList();
}
proxy?.$modal.msgSuccess('撤销成功');
};
//
const handleOpen = async (row, type) => {
const routerJumpVo = reactive<RouterJumpVo>({
wfDefinitionConfigVo: row.wfDefinitionConfigVo,
wfNodeConfigVo: row.wfNodeConfigVo,
businessKey: row.businessKey,
taskId: row.id,
type: type
});
workflowCommon.routerJump(routerJumpVo, proxy);
};
</script>

150
im-admin-ui/src/views/workflow/task/taskCopyList.vue

@ -1,150 +0,0 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="任务名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入任务名称" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程定义名称" label-width="100" prop="processDefinitionName">
<el-input v-model="queryParams.processDefinitionName" placeholder="请输入流程定义名称" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程定义KEY" label-width="100" prop="processDefinitionKey">
<el-input v-model="queryParams.processDefinitionKey" placeholder="请输入流程定义KEY" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<right-toolbar v-model:showSearch="showSearch" @query-table="handleQuery"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" border :data="taskList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column align="center" type="index" label="序号" width="60"></el-table-column>
<el-table-column :show-overflow-tooltip="true" align="center" label="流程定义名称">
<template #default="scope">
<span>{{ scope.row.processDefinitionName }}v{{ scope.row.processDefinitionVersion }}.0</span>
</template>
</el-table-column>
<el-table-column align="center" prop="processDefinitionKey" label="流程定义KEY"></el-table-column>
<el-table-column align="center" prop="name" label="任务名称"></el-table-column>
<el-table-column align="center" prop="assigneeName" label="办理人">
<template #default="scope">
<template v-if="scope.row.participantVo && scope.row.assignee === null">
<el-tag v-for="(item, index) in scope.row.participantVo.candidateName" :key="index" type="success">
{{ item }}
</el-tag>
</template>
<template v-else>
<el-tag type="success">
{{ scope.row.assigneeName || '无' }}
</el-tag>
</template>
</template>
</el-table-column>
<el-table-column align="center" label="流程状态" min-width="70">
<template #default="scope">
<dict-tag :options="wf_business_status" :value="scope.row.businessStatus"></dict-tag>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="200">
<template #default="scope">
<el-button type="primary" size="small" icon="View" @click="handleView(scope.row)">查看</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
:total="total"
@pagination="handleQuery"
/>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { getPageByTaskCopy } from '@/api/workflow/task';
import { TaskQuery } from '@/api/workflow/task/types';
import workflowCommon from '@/api/workflow/workflowCommon';
import { RouterJumpVo } from '@/api/workflow/workflowCommon/types';
//
const queryFormRef = ref<ElFormInstance>();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { wf_business_status } = toRefs<any>(proxy?.useDict('wf_business_status'));
//
const loading = ref(true);
//
const ids = ref<Array<any>>([]);
//
const single = ref(true);
//
const multiple = ref(true);
//
const showSearch = ref(true);
//
const total = ref(0);
//
const taskList = ref([]);
//
const queryParams = ref<TaskQuery>({
pageNum: 1,
pageSize: 10,
name: undefined,
processDefinitionName: undefined,
processDefinitionKey: undefined
});
/** 搜索按钮操作 */
const handleQuery = () => {
getTaskCopyList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
queryParams.value.pageNum = 1;
queryParams.value.pageSize = 10;
handleQuery();
};
//
const handleSelectionChange = (selection: any) => {
ids.value = selection.map((item: any) => item.id);
single.value = selection.length !== 1;
multiple.value = !selection.length;
};
//
const getTaskCopyList = () => {
loading.value = true;
getPageByTaskCopy(queryParams.value).then((resp) => {
taskList.value = resp.rows;
total.value = resp.total;
loading.value = false;
});
};
/** 查看按钮操作 */
const handleView = (row) => {
const routerJumpVo = reactive<RouterJumpVo>({
wfDefinitionConfigVo: row.wfDefinitionConfigVo,
wfNodeConfigVo: row.wfNodeConfigVo,
businessKey: row.businessKey,
taskId: row.id,
type: 'view'
});
workflowCommon.routerJump(routerJumpVo, proxy);
};
onMounted(() => {
getTaskCopyList();
});
</script>

136
im-admin-ui/src/views/workflow/task/taskFinish.vue

@ -1,136 +0,0 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="任务名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入任务名称" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程定义名称" label-width="100" prop="processDefinitionName">
<el-input v-model="queryParams.processDefinitionName" placeholder="请输入流程定义名称" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程定义KEY" label-width="100" prop="processDefinitionKey">
<el-input v-model="queryParams.processDefinitionKey" placeholder="请输入流程定义KEY" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<right-toolbar v-model:showSearch="showSearch" @query-table="handleQuery"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" border :data="taskList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column align="center" type="index" label="序号" width="60"></el-table-column>
<el-table-column :show-overflow-tooltip="true" align="center" label="流程定义名称">
<template #default="scope">
<span>{{ scope.row.processDefinitionName }}v{{ scope.row.processDefinitionVersion }}.0</span>
</template>
</el-table-column>
<el-table-column align="center" prop="processDefinitionKey" label="流程定义KEY"></el-table-column>
<el-table-column align="center" prop="name" label="任务名称"></el-table-column>
<el-table-column align="center" prop="assigneeName" label="办理人">
<template #default="scope">
<el-tag type="success">
{{ scope.row.assigneeName || '无' }}
</el-tag>
</template>
</el-table-column>
<el-table-column align="center" prop="startTime" label="创建时间" width="160"></el-table-column>
<el-table-column label="操作" align="center" width="200">
<template #default="scope">
<el-button type="primary" size="small" icon="View" @click="handleView(scope.row)">查看</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
:total="total"
@pagination="handleQuery"
/>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { getPageByTaskFinish } from '@/api/workflow/task';
import { TaskQuery, TaskVO } from '@/api/workflow/task/types';
import workflowCommon from '@/api/workflow/workflowCommon';
import { RouterJumpVo } from '@/api/workflow/workflowCommon/types';
//
const queryFormRef = ref<ElFormInstance>();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
//
const loading = ref(true);
//
const ids = ref<Array<any>>([]);
//
const single = ref(true);
//
const multiple = ref(true);
//
const showSearch = ref(true);
//
const total = ref(0);
//
const taskList = ref([]);
//
const queryParams = ref<TaskQuery>({
pageNum: 1,
pageSize: 10,
name: undefined,
processDefinitionName: undefined,
processDefinitionKey: undefined
});
/** 搜索按钮操作 */
const handleQuery = () => {
getFinishList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
queryParams.value.pageNum = 1;
queryParams.value.pageSize = 10;
handleQuery();
};
//
const handleSelectionChange = (selection: any) => {
ids.value = selection.map((item: any) => item.id);
single.value = selection.length !== 1;
multiple.value = !selection.length;
};
const getFinishList = () => {
loading.value = true;
getPageByTaskFinish(queryParams.value).then((resp) => {
taskList.value = resp.rows;
total.value = resp.total;
loading.value = false;
});
};
/** 查看按钮操作 */
const handleView = (row: TaskVO) => {
const routerJumpVo = reactive<RouterJumpVo>({
wfDefinitionConfigVo: row.wfDefinitionConfigVo,
wfNodeConfigVo: row.wfNodeConfigVo,
businessKey: row.businessKey,
taskId: row.id,
type: 'view'
});
workflowCommon.routerJump(routerJumpVo, proxy);
};
onMounted(() => {
getFinishList();
});
</script>

149
im-admin-ui/src/views/workflow/task/taskWaiting.vue

@ -1,149 +0,0 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="任务名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入任务名称" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程定义名称" label-width="100" prop="processDefinitionName">
<el-input v-model="queryParams.processDefinitionName" placeholder="请输入流程定义名称" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程定义KEY" label-width="100" prop="processDefinitionKey">
<el-input v-model="queryParams.processDefinitionKey" placeholder="请输入流程定义KEY" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<right-toolbar v-model:showSearch="showSearch" @query-table="handleQuery"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" border :data="taskList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column align="center" type="index" label="序号" width="60"></el-table-column>
<el-table-column :show-overflow-tooltip="true" align="center" label="流程定义名称">
<template #default="scope">
<span>{{ scope.row.processDefinitionName }}v{{ scope.row.processDefinitionVersion }}.0</span>
</template>
</el-table-column>
<el-table-column align="center" prop="processDefinitionKey" label="流程定义KEY"></el-table-column>
<el-table-column align="center" prop="name" label="任务名称"></el-table-column>
<el-table-column align="center" prop="assigneeName" label="办理人">
<template #default="scope">
<template v-if="scope.row.participantVo && scope.row.assignee === null">
<el-tag v-for="(item, index) in scope.row.participantVo.candidateName" :key="index" type="success">
{{ item }}
</el-tag>
</template>
<template v-else>
<el-tag type="success">
{{ scope.row.assigneeName || '无' }}
</el-tag>
</template>
</template>
</el-table-column>
<el-table-column align="center" label="流程状态" min-width="70">
<template #default="scope">
<dict-tag :options="wf_business_status" :value="scope.row.businessStatus"></dict-tag>
</template>
</el-table-column>
<el-table-column align="center" prop="createTime" label="创建时间" width="160"></el-table-column>
<el-table-column label="操作" align="center" width="200">
<template #default="scope">
<el-button type="primary" size="small" icon="Edit" @click="handleOpen(scope.row)">办理</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
:total="total"
@pagination="handleQuery"
/>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { getPageByTaskWait } from '@/api/workflow/task';
import { TaskQuery, TaskVO } from '@/api/workflow/task/types';
import workflowCommon from '@/api/workflow/workflowCommon';
import { RouterJumpVo } from '@/api/workflow/workflowCommon/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { wf_business_status } = toRefs<any>(proxy?.useDict('wf_business_status'));
//
const queryFormRef = ref<ElFormInstance>();
//
const loading = ref(true);
//
const ids = ref<Array<any>>([]);
//
const single = ref(true);
//
const multiple = ref(true);
//
const showSearch = ref(true);
//
const total = ref(0);
//
const taskList = ref([]);
//
const queryParams = ref<TaskQuery>({
pageNum: 1,
pageSize: 10,
name: undefined,
processDefinitionName: undefined,
processDefinitionKey: undefined
});
onMounted(() => {
getWaitingList();
});
/** 搜索按钮操作 */
const handleQuery = () => {
getWaitingList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
queryParams.value.pageNum = 1;
queryParams.value.pageSize = 10;
handleQuery();
};
//
const handleSelectionChange = (selection: any) => {
ids.value = selection.map((item: any) => item.id);
single.value = selection.length !== 1;
multiple.value = !selection.length;
};
//
const getWaitingList = () => {
loading.value = true;
getPageByTaskWait(queryParams.value).then((resp) => {
taskList.value = resp.rows;
total.value = resp.total;
loading.value = false;
});
};
//
const handleOpen = async (row: TaskVO) => {
const routerJumpVo = reactive<RouterJumpVo>({
wfDefinitionConfigVo: row.wfDefinitionConfigVo,
wfNodeConfigVo: row.wfNodeConfigVo,
businessKey: row.businessKey,
taskId: row.id,
type: 'approval'
});
workflowCommon.routerJump(routerJumpVo, proxy);
};
</script>

2
im-admin-ui/vite.config.ts

@ -25,7 +25,7 @@ export default defineConfig(({ mode, command }: ConfigEnv): UserConfig => {
open: true,
proxy: {
[env.VITE_APP_BASE_API]: {
target: 'http://localhost:8080',
target: 'http://localhost:8889',
changeOrigin: true,
ws: true,
rewrite: (path) => path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '')

18
im-admin/.editorconfig

@ -1,18 +0,0 @@
# http://editorconfig.org
root = true
# 空格替代Tab缩进在各种编辑工具下效果一致
[*]
indent_style = space
indent_size = 4
charset = utf-8
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
[*.{json,yml,yaml}]
indent_size = 2
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false

48
im-admin/.gitignore

@ -1,48 +0,0 @@
######################################################################
# Build Tools
.gradle
/build/
!gradle/wrapper/gradle-wrapper.jar
target/
!.mvn/wrapper/maven-wrapper.jar
######################################################################
# IDE
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### JRebel ###
rebel.xml
### NetBeans ###
nbproject/private/
build/*
nbbuild/
nbdist/
.nb-gradle/
######################################################################
# Others
*.log
*.xml.versionsBackup
*.swp
!*/build/*.java
!*/build/*.html
!*/build/*.xml
.flattened-pom.xml

182
im-admin/README.md

@ -1,182 +0,0 @@
<img src="https://foruda.gitee.com/images/1679673773341074847/178e8451_1766278.png" width="50%" height="50%">
<div style="height: 10px; clear: both;"></div>
- - -
## 平台简介
[![码云Gitee](https://gitee.com/dromara/RuoYi-Vue-Plus/badge/star.svg?theme=blue)](https://gitee.com/dromara/RuoYi-Vue-Plus)
[![GitHub](https://img.shields.io/github/stars/dromara/RuoYi-Vue-Plus.svg?style=social&label=Stars)](https://github.com/dromara/RuoYi-Vue-Plus)
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/master/LICENSE)
[![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
<br>
[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.2.3-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus)
[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.2-blue.svg)]()
[![JDK-17](https://img.shields.io/badge/JDK-17-green.svg)]()
[![JDK-21](https://img.shields.io/badge/JDK-21-green.svg)]()
> RuoYi-Vue-Plus 是重写 RuoYi-Vue 针对 `分布式集群与多租户` 场景全方位升级(不兼容原框架)
> 项目代码、文档 均开源免费可商用 遵循开源协议在项目中保留开源协议文件即可<br>
活到老写到老 为兴趣而开源 为学习而开源 为让大家真正可以学到技术而开源
> 系统演示: [传送门](https://plus-doc.dromara.org/#/common/demo_system)
> 前端项目地址: [plus-ui](https://gitee.com/JavaLionLi/plus-ui)
> 文档地址: [plus-doc](https://plus-doc.dromara.org)
## 赞助商
MaxKey 业界领先单点登录产品 - https://gitee.com/dromara/MaxKey <br>
CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow <br>
数舵科技 软件定制开发APP小程序等 - http://www.shuduokeji.com/ <br>
引迈信息 软件开发平台 - https://www.jnpfsoft.com/index.html?from=plus-doc <br>
[如何成为赞助商 加群联系作者详谈](https://plus-doc.dromara.org/#/common/add_group)
# 本框架与RuoYi的功能差异
| 功能 | 本框架 | RuoYi |
|-------------|-------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------|
| 前端项目 | 采用 Vue3 + TS + ElementPlus 重写 | 基于Vue2/Vue3 + JS |
| 后端项目结构 | 采用插件化 + 扩展包形式 结构解耦 易于扩展 | 模块相互注入耦合严重难以扩展 |
| 后端代码风格 | 严格遵守Alibaba规范与项目统一配置的代码格式化 | 代码书写与常规结构不同阅读障碍大 |
| Web容器 | 采用 Undertow 基于 XNIO 的高性能容器 | 采用 Tomcat |
| 权限认证 | 采用 Sa-Token、Jwt 静态使用功能齐全 低耦合 高扩展 | Spring Security 配置繁琐扩展性极差 |
| 权限注解 | 采用 Sa-Token 支持注解 登录校验、角色校验、权限校验、二级认证校验、HttpBasic校验、忽略校验<br/>角色与权限校验支持多种条件 如 `AND` `OR``权限 OR 角色` 等复杂表达式 | 只支持是否存在匹配 |
| 三方鉴权 | 采用 JustAuth 第三方登录组件 支持微信、钉钉等数十种三方认证 | 无 |
| 关系数据库支持 | 原生支持 MySQL、Oracle、PostgreSQL、SQLServer<br/>可同时使用异构切换(支持其他 mybatis-plus 支持的所有数据库 只需要增加jdbc依赖即可使用 达梦金仓等均有成功案例) | 支持 Mysql、Oracle 不支持同时使用、不支持异构切换 |
| 缓存数据库 | 支持 Redis 5-7 支持大部分新功能特性 如 分布式限流、分布式队列 | Redis 简单 get set 支持 |
| Redis客户端 | 采用 Redisson Redis官方推荐 基于Netty的客户端工具<br/>支持Redis 90%以上的命令 底层优化规避很多不正确的用法 例如: keys被转换为scan<br/>支持单机、哨兵、单主集群、多主集群等模式 | Lettuce + RedisTemplate 支持模式少 工具使用繁琐<br/>连接池采用 common-pool Bug多经常性出问题 |
| 缓存注解 | 采用 Spring-Cache 注解 对其扩展了实现支持了更多功能<br/>例如 过期时间 最大空闲时间 组最大长度等 只需一个注解即可完成数据自动缓存 | 需手动编写Redis代码逻辑 |
| ORM框架 | 采用 Mybatis-Plus 基于对象几乎不用写SQL全java操作 功能强大插件众多<br/>例如多租户插件 分页插件 乐观锁插件等等 | 采用 Mybatis 基于XML需要手写SQL |
| SQL监控 | 采用 p6spy 可输出完整SQL与执行时间监控 | log输出 需手动拼接sql与参数无法快速查看调试问题 |
| 数据分页 | 采用 Mybatis-Plus 分页插件<br/>框架对其进行了扩展 对象化分页对象 支持多种方式传参 支持前端多排序 复杂排序 | 采用 PageHelper 仅支持单查询分页 参数只能从param传 只能单排序 功能扩展性差 体验不好 |
| 数据权限 | 采用 Mybatis-Plus 插件 自行分析拼接SQL 无感式过滤<br/>只需为Mapper设置好注解条件 支持多种自定义 不限于部门角色 | 采用 注解+aop 实现 基于部门角色 生成的sql兼容性差 不支持其他业务扩展<br/>生成sql后需手动拼接到具体业务sql上 对于多个Mapper查询不起作用 |
| 数据脱敏 | 采用 注解 + jackson 序列化期间脱敏 支持不同模块不同的脱敏条件<br/>支持多种策略 如身份证、手机号、地址、邮箱、银行卡等 可自行扩展 | 无 |
| 数据加解密 | 采用 注解 + mybatis 拦截器 对存取数据期间自动加解密<br/>支持多种策略 如BASE64、AES、RSA、SM2、SM4等 | 无 |
| 接口传输加密 | 采用 动态 AES + RSA 加密请求 body 每一次请求秘钥都不同大幅度降低可破解性 | 无 |
| 数据翻译 | 采用 注解 + jackson 序列化期间动态修改数据 数据进行翻译<br/>支持多种模式: `映射翻译` `直接翻译` `其他扩展条件翻译` 接口化两步即可完成自定义扩展 内置多种翻译实现 | 无 |
| 多数据源框架 | 采用 dynamic-datasource 支持市面大部分数据库<br/>通过yml配置即可动态管理异构不同种类的数据库 也可通过前端页面添加数据源<br/>支持spel表达式从请求头参数等条件切换数据源 | 基于 druid 手动编写代码配置数据源 配置繁琐 支持性差 |
| 多数据源事务 | 采用 dynamic-datasource 支持多数据源不同种类的数据库事务回滚 | 不支持 |
| 数据库连接池 | 采用 HikariCP Spring官方内置连接池 配置简单 以性能与稳定性闻名天下 | 采用 druid bug众多 社区维护差 活跃度低 配置众多繁琐性能一般 |
| 数据库主键 | 采用 雪花ID 基于时间戳的 有序增长 唯一ID 再也不用为分库分表 数据合并主键冲突重复而发愁 | 采用 数据库自增ID 支持数据量有限 不支持多数据源主键唯一 |
| WebSocket协议 | 基于 Spring 封装的 WebSocket 协议 扩展了Token鉴权与分布式会话同步 不再只是基于单机的废物 | 无 |
| SSE推送 | 采用 Spring SSE 实现 扩展了Token鉴权与分布式会话同步 | 无 |
| 序列化 | 采用 Jackson Spring官方内置序列化 靠谱!!! | 采用 fastjson bugjson 远近闻名 |
| 分布式幂等 | 参考美团GTIS防重系统简化实现(细节可看文档) | 手动编写注解基于aop实现 |
| 分布式锁 | 采用 Lock4j 底层基于 Redisson | 无 |
| 分布式任务调度 | 采用 SnailJob 天生支持分布式 统一的管理中心 支持多种数据库 支持分片重试DAG任务流等 | 采用 Quartz 基于数据库锁性能差 集群需要做很多配置与改造 |
| 文件存储 | 采用 Minio 分布式文件存储 天生支持多机、多硬盘、多分片、多副本存储<br/>支持权限管理 安全可靠 文件可加密存储 | 采用 本机文件存储 文件裸漏 易丢失泄漏 不支持集群有单点效应 |
| 云存储 | 采用 AWS S3 协议客户端 支持 七牛、阿里、腾讯 等一切支持S3协议的厂家 | 不支持 |
| 短信 | 采用 sms4j 短信融合包 支持数十种短信厂家 只需在yml配置好厂家密钥即可使用 可多厂家共用 | 不支持 |
| 邮件 | 采用 mail-api 通用协议支持大部分邮件厂商 | 不支持 |
| 接口文档 | 采用 SpringDoc、javadoc 无注解零入侵基于java注释<br/>只需把注释写好 无需再写一大堆的文档注解了 | 采用 Springfox 已停止维护 需要编写大量的注解来支持文档生成 |
| 校验框架 | 采用 Validation 支持注解与工具类校验 注解支持国际化 | 仅支持注解 且注解不支持国际化 |
| Excel框架 | 采用 Alibaba EasyExcel 基于插件化<br/>框架对其增加了很多功能 例如 自动合并相同内容 自动排列布局 字典翻译等 | 基于 POI 手写实现 功能有限 复杂 扩展性差 |
| 工作流支持 | 支持各种复杂审批 转办 委派 加减签 会签 或签 票签 等功能 | 无 |
| 工具类框架 | 采用 Hutool、Lombok 上百种工具覆盖90%的使用需求 基于注解自动生成 get set 等简化框架大量代码 | 手写工具稳定性差易出问题 工具数量有限 代码臃肿需自己手写 get set 等 |
| 监控框架 | 采用 SpringBoot-Admin 基于SpringBoot官方 actuator 探针机制<br/>实时监控服务状态 框架还为其扩展了在线日志查看监控 | 无 |
| 链路追踪 | 采用 Apache SkyWalking 还在为请求不知道去哪了 到哪出了问题而烦恼吗<br/>用了它即可实时查看请求经过的每一处每一个节点 | 无 |
| 代码生成器 | 只需设计好表结构 一键生成所有crud代码与页面<br/>降低80%的开发量 把精力都投入到业务设计上<br/>框架为其适配MP、SpringDoc规范化代码 同时支持动态多数据源代码生成 | 代码生成原生结构 只支持单数据源生成 |
| 部署方式 | 支持 Docker 编排 一键搭建所有环境 让开发人员从此不再为搭建环境而烦恼 | 原生jar部署 其他环境需手动下载安装 自行搭建 |
| 项目路径修改 | 提供详细的修改方案文档 并为其做了一些改动 非常简单即可修改成自己想要的 | 需要做很多改造 文档说明有限 |
| 国际化 | 基于请求头动态返回不同语种的文本内容 开发难度低 有对应的工具类 支持大部分注解内容国际化 | 只提供基础功能 其他需自行编写扩展 |
| 代码单例测试 | 提供单例测试 使用方式编写方法与maven多环境单测插件 | 只提供基础功能 其他需自行编写扩展 |
| Demo案例 | 提供框架功能的实际使用案例 单独一个模块提供了很多很全 | 无 |
## 本框架与RuoYi的业务差异
| 业务 | 功能说明 | 本框架 | RuoYi |
|--------|----------------------------------------------------------------------|-----|------------------|
| 租户管理 | 系统内租户的管理 如:租户套餐、过期时间、用户数量、企业信息等 | 支持 | 无 |
| 租户套餐管理 | 系统内租户所能使用的套餐管理 如:套餐内所包含的菜单等 | 支持 | 无 |
| 客户端管理 | 系统内对接的所有客户端管理 如: pc端、小程序端等<br>支持动态授权登录方式 如: 短信登录、密码登录等 支持动态控制token时效 | 支持 | 无 |
| 用户管理 | 用户的管理配置 如:新增用户、分配用户所属部门、角色、岗位等 | 支持 | 支持 |
| 部门管理 | 配置系统组织机构(公司、部门、小组) 树结构展现支持数据权限 | 支持 | 支持 |
| 岗位管理 | 配置系统用户所属担任职务 | 支持 | 支持 |
| 菜单管理 | 配置系统菜单、操作权限、按钮权限标识等 | 支持 | 支持 |
| 角色管理 | 角色菜单权限分配、设置角色按机构进行数据范围权限划分 | 支持 | 支持 |
| 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护 | 支持 | 支持 |
| 参数管理 | 对系统动态配置常用参数 | 支持 | 支持 |
| 通知公告 | 系统通知公告信息发布维护 | 支持 | 支持 |
| 操作日志 | 系统正常操作日志记录和查询 系统异常信息日志记录和查询 | 支持 | 支持 |
| 登录日志 | 系统登录日志记录查询包含登录异常 | 支持 | 支持 |
| 文件管理 | 系统文件展示、上传、下载、删除等管理 | 支持 | 无 |
| 文件配置管理 | 系统文件上传、下载所需要的配置信息动态添加、修改、删除等管理 | 支持 | 无 |
| 在线用户管理 | 已登录系统的在线用户信息监控与强制踢出操作 | 支持 | 支持 |
| 定时任务 | 运行报表、任务管理(添加、修改、删除)、日志管理、执行器管理等 | 支持 | 仅支持任务与日志管理 |
| 代码生成 | 多数据源前后端代码的生成(java、html、xml、sql)支持CRUD下载 | 支持 | 仅支持单数据源 |
| 系统接口 | 根据业务代码自动生成相关的api接口文档 | 支持 | 支持 |
| 服务监控 | 监视集群系统CPU、内存、磁盘、堆栈、在线日志、Spring相关配置等 | 支持 | 仅支持单机CPU、内存、磁盘监控 |
| 缓存监控 | 对系统的缓存信息查询,命令统计等。 | 支持 | 支持 |
| 在线构建器 | 拖动表单元素生成相应的HTML代码。 | 支持 | 支持 |
| 使用案例 | 系统的一些功能案例 | 支持 | 不支持 |
## 参考文档
使用框架前请仔细阅读文档重点注意事项
<br>
>[初始化项目 必看](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/init)
>>[https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/init](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/init)
>
>[专栏与视频 入门必看](https://plus-doc.dromara.org/#/common/column)
>>[https://plus-doc.dromara.org/#/common/column](https://plus-doc.dromara.org/#/common/column)
>
>[部署项目 必看](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/deploy)
>>[https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/deploy](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/deploy)
>
>[如何加群](https://plus-doc.dromara.org/#/common/add_group)
>>[https://plus-doc.dromara.org/#/common/add_group](https://plus-doc.dromara.org/#/common/add_group)
>
>[参考文档 Wiki](https://plus-doc.dromara.org)
>>[https://plus-doc.dromara.org](https://plus-doc.dromara.org)
## 软件架构图
![Plus部署架构图](https://foruda.gitee.com/images/1678981882624240692/ae2a3f3e_1766278.png "Plus部署架构图.png")
## 如何参与贡献
[参与贡献的方式 https://plus-doc.dromara.org/#/common/contribution](https://plus-doc.dromara.org/#/common/contribution)
## 捐献作者
作者为兼职做开源,平时还需要工作,如果帮到了您可以请作者吃个盒饭
<img src="https://foruda.gitee.com/images/1678975784848381069/d8661ed9_1766278.png" width="300px" height="450px" />
<img src="https://foruda.gitee.com/images/1678975801230205215/6f96229d_1766278.png" width="300px" height="450px" />
## 演示图例
| | |
|--------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|
| ![输入图片说明](https://foruda.gitee.com/images/1680077524361362822/270bb429_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680077619939771291/989bf9b6_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680077681751513929/1c27c5bd_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680077721559267315/74d63e23_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680077765638904515/1b75d4a6_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078026375951297/eded7a4b_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078237104531207/0eb1b6a7_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078254306078709/5931e22f_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078287971528493/0b9af60a_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078308138770249/8d3b6696_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078352553634393/db5ef880_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078378238393374/601e4357_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078414983206024/2aae27c1_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078446738419874/ecce7d59_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078475971341775/149e8634_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078491666717143/3fadece7_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078558863188826/fb8ced2a_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078574561685461/ae68a0b2_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078594932772013/9d8bfec6_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078626493093532/fcfe4ff6_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078643608812515/0295bd4f_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078685196286463/d7612c81_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078703877318597/56fce0bc_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078716586545643/b6dbd68f_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078734103217688/eb1e6aa6_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078759131415480/73c525d8_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078779416197879/75e3ed02_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078802329118061/77e10915_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078893627848351/34a1c342_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078928175016986/f126ec4a_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078941718318363/b68a0f72_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078963175518631/3bb769a1_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078982294090567/b31c343d_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680079000642440444/77ca82a9_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680079020995074177/03b7d52e_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680079039367822173/76811806_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680079274333484664/4dfdc7c0_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680079290467458224/d6715fcf_1766278.png "屏幕截图") |

26
im-admin/pom.xml

@ -401,32 +401,6 @@
<excludedGroups>exclude</excludedGroups>
</configuration>
</plugin>
<!-- 统一版本号管理 -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>${flatten-maven-plugin.version}</version>
<configuration>
<updatePomFile>true</updatePomFile>
<flattenMode>resolveCiFriendliesOnly</flattenMode>
</configuration>
<executions>
<execution>
<id>flatten</id>
<phase>process-resources</phase>
<goals>
<goal>flatten</goal>
</goals>
</execution>
<execution>
<id>flatten.clean</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>

23
im-admin/ruoyi-admin/src/main/java/org/dromara/DromaraApplication.java

@ -1,23 +0,0 @@
package org.dromara;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
/**
* 启动程序
*
* @author Lion Li
*/
@SpringBootApplication
public class DromaraApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(DromaraApplication.class);
application.setApplicationStartup(new BufferingApplicationStartup(2048));
application.run(args);
System.out.println("盒子IM后台管理服务启动成功");
}
}

18
im-admin/ruoyi-admin/src/main/java/org/dromara/DromaraServletInitializer.java

@ -1,18 +0,0 @@
package org.dromara;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
/**
* web容器中进行部署
*
* @author Lion Li
*/
public class DromaraServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(DromaraApplication.class);
}
}

15
im-admin/ruoyi-admin/src/main/resources/application.yml

@ -8,7 +8,7 @@ app:
copyrightYear: 2024
captcha:
enable: true
enable: false
# 页面 <参数设置> 可开启关闭 验证码校验
# 验证码类型 math 数组计算 char 字符验证
type: MATH
@ -153,19 +153,6 @@ mybatis-encryptor:
publicKey:
privateKey:
# api接口加密
api-decrypt:
# 是否开启全局接口加密
enabled: true
# AES 加密头标识
headerFlag: encrypt-key
# 响应加密公钥 非对称算法的公私钥 如:SM2,RSA 使用者请自行更换
# 对应前端解密私钥 MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE=
publicKey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJnNwrj4hi/y3CCJu868ghCG5dUj8wZK++RNlTLcXoMmdZWEQ/u02RgD5LyLAXGjLOjbMtC+/J9qofpSGTKSx/MCAwEAAQ==
# 请求解密私钥 非对称算法的公私钥 如:SM2,RSA 使用者请自行更换
# 对应前端加密公钥 MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==
privateKey: MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKNPuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gAkM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWowcSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99EcvDQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthhYhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3UP8iWi1Qw0Y=
springdoc:
api-docs:
# 是否开启接口文档

Loading…
Cancel
Save