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

This commit is contained in:
YunaiV 2022-01-19 12:45:12 +08:00
parent 8e18a63d34
commit 1425f7dfcc
8 changed files with 204 additions and 70 deletions

View File

@ -20,4 +20,20 @@ public class BpmActivityRespVO {
@ApiModelProperty(value = "流程活动的结束时间", required = true) @ApiModelProperty(value = "流程活动的结束时间", required = true)
private Date endTime; 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;
}
} }

View File

@ -1,6 +1,8 @@
package cn.iocoder.yudao.adminserver.modules.bpm.convert.task; 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.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.activiti.engine.history.HistoricActivityInstance;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.Mapping; import org.mapstruct.Mapping;
@ -8,6 +10,7 @@ import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers; import org.mapstruct.factory.Mappers;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* BPM 活动 Convert * BPM 活动 Convert
@ -19,7 +22,16 @@ public interface BpmActivityConvert {
BpmActivityConvert INSTANCE = Mappers.getMapper(BpmActivityConvert.class); 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({ @Mappings({
@Mapping(source = "activityId", target = "key"), @Mapping(source = "activityId", target = "key"),

View File

@ -46,6 +46,12 @@ public class BpmTaskExtDO extends BaseDO {
* 关联 {@link Task#getId()} * 关联 {@link Task#getId()}
*/ */
private String taskId; private String taskId;
// /**
// * 任务的标识
// *
// * 关联 {@link Task#getTaskDefinitionKey()}
// */
// private String definitionKey;
/** /**
* 任务的结果 * 任务的结果
* *

View File

@ -19,4 +19,7 @@ public interface BpmTaskExtMapper extends BaseMapperX<BpmTaskExtDO> {
return selectList("task_id", taskIds); return selectList("task_id", taskIds);
} }
default List<BpmTaskExtDO> selectListByProcessInstanceId(String processInstanceId) {
return selectList("process_instance_id", processInstanceId);
}
} }

View File

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

View File

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

View File

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

View File

@ -82,55 +82,63 @@ export default {
}, },
/* 高亮流程图 */ /* 高亮流程图 */
async highlightDiagram() { async highlightDiagram() {
// let tasks = this.tasks.filter(task => { let activityList = this.tasks;
// if (task.type !== 'sequenceFlow') { // 线 if (activityList.length === 0) {
// return true;
// }
// });
let tasks = this.tasks;
if (tasks.length === 0) {
return; return;
} }
// https://gitee.com/tony2y/RuoYi-flowable/blob/master/ruoyi-ui/src/components/Process/index.vue#L222 // https://gitee.com/tony2y/RuoYi-flowable/blob/master/ruoyi-ui/src/components/Process/index.vue#L222
//
let canvas = this.bpmnModeler.get('canvas'); 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 => { this.bpmnModeler.getDefinitions().rootElements[0].flowElements?.forEach(n => {
let completeTask = tasks.find(m => m.key === n.id) let activity = activityList.find(m => m.key === n.id) //
let todoTask = tasks.find(m => !m.endTime)
let endTask = tasks[tasks.length - 1]
if (n.$type === 'bpmn:UserTask') { // if (n.$type === 'bpmn:UserTask') { //
if (completeTask) { if (!activity) {
canvas.addMarker(n.id, completeTask.endTime ? 'highlight' : 'highlight-todo'); return;
// 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.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') { // } else if (n.$type === 'bpmn:ExclusiveGateway') { //
n.outgoing?.forEach(nn => { 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) { if (targetTask) {
canvas.addMarker(nn.id, targetTask.endTime ? 'highlight' : 'highlight-todo'); canvas.addMarker(nn.id, targetTask.endTime ? 'highlight' : 'highlight-todo');
} }
}) })
} else if (n.$type === 'bpmn:ParallelGateway') { // } else if (n.$type === 'bpmn:ParallelGateway') { //
if (completeTask) { if (activity) {
canvas.addMarker(n.id, completeTask.endTime ? 'highlight' : 'highlight-todo') canvas.addMarker(n.id, activity.endTime ? 'highlight' : 'highlight-todo')
n.outgoing?.forEach(nn => { n.outgoing?.forEach(nn => {
const targetTask = this.taskList.find(m => m.key === nn.targetRef.id) const targetTask = this.taskList.find(m => m.key === nn.targetRef.id)
if (targetTask) { if (targetTask) {
@ -140,18 +148,21 @@ export default {
}) })
} }
} else if (n.$type === 'bpmn:StartEvent') { // } else if (n.$type === 'bpmn:StartEvent') { //
n.outgoing?.forEach(nn => { n.outgoing?.forEach(nn => { // outgoing bpmn:SequenceFlow线
let completeTask = tasks.find(m => m.key === nn.targetRef.id) let fromTask = activityList.find(m => m.key === nn.targetRef.id)
if (completeTask) { if (fromTask) {
canvas.addMarker(nn.id, 'highlight'); canvas.addMarker(nn.id, 'highlight');
canvas.addMarker(n.id, 'highlight'); canvas.addMarker(n.id, 'highlight');
return
} }
}); });
} else if (n.$type === 'bpmn:EndEvent') { // } else if (n.$type === 'bpmn:EndEvent') { //
if (endTask.key === n.id && endTask.endTime) { if (endActivity.key !== n.id) { // endActivity EndEvent
canvas.addMarker(n.id, 'highlight') return;
return }
// endActivity endTime
// todoActivity
if (endActivity.endTime && !todoActivity) {
canvas.addMarker(n.id, 'highlight');
} }
} }
}) })
@ -162,6 +173,7 @@ export default {
<style> <style>
/** 通过 */
.highlight.djs-shape .djs-visual > :nth-child(1) { .highlight.djs-shape .djs-visual > :nth-child(1) {
fill: green !important; fill: green !important;
stroke: green !important; stroke: green !important;
@ -179,18 +191,6 @@ export default {
stroke: green !important; 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) { .highlight:not(.djs-connection) .djs-visual > :nth-child(1) {
fill: green !important; /* color elements as green */ fill: green !important; /* color elements as green */
} }
@ -211,6 +211,20 @@ export default {
/deep/.highlight.djs-connection > .djs-visual > path { /deep/.highlight.djs-connection > .djs-visual > path {
stroke: green !important; 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 { /deep/.highlight-todo.djs-connection > .djs-visual > path {
stroke: orange !important; stroke: orange !important;
stroke-dasharray: 4px !important; stroke-dasharray: 4px !important;
@ -224,4 +238,81 @@ export default {
fill-opacity: 0.2 !important; 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> </style>