`

activiti流程图上获取各节点的信息获取

阅读更多

背景:

        由于项目的需要,当用户在查看流程图时,当点击某个流程图片上的节点时,需要提示一些信息,这就需要获取各个节点的信息,此处获取id和name的值         

         

       注意这个并不是流程图的高亮,即当点击网点申请环节时,获取该节点的id和name,即B001和网点申请,点击部门申请时,获取B002和部门经理审批

解释说明:

     1.下方说的x和y:     

    

      2.流程定义的key: 即为下方id的值

    

     3.节点的id和name的值:

    

难点分析:

      由 于activiti在部署时,如果没有流程图片,则activiti会自动生成一张图片,而我们项目中使用的是activiti modeler实现的在线画流程图,部署时没有图片,是由activiti自动生成。而activiti在生成图片时,会对图片做一个裁剪操作,所有最终 各个节点的坐标会比实际的要小。

      (即:activiti自动生成的图片,会做一个裁剪操作,各个节点实际的x和y的值比xml文件中的要小)

        而我们的难点在于,各个节点实际坐标的获取。如下图所示:         

        

        即我们实际上获取的坐标需要在减去一个minX和minY,得到的才是我们的各个节点实际的坐标。

 

步骤分析:

      1.根据流程定义的key,重新生成流程图片,而不是获取流程图片。

      2.还是根据流程定义的key,获取各个节点的信息。(此处需要注意的是各个节点实际的x和y的值的获取的方法

      3.在jsp页面上使用绝对定位,给点击的节点加上高亮。

 

步骤实现:  1.根据流程定义的key,重新生成流程图片,而不是获取流程图片。

    此处重新生成图片的原因:

           因为有些时候我们在部署流程时,将图片也部署进去了,此时使用的就是自己的图片,activiti不会进行图片的裁剪。因为我在下一步获取流程节点的信息时,对x和y的进行了特殊处理,因此此处需要重新生成 流程图片。

 

/**
* 根据流程的key生成图片
* 
* @param request
* @param response
* @param wfKey 流程定义的key
*/
@RequestMapping("/genericImageByWfKey")
public void genericImageByWfKey(HttpServletRequest request, HttpServletResponse response, String wfKey) {
    Context.setProcessEngineConfiguration(processEngineConfiguration);
    RepositoryService repositoryService = this.processEngine.getRepositoryService();
    ProcessDefinition pd = repositoryService.createProcessDefinitionQuery().processDefinitionKey(wfKey).latestVersion().singleResult();
    BpmnModel bm = repositoryService.getBpmnModel(pd.getId());
    InputStream is = ProcessDiagramGenerator.generatePngDiagram(bm); // 生成图片,获取图片的输入流
    try {
        int size = is.available();
        byte data[] = new byte[size];
        is.read(data);
        response.setContentType("image/png"); // 设置返回的文件类型
        OutputStream os = response.getOutputStream();
        os.write(data);
        os.flush();
        os.close();
    } catch (IOException e) {
        log.error("读写流程图时出现异常!");
    }
        log.info("end....");
}

 2.还是根据流程定义的key,获取各个节点的信息。

    获取各个节点的坐标之前,我们先看一下activiti中是如果获取到最小的x和y的,然后是如何裁剪图片的

    2.1获取节点包括线的最小x和最小y :

   跟踪acticiti的源码可以发现,最小x和y的获取(org.activiti.engine.impl.bpmn.diagram.ProcessDiagramGenerator.initProcessDiagramCanvas(BpmnModel)

 

protected static ProcessDiagramCanvas initProcessDiagramCanvas(BpmnModel bpmnModel) {
    // We need to calculate maximum values to know how big the image will be in its entirety
    double minX = Double.MAX_VALUE;
    double maxX = 0;
    double minY = Double.MAX_VALUE;
    double maxY = 0;

    for (Pool pool : bpmnModel.getPools()) {
      GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(pool.getId());
      minX = graphicInfo.getX();
      maxX = graphicInfo.getX() + graphicInfo.getWidth();
      minY = graphicInfo.getY();
      maxY = graphicInfo.getY() + graphicInfo.getHeight();
    }
    
    List<FlowNode> flowNodes = gatherAllFlowNodes(bpmnModel);
    for (FlowNode flowNode : flowNodes) {

      GraphicInfo flowNodeGraphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
      
      // width
      if (flowNodeGraphicInfo.getX() + flowNodeGraphicInfo.getWidth() > maxX) {
        maxX = flowNodeGraphicInfo.getX() + flowNodeGraphicInfo.getWidth();
      }
      if (flowNodeGraphicInfo.getX() < minX) {
        minX = flowNodeGraphicInfo.getX();
      }
      // height
      if (flowNodeGraphicInfo.getY() + flowNodeGraphicInfo.getHeight() > maxY) {
        maxY = flowNodeGraphicInfo.getY() + flowNodeGraphicInfo.getHeight();
      }
      if (flowNodeGraphicInfo.getY() < minY) {
        minY = flowNodeGraphicInfo.getY();
      }

      for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {
        List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId());
        for (GraphicInfo graphicInfo : graphicInfoList) {
          // width
          if (graphicInfo.getX() > maxX) {
            maxX = graphicInfo.getX();
          }
          if (graphicInfo.getX() < minX) {
            minX = graphicInfo.getX();
          }
          // height
          if (graphicInfo.getY() > maxY) {
            maxY = graphicInfo.getY();
          }
          if (graphicInfo.getY()< minY) {
            minY = graphicInfo.getY();
          }
        }
      }
    }
    
    int nrOfLanes = 0;
    for (Process process : bpmnModel.getProcesses()) {
      for (Lane l : process.getLanes()) {
        
        nrOfLanes++;
        
        GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(l.getId());
        // // width
        if (graphicInfo.getX() + graphicInfo.getWidth() > maxX) {
          maxX = graphicInfo.getX() + graphicInfo.getWidth();
        }
        if (graphicInfo.getX() < minX) {
          minX = graphicInfo.getX();
        }
        // height
        if (graphicInfo.getY() + graphicInfo.getHeight() > maxY) {
          maxY = graphicInfo.getY() + graphicInfo.getHeight();
        }
        if (graphicInfo.getY() < minY) {
          minY = graphicInfo.getY();
        }
      }
    }
    
    // Special case, see http://jira.codehaus.org/browse/ACT-1431
    if (flowNodes.size() == 0 && bpmnModel.getPools().size() == 0 && nrOfLanes == 0) {
      // Nothing to show
      minX = 0;
      minY = 0;
    }
    
    return new ProcessDiagramCanvas((int) maxX + 10,(int) maxY + 10, (int) minX, (int) minY);
  }

   2.2 图片的裁剪:

   还是activiti的源码:(org.activiti.engine.impl.bpmn.diagram.ProcessDiagramCanvas.generateImage(String)

  

/**
   * Generates an image of what currently is drawn on the canvas.
   * 
   * Throws an {@link ActivitiException} when {@link #close()} is already
   * called.
   */
  public InputStream generateImage(String imageType) {
    if (closed) {
      throw new ActivitiException("ProcessDiagramGenerator already closed");
    }

    ByteArrayOutputStream out = new ByteArrayOutputStream();
    try {
      // Try to remove white space
      minX = (minX <= 5) ? 5 : minX;
      minY = (minY <= 5) ? 5 : minY;
      BufferedImage imageToSerialize = processDiagram;
      if (minX >= 0 && minY >= 0) {
        // 此处的最小x和最小y减去了5
        imageToSerialize = processDiagram.getSubimage(minX - 5, minY - 5, canvasWidth - minX + 5, canvasHeight - minY + 5); // 此处可以看到,activiti对图像做了裁剪的操作。
      }
      ImageIO.write(imageToSerialize, imageType, out);
    } catch (IOException e) {
      throw new ActivitiException("Error while generating process image", e);
    } finally {
      IoUtil.closeSilently(out);
    }
    return new ByteArrayInputStream(out.toByteArray());
  }

    2.3 我们自己的各个节点的信息获取

@RequestMapping("/getProcessTrace")
	@ResponseBody
	/**
	 * 获取各个节点的具体的信息
	 * @param wfKey
	 * 		流程定义的key
	 * @return
	 */
	public List<Map<String, Object>> getProcessTrace(String wfKey) throws Exception {
		List<Map<String, Object>> activityInfos = new ArrayList<Map<String, Object>>();
		RepositoryService repositoryService = processEngine.getRepositoryService();
		ProcessDefinition pd = repositoryService.createProcessDefinitionQuery().processDefinitionKey(wfKey).latestVersion().singleResult();
		ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(pd.getId());
		List<ActivityImpl> activitiList = processDefinition.getActivities();
		InputStream xmlIs = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName());
		BpmnModel bm = new BpmnXMLConverter().convertToBpmnModel(new InputStreamSource(xmlIs), false, true);

                // 下方使用反射获取最小的x和y,仔细看就会发现调用的是上方2.1节的方法
		Class<?> clazz = Class.forName("org.activiti.engine.impl.bpmn.diagram.ProcessDiagramGenerator");
		Method method = clazz.getDeclaredMethod("initProcessDiagramCanvas", BpmnModel.class);
		method.setAccessible(true); 
		ProcessDiagramCanvas pdc = (ProcessDiagramCanvas) method.invoke(clazz.newInstance(), bm); // 调用方法

		clazz = Class.forName("org.activiti.engine.impl.bpmn.diagram.ProcessDiagramCanvas");
		Field minXField = clazz.getDeclaredField("minX"); // 得到minX字段
		Field minYField = clazz.getDeclaredField("minY");
		minXField.setAccessible(true);
		minYField.setAccessible(true);
		int minX = minXField.getInt(pdc);// 最小的x值  
		int minY = minYField.getInt(pdc); // 最小的y的值


		minX = minX > 0 ? minX - 5 : 0;   // 此处为什么需要减5,上方2.2中activiti源码中有
		minY = minY > 0 ? minY - 5 : 0;
		for (ActivityImpl activity : activitiList) {
			Map<String, Object> activityInfo = new HashMap<String, Object>();
			activityInfo.put("width", activity.getWidth());
			activityInfo.put("height", activity.getHeight());
			activityInfo.put("x", activity.getX() - minX);
			activityInfo.put("y", activity.getY() - minY);
			activityInfo.put("actId", activity.getId());
			activityInfo.put("name", activity.getProperty("name"));  // ActivityImpl 中没有getName方法,所以此处需要这样获取。
			activityInfos.add(activityInfo);
		}
		return activityInfos;
	}

   3.在jsp页面上使用绝对定位,给点击的节点加上高亮。

 

<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
		<title>Insert title here</title>
		<style type="text/css">
			.activity-attr{border-radius: 10px; border: 3px solid red; transition:ease-out 0.5s; box-shadow:0px 0px 9px red;}
			#processKey{color: red;}
			#flowImageAndRect{position: relative;overflow: scroll;height:300px; heibackground-image: url('${ctx}/resources/images/workflow/grid_10.png')}
			body,html{margin: 0px;padding:0px;}
		</style>
		<script type="text/javascript">
			$(function(){
				var wfKey = '${param.wfKey}'; // 流程定义的key
				var $flowImageAndRect = $('#flowImageAndRect');
				$('#processKey').html('流程定义的key --> ' + wfKey);
				// 加载流程图片
				loadProcessImage(wfKey,$flowImageAndRect);
				// 加载各节点信息,最终实现,在点击图片上的各节点时,出现高亮
				setTimeout(function(){
					loadProcessTrace(wfKey,$flowImageAndRect);
				},200);
				
				var $revClickRect = null; // 上次点击的图形
				// 绑定click事件,点击实现,只有点击的不是同一个时,才显示红色的边框(如果多次点击同一个,红色的边框只出现一次)
				$('#flowImageAndRect').off('click').on('click','.activity-attr',function(e){
					var $this = $(this);
					var prevFlag = false; // 是上一个图形,避免多次点击同一个
					if($revClickRect){ // 说明不是第一次点击
						prevFlag = ($revClickRect.attr('actId')!=$this.attr('actId')) ? false : true;// 说明2次点击的不是同一个
						if(!prevFlag)
							$revClickRect.css('opacity','0');
					}
					if(!prevFlag){ // 此处可以请求后台,加载相关的数据(多次点击同一个,下方可确保只执行一次)
						$this.css('opacity','1'); // 显示当前的
						$revClickRect = $this; // 将当前设置为上次点击的
						$('#info').html('节点ID --> ' + $this.attr('actId') + "  |  " + "节点名称 --> " + $this.attr('name'));
					}
				});
			});
			 
			/**
			 * 加载图片
			 */
			function loadProcessImage(wfKey,$flowImageAndRect){
				var imageUrl = '${ctx}/workflow/monitor/genericImageByWfKey.do?wfKey='+wfKey;
				// 加载图片
				$('<img />',{
					"src" : imageUrl,
					"alt" : ''
				}).appendTo($flowImageAndRect);	
			}
			
			/**
			 * 加载流程中各节点的信息 
			 * @param wfKey : 流程定义的key
			 * @param $flowImageAndRect 
			 */
			function loadProcessTrace(wfKey,$flowImageAndRect){
				var traceUrl = '${ctx}/workflow/monitor/getProcessTrace.do?wfKey='+wfKey;
				$.getJSON(traceUrl,function(infos){
					var html = "";
					$.each(infos,function(i,v){
						// 矩形的div
						var $div = $('<div/>', {
				                'class': 'activity-attr'
			            }).css({
			                position: 'absolute',
			                left: v.x,
			                top: v.y,
			                width: v.width - 3,
			                height:v.height - 3,
			                opacity: 0,
			                zIndex: 100,
					cursor : 'pointer'
			            }).attr({'actId':v.actId,'name':v.name});
						html += $div.prop("outerHTML");
					});
					$('<div />',{'id':'processRect'}).html(html).appendTo($flowImageAndRect);
				});
			}
		</script>
	</head>
	<body>
		<div id="main">
			<div id="flowImageAndRect">
				
			</div>
			
			<div id="processKey" style="font-size: 52px;text-align: center;margin-bottom: 50px;">
			
			</div>
			
			<div id="info" style="font-size: 52px;text-align: center;">
				
			</div>
		</div>
	</body>
</html>

   到此,已经完成了。

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 大小: 70.9 KB
  • 大小: 72.7 KB
  • 大小: 25.6 KB
  • 大小: 8.7 KB
  • 大小: 62.1 KB
  • 大小: 35.2 KB
9
2
分享到:
评论
5 楼 huan1993 2017-10-28  
鬼一天 写道
遇到子流程的情况就不行了

子流程当时我就没有测试了。
4 楼 鬼一天 2017-10-24  
遇到子流程的情况就不行了
3 楼 huan1993 2015-07-03  
euhappyday 写道
恩是个好方法,要么就是统一使用ide生成的图。就省去了图片剪裁的问题。


如果使用activiti自动生成图片,那么生成的图片就会进行裁剪。
2 楼 euhappyday 2015-07-03  
恩是个好方法,要么就是统一使用ide生成的图。就省去了图片剪裁的问题。
1 楼 billbingqine 2015-07-03  
高,马克一下

相关推荐

    activiti流程图查看demo

    这个"activiti流程图查看demo"是一个示例项目,旨在帮助用户理解如何在Activiti中查看和操作流程图。在本篇文章中,我们将深入探讨Activiti的核心概念、流程图的查看方法以及如何利用diagram-viewer工具来实现这一...

    Activiti12流程跟踪任务节点和线高亮显示

    Activiti流程跟踪任务节点和线高亮显示 Activiti是Activiti BPMN 2.0流程引擎的Java实现,提供了强大的流程管理和自动化功能。在Activiti中,我们可以通过流程跟踪功能来追踪和监控流程的执行情况。流程跟踪任务...

    Activiti流程图部署及流程图部分操作.pdf

    总之,Activiti流程图的部署和操作涉及到流程定义的生命周期管理,包括创建、部署、查询和更新等步骤。正确地部署和操作流程图能够确保业务流程在系统中按照预期执行,从而提升企业的运营效率和自动化水平。熟悉并...

    ACTIVITI如何获取下一步节点

    3. **分析流程图**:流程定义中包含了一个XML表示的流程图,它定义了各个活动的顺序和条件。通过解析这个XML,我们可以找到当前活动的出口(Outgoing Flows),即下一步节点。 4. **处理条件表达式**:在某些情况下...

    activiti6.0实现流程图片自定义颜色 当前任务为红色,走过的任务为绿色,自定义文字颜色连线文字显示

    4. 生成流程图:使用扩展的ProcessDiagramGenerator和Canvas类,将颜色信息应用到流程图上,然后生成新的流程图片。 5. 显示流程图:最后,将生成的自定义颜色流程图展示给用户,使他们能清楚地看到流程的当前状态...

    使用activiti工作流引擎显示流程图时高亮显示流程图中已执行节点和已执行路径方法源代码

    接下来,我们需要解析流程图的 XML 文件(*.bpmn20.xml),从中获取节点和连接线的信息。这可以通过 Activiti 的 BpmnModel 类来完成。BpmnModel 对象包含了流程图的所有元素,我们可以根据历史数据找到对应的节点和...

    activiti工作流流程跟踪图(图片(两种方式))

    将这些任务节点在流程图上标记出来,可以直观地看到流程的停滞点或瓶颈。 为了生成这两种高亮显示的流程图,通常需要结合Activiti的API与图像处理库,比如Java的Batik库,它可以解析BPMN流程图的XML描述,并将其...

    activiti工作流高亮图配置.zip

    下面将详细介绍如何配置Activiti流程图的高亮显示。 首先,理解Activiti的核心概念。Activiti是一个基于Java的业务流程管理系统,它支持BPMN 2.0标准,能够处理复杂的业务流程定义。流程图是用图形化的方式表示这些...

    activiti5.22 流程设计器绘画的流程图条件线上的字不显示

    在Activiti中,流程图是通过一个可视化的设计器绘制的,这些图形化表示不仅包含流程节点,还包含连接这些节点的线条,它们可以带有条件表达式,以指示流程如何根据不同的条件分支。 条件线,也称为网关线,是流程图...

    Activiti 学习笔记十:开始活动节点(判断流程是否结束及查询历史)

    在 Activiti 流程图中,开始活动节点(Start Event)标志着流程的起始点。它是一个圆圈,通常内含一个箭头,表示流程的启动。开始事件可以有多种类型,如无事件开始、定时器开始、信号开始等。无事件开始是最常见的...

    Activiti学习文档(二)之画流程图并部署流程

    1. **Process Definition**:这是对流程的抽象描述,包含了流程图的所有信息。 2. **Process Instance**:当流程定义被激活,即启动一个流程实例。每个实例代表了流程的一个具体运行。 3. **Task**:任务是流程中的...

    activiti 5.22流程图连线名称不显示bug修复

    然而,在实际使用过程中,可能会遇到各种问题,其中之一就是在Activiti 5.22版本中出现的“流程图连线名称不显示”的bug。 该问题主要体现在使用Activiti的Modeler设计器时,用户在设计流程图时可以清晰地看到连线...

    Activiti5学习-在流程图中为带条件的顺序流添加提示信息

    在查看流程图时,当鼠标悬停在条件顺序流上时,可以弹出一个提示框,显示对应的`customAnnotation`值。这样,业务人员和开发者都能够更好地理解流程的执行逻辑。 此外,对于复杂流程,我们还可以使用 Activiti 的...

    Activiti6-流程跟踪监控图-节点-流程线高亮显示-支持通过、不通过、驳回、退回

    Activiti6-流程跟踪监控图-节点-流程线高亮显示-支持通过、不通过、驳回、退回 支持内容: 已完成节点高亮显示、当前执行中节点红色显示 支持一个节点多条流出线,包括通过、不通过、驳回、退回,按照已执行操作正确...

    Activiti如何动态获取流程图过程详解

    Activiti动态获取流程图过程详解 Activiti是一个基于Java的开源工作流引擎,广泛应用于企业级业务流程管理系统中。Activiti提供了动态获取流程图的功能,通过本文,我们将详细介绍Activiti如何动态获取流程图的过程...

    activiti6.0流程图.zip

    自定义画布可以让你改变连接线的颜色和文字样式,比如设置文字颜色为特定的值,使得流程图的连线更加清晰易读,同时提供更丰富的信息。 `微信图片_20200509112823.png` 和 `微信图片_20200509112947.png` 可能是...

    activiti6.0 自定义流程路径颜色和任务颜色

    在"activiti6.0 自定义流程路径颜色和任务颜色"这个主题中,我们主要关注如何根据业务需求定制流程图的显示效果,特别是流程路径和任务节点的颜色。 首先,流程图的颜色定制对于理解和跟踪流程状态至关重要。默认...

    activiti生成图片--解决坐标错位问题

    1. **解析BPMN文件**:使用Activiti提供的API,如`BpmnModel`类,读取并解析BPMN文件,获取流程图的结构和每个元素的坐标信息。 2. **处理坐标信息**:在BPMN文件中,坐标信息通常以相对单位表示,需要根据流程图的...

    流程跟踪监控图-节点-流程线高亮显示.rar

    4. **ActivitiUtils.java**:这通常是一个工具类,包含了一些与Activiti相关的通用方法,比如获取流程实例信息,解析BPMN模型,或者处理与流程图渲染相关的辅助任务。 通过上述文件,开发者可以了解如何在Activiti...

Global site tag (gtag.js) - Google Analytics