mirror of
				https://github.com/YunaiV/ruoyi-vue-pro.git
				synced 2025-11-04 08:06:12 +08:00 
			
		
		
		
	修改流程图的高亮实现,采用 activity 替代 task 作为数据源
This commit is contained in:
		@ -1,7 +1,9 @@
 | 
			
		||||
package cn.iocoder.yudao.adminserver.modules.bpm.controller.task;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.util.StrUtil;
 | 
			
		||||
import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.activity.BpmActivityRespVO;
 | 
			
		||||
import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmActivityService;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
 | 
			
		||||
import io.swagger.annotations.Api;
 | 
			
		||||
import io.swagger.annotations.ApiImplicitParam;
 | 
			
		||||
@ -15,6 +17,9 @@ import org.springframework.web.bind.annotation.RestController;
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
import javax.servlet.http.HttpServletResponse;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 | 
			
		||||
 | 
			
		||||
@Api(tags = "流程活动实例")
 | 
			
		||||
@RestController
 | 
			
		||||
@ -25,11 +30,20 @@ public class BpmActivityController {
 | 
			
		||||
    @Resource
 | 
			
		||||
    private BpmActivityService activityService;
 | 
			
		||||
 | 
			
		||||
    // TODO 芋艿:注解、权限、validtion
 | 
			
		||||
    // TODO 芋艿:权限
 | 
			
		||||
 | 
			
		||||
    @GetMapping("/list")
 | 
			
		||||
    @ApiOperation(value = "生成指定流程实例的高亮流程图",
 | 
			
		||||
            notes = "只高亮进行中的任务。不过要注意,该接口暂时没用,通过前端的 ProcessViewer.vue 界面的 highlightDiagram 方法生成")
 | 
			
		||||
    @ApiImplicitParam(name = "id", value = "流程实例的编号", required = true, dataTypeClass = String.class)
 | 
			
		||||
    public CommonResult<List<BpmActivityRespVO>> getActivityList(
 | 
			
		||||
            @RequestParam("processInstanceId") String processInstanceId) {
 | 
			
		||||
        return success(activityService.getActivityListByProcessInstanceId(processInstanceId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @GetMapping("/generate-highlight-diagram")
 | 
			
		||||
    @ApiOperation(value = "生成指定流程实例的高亮流程图",
 | 
			
		||||
            notes = "只高亮进行中的任务。不过要注意,该接口暂时没用,通过前端的 ProcessViewer.vue 界面的 highlightDiagram 方法生成")
 | 
			
		||||
    @ApiImplicitParam(name = "id", value = "流程实例的编号", required = true, dataTypeClass = String.class)
 | 
			
		||||
    public void generateHighlightDiagram(@RequestParam("processInstanceId") String processInstanceId,
 | 
			
		||||
                                         HttpServletResponse response) throws IOException {
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,23 @@
 | 
			
		||||
package cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.activity;
 | 
			
		||||
 | 
			
		||||
import io.swagger.annotations.ApiModel;
 | 
			
		||||
import io.swagger.annotations.ApiModelProperty;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
 | 
			
		||||
@ApiModel("流程活动的 Response VO")
 | 
			
		||||
@Data
 | 
			
		||||
public class BpmActivityRespVO {
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "流程活动的标识", required = true, example = "1024")
 | 
			
		||||
    private String key;
 | 
			
		||||
    @ApiModelProperty(value = "流程活动的类型", required = true, example = "StartEvent")
 | 
			
		||||
    private String type;
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "流程活动的开始时间", required = true)
 | 
			
		||||
    private Date startTime;
 | 
			
		||||
    @ApiModelProperty(value = "流程活动的结束时间", required = true)
 | 
			
		||||
    private Date endTime;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,30 @@
 | 
			
		||||
package cn.iocoder.yudao.adminserver.modules.bpm.convert.task;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.activity.BpmActivityRespVO;
 | 
			
		||||
import org.activiti.engine.history.HistoricActivityInstance;
 | 
			
		||||
import org.mapstruct.Mapper;
 | 
			
		||||
import org.mapstruct.Mapping;
 | 
			
		||||
import org.mapstruct.Mappings;
 | 
			
		||||
import org.mapstruct.factory.Mappers;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * BPM 活动 Convert
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
@Mapper
 | 
			
		||||
public interface BpmActivityConvert {
 | 
			
		||||
 | 
			
		||||
    BpmActivityConvert INSTANCE = Mappers.getMapper(BpmActivityConvert.class);
 | 
			
		||||
 | 
			
		||||
    List<BpmActivityRespVO> convertList(List<HistoricActivityInstance> list);
 | 
			
		||||
 | 
			
		||||
    @Mappings({
 | 
			
		||||
            @Mapping(source = "activityId", target = "key"),
 | 
			
		||||
            @Mapping(source = "activityType", target = "type")
 | 
			
		||||
    })
 | 
			
		||||
    BpmActivityRespVO convert(HistoricActivityInstance bean);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -1,5 +1,9 @@
 | 
			
		||||
package cn.iocoder.yudao.adminserver.modules.bpm.service.task;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.activity.BpmActivityRespVO;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * BPM 活动实例 Service 接口
 | 
			
		||||
 *
 | 
			
		||||
@ -7,6 +11,14 @@ package cn.iocoder.yudao.adminserver.modules.bpm.service.task;
 | 
			
		||||
 */
 | 
			
		||||
public interface BpmActivityService {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获得指定流程实例的活动实例列表
 | 
			
		||||
     *
 | 
			
		||||
     * @param processInstanceId 流程实例的编号
 | 
			
		||||
     * @return 活动实例列表
 | 
			
		||||
     */
 | 
			
		||||
    List<BpmActivityRespVO> getActivityListByProcessInstanceId(String processInstanceId);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 生成指定流程实例的高亮流程图,只高亮进行中的任务
 | 
			
		||||
     *
 | 
			
		||||
@ -17,7 +29,7 @@ public interface BpmActivityService {
 | 
			
		||||
     *
 | 
			
		||||
     * 如果你想实现高亮已完成的任务,可参考 https://blog.csdn.net/qiuxinfa123/article/details/119579863 博客。不过测试下来,貌似不太对~
 | 
			
		||||
     *
 | 
			
		||||
     * @param processInstanceId 实例Id
 | 
			
		||||
     * @param processInstanceId 流程实例的编号
 | 
			
		||||
     * @return 图的字节数组
 | 
			
		||||
     */
 | 
			
		||||
    byte[] generateHighlightDiagram(String processInstanceId);
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,9 @@
 | 
			
		||||
package cn.iocoder.yudao.adminserver.modules.bpm.service.task.impl;
 | 
			
		||||
 | 
			
		||||
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.service.definition.BpmProcessDefinitionService;
 | 
			
		||||
import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmActivityService;
 | 
			
		||||
import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmProcessInstanceService;
 | 
			
		||||
@ -11,6 +14,7 @@ import org.activiti.bpmn.model.BpmnModel;
 | 
			
		||||
import org.activiti.engine.HistoryService;
 | 
			
		||||
import org.activiti.engine.RepositoryService;
 | 
			
		||||
import org.activiti.engine.RuntimeService;
 | 
			
		||||
import org.activiti.engine.history.HistoricActivityInstance;
 | 
			
		||||
import org.activiti.engine.history.HistoricProcessInstance;
 | 
			
		||||
import org.activiti.engine.task.Task;
 | 
			
		||||
import org.activiti.image.ProcessDiagramGenerator;
 | 
			
		||||
@ -22,8 +26,7 @@ import java.io.InputStream;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.PROCESS_DEFINITION_BPMN_MODEL_NOT_EXISTS;
 | 
			
		||||
import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS;
 | 
			
		||||
import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.*;
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@ -40,6 +43,8 @@ public class BpmActivityServiceImpl implements BpmActivityService {
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private ProcessDiagramGenerator processDiagramGenerator;
 | 
			
		||||
    @Resource
 | 
			
		||||
    private HistoryService historyService;
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private BpmProcessInstanceService processInstanceService;
 | 
			
		||||
@ -48,6 +53,13 @@ public class BpmActivityServiceImpl implements BpmActivityService {
 | 
			
		||||
    @Resource
 | 
			
		||||
    private BpmTaskService taskService;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public List<BpmActivityRespVO> getActivityListByProcessInstanceId(String processInstanceId) {
 | 
			
		||||
        List<HistoricActivityInstance> activityList = historyService.createHistoricActivityInstanceQuery()
 | 
			
		||||
                .processInstanceId(processInstanceId).list();
 | 
			
		||||
        return BpmActivityConvert.INSTANCE.convertList(activityList);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public byte[] generateHighlightDiagram(String processInstanceId) {
 | 
			
		||||
        // 获得流程实例
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								yudao-admin-ui/src/api/bpm/activity.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								yudao-admin-ui/src/api/bpm/activity.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
import request from '@/utils/request'
 | 
			
		||||
 | 
			
		||||
export function getActivityList(query) {
 | 
			
		||||
  return request({
 | 
			
		||||
    url: '/bpm/activity/list',
 | 
			
		||||
    method: 'get',
 | 
			
		||||
    params: query
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
@ -82,24 +82,27 @@ export default {
 | 
			
		||||
    },
 | 
			
		||||
    /* 高亮流程图 */
 | 
			
		||||
    async highlightDiagram() {
 | 
			
		||||
      if (this.tasks.length === 0) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      if (!this.bpmnModeler.getDefinitions().rootElements[0].flowElements) {
 | 
			
		||||
      // let tasks = this.tasks.filter(task => {
 | 
			
		||||
      //   if (task.type !== 'sequenceFlow') { // 去除连线元素
 | 
			
		||||
      //     return true;
 | 
			
		||||
      //   }
 | 
			
		||||
      // });
 | 
			
		||||
      let tasks = this.tasks;
 | 
			
		||||
      if (tasks.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');
 | 
			
		||||
      this.bpmnModeler.getDefinitions().rootElements[0].flowElements?.forEach(n => {
 | 
			
		||||
        let completeTask = this.tasks.find(m => m.definitionKey === n.id)
 | 
			
		||||
        let todoTask = this.tasks.find(m => !m.endTime)
 | 
			
		||||
        let endTask = this.tasks[this.tasks.length - 1]
 | 
			
		||||
        let completeTask = tasks.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 (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 = this.tasks.find(m => m.definitionKey === nn.targetRef.id)
 | 
			
		||||
              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') {
 | 
			
		||||
@ -107,7 +110,7 @@ export default {
 | 
			
		||||
                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.definitionKey === n.id) {
 | 
			
		||||
                if (!todoTask && endTask.key === n.id) {
 | 
			
		||||
                  canvas.addMarker(nn.id, 'highlight');
 | 
			
		||||
                  canvas.addMarker(nn.targetRef.id, 'highlight');
 | 
			
		||||
                }
 | 
			
		||||
@ -120,7 +123,7 @@ export default {
 | 
			
		||||
          }
 | 
			
		||||
        } else if (n.$type === 'bpmn:ExclusiveGateway') { // 排它网关
 | 
			
		||||
          n.outgoing?.forEach(nn => {
 | 
			
		||||
            let targetTask = this.tasks.find(m => m.definitionKey === nn.targetRef.id)
 | 
			
		||||
            let targetTask = tasks.find(m => m.key === nn.targetRef.id)
 | 
			
		||||
            if (targetTask) {
 | 
			
		||||
              canvas.addMarker(nn.id, targetTask.endTime ? 'highlight' : 'highlight-todo');
 | 
			
		||||
            }
 | 
			
		||||
@ -129,7 +132,7 @@ export default {
 | 
			
		||||
          if (completeTask) {
 | 
			
		||||
            canvas.addMarker(n.id, completeTask.endTime ? 'highlight' : 'highlight-todo')
 | 
			
		||||
            n.outgoing?.forEach(nn => {
 | 
			
		||||
              const targetTask = this.taskList.find(m => m.definitionKey === nn.targetRef.id)
 | 
			
		||||
              const targetTask = this.taskList.find(m => m.key === nn.targetRef.id)
 | 
			
		||||
              if (targetTask) {
 | 
			
		||||
                canvas.addMarker(nn.id, targetTask.endTime ? 'highlight' : 'highlight-todo')
 | 
			
		||||
                canvas.addMarker(nn.targetRef.id, targetTask.endTime ? 'highlight' : 'highlight-todo')
 | 
			
		||||
@ -138,7 +141,7 @@ export default {
 | 
			
		||||
          }
 | 
			
		||||
        } else if (n.$type === 'bpmn:StartEvent') { // 开始节点
 | 
			
		||||
          n.outgoing?.forEach(nn => {
 | 
			
		||||
            let completeTask = this.tasks.find(m => m.definitionKey === nn.targetRef.id)
 | 
			
		||||
            let completeTask = tasks.find(m => m.key === nn.targetRef.id)
 | 
			
		||||
            if (completeTask) {
 | 
			
		||||
              canvas.addMarker(nn.id, 'highlight');
 | 
			
		||||
              canvas.addMarker(n.id, 'highlight');
 | 
			
		||||
@ -146,7 +149,7 @@ export default {
 | 
			
		||||
            }
 | 
			
		||||
          });
 | 
			
		||||
        } else if (n.$type === 'bpmn:EndEvent') { // 结束节点
 | 
			
		||||
          if (endTask.definitionKey === n.id && endTask.endTime) {
 | 
			
		||||
          if (endTask.key === n.id && endTask.endTime) {
 | 
			
		||||
            canvas.addMarker(n.id, 'highlight')
 | 
			
		||||
            return
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
@ -73,7 +73,7 @@
 | 
			
		||||
      <div slot="header" class="clearfix">
 | 
			
		||||
        <span class="el-icon-picture-outline">流程图</span>
 | 
			
		||||
      </div>
 | 
			
		||||
      <my-process-viewer key="designer" v-model="bpmnXML" v-bind="bpmnControlForm" :taskData="tasks" />
 | 
			
		||||
      <my-process-viewer key="designer" v-model="bpmnXML" v-bind="bpmnControlForm" :taskData="activityList" />
 | 
			
		||||
    </el-card>
 | 
			
		||||
 | 
			
		||||
    <!-- 对话框(转派审批人) -->
 | 
			
		||||
@ -103,6 +103,7 @@ import {createProcessInstance, getProcessInstance} from "@/api/bpm/processInstan
 | 
			
		||||
import {approveTask, getTaskListByProcessInstanceId, rejectTask, updateTaskAssignee} from "@/api/bpm/task";
 | 
			
		||||
import {getDate} from "@/utils/dateUtils";
 | 
			
		||||
import {listSimpleUsers} from "@/api/system/user";
 | 
			
		||||
import {getActivityList} from "@/api/bpm/activity";
 | 
			
		||||
 | 
			
		||||
// 流程实例的详情页,可用于审批
 | 
			
		||||
export default {
 | 
			
		||||
@ -128,6 +129,7 @@ export default {
 | 
			
		||||
      bpmnControlForm: {
 | 
			
		||||
        prefix: "activiti"
 | 
			
		||||
      },
 | 
			
		||||
      activityList: [],
 | 
			
		||||
 | 
			
		||||
      // 审批记录
 | 
			
		||||
      tasksLoad: true,
 | 
			
		||||
@ -196,12 +198,18 @@ export default {
 | 
			
		||||
          if (val) {
 | 
			
		||||
            item.__config__.defaultValue = val
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // 加载流程图
 | 
			
		||||
        getProcessDefinitionBpmnXML(this.processInstance.processDefinition.id).then(response => {
 | 
			
		||||
          this.bpmnXML = response.data
 | 
			
		||||
        })
 | 
			
		||||
        });
 | 
			
		||||
        // 加载活动列表
 | 
			
		||||
        getActivityList({
 | 
			
		||||
          processInstanceId: this.processInstance.id
 | 
			
		||||
        }).then(response => {
 | 
			
		||||
          this.activityList = response.data;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // 取消加载中
 | 
			
		||||
        this.processInstanceLoading = false;
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user