用于Activiti前端显示流程图的插件
因为现在做一个基于Activit的工作流OA,在设计过程中需要显示用户设计出来的流程图。
所以需要使用一个流程图插件,可以用来加载流程,也可以直接通过拖拽的方式设计流程。
思来想去,搜索了很多插件,最后选择了jsplubm这款插件
官网地址是jsplumbtoolkit.com
流程包含了基本的用户任务,连接线。整个流程自上而下,且包含了“层”的概念,比如节点2和节点3是一个层的,节点4是单独一个层的,
主要是方便理清具体执行的顺序。
通过自己手动解析流程,可以展示基本的直线顺序流,并行切分汇聚顺序流,或者逆向的顺序流。当然这些都是后端的逻辑。
我是直接使用插件的flowchat demo (https://jsplumbtoolkit.com/community/demo/flowchart/index.html)直接改的
看了这个demo,我发现需要需要我手动在网页上添加用户任务
<div class="window jtk-node jsplumb-endpoint-anchor jsplumb-draggable jsplumb-connected" style="top: 150px; left: 120px; background-color: rgb(255, 255, 255);" id="sid-2-22737CD1-B482-2B90-5E6E-9E3BFB86FEF9">
节点2
<br> <br>
</div>
具体实施中,每个用户任务都用一个div表示 class是插件定义的 然后使用top和left属性确定他在页面上的位置。
id是用户任务的id
当流程执行的时候,在某个时刻,永远只会执行某一层的一个或者多个用户任务。
所以我们必须有个节点的数据,这个数据是金字塔一样一层一层盖上去的。
第一层是起始节点,第二层是起始节点的下级节点,第三层是第二层所有节点的下级节点。。。。
然后在网页上,用DIV一个个画出来,并附上相关的属性。
//加载所有的节点 //data 所有的节点数据 //FlowID 流程定义ID function loadMe(data,FlowID){ nodes=new Array(); for(var i=0;i<data.length;i++){//遍历每一层 createActivity(cleanID(data[i]),i+1); } CreateSequence(FlowID,data);//创建连接线 initcontrol(); // 初始化每个节点 } var nodes=new Array(); //构建一行节点 //itmes:这行的所有元素集合 //rows: 第几行 value >=1 function createActivity(items,rows){ var height=50+(rows-1)*100 //高顿 var MarginLeft=new Array(); //更具不同的元素个数 初始化元素的左边距 switch(items.length){ case 1: MarginLeft[0]=180; break; case 2: MarginLeft[0]=120; MarginLeft[1]=240; break; case 3: MarginLeft[0]=60; MarginLeft[1]=180; MarginLeft[2]=300; break; case 4: MarginLeft[0]=10; MarginLeft[1]=130; MarginLeft[2]=250; MarginLeft[3]=380; break; } for(var i=0;i<items.length;i++){ var mdiv='<div class="window jtk-node" style="top:'+height+'px;left:'+MarginLeft[i]+'px" class=\"window jtk-node\" id=\"'+items[i].NODE_ID+'\">'+items[i].NODE_NAME +'<br /> <br /></div>'; $('#canvas').append(mdiv); } }
节点创建后就需要初始化节点的基本功能以及创建连接线了,基本代码如下
function CreateSequence(FlowID, Rowsdata) { var instance = window.jsp = jsPlumb.getInstance({ // default drag options 默认拖动选项 DragOptions : { cursor : 'pointer', zIndex : 2000 }, // the overlays to decorate each connection with. note that the label // overlay uses a function to generate the label text; in this // case it returns the 'labelText' member that we set on each connection // in the 'init' method below. // 默认连接线的样式 ConnectionOverlays : [ [ "Arrow", { // 连接线箭头的样式 location : 1, visible : true, id : "ARROW", length : 10, events : { // 点击连接线事件 click : function() { alert("you clicked on the arrow overlay") } } } ], // [ "Label", { // location: 0.1, // id: "label", // cssClass: "aLabel", // events:{ // tap:function() { alert("hey"); } // } // }] ], Container : "canvas" }); var basicType = { connector : "statemachine", proximityLimit : 1, paintStyle : { dashstyle : "0 0", strokeStyle : "#61B7CF", lineWidth : 2 }, // dashstyle 虚设置为线 hoverPaintStyle : { strokeStyle : "#61B7CF" }, // overlays: [ // "Arrow" // ] }; instance.registerConnectionType("basic", basicType); // this is the paint style for the connecting lines.. var connectorPaintStyle = { lineWidth : 2, strokeStyle : "#61B7CF", joinstyle : "round", outlineColor : "white", outlineWidth : 2 }, // .. and this is the hover style. connectorHoverStyle = { lineWidth : 2, strokeStyle : "#61B7CF", outlineWidth : 0, outlineColor : "#61B7CF" }, endpointHoverStyle = { fillStyle : "#61B7CF", strokeStyle : "#61B7CF" }, // the definition of source endpoints (the small blue ones) sourceEndpoint = { endpoint : "Blank", // 隐藏连接点 显示连接点 Dot Blank paintStyle : { strokeStyle : "#7AB02C", fillStyle : "transparent", radius : 4, lineWidth : 3 }, maxConnections : -1, isSource : true, connectorOverlays : [ [ "Arrow", { width : 5, length : 1, location : 0 } ] ], // gap 连接点的缺口 stub 线的最短高度和宽度 cornerRadius 连接点折点的弧度 StateMachine // Flowchart connector : [ "Flowchart", { stub : [ 20, 30 ], gap : 0, cornerRadius : 5, alwaysRespectStubs : true } ], connectorStyle : connectorPaintStyle, hoverPaintStyle : endpointHoverStyle, connectorHoverStyle : connectorHoverStyle, dragOptions : {}, overlays : [ [ "Label", { location : [ 0.5, 1.5 ], // label: "Drag", // cssClass: "endpointSourceLabel", // visible:false } ] ] }, // the definition of target endpoints (will appear when the user drags a // connection) targetEndpoint = { endpoint : "Blank", // DOt21 paintStyle : { fillStyle : "#7AB02C", radius : 5 }, hoverPaintStyle : endpointHoverStyle, maxConnections : 30, dropOptions : { hoverClass : "hover", activeClass : "active" }, isTarget : true, overlays : [ [ "Label", { location : [ 0.5, -0.5 ], label : "Drop", cssClass : "endpointTargetLabel", visible : false } ] ] }, init = function(connection) { // connection.getOverlay("label").setLabel(connection.sourceId.substring(15) // + "-" + connection.targetId.substring(15)); }; var _addEndpoints = function(toId, sourceAnchors, targetAnchors) { for (var i = 0; i < sourceAnchors.length; i++) { var sourceUUID = toId + sourceAnchors[i]; instance.addEndpoint(toId, sourceEndpoint, { anchor : sourceAnchors[i], uuid : sourceUUID }); } for (var j = 0; j < targetAnchors.length; j++) { var targetUUID = toId + targetAnchors[j]; instance.addEndpoint(toId, targetEndpoint, { anchor : targetAnchors[j], uuid : targetUUID }); } }; // suspend drawing and initialise. instance.batch(function() { var Sequence; $.ajax({ type : 'POST', async : false, url : "Form/GetSequenceByFlowID", data : "ActivityID=" + FlowID, success : function(data) { Sequence = data; }, error : function(data) { alert("连接线数据加载失败:" + data) }, // DataType:json }); if (Sequence.length == 0) {// 只有一个开始节点 $('.window.jtk-node').attr("class","window jtk-node jsplumb"+ "-endpoint-anchor jsplumb-draggable jsplumb-connected"); return; } for (var x = 0; x < Sequence.length; x++) { _addEndpoints(Sequence[x].SOURE_NODE, [ "BottomCenter" ], [ "TopCenter", "LeftMiddle", "RightMiddle" ]); _addEndpoints(Sequence[x].TARGET_NODE, [ "BottomCenter" ], [ "TopCenter", "LeftMiddle", "RightMiddle" ]); console.log('xxx'); } instance.bind("connection", function(connInfo, originalEvent) { init(connInfo.connection); }); // make all the window divs draggable instance.draggable(jsPlumb .getSelector(".flowchart-demo .window"), { grid : [ 20, 20 ] }); // 判断连接线是不是撤回的 function isBack(SOURE_NODE, TARGET_NODE) { for (var i = 0; i < Rowsdata.length; i++) { for (var x = 0; x < Rowsdata[i].length; x++) { if (Rowsdata[i][x].NODE_ID == SOURE_NODE) { SOURE_NODE = i; } } } for (var i = 0; i < Rowsdata.length; i++) { for (var x = 0; x < Rowsdata[i].length; x++) { if (Rowsdata[i][x].NODE_ID == TARGET_NODE) { SOURE_NODE = i; } } } return SOURE_NODE > SOURE_NODE; }
在这个方法中初始化每个节点,比如添加端点【endpoints 】,设置连接线的样式,设置箭头的样式,设置点击连接线的事件等等。
执行到这个的时候,流程图基本上就画出来了。
而且你可以拖动它。
Tips:
默认情况下端点是显示出来的,你还可以设置每个端点最多能连出或者连入多少条线,或者隐藏这个端点。
但隐藏后你就不能手动修改连接线了
附件
很久之前写的demo
http://pan.baidu.com/s/1miyEvCs
参考文档:http://www.cnblogs.com/sggx/p/3836432.html