activiti5.21 + SVG 绘制流程图 高亮显示已完成节点
本功能实现是结合actitivi5.21的数据结构来获取相关数据,并通过AJAX调用进行SVG图形的绘制。
1、SpringBoot 接口及相关实体
/** * 读取组装绘制流程图的数据 */ @RequestMapping(value = "/getDrawingFlowChart/{processInstanceId}") @ResponseBody public ImgJSON getDrawingFlowChart(HttpServletResponse response,@PathVariable String processInstanceId){ //获取历史流程实例 HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); //获取流程图 BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId()); List<BmpNodeInfo> bmpNodeInfos=new ArrayList<BmpNodeInfo>(); if(bpmnModel!=null){ List<FlowElement> flowElements= (List<FlowElement>) bpmnModel.getProcesses().get(0).getFlowElements(); for(int i=0;i<flowElements.size();i++){ BmpNodeInfo bmpNodeInfo=new BmpNodeInfo(); bmpNodeInfo.setClassType(flowElements.get(i).getClass().getName()); bmpNodeInfo.setId(flowElements.get(i).getId()); bmpNodeInfo.setName(flowElements.get(i).getName()); bmpNodeInfos.add(bmpNodeInfo); } } ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngineConfiguration = processEngine.getProcessEngineConfiguration(); Context.setProcessEngineConfiguration((ProcessEngineConfigurationImpl) processEngineConfiguration); ProcessDiagramGenerator diagramGenerator = processEngineConfiguration.getProcessDiagramGenerator(); ProcessDefinitionEntity definitionEntity = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(processInstance.getProcessDefinitionId()); List<HistoricActivityInstance> highLightedActivitList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).list(); //高亮环节id集合 List<String> highLightedActivitis = new ArrayList<String>(); //高亮线路id集合 List<String> highLightedFlows = DrawingFlowChartUtil.getHighLightedFlows(definitionEntity, highLightedActivitList); for (HistoricActivityInstance tempActivity : highLightedActivitList) { String activityId = tempActivity.getActivityId(); highLightedActivitis.add(activityId); } ImgJSON imgJSON=new ImgJSON(); imgJSON.setBpmnModel(bpmnModel); imgJSON.setHighLightedActivitList(highLightedActivitList); imgJSON.setHighLightedActivitis(highLightedActivitis); imgJSON.setHighLightedFlows(highLightedFlows); imgJSON.setList(bmpNodeInfos); return imgJSON; }
public class BmpNodeInfo { private String classType; private String id; private String name; public String getClassType() { return classType; } public void setClassType(String classType) { this.classType = classType; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
import org.activiti.bpmn.model.BpmnModel; import org.activiti.engine.history.HistoricActivityInstance; import java.io.Serializable; import java.util.List; public class ImgJSON implements Serializable { private BpmnModel bpmnModel; private List<HistoricActivityInstance> highLightedActivitList; private List<String> highLightedActivitis; private List<String> highLightedFlows; private List<BmpNodeInfo> list; public BpmnModel getBpmnModel() { return bpmnModel; } public void setBpmnModel(BpmnModel bpmnModel) { this.bpmnModel = bpmnModel; } public List<HistoricActivityInstance> getHighLightedActivitList() { return highLightedActivitList; } public void setHighLightedActivitList(List<HistoricActivityInstance> highLightedActivitList) { this.highLightedActivitList = highLightedActivitList; } public List<String> getHighLightedActivitis() { return highLightedActivitis; } public void setHighLightedActivitis(List<String> highLightedActivitis) { this.highLightedActivitis = highLightedActivitis; } public List<String> getHighLightedFlows() { return highLightedFlows; } public void setHighLightedFlows(List<String> highLightedFlows) { this.highLightedFlows = highLightedFlows; } public List<BmpNodeInfo> getList() { return list; } public void setList(List<BmpNodeInfo> list) { this.list = list; } }
2、JS文件
//activiti_version 5.21 svg //author sqp //create_time 2018-6-8 function createRects(rects,list,hights,draw) { $.each(rects,function (key,values) { var nodeType=getNodeType(key,list); if(nodeType!=null && nodeType!=""){ if(nodeType[0]=="StartEvent" || nodeType[0]=="EndEvent"){ draw.appendChild(createCircle(values.x+values.width/2,values.y+values.width/2,values.width/2,getNodeHightLighte(key,hights),nodeType[1],nodeType[0])); }else if(nodeType[0]=="InclusiveGateway" ||nodeType[0]==" ExclusvieGateway" ||nodeType[0]=="EventGateway" ||nodeType[0]=="ParallelGateway"){ draw.appendChild(createDoublePolygon(values.x,values.y,values.width,values.height,getNodeHightLighte(key,hights),nodeType[1])); }else{ draw.appendChild(createRect(values.x,values.y,values.width,values.height,getNodeHightLighte(key,hights),nodeType[1])); } } }); } //判断节点是否需要高亮显示 function getNodeHightLighte(key,hights) { var boo=false; $.each(hights,function (index,item) { if(key==item){ boo=true; } }); return boo; } //查找节点的类型 function getNodeType(key,list) { var name={}; var re=new Array(); $.each(list,function (index,item) { if(key==item.id){ name= item.classType.split("."); re[1]=item.name; } }); if(name!=null && name.length>0){ re[0]=name[name.length-1]; } return re; } function createDoublePolygon(x,y,width,height,hl) { var g=document.createElementNS("http://www.w3.org/2000/svg","g"); g.appendChild(createPolygon(x,y,width,height,hl)); g.appendChild(createPolygon(x+width/4,y+height/4,width/2,height/2,hl)); return g; } //画一个棱形 function createPolygon (x,y,width,height,hl) { var node; node=document.createElementNS("http://www.w3.org/2000/svg","polygon"); var point=(x+width/2)+","+y; point=point+" "+(x+width)+","+(y+height/2); point=point+" "+(x+width/2)+","+(y+height); point=point+" "+x+","+(y+height/2); node.setAttribute("points",point); node.setAttribute("fill","#FFFFFF"); if(hl==true){ node.setAttribute("stroke","#1ccb6f"); }else{ node.setAttribute("stroke","#000000"); } node.setAttribute("stroke-width","2"); return node; } //画一个圆 function createCircle(x,y,r,hl,text_str,nodetype) { var node; var g=document.createElementNS("http://www.w3.org/2000/svg","g"); node=document.createElementNS("http://www.w3.org/2000/svg","circle"); var text=document.createElementNS("http://www.w3.org/2000/svg","text"); node.setAttribute("cx",x); node.setAttribute("cy",y); node.setAttribute("r",r); if(hl==true){ node.setAttribute("stroke","#1ccb6f"); }else{ node.setAttribute("stroke","#000000"); } node.setAttribute("stroke-width","2"); node.setAttribute("fill","#FFFFFF"); text.setAttribute("text-anchor","middle"); text.style.fontSize="0.5em"; text.textContent=text_str; text.setAttribute("startOffset","1"); text.setAttribute("x",x); if(nodetype=="StartEvent"){ text.setAttribute("y",y-r-10); }else{ text.setAttribute("y",y+r+10); } text.setAttribute("fill","black"); g.appendChild(node); g.appendChild(text); return g; } // 画一个矩形 function createRect(x,y,width,height,hl,text_str) { var node; var g=document.createElementNS("http://www.w3.org/2000/svg","g"); var text=document.createElementNS("http://www.w3.org/2000/svg","text"); // <foreignObject width="120" height="50"> // <body xmlns="http://www.w3.org/1999/xhtml"> // <p style="font-size:12px;margin:0;">一段需要word wrap的文字。</p> // </body> // </foreignObject> g.setAttribute("type","userTask"); // g.setAttribute("id","id_"+v.id); node=document.createElementNS("http://www.w3.org/2000/svg","rect"); node.setAttribute("x",x);//矩形的左侧位置 node.setAttribute("y",y);//矩形的顶部位置 node.setAttribute("rx","5");//圆角 node.setAttribute("ry","5");//圆角 node.setAttribute("height",height);//矩形的高度 node.setAttribute("width",width);//矩形的宽度 if(hl==true){ node.setAttribute("stroke","#1ccb6f"); }else{ node.setAttribute("stroke","#000000"); } node.setAttribute("stroke-width","2"); node.setAttribute("fill","#FFFFFF"); text.setAttribute("text-anchor","middle"); text.setAttribute("height",height);//矩形的高度 text.setAttribute("width",width);//矩形的宽度 // var body=document.createElementNS("http://www.w3.org/2000/svg","body"); // body.setAttribute("xmlns","http://www.w3.org/1999/xhtml"); // var p=document.createElementNS("http://www.w3.org/2000/svg","p"); // // p.css({"font-size":"12px","margin":"0"}); // p.textContent=text_str; // body.appendChild(p); // text.appendChild(body); text.style.fontSize="0.7em"; text.textContent=text_str; text.setAttribute("startOffset","1"); text.setAttribute("x",x+width/2); text.setAttribute("y",y+height-10); // // node.appendChild(text); g.appendChild(node); g.appendChild(text); return g; } function createLines(lines,hight,drwa) { $.each(lines,function (key,item) { if(item.length>2){ var points=new Array(); var j=0; for(var i=0;i<item.length;i++){ var pp=new Array(); pp[0]=item[i].x; pp[1]=item[i].y; points[j++]=pp; } draw.appendChild( createBrokenLine(points,getNodeHightLighte(key,hight))); }else{ draw.appendChild( createLine(item[0].x,item[0].y,item[1].x,item[1].y,getNodeHightLighte(key,hight))); } }); } function createLine(x1,y1,x2,y2,hl) { var node; var g=document.createElementNS("http://www.w3.org/2000/svg","g"); // var text=document.createElementNS("http://www.w3.org/2000/svg","text"); g.setAttribute("type","userTask"); // g.setAttribute("id","id_"+v.id); node=document.createElementNS("http://www.w3.org/2000/svg","path"); node.setAttribute("fill","none"); if(hl==true){ node.setAttribute("stroke","#1ccb6f"); }else{ node.setAttribute("stroke","#000000");//线颜色 } // text.setAttribute("startOffset","1"); // text.setAttribute("x",parseInt(node.getAttribute("x"))+parseInt(node.getAttribute("width"))/2); // text.setAttribute("y",parseInt(node.getAttribute("y"))+parseInt(node.getAttribute("height"))/2); // text.setAttribute("fill","black"); node.setAttribute("d",drawLineArrow(x1,y1,x2,y2)); g.appendChild(node); // g.appendChild(text); return g; } function createBrokenLine(points,hl) { var node; var g=document.createElementNS("http://www.w3.org/2000/svg","g"); // var text=document.createElementNS("http://www.w3.org/2000/svg","text"); g.setAttribute("type","userTask"); // g.setAttribute("id","id_"+v.id); node=document.createElementNS("http://www.w3.org/2000/svg","path"); node.setAttribute("fill","none"); if(hl==true){ node.setAttribute("stroke","#1ccb6f"); }else{ node.setAttribute("stroke","#000000");//线颜色 } // text.setAttribute("startOffset","1"); // text.setAttribute("x",parseInt(node.getAttribute("x"))+parseInt(node.getAttribute("width"))/2); // text.setAttribute("y",parseInt(node.getAttribute("y"))+parseInt(node.getAttribute("height"))/2); // text.setAttribute("fill","black"); node.setAttribute("d",drawBrokenLineArrow(points)); g.appendChild(node); // g.appendChild(text); return g; } // 画折线和箭头 function drawBrokenLineArrow(points) { var path = ""; for (var i = 0; i <= points.length - 2; i++) { var p = points[i]; var p2 = points[i + 1]; if (i == points.length - 2) { path += drawLineArrow(p[0], p[1], p2[0], p2[1]); } else { path += "M" + p[0] + "," + p[1] + " L" + p2[0] + "," + p2[1]; } } console.log("BrokenLine, path=" + path); return path; } // 画直线和箭头 function drawLineArrow(x1, y1, x2, y2) { var path; var slopy, cosy, siny; var Par = 10.0; var x3, y3; slopy = Math.atan2((y1 - y2), (x1 - x2)); cosy = Math.cos(slopy); siny = Math.sin(slopy); path = "M" + x1 + "," + y1 + " L" + x2 + "," + y2; x3 = x2; y3 = y2; path += " M" + x3 + "," + y3; path += " L" + (Number(x3) + Number(Par * cosy - (Par / 2.0 * siny))) + "," + (Number(y3) + Number(Par * siny + (Par / 2.0 * cosy))); path += " M" + (Number(x3) + Number(Par * cosy + Par / 2.0 * siny) + "," + (Number(y3) - Number(Par / 2.0 * cosy - Par * siny))); path += " L" + x3 + "," + y3; console.log("path=" + path); return path; } function moveMouse(even) { var rects=document.getElementsByTagName("rect"); $.each(rects,function (index,item) { $(item).on('click',function (event) { alert("Ss"); }); }) }
3、HTML调用
<!DOCTYPE html> <html lang="zh" xmlns:th="http://www.w3.org/1999/xhtml"> <head> <meta charset="UTF-8"/> <title>Title</title> <script type="application/javascript" th:src="@{/my_js/jquery2.1.4.js}"></script> <script type="application/javascript" th:src="@{/bootstrap-3.3.7-dist/js/bootstrap.js}"></script> <script type="application/javascript" th:src="@{/my_js/activiti_svg.js}"></script> <script type="application/javascript"> /*<![CDATA[*/ var draw_width=800//$(window).width();//初始画布大小为浏览器可视区域大小 var draw_height=500//$(window).height();//初始画布大小为浏览器可视区域大小 var draw = document.createElementNS("http://www.w3.org/2000/svg","svg"); $(draw).css({"width":draw_width+"px","height":draw_height+"px","border":"1px sold #000000"}); $(function () { getImgJson(); }); function getImgJson() { $.ajax({ url:'/getDrawingFlowChart/122663', type:'GET', //GET async : true, data:{ }, timeout:10000, //超时时间 dataType:'json', //返回的数据格式: success:function(json){ createRects(json.bpmnModel.locationMap,json.list,json.highLightedActivitis,draw); createLines(json.bpmnModel.flowLocationMap,json.highLightedFlows); $('#svg_c').html(draw); console.log(json); }, error:function(xhr,textStatus){ console.log('错误') console.log(xhr) console.log(textStatus) } }); } /*]]>*/ </script> </head> <body > <div id="svg_c"> </div> </body> </html>
4、示例(任意拖拉流程) bpm文件效果
html 绘制效果
大家有时间可以将折线绘制了好看点,对点进行计算形成四个点的折线会更加好看