Bläddra i källkod

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

YunaiV 3 år sedan
förälder
incheckning
1425f7dfcc

+ 16 - 0
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 {
     @ApiModelProperty(value = "流程活动的结束时间", required = true)
     private Date endTime;
 
+    /**
+     * 关联的流程任务,只有 UserTask 类型才有
+     */
+    private Task task;
+
+    @ApiModel(value = "流程任务")
+    @Data
+    public static class Task {
+
+        @ApiModelProperty(value = "关联的流程任务的编号", required = true, example = "2048")
+        private String id;
+        @ApiModelProperty(value = "关联的流程任务的结果", required = true, example = "2", notes = "参见 bpm_process_instance_result 枚举")
+        private Integer result;
+
+    }
+
 }

+ 13 - 1
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/task/BpmActivityConvert.java

@@ -1,6 +1,8 @@
 package cn.iocoder.yudao.adminserver.modules.bpm.convert.task;
 
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.activity.BpmActivityRespVO;
+import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task.BpmTaskExtDO;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import org.activiti.engine.history.HistoricActivityInstance;
 import org.mapstruct.Mapper;
 import org.mapstruct.Mapping;
@@ -8,6 +10,7 @@ import org.mapstruct.Mappings;
 import org.mapstruct.factory.Mappers;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * BPM 活动 Convert
@@ -19,7 +22,16 @@ public interface BpmActivityConvert {
 
     BpmActivityConvert INSTANCE = Mappers.getMapper(BpmActivityConvert.class);
 
-    List<BpmActivityRespVO> convertList(List<HistoricActivityInstance> list);
+    default List<BpmActivityRespVO> convertList(List<HistoricActivityInstance> list, Map<String, BpmTaskExtDO> taskExtMap) {
+        return CollectionUtils.convertList(list, bean -> {
+            BpmActivityRespVO respVO = convert(bean);
+            BpmTaskExtDO taskExt = taskExtMap.get(bean.getTaskId());
+            if (taskExt != null) {
+                respVO.setTask(new BpmActivityRespVO.Task().setId(taskExt.getTaskId()).setResult(taskExt.getResult()));
+            }
+            return respVO;
+        });
+    }
 
     @Mappings({
             @Mapping(source = "activityId", target = "key"),

+ 6 - 0
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 {
      * 关联 {@link Task#getId()}
      */
     private String taskId;
+//    /**
+//     * 任务的标识
+//     *
+//     * 关联 {@link Task#getTaskDefinitionKey()}
+//     */
+//    private String definitionKey;
     /**
      * 任务的结果
      *

+ 3 - 0
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<BpmTaskExtDO> {
         return selectList("task_id", taskIds);
     }
 
+    default List<BpmTaskExtDO> selectListByProcessInstanceId(String processInstanceId) {
+        return selectList("process_instance_id", processInstanceId);
+    }
 }

+ 9 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/BpmTaskService.java

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.adminserver.modules.bpm.service.task;
 
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task.*;
+import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task.BpmTaskExtDO;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import org.activiti.engine.task.Task;
@@ -139,4 +140,12 @@ public interface BpmTaskService {
      */
     void updateTaskExtComplete(org.activiti.api.task.model.Task task);
 
+    /**
+     * 获得流程实例对应的 Task 拓展列表
+     *
+     * @param processInstanceId 流程实例的编号
+     * @return Task 拓展列表
+     */
+    List<BpmTaskExtDO> getTaskExtListByProcessInstanceId(String processInstanceId);
+
 }

+ 7 - 1
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;
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.activity.BpmActivityRespVO;
 import cn.iocoder.yudao.adminserver.modules.bpm.convert.task.BpmActivityConvert;
 import cn.iocoder.yudao.adminserver.modules.bpm.convert.task.BpmTaskConvert;
+import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task.BpmTaskExtDO;
 import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmProcessDefinitionService;
 import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmActivityService;
 import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmProcessInstanceService;
@@ -25,9 +26,11 @@ import javax.annotation.Resource;
 import java.io.InputStream;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 
 import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.*;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
 
 /**
  * BPM 活动实例 Service 实现类
@@ -57,7 +60,10 @@ public class BpmActivityServiceImpl implements BpmActivityService {
     public List<BpmActivityRespVO> getActivityListByProcessInstanceId(String processInstanceId) {
         List<HistoricActivityInstance> activityList = historyService.createHistoricActivityInstanceQuery()
                 .processInstanceId(processInstanceId).list();
-        return BpmActivityConvert.INSTANCE.convertList(activityList);
+        // 拼接数据
+        List<BpmTaskExtDO> taskExts = taskService.getTaskExtListByProcessInstanceId(processInstanceId);
+        Map<String, BpmTaskExtDO> taskExtMap = convertMap(taskExts, BpmTaskExtDO::getTaskId);
+        return BpmActivityConvert.INSTANCE.convertList(activityList, taskExtMap);
     }
 
     @Override

+ 5 - 14
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/impl/BpmTaskServiceImpl.java

@@ -1,7 +1,6 @@
 package cn.iocoder.yudao.adminserver.modules.bpm.service.task.impl;
 
 import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.io.IoUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task.*;
 import cn.iocoder.yudao.adminserver.modules.bpm.convert.task.BpmTaskConvert;
@@ -16,36 +15,23 @@ import cn.iocoder.yudao.adminserver.modules.system.service.user.SysUserService;
 import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
 import cn.iocoder.yudao.framework.activiti.core.util.ActivitiUtils;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
 import lombok.extern.slf4j.Slf4j;
-import org.activiti.bpmn.constants.BpmnXMLConstants;
-import org.activiti.bpmn.model.BpmnModel;
-import org.activiti.bpmn.model.FlowNode;
-import org.activiti.bpmn.model.SequenceFlow;
 import org.activiti.engine.HistoryService;
-import org.activiti.engine.RepositoryService;
-import org.activiti.engine.RuntimeService;
 import org.activiti.engine.TaskService;
-import org.activiti.engine.history.HistoricActivityInstance;
 import org.activiti.engine.history.HistoricProcessInstance;
 import org.activiti.engine.history.HistoricTaskInstance;
 import org.activiti.engine.history.HistoricTaskInstanceQuery;
-import org.activiti.engine.repository.ProcessDefinition;
 import org.activiti.engine.runtime.ProcessInstance;
 import org.activiti.engine.task.Task;
 import org.activiti.engine.task.TaskQuery;
-import org.activiti.image.ProcessDiagramGenerator;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
-import org.springframework.util.ObjectUtils;
 
 import javax.annotation.Resource;
 import javax.validation.Valid;
-import java.io.IOException;
-import java.io.InputStream;
 import java.util.*;
 
 import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.*;
@@ -307,4 +293,9 @@ public class BpmTaskServiceImpl implements BpmTaskService {
         taskExtMapper.updateByTaskId(taskExtDO);
     }
 
+    @Override
+    public List<BpmTaskExtDO> getTaskExtListByProcessInstanceId(String processInstanceId) {
+        return taskExtMapper.selectListByProcessInstanceId(processInstanceId);
+    }
+
 }

+ 145 - 54
yudao-admin-ui/src/components/bpmnProcessDesigner/package/designer/ProcessViewer.vue

@@ -82,55 +82,63 @@ export default {
     },
     /* 高亮流程图 */
     async highlightDiagram() {
-      // let tasks = this.tasks.filter(task => {
-      //   if (task.type !== 'sequenceFlow') { // 去除连线元素
-      //     return true;
-      //   }
-      // });
-      let tasks = this.tasks;
-      if (tasks.length === 0) {
+      let activityList = this.tasks;
+      if (activityList.length === 0) {
         return;
       }
       // 参考自 https://gitee.com/tony2y/RuoYi-flowable/blob/master/ruoyi-ui/src/components/Process/index.vue#L222 实现
+      // 再次基础上,增加不同审批结果的颜色等等
       let canvas = this.bpmnModeler.get('canvas');
+      let todoActivity = activityList.find(m => !m.endTime) // 找到待办的任务
+      let endActivity = activityList[activityList.length - 1] // 找到结束任务
       this.bpmnModeler.getDefinitions().rootElements[0].flowElements?.forEach(n => {
-        let completeTask = tasks.find(m => m.key === n.id)
-        let todoTask = tasks.find(m => !m.endTime)
-        let endTask = tasks[tasks.length - 1]
+        let activity = activityList.find(m => m.key === n.id) // 找到对应的活动
         if (n.$type === 'bpmn:UserTask') { // 用户任务
-          if (completeTask) {
-            canvas.addMarker(n.id, completeTask.endTime ? 'highlight' : 'highlight-todo');
-            // console.log(n.id + ' : ' + (completeTask.endTime ? 'highlight' : 'highlight-todo'));
-            n.outgoing?.forEach(nn => {
-              let targetTask = tasks.find(m => m.key === nn.targetRef.id)
-              if (targetTask) {
-                canvas.addMarker(nn.id, targetTask.endTime ? 'highlight' : 'highlight-todo');
-              } else if (nn.targetRef.$type === 'bpmn:ExclusiveGateway') {
-                // canvas.addMarker(nn.id, 'highlight');
-                canvas.addMarker(nn.id, completeTask.endTime ? 'highlight' : 'highlight-todo');
-                canvas.addMarker(nn.targetRef.id, completeTask.endTime ? 'highlight' : 'highlight-todo');
-              } else if (nn.targetRef.$type === 'bpmn:EndEvent') {
-                if (!todoTask && endTask.key === n.id) {
-                  canvas.addMarker(nn.id, 'highlight');
-                  canvas.addMarker(nn.targetRef.id, 'highlight');
-                }
-                if (!completeTask.endTime) {
-                  canvas.addMarker(nn.id, 'highlight-todo');
-                  canvas.addMarker(nn.targetRef.id, 'highlight-todo');
-                }
-              }
-            });
+          if (!activity) {
+            return;
           }
+          if (activity.task) {
+            const result = activity.task.result;
+            if (result === 1) {
+              canvas.addMarker(n.id, 'highlight-todo');
+            } else if (result === 2) {
+              canvas.addMarker(n.id, 'highlight');
+            } else if (result === 3) {
+              canvas.addMarker(n.id, 'highlight-reject');
+            } else if (result === 4) {
+              canvas.addMarker(n.id, 'highlight-cancel');
+            }
+          }
+
+          n.outgoing?.forEach(nn => {
+            let targetTask = activityList.find(m => m.key === nn.targetRef.id)
+            if (targetTask) {
+              canvas.addMarker(nn.id, targetTask.endTime ? 'highlight' : 'highlight-todo');
+            } else if (nn.targetRef.$type === 'bpmn:ExclusiveGateway') {
+              // canvas.addMarker(nn.id, 'highlight');
+              canvas.addMarker(nn.id, activity.endTime ? 'highlight' : 'highlight-todo');
+              canvas.addMarker(nn.targetRef.id, activity.endTime ? 'highlight' : 'highlight-todo');
+            } else if (nn.targetRef.$type === 'bpmn:EndEvent') {
+              if (!todoActivity && endActivity.key === n.id) {
+                canvas.addMarker(nn.id, 'highlight');
+                canvas.addMarker(nn.targetRef.id, 'highlight');
+              }
+              if (!activity.endTime) {
+                canvas.addMarker(nn.id, 'highlight-todo');
+                canvas.addMarker(nn.targetRef.id, 'highlight-todo');
+              }
+            }
+          });
         } else if (n.$type === 'bpmn:ExclusiveGateway') { // 排它网关
           n.outgoing?.forEach(nn => {
-            let targetTask = tasks.find(m => m.key === nn.targetRef.id)
+            let targetTask = activityList.find(m => m.key === nn.targetRef.id)
             if (targetTask) {
               canvas.addMarker(nn.id, targetTask.endTime ? 'highlight' : 'highlight-todo');
             }
           })
         } else if (n.$type === 'bpmn:ParallelGateway') { // 并行网关
-          if (completeTask) {
-            canvas.addMarker(n.id, completeTask.endTime ? 'highlight' : 'highlight-todo')
+          if (activity) {
+            canvas.addMarker(n.id, activity.endTime ? 'highlight' : 'highlight-todo')
             n.outgoing?.forEach(nn => {
               const targetTask = this.taskList.find(m => m.key === nn.targetRef.id)
               if (targetTask) {
@@ -140,18 +148,21 @@ export default {
             })
           }
         } else if (n.$type === 'bpmn:StartEvent') { // 开始节点
-          n.outgoing?.forEach(nn => {
-            let completeTask = tasks.find(m => m.key === nn.targetRef.id)
-            if (completeTask) {
+          n.outgoing?.forEach(nn => { // outgoing 例如说【bpmn:SequenceFlow】连线
+            let fromTask = activityList.find(m => m.key === nn.targetRef.id)
+            if (fromTask) {
               canvas.addMarker(nn.id, 'highlight');
               canvas.addMarker(n.id, 'highlight');
-              return
             }
           });
         } else if (n.$type === 'bpmn:EndEvent') { // 结束节点
-          if (endTask.key === n.id && endTask.endTime) {
-            canvas.addMarker(n.id, 'highlight')
-            return
+          if (endActivity.key !== n.id) { // 保证 endActivity 就是 EndEvent
+            return;
+          }
+          // 在并行网关后,跟着多个任务,如果其中一个任务完成,endActivity 的 endTime 就会存在值
+          // 所以,通过 todoActivity 在做一次判断
+          if (endActivity.endTime && !todoActivity) {
+            canvas.addMarker(n.id, 'highlight');
           }
         }
       })
@@ -162,6 +173,7 @@ export default {
 
 <style>
 
+/** 通过 */
 .highlight.djs-shape .djs-visual > :nth-child(1) {
   fill: green !important;
   stroke: green !important;
@@ -179,18 +191,6 @@ export default {
   stroke: green !important;
 }
 
-.highlight-todo.djs-connection > .djs-visual > path {
-  stroke: orange !important;
-  stroke-dasharray: 4px !important;
-  fill-opacity: 0.2 !important;
-}
-.highlight-todo.djs-shape .djs-visual > :nth-child(1) {
-  fill: orange !important;
-  stroke: orange !important;
-  stroke-dasharray: 4px !important;
-  fill-opacity: 0.2 !important;
-}
-
 .highlight:not(.djs-connection) .djs-visual > :nth-child(1) {
   fill: green !important; /* color elements as green */
 }
@@ -211,6 +211,20 @@ export default {
 /deep/.highlight.djs-connection > .djs-visual > path {
   stroke: green !important;
 }
+
+/** 处理中 */
+.highlight-todo.djs-connection > .djs-visual > path {
+  stroke: orange !important;
+  stroke-dasharray: 4px !important;
+  fill-opacity: 0.2 !important;
+}
+.highlight-todo.djs-shape .djs-visual > :nth-child(1) {
+  fill: orange !important;
+  stroke: orange !important;
+  stroke-dasharray: 4px !important;
+  fill-opacity: 0.2 !important;
+}
+
 /deep/.highlight-todo.djs-connection > .djs-visual > path {
   stroke: orange !important;
   stroke-dasharray: 4px !important;
@@ -224,4 +238,81 @@ export default {
   fill-opacity: 0.2 !important;
 }
 
+/** 不通过 */
+.highlight-reject.djs-shape .djs-visual > :nth-child(1) {
+  fill: red !important;
+  stroke: red !important;
+  fill-opacity: 0.2 !important;
+}
+.highlight-reject.djs-shape .djs-visual > :nth-child(2) {
+  fill: red !important;
+}
+.highlight-reject.djs-shape .djs-visual > path {
+  fill: red !important;
+  fill-opacity: 0.2 !important;
+  stroke: red !important;
+}
+.highlight-reject.djs-connection > .djs-visual > path {
+  stroke: red !important;
+}
+
+.highlight-reject:not(.djs-connection) .djs-visual > :nth-child(1) {
+  fill: red !important; /* color elements as green */
+}
+
+/deep/.highlight-reject.djs-shape .djs-visual > :nth-child(1) {
+  fill: red !important;
+  stroke: red !important;
+  fill-opacity: 0.2 !important;
+}
+/deep/.highlight-reject.djs-shape .djs-visual > :nth-child(2) {
+  fill: red !important;
+}
+/deep/.highlight-reject.djs-shape .djs-visual > path {
+  fill: red !important;
+  fill-opacity: 0.2 !important;
+  stroke: red !important;
+}
+/deep/.highlight-reject.djs-connection > .djs-visual > path {
+  stroke: red !important;
+}
+
+/** 已取消 */
+.highlight-cancel.djs-shape .djs-visual > :nth-child(1) {
+  fill: grey !important;
+  stroke: grey !important;
+  fill-opacity: 0.2 !important;
+}
+.highlight-cancel.djs-shape .djs-visual > :nth-child(2) {
+  fill: grey !important;
+}
+.highlight-cancel.djs-shape .djs-visual > path {
+  fill: grey !important;
+  fill-opacity: 0.2 !important;
+  stroke: grey !important;
+}
+.highlight-cancel.djs-connection > .djs-visual > path {
+  stroke: grey !important;
+}
+
+.highlight-cancel:not(.djs-connection) .djs-visual > :nth-child(1) {
+  fill: grey !important; /* color elements as green */
+}
+
+/deep/.highlight-cancel.djs-shape .djs-visual > :nth-child(1) {
+  fill: grey !important;
+  stroke: grey !important;
+  fill-opacity: 0.2 !important;
+}
+/deep/.highlight-cancel.djs-shape .djs-visual > :nth-child(2) {
+  fill: grey !important;
+}
+/deep/.highlight-cancel.djs-shape .djs-visual > path {
+  fill: grey !important;
+  fill-opacity: 0.2 !important;
+  stroke: grey !important;
+}
+/deep/.highlight-cancel.djs-connection > .djs-visual > path {
+  stroke: grey !important;
+}
 </style>