Commit 1425f7dfcc9c1fa7bf767387a5b369b00bf03090

Authored by YunaiV
1 parent 8e18a63d

优化 ProcessViewer.vue 展示流程图的效果,增加红色、绿色、灰色、橙色

yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/vo/activity/BpmActivityRespVO.java
... ... @@ -20,4 +20,20 @@ public class BpmActivityRespVO {
20 20 @ApiModelProperty(value = "流程活动的结束时间", required = true)
21 21 private Date endTime;
22 22  
  23 + /**
  24 + * 关联的流程任务,只有 UserTask 类型才有
  25 + */
  26 + private Task task;
  27 +
  28 + @ApiModel(value = "流程任务")
  29 + @Data
  30 + public static class Task {
  31 +
  32 + @ApiModelProperty(value = "关联的流程任务的编号", required = true, example = "2048")
  33 + private String id;
  34 + @ApiModelProperty(value = "关联的流程任务的结果", required = true, example = "2", notes = "参见 bpm_process_instance_result 枚举")
  35 + private Integer result;
  36 +
  37 + }
  38 +
23 39 }
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/task/BpmActivityConvert.java
1 1 package cn.iocoder.yudao.adminserver.modules.bpm.convert.task;
2 2  
3 3 import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.activity.BpmActivityRespVO;
  4 +import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task.BpmTaskExtDO;
  5 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
4 6 import org.activiti.engine.history.HistoricActivityInstance;
5 7 import org.mapstruct.Mapper;
6 8 import org.mapstruct.Mapping;
... ... @@ -8,6 +10,7 @@ import org.mapstruct.Mappings;
8 10 import org.mapstruct.factory.Mappers;
9 11  
10 12 import java.util.List;
  13 +import java.util.Map;
11 14  
12 15 /**
13 16 * BPM 活动 Convert
... ... @@ -19,7 +22,16 @@ public interface BpmActivityConvert {
19 22  
20 23 BpmActivityConvert INSTANCE = Mappers.getMapper(BpmActivityConvert.class);
21 24  
22   - List<BpmActivityRespVO> convertList(List<HistoricActivityInstance> list);
  25 + default List<BpmActivityRespVO> convertList(List<HistoricActivityInstance> list, Map<String, BpmTaskExtDO> taskExtMap) {
  26 + return CollectionUtils.convertList(list, bean -> {
  27 + BpmActivityRespVO respVO = convert(bean);
  28 + BpmTaskExtDO taskExt = taskExtMap.get(bean.getTaskId());
  29 + if (taskExt != null) {
  30 + respVO.setTask(new BpmActivityRespVO.Task().setId(taskExt.getTaskId()).setResult(taskExt.getResult()));
  31 + }
  32 + return respVO;
  33 + });
  34 + }
23 35  
24 36 @Mappings({
25 37 @Mapping(source = "activityId", target = "key"),
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/dataobject/task/BpmTaskExtDO.java
... ... @@ -46,6 +46,12 @@ public class BpmTaskExtDO extends BaseDO {
46 46 * 关联 {@link Task#getId()}
47 47 */
48 48 private String taskId;
  49 +// /**
  50 +// * 任务的标识
  51 +// *
  52 +// * 关联 {@link Task#getTaskDefinitionKey()}
  53 +// */
  54 +// private String definitionKey;
49 55 /**
50 56 * 任务的结果
51 57 *
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/mysql/task/BpmTaskExtMapper.java
... ... @@ -19,4 +19,7 @@ public interface BpmTaskExtMapper extends BaseMapperX&lt;BpmTaskExtDO&gt; {
19 19 return selectList("task_id", taskIds);
20 20 }
21 21  
  22 + default List<BpmTaskExtDO> selectListByProcessInstanceId(String processInstanceId) {
  23 + return selectList("process_instance_id", processInstanceId);
  24 + }
22 25 }
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/BpmTaskService.java
1 1 package cn.iocoder.yudao.adminserver.modules.bpm.service.task;
2 2  
3 3 import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task.*;
  4 +import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task.BpmTaskExtDO;
4 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 6 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
6 7 import org.activiti.engine.task.Task;
... ... @@ -139,4 +140,12 @@ public interface BpmTaskService {
139 140 */
140 141 void updateTaskExtComplete(org.activiti.api.task.model.Task task);
141 142  
  143 + /**
  144 + * 获得流程实例对应的 Task 拓展列表
  145 + *
  146 + * @param processInstanceId 流程实例的编号
  147 + * @return Task 拓展列表
  148 + */
  149 + List<BpmTaskExtDO> getTaskExtListByProcessInstanceId(String processInstanceId);
  150 +
142 151 }
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/impl/BpmActivityServiceImpl.java
... ... @@ -4,6 +4,7 @@ import cn.hutool.core.io.IoUtil;
4 4 import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.activity.BpmActivityRespVO;
5 5 import cn.iocoder.yudao.adminserver.modules.bpm.convert.task.BpmActivityConvert;
6 6 import cn.iocoder.yudao.adminserver.modules.bpm.convert.task.BpmTaskConvert;
  7 +import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task.BpmTaskExtDO;
7 8 import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmProcessDefinitionService;
8 9 import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmActivityService;
9 10 import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmProcessInstanceService;
... ... @@ -25,9 +26,11 @@ import javax.annotation.Resource;
25 26 import java.io.InputStream;
26 27 import java.util.Collections;
27 28 import java.util.List;
  29 +import java.util.Map;
28 30  
29 31 import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.*;
30 32 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  33 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
31 34  
32 35 /**
33 36 * BPM 活动实例 Service 实现类
... ... @@ -57,7 +60,10 @@ public class BpmActivityServiceImpl implements BpmActivityService {
57 60 public List<BpmActivityRespVO> getActivityListByProcessInstanceId(String processInstanceId) {
58 61 List<HistoricActivityInstance> activityList = historyService.createHistoricActivityInstanceQuery()
59 62 .processInstanceId(processInstanceId).list();
60   - return BpmActivityConvert.INSTANCE.convertList(activityList);
  63 + // 拼接数据
  64 + List<BpmTaskExtDO> taskExts = taskService.getTaskExtListByProcessInstanceId(processInstanceId);
  65 + Map<String, BpmTaskExtDO> taskExtMap = convertMap(taskExts, BpmTaskExtDO::getTaskId);
  66 + return BpmActivityConvert.INSTANCE.convertList(activityList, taskExtMap);
61 67 }
62 68  
63 69 @Override
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/impl/BpmTaskServiceImpl.java
1 1 package cn.iocoder.yudao.adminserver.modules.bpm.service.task.impl;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4   -import cn.hutool.core.io.IoUtil;
5 4 import cn.hutool.core.util.StrUtil;
6 5 import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task.*;
7 6 import cn.iocoder.yudao.adminserver.modules.bpm.convert.task.BpmTaskConvert;
... ... @@ -16,36 +15,23 @@ import cn.iocoder.yudao.adminserver.modules.system.service.user.SysUserService;
16 15 import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
17 16 import cn.iocoder.yudao.framework.activiti.core.util.ActivitiUtils;
18 17 import cn.iocoder.yudao.framework.common.pojo.PageResult;
19   -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
20 18 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
21 19 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
22 20 import lombok.extern.slf4j.Slf4j;
23   -import org.activiti.bpmn.constants.BpmnXMLConstants;
24   -import org.activiti.bpmn.model.BpmnModel;
25   -import org.activiti.bpmn.model.FlowNode;
26   -import org.activiti.bpmn.model.SequenceFlow;
27 21 import org.activiti.engine.HistoryService;
28   -import org.activiti.engine.RepositoryService;
29   -import org.activiti.engine.RuntimeService;
30 22 import org.activiti.engine.TaskService;
31   -import org.activiti.engine.history.HistoricActivityInstance;
32 23 import org.activiti.engine.history.HistoricProcessInstance;
33 24 import org.activiti.engine.history.HistoricTaskInstance;
34 25 import org.activiti.engine.history.HistoricTaskInstanceQuery;
35   -import org.activiti.engine.repository.ProcessDefinition;
36 26 import org.activiti.engine.runtime.ProcessInstance;
37 27 import org.activiti.engine.task.Task;
38 28 import org.activiti.engine.task.TaskQuery;
39   -import org.activiti.image.ProcessDiagramGenerator;
40 29 import org.springframework.context.annotation.Lazy;
41 30 import org.springframework.stereotype.Service;
42 31 import org.springframework.transaction.annotation.Transactional;
43   -import org.springframework.util.ObjectUtils;
44 32  
45 33 import javax.annotation.Resource;
46 34 import javax.validation.Valid;
47   -import java.io.IOException;
48   -import java.io.InputStream;
49 35 import java.util.*;
50 36  
51 37 import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.*;
... ... @@ -307,4 +293,9 @@ public class BpmTaskServiceImpl implements BpmTaskService {
307 293 taskExtMapper.updateByTaskId(taskExtDO);
308 294 }
309 295  
  296 + @Override
  297 + public List<BpmTaskExtDO> getTaskExtListByProcessInstanceId(String processInstanceId) {
  298 + return taskExtMapper.selectListByProcessInstanceId(processInstanceId);
  299 + }
  300 +
310 301 }
... ...
yudao-admin-ui/src/components/bpmnProcessDesigner/package/designer/ProcessViewer.vue
... ... @@ -82,55 +82,63 @@ export default {
82 82 },
83 83 /* 高亮流程图 */
84 84 async highlightDiagram() {
85   - // let tasks = this.tasks.filter(task => {
86   - // if (task.type !== 'sequenceFlow') { // 去除连线元素
87   - // return true;
88   - // }
89   - // });
90   - let tasks = this.tasks;
91   - if (tasks.length === 0) {
  85 + let activityList = this.tasks;
  86 + if (activityList.length === 0) {
92 87 return;
93 88 }
94 89 // 参考自 https://gitee.com/tony2y/RuoYi-flowable/blob/master/ruoyi-ui/src/components/Process/index.vue#L222 实现
  90 + // 再次基础上,增加不同审批结果的颜色等等
95 91 let canvas = this.bpmnModeler.get('canvas');
  92 + let todoActivity = activityList.find(m => !m.endTime) // 找到待办的任务
  93 + let endActivity = activityList[activityList.length - 1] // 找到结束任务
96 94 this.bpmnModeler.getDefinitions().rootElements[0].flowElements?.forEach(n => {
97   - let completeTask = tasks.find(m => m.key === n.id)
98   - let todoTask = tasks.find(m => !m.endTime)
99   - let endTask = tasks[tasks.length - 1]
  95 + let activity = activityList.find(m => m.key === n.id) // 找到对应的活动
100 96 if (n.$type === 'bpmn:UserTask') { // 用户任务
101   - if (completeTask) {
102   - canvas.addMarker(n.id, completeTask.endTime ? 'highlight' : 'highlight-todo');
103   - // console.log(n.id + ' : ' + (completeTask.endTime ? 'highlight' : 'highlight-todo'));
104   - n.outgoing?.forEach(nn => {
105   - let targetTask = tasks.find(m => m.key === nn.targetRef.id)
106   - if (targetTask) {
107   - canvas.addMarker(nn.id, targetTask.endTime ? 'highlight' : 'highlight-todo');
108   - } else if (nn.targetRef.$type === 'bpmn:ExclusiveGateway') {
109   - // canvas.addMarker(nn.id, 'highlight');
110   - canvas.addMarker(nn.id, completeTask.endTime ? 'highlight' : 'highlight-todo');
111   - canvas.addMarker(nn.targetRef.id, completeTask.endTime ? 'highlight' : 'highlight-todo');
112   - } else if (nn.targetRef.$type === 'bpmn:EndEvent') {
113   - if (!todoTask && endTask.key === n.id) {
114   - canvas.addMarker(nn.id, 'highlight');
115   - canvas.addMarker(nn.targetRef.id, 'highlight');
116   - }
117   - if (!completeTask.endTime) {
118   - canvas.addMarker(nn.id, 'highlight-todo');
119   - canvas.addMarker(nn.targetRef.id, 'highlight-todo');
120   - }
121   - }
122   - });
  97 + if (!activity) {
  98 + return;
123 99 }
  100 + if (activity.task) {
  101 + const result = activity.task.result;
  102 + if (result === 1) {
  103 + canvas.addMarker(n.id, 'highlight-todo');
  104 + } else if (result === 2) {
  105 + canvas.addMarker(n.id, 'highlight');
  106 + } else if (result === 3) {
  107 + canvas.addMarker(n.id, 'highlight-reject');
  108 + } else if (result === 4) {
  109 + canvas.addMarker(n.id, 'highlight-cancel');
  110 + }
  111 + }
  112 +
  113 + n.outgoing?.forEach(nn => {
  114 + let targetTask = activityList.find(m => m.key === nn.targetRef.id)
  115 + if (targetTask) {
  116 + canvas.addMarker(nn.id, targetTask.endTime ? 'highlight' : 'highlight-todo');
  117 + } else if (nn.targetRef.$type === 'bpmn:ExclusiveGateway') {
  118 + // canvas.addMarker(nn.id, 'highlight');
  119 + canvas.addMarker(nn.id, activity.endTime ? 'highlight' : 'highlight-todo');
  120 + canvas.addMarker(nn.targetRef.id, activity.endTime ? 'highlight' : 'highlight-todo');
  121 + } else if (nn.targetRef.$type === 'bpmn:EndEvent') {
  122 + if (!todoActivity && endActivity.key === n.id) {
  123 + canvas.addMarker(nn.id, 'highlight');
  124 + canvas.addMarker(nn.targetRef.id, 'highlight');
  125 + }
  126 + if (!activity.endTime) {
  127 + canvas.addMarker(nn.id, 'highlight-todo');
  128 + canvas.addMarker(nn.targetRef.id, 'highlight-todo');
  129 + }
  130 + }
  131 + });
124 132 } else if (n.$type === 'bpmn:ExclusiveGateway') { // 排它网关
125 133 n.outgoing?.forEach(nn => {
126   - let targetTask = tasks.find(m => m.key === nn.targetRef.id)
  134 + let targetTask = activityList.find(m => m.key === nn.targetRef.id)
127 135 if (targetTask) {
128 136 canvas.addMarker(nn.id, targetTask.endTime ? 'highlight' : 'highlight-todo');
129 137 }
130 138 })
131 139 } else if (n.$type === 'bpmn:ParallelGateway') { // 并行网关
132   - if (completeTask) {
133   - canvas.addMarker(n.id, completeTask.endTime ? 'highlight' : 'highlight-todo')
  140 + if (activity) {
  141 + canvas.addMarker(n.id, activity.endTime ? 'highlight' : 'highlight-todo')
134 142 n.outgoing?.forEach(nn => {
135 143 const targetTask = this.taskList.find(m => m.key === nn.targetRef.id)
136 144 if (targetTask) {
... ... @@ -140,18 +148,21 @@ export default {
140 148 })
141 149 }
142 150 } else if (n.$type === 'bpmn:StartEvent') { // 开始节点
143   - n.outgoing?.forEach(nn => {
144   - let completeTask = tasks.find(m => m.key === nn.targetRef.id)
145   - if (completeTask) {
  151 + n.outgoing?.forEach(nn => { // outgoing 例如说【bpmn:SequenceFlow】连线
  152 + let fromTask = activityList.find(m => m.key === nn.targetRef.id)
  153 + if (fromTask) {
146 154 canvas.addMarker(nn.id, 'highlight');
147 155 canvas.addMarker(n.id, 'highlight');
148   - return
149 156 }
150 157 });
151 158 } else if (n.$type === 'bpmn:EndEvent') { // 结束节点
152   - if (endTask.key === n.id && endTask.endTime) {
153   - canvas.addMarker(n.id, 'highlight')
154   - return
  159 + if (endActivity.key !== n.id) { // 保证 endActivity 就是 EndEvent
  160 + return;
  161 + }
  162 + // 在并行网关后,跟着多个任务,如果其中一个任务完成,endActivity 的 endTime 就会存在值
  163 + // 所以,通过 todoActivity 在做一次判断
  164 + if (endActivity.endTime && !todoActivity) {
  165 + canvas.addMarker(n.id, 'highlight');
155 166 }
156 167 }
157 168 })
... ... @@ -162,6 +173,7 @@ export default {
162 173  
163 174 <style>
164 175  
  176 +/** 通过 */
165 177 .highlight.djs-shape .djs-visual > :nth-child(1) {
166 178 fill: green !important;
167 179 stroke: green !important;
... ... @@ -179,18 +191,6 @@ export default {
179 191 stroke: green !important;
180 192 }
181 193  
182   -.highlight-todo.djs-connection > .djs-visual > path {
183   - stroke: orange !important;
184   - stroke-dasharray: 4px !important;
185   - fill-opacity: 0.2 !important;
186   -}
187   -.highlight-todo.djs-shape .djs-visual > :nth-child(1) {
188   - fill: orange !important;
189   - stroke: orange !important;
190   - stroke-dasharray: 4px !important;
191   - fill-opacity: 0.2 !important;
192   -}
193   -
194 194 .highlight:not(.djs-connection) .djs-visual > :nth-child(1) {
195 195 fill: green !important; /* color elements as green */
196 196 }
... ... @@ -211,6 +211,20 @@ export default {
211 211 /deep/.highlight.djs-connection > .djs-visual > path {
212 212 stroke: green !important;
213 213 }
  214 +
  215 +/** 处理中 */
  216 +.highlight-todo.djs-connection > .djs-visual > path {
  217 + stroke: orange !important;
  218 + stroke-dasharray: 4px !important;
  219 + fill-opacity: 0.2 !important;
  220 +}
  221 +.highlight-todo.djs-shape .djs-visual > :nth-child(1) {
  222 + fill: orange !important;
  223 + stroke: orange !important;
  224 + stroke-dasharray: 4px !important;
  225 + fill-opacity: 0.2 !important;
  226 +}
  227 +
214 228 /deep/.highlight-todo.djs-connection > .djs-visual > path {
215 229 stroke: orange !important;
216 230 stroke-dasharray: 4px !important;
... ... @@ -224,4 +238,81 @@ export default {
224 238 fill-opacity: 0.2 !important;
225 239 }
226 240  
  241 +/** 不通过 */
  242 +.highlight-reject.djs-shape .djs-visual > :nth-child(1) {
  243 + fill: red !important;
  244 + stroke: red !important;
  245 + fill-opacity: 0.2 !important;
  246 +}
  247 +.highlight-reject.djs-shape .djs-visual > :nth-child(2) {
  248 + fill: red !important;
  249 +}
  250 +.highlight-reject.djs-shape .djs-visual > path {
  251 + fill: red !important;
  252 + fill-opacity: 0.2 !important;
  253 + stroke: red !important;
  254 +}
  255 +.highlight-reject.djs-connection > .djs-visual > path {
  256 + stroke: red !important;
  257 +}
  258 +
  259 +.highlight-reject:not(.djs-connection) .djs-visual > :nth-child(1) {
  260 + fill: red !important; /* color elements as green */
  261 +}
  262 +
  263 +/deep/.highlight-reject.djs-shape .djs-visual > :nth-child(1) {
  264 + fill: red !important;
  265 + stroke: red !important;
  266 + fill-opacity: 0.2 !important;
  267 +}
  268 +/deep/.highlight-reject.djs-shape .djs-visual > :nth-child(2) {
  269 + fill: red !important;
  270 +}
  271 +/deep/.highlight-reject.djs-shape .djs-visual > path {
  272 + fill: red !important;
  273 + fill-opacity: 0.2 !important;
  274 + stroke: red !important;
  275 +}
  276 +/deep/.highlight-reject.djs-connection > .djs-visual > path {
  277 + stroke: red !important;
  278 +}
  279 +
  280 +/** 已取消 */
  281 +.highlight-cancel.djs-shape .djs-visual > :nth-child(1) {
  282 + fill: grey !important;
  283 + stroke: grey !important;
  284 + fill-opacity: 0.2 !important;
  285 +}
  286 +.highlight-cancel.djs-shape .djs-visual > :nth-child(2) {
  287 + fill: grey !important;
  288 +}
  289 +.highlight-cancel.djs-shape .djs-visual > path {
  290 + fill: grey !important;
  291 + fill-opacity: 0.2 !important;
  292 + stroke: grey !important;
  293 +}
  294 +.highlight-cancel.djs-connection > .djs-visual > path {
  295 + stroke: grey !important;
  296 +}
  297 +
  298 +.highlight-cancel:not(.djs-connection) .djs-visual > :nth-child(1) {
  299 + fill: grey !important; /* color elements as green */
  300 +}
  301 +
  302 +/deep/.highlight-cancel.djs-shape .djs-visual > :nth-child(1) {
  303 + fill: grey !important;
  304 + stroke: grey !important;
  305 + fill-opacity: 0.2 !important;
  306 +}
  307 +/deep/.highlight-cancel.djs-shape .djs-visual > :nth-child(2) {
  308 + fill: grey !important;
  309 +}
  310 +/deep/.highlight-cancel.djs-shape .djs-visual > path {
  311 + fill: grey !important;
  312 + fill-opacity: 0.2 !important;
  313 + stroke: grey !important;
  314 +}
  315 +/deep/.highlight-cancel.djs-connection > .djs-visual > path {
  316 + stroke: grey !important;
  317 +}
227 318 </style>
... ...