背景:

        由于项目的需要,当用户在查看流程图时,当点击某个流程图片上的节点时,需要提示一些信息,这就需要获取各个节点的信息,此处获取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的进行了特殊处理,因此此处需要重新生成 流程图片。

  1. /**
  2. * 根据流程的key生成图片
  3. *
  4. * @param request
  5. * @param response
  6. * @param wfKey 流程定义的key
  7. */
  8. @RequestMapping("/genericImageByWfKey")
  9. public void genericImageByWfKey(HttpServletRequest request, HttpServletResponse response, String wfKey) {
  10. Context.setProcessEngineConfiguration(processEngineConfiguration);
  11. RepositoryService repositoryService = this.processEngine.getRepositoryService();
  12. ProcessDefinition pd = repositoryService.createProcessDefinitionQuery().processDefinitionKey(wfKey).latestVersion().singleResult();
  13. BpmnModel bm = repositoryService.getBpmnModel(pd.getId());
  14. InputStream is = ProcessDiagramGenerator.generatePngDiagram(bm); // 生成图片,获取图片的输入流
  15. try {
  16. int size = is.available();
  17. byte data[] = new byte[size];
  18. is.read(data);
  19. response.setContentType("image/png"); // 设置返回的文件类型
  20. OutputStream os = response.getOutputStream();
  21. os.write(data);
  22. os.flush();
  23. os.close();
  24. } catch (IOException e) {
  25. log.error("读写流程图时出现异常!");
  26. }
  27. log.info("end....");
  28. }InputStream is = ProcessDiagramGenerator.generatePngDiagram(bm); // 生成图片,获取图片的输入流
  29. try {
  30. int size = is.available();
  31. byte data[] = new byte[size];
  32. is.read(data);
  33. response.setContentType("image/png"); // 设置返回的文件类型
  34. OutputStream os = response.getOutputStream();
  35. os.write(data);
  36. os.flush();
  37. os.close();
  38. } catch (IOException e) {
  39. log.error("读写流程图时出现异常!");
  40. }
  41. log.info("end....");
  42. }

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

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

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

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

  1. protected static ProcessDiagramCanvas initProcessDiagramCanvas(BpmnModel bpmnModel) {
  2. // We need to calculate maximum values to know how big the image will be in its entirety
  3. double minX = Double.MAX_VALUE;
  4. double maxX = 0;
  5. double minY = Double.MAX_VALUE;
  6. double maxY = 0;
  7. for (Pool pool : bpmnModel.getPools()) {
  8. GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(pool.getId());
  9. minX = graphicInfo.getX();
  10. maxX = graphicInfo.getX() + graphicInfo.getWidth();
  11. minY = graphicInfo.getY();
  12. maxY = graphicInfo.getY() + graphicInfo.getHeight();
  13. }
  14. List<FlowNode> flowNodes = gatherAllFlowNodes(bpmnModel);
  15. for (FlowNode flowNode : flowNodes) {
  16. GraphicInfo flowNodeGraphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
  17. // width
  18. if (flowNodeGraphicInfo.getX() + flowNodeGraphicInfo.getWidth() > maxX) {
  19. maxX = flowNodeGraphicInfo.getX() + flowNodeGraphicInfo.getWidth();
  20. }
  21. if (flowNodeGraphicInfo.getX() < minX) {
  22. minX = flowNodeGraphicInfo.getX();
  23. }
  24. // height
  25. if (flowNodeGraphicInfo.getY() + flowNodeGraphicInfo.getHeight() > maxY) {
  26. maxY = flowNodeGraphicInfo.getY() + flowNodeGraphicInfo.getHeight();
  27. }
  28. if (flowNodeGraphicInfo.getY() < minY) {
  29. minY = flowNodeGraphicInfo.getY();
  30. }
  31. for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {
  32. List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId());
  33. for (GraphicInfo graphicInfo : graphicInfoList) {
  34. // width
  35. if (graphicInfo.getX() > maxX) {
  36. maxX = graphicInfo.getX();
  37. }
  38. if (graphicInfo.getX() < minX) {
  39. minX = graphicInfo.getX();
  40. }
  41. // height
  42. if (graphicInfo.getY() > maxY) {
  43. maxY = graphicInfo.getY();
  44. }
  45. if (graphicInfo.getY()< minY) {
  46. minY = graphicInfo.getY();
  47. }
  48. }
  49. }
  50. }
  51. int nrOfLanes = 0;
  52. for (Process process : bpmnModel.getProcesses()) {
  53. for (Lane l : process.getLanes()) {
  54. nrOfLanes++;
  55. GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(l.getId());
  56. // // width
  57. if (graphicInfo.getX() + graphicInfo.getWidth() > maxX) {
  58. maxX = graphicInfo.getX() + graphicInfo.getWidth();
  59. }
  60. if (graphicInfo.getX() < minX) {
  61. minX = graphicInfo.getX();
  62. }
  63. // height
  64. if (graphicInfo.getY() + graphicInfo.getHeight() > maxY) {
  65. maxY = graphicInfo.getY() + graphicInfo.getHeight();
  66. }
  67. if (graphicInfo.getY() < minY) {
  68. minY = graphicInfo.getY();
  69. }
  70. }
  71. }
  72. // Special case, see http://jira.codehaus.org/browse/ACT-1431
  73. if (flowNodes.size() == 0 && bpmnModel.getPools().size() == 0 && nrOfLanes == 0) {
  74. // Nothing to show
  75. minX = 0;
  76. minY = 0;
  77. }
  78. return new ProcessDiagramCanvas((int) maxX + 10,(int) maxY + 10, (int) minX, (int) minY);
  79. }new ProcessDiagramCanvas((int) maxX + 10,(int) maxY + 10, (int) minX, (int) minY);
  80. }

2.2 图片的裁剪:

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

  1. /**
  2. * Generates an image of what currently is drawn on the canvas.
  3. *
  4. * Throws an {@link ActivitiException} when {@link #close()} is already
  5. * called.
  6. */
  7. public InputStream generateImage(String imageType) {
  8. if (closed) {
  9. throw new ActivitiException("ProcessDiagramGenerator already closed");
  10. }
  11. ByteArrayOutputStream out = new ByteArrayOutputStream();
  12. try {
  13. // Try to remove white space
  14. minX = (minX <= 5) ? 5 : minX;
  15. minY = (minY <= 5) ? 5 : minY;
  16. BufferedImage imageToSerialize = processDiagram;
  17. if (minX >= 0 && minY >= 0) {
  18. // 此处的最小x和最小y减去了5
  19. imageToSerialize = processDiagram.getSubimage(minX - 5, minY - 5, canvasWidth - minX + 5, canvasHeight - minY + 5); // 此处可以看到,activiti对图像做了裁剪的操作。
  20. }
  21. ImageIO.write(imageToSerialize, imageType, out);
  22. } catch (IOException e) {
  23. throw new ActivitiException("Error while generating process image", e);
  24. } finally {
  25. IoUtil.closeSilently(out);
  26. }
  27. return new ByteArrayInputStream(out.toByteArray());
  28. } // 此处的最小x和最小y减去了5
  29. imageToSerialize = processDiagram.getSubimage(minX - 5, minY - 5, canvasWidth - minX + 5, canvasHeight - minY + 5); // 此处可以看到,activiti对图像做了裁剪的操作。
  30. }
  31. ImageIO.write(imageToSerialize, imageType, out);
  32. } catch (IOException e) {
  33. throw new ActivitiException("Error while generating process image", e);
  34. } finally {
  35. IoUtil.closeSilently(out);
  36. }
  37. return new ByteArrayInputStream(out.toByteArray());
  38. }

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

  1. @RequestMapping("/getProcessTrace")
  2. @ResponseBody
  3. /**
  4. * 获取各个节点的具体的信息
  5. * @param wfKey
  6. * 流程定义的key
  7. * @return
  8. */
  9. public List<Map<String, Object>> getProcessTrace(String wfKey) throws Exception {
  10. List<Map<String, Object>> activityInfos = new ArrayList<Map<String, Object>>();
  11. RepositoryService repositoryService = processEngine.getRepositoryService();
  12. ProcessDefinition pd = repositoryService.createProcessDefinitionQuery().processDefinitionKey(wfKey).latestVersion().singleResult();
  13. ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(pd.getId());
  14. List<ActivityImpl> activitiList = processDefinition.getActivities();
  15. InputStream xmlIs = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName());
  16. BpmnModel bm = new BpmnXMLConverter().convertToBpmnModel(new InputStreamSource(xmlIs), false, true);
  17. // 下方使用反射获取最小的x和y,仔细看就会发现调用的是上方2.1节的方法
  18. Class<?> clazz = Class.forName("org.activiti.engine.impl.bpmn.diagram.ProcessDiagramGenerator");
  19. Method method = clazz.getDeclaredMethod("initProcessDiagramCanvas", BpmnModel.class);
  20. method.setAccessible(true);
  21. ProcessDiagramCanvas pdc = (ProcessDiagramCanvas) method.invoke(clazz.newInstance(), bm); // 调用方法
  22. clazz = Class.forName("org.activiti.engine.impl.bpmn.diagram.ProcessDiagramCanvas");
  23. Field minXField = clazz.getDeclaredField("minX"); // 得到minX字段
  24. Field minYField = clazz.getDeclaredField("minY");
  25. minXField.setAccessible(true);
  26. minYField.setAccessible(true);
  27. int minX = minXField.getInt(pdc);// 最小的x值
  28. int minY = minYField.getInt(pdc); // 最小的y的值

  29. minX = minX > 0 ? minX - 5 : 0; // 此处为什么需要减5,上方2.2中activiti源码中有
  30. minY = minY > 0 ? minY - 5 : 0;
  31. for (ActivityImpl activity : activitiList) {
  32. Map<String, Object> activityInfo = new HashMap<String, Object>();
  33. activityInfo.put("width", activity.getWidth());
  34. activityInfo.put("height", activity.getHeight());
  35. activityInfo.put("x", activity.getX() - minX);
  36. activityInfo.put("y", activity.getY() - minY);
  37. activityInfo.put("actId", activity.getId());
  38. activityInfo.put("name", activity.getProperty("name")); // ActivityImpl 中没有getName方法,所以此处需要这样获取。
  39. activityInfos.add(activityInfo);
  40. }
  41. return activityInfos;
  42. } // 下方使用反射获取最小的x和y,仔细看就会发现调用的是上方2.1节的方法
  43. Class<?> clazz = Class.forName("org.activiti.engine.impl.bpmn.diagram.ProcessDiagramGenerator");
  44. Method method = clazz.getDeclaredMethod("initProcessDiagramCanvas", BpmnModel.class);
  45. method.setAccessible(true);
  46. ProcessDiagramCanvas pdc = (ProcessDiagramCanvas) method.invoke(clazz.newInstance(), bm); // 调用方法
  47. clazz = Class.forName("org.activiti.engine.impl.bpmn.diagram.ProcessDiagramCanvas");
  48. Field minXField = clazz.getDeclaredField("minX"); // 得到minX字段
  49. Field minYField = clazz.getDeclaredField("minY");
  50. minXField.setAccessible(true);
  51. minYField.setAccessible(true);
  52. int minX = minXField.getInt(pdc);// 最小的x值
  53. int minY = minYField.getInt(pdc); // 最小的y的值

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

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

  1. <html>
  2. <head>
  3. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  4. <title>Insert title here</title>
  5. <style type="text/css">
  6. .activity-attr{border-radius: 10px; border: 3px solid red; transition:ease-out 0.5s; box-shadow:0px 0px 9px red;}
  7. #processKey{color: red;}
  8. #flowImageAndRect{position: relative;overflow: scroll;height:300px; heibackground-image: url('${ctx}/resources/images/workflow/grid_10.png')}
  9. body,html{margin: 0px;padding:0px;}
  10. </style>
  11. <script type="text/javascript">
  12. $(function(){
  13. var wfKey = '${param.wfKey}'; // 流程定义的key
  14. var $flowImageAndRect = $('#flowImageAndRect');
  15. $('#processKey').html('流程定义的key --> ' + wfKey);
  16. // 加载流程图片
  17. loadProcessImage(wfKey,$flowImageAndRect);
  18. // 加载各节点信息,最终实现,在点击图片上的各节点时,出现高亮
  19. setTimeout(function(){
  20. loadProcessTrace(wfKey,$flowImageAndRect);
  21. },200);
  22. var $revClickRect = null; // 上次点击的图形
  23. // 绑定click事件,点击实现,只有点击的不是同一个时,才显示红色的边框(如果多次点击同一个,红色的边框只出现一次)
  24. $('#flowImageAndRect').off('click').on('click','.activity-attr',function(e){
  25. var $this = $(this);
  26. var prevFlag = false; // 是上一个图形,避免多次点击同一个
  27. if($revClickRect){ // 说明不是第一次点击
  28. prevFlag = ($revClickRect.attr('actId')!=$this.attr('actId')) ? false : true;// 说明2次点击的不是同一个
  29. if(!prevFlag)
  30. $revClickRect.css('opacity','0');
  31. }
  32. if(!prevFlag){ // 此处可以请求后台,加载相关的数据(多次点击同一个,下方可确保只执行一次)
  33. $this.css('opacity','1'); // 显示当前的
  34. $revClickRect = $this; // 将当前设置为上次点击的
  35. $('#info').html('节点ID --> ' + $this.attr('actId') + " | " + "节点名称 --> " + $this.attr('name'));
  36. }
  37. });
  38. });
  39. /**
  40. * 加载图片
  41. */
  42. function loadProcessImage(wfKey,$flowImageAndRect){
  43. var imageUrl = '${ctx}/workflow/monitor/genericImageByWfKey.do?wfKey='+wfKey;
  44. // 加载图片
  45. $('<img />',{
  46. "src" : imageUrl,
  47. "alt" : ''
  48. }).appendTo($flowImageAndRect);
  49. }
  50. /**
  51. * 加载流程中各节点的信息
  52. * @param wfKey : 流程定义的key
  53. * @param $flowImageAndRect
  54. */
  55. function loadProcessTrace(wfKey,$flowImageAndRect){
  56. var traceUrl = '${ctx}/workflow/monitor/getProcessTrace.do?wfKey='+wfKey;
  57. $.getJSON(traceUrl,function(infos){
  58. var html = "";
  59. $.each(infos,function(i,v){
  60. // 矩形的div
  61. var $div = $('<div/>', {
  62. 'class': 'activity-attr'
  63. }).css({
  64. position: 'absolute',
  65. left: v.x,
  66. top: v.y,
  67. width: v.width - 3,
  68. height:v.height - 3,
  69. opacity: 0,
  70. zIndex: 100,
  71. cursor : 'pointer'
  72. }).attr({'actId':v.actId,'name':v.name});
  73. html += $div.prop("outerHTML");
  74. });
  75. $('<div />',{'id':'processRect'}).html(html).appendTo($flowImageAndRect);
  76. });
  77. }
  78. </script>
  79. </head>
  80. <body>
  81. <div id="main">
  82. <div id="flowImageAndRect">
  83. </div>
  84. <div id="processKey" style="font-size: 52px;text-align: center;margin-bottom: 50px;">
  85. </div>
  86. <div id="info" style="font-size: 52px;text-align: center;">
  87. </div>
  88. </div>
  89. </body>
  90. </html>

到此,已经完成了。

activiti流程图上获取各节点的信息获取的更多相关文章

  1. 用python 获取照片的Exif 信息(获取拍摄设备,时间,地点等信息)

    第一步:先安装 pip install exifread 第二部:上代码 import exifread import requests class PhotoExifInfo(): def __in ...

  2. js获取dom节点之 id 获取

    在JavaScript中,标准的id选择器调用语法是: document.getElementById('myid').style.width = pc + "%"; 但是,今天发 ...

  3. JS获取子节点、父节点和兄弟节点的方法实例总结

    转自:https://www.jb51.net/article/143286.htm 本文实例讲述了JS获取子节点.父节点和兄弟节点的方法.分享给大家供大家参考,具体如下: 一.js获取子节点的方式 ...

  4. DA - 信息获取途径汇总

    目的驱动 大多数情况下,都是为了解决某个问题或完成某项任务,才需要进行针对性的.大范围的.细致化的信息获取. 那么,信息获取的方式和来源,就应该紧紧围绕这个"问题和任务"本身来确定 ...

  5. Info - 信息获取途径汇总

    目的驱动 大多数情况下,都是为了解决某个问题或完成某项任务,才需要进行针对性的.大范围的.细致化的信息获取. 那么,信息获取的方式和来源,就应该紧紧围绕这个"问题和任务"本身来确定 ...

  6. E1.获取Elixir/Erlang版本信息

    E1.获取Elixir/Erlang版本信息 获取Elixir版本 直接在shel中打开iex (interactive shell),就可以查到具体的版本信息: iex Erlang/OTP 22 ...

  7. activiti 配置节点 连线信息获取

    1.1.1. 前言 当使用eclipse插件进行流程设计的时候,部署流程之后,我们如何获取我们定义的所有的节点.连线.关口等配置信息呢?有的人看到这个需求,不免窃喜,这不很简单嘛,重新打来bmpn中定 ...

  8. 通过DOM节点操作来获取表单信息

    这是之前突发奇想地用dom节点的关系来操作表单的故事.. 事情的经过是这样的,大概就是一个平台注册后有留言功能,管理员登录之后可以对这些留言进行回复.这个页面呢,就是通过foreach获取到数据库里的 ...

  9. activiti官网实例项目activiti-explorer之获取流程节点

    如上图在保存步骤中添加获取节点信息方法nodes(); 方法如下: //获取所有节点    JsonNode modelNode = new ObjectMapper().readTree(repos ...

随机推荐

  1. javascript对象——基本对象

    1.array对象 2.Boolean包装类对象和Date对象   3.Math对象 1到100随机数 4.RegExp对象 4.1 正则规则 如: 4.2 两种创建正则对象方式 /正则表达式/    ...

  2. 数据库实验sql代码 myemployees 以及mygirl

    /* Navicat Premium Data Transfer Source Server : mysql Source Server Type : MySQL Source Server Vers ...

  3. Git - 命令行 常用

    一.合并其他分支的commit(A分支中的commit合并至B分支) 切换到A分支,查询commit历史命令行 : $ git log 复制要合并的commit id (如:663802dfb121e ...

  4. 【OI】竖式问题分析与解答

    题目:找出所有形如abc*de(三位数乘以两位数)的算式,使得在完整的竖式中,所有数字都属于一个特定的数字集合.输入数字集合(相邻数字之间没有空格),输出所有竖式.每个竖式前应有编号,之后应有一个空行 ...

  5. dedecms编辑器不能复制word格式的处理方法

    在word文档中编辑一篇文章,格式段落都整理好后,粘贴到dede编辑器里面却发现,格式都无效了,可能dede有自己的打算,比如这样可以文章字体大小统一,样色统一,整体比较整洁.但是用惯了word编辑文 ...

  6. 搞不定 NodeJS 内存泄漏?先从了解垃圾回收开始

    通常来说,内存管理有两种方式,一种是手动管理,一种是自动管理. 手动管理需要开发者自己管理内存,什么时候申请内存空间,什么时候释放都需要小心处理,否则容易形成内存泄漏和指针乱飞的局面.C 语言开发是典 ...

  7. pyqt5 GUI教程

    from PyQt5 import QtCore, QtGui, QtWidgets import sys import qtawesome class MainUi(QtWidgets.QMainW ...

  8. [转载]CentOS 7 用户怎样安装 LNMP(Nginx+PHP+MySQL)

    关于 Nginx (发音 "engine x")这是一款免费.开源.高效的 HTTP 服务器,Nginx是以稳定著称,丰富的功能,结构简单,低资源消耗.本教程演示如何在CentOS ...

  9. 鸿蒙内核源码分析(线程概念篇) | 是谁在不停的折腾CPU? | 百篇博客分析OpenHarmony源码 | v21.06

    百篇博客系列篇.本篇为: v21.xx 鸿蒙内核源码分析(线程概念篇) | 是谁在不断的折腾CPU | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调 ...

  10. P6295-有标号 DAG 计数【多项式求逆,多项式ln】

    正题 题目链接:https://www.luogu.com.cn/problem/P6295 题目大意 求所有\(n\)个点的弱联通\(DAG\)数量. \(1\leq n\leq 10^5\) 解题 ...