自己写这个流程编辑器的原因
1:有些人弄了个流程编辑器出来,看了一下还不错,但是,不给源码,不知道有什么技术难题,有什么值得保密的。
2:还有写人理论讲了一大堆也没看出弄出个什么东西出来。
本人用了2天空有时间,基于jQuery,与Raphel 做了一个流程编辑器。
操作习惯类似与PowerBuilder。
附件是工程代码,可以随意使用,呵呵。
主要的业务代码webFlow.js
/* * 记录工具面板中被选中的工具 * pointer:指针,用来选择、移动画出的图形 * * line:划线 * * */ var g_selected = "pointer"; //恢复操作栈 var g_redoStack = []; /* * * 节点信息 * {node: [],il: [],ol: [],nodeType: nodeType} * */ var g_nodes = []; /* * 线条信息 * {lineId,bNodeId,eNodeId} * */ var g_lines = []; //线的起点坐标 var g_lsp = { x: 0, y: 0 }; //线的开始节点标识 var g_lsNodeId = 0; var g_preLine; //当前移动节点标识 var g_moveNodeId = 0; var g_mskNodeSet = []; var g_cr = 15, g_sw = 100, g_sh = 60, g_sr = 10, g_mskNodeObj; var g_ndsMask = { "stroke-dasharray": "- .", "fill-opacity": 0, stroke: "black", "stroke-width": 5, fill: "white" } //删除草图 function rmPreDoodle(e){ switch (g_selected) { case "pointer": _pointerRMDHandler(e); break; case "line": _lineRMDHandler(e); break; } } //节点样式对象 var g_ndsSelected = { "stroke-dasharray": "- .", "fill-opacity": 1, stroke: "black", "stroke-width": 5 } var g_ndsNormal = { "stroke-dasharray": "", "stroke-width": 1, fill: "#4674a8", "stroke-opacity": .3, "fill-opacity": .7, stroke: "" } var g_ndsHide = { "stroke-dasharray": "", "stroke-width": 1, fill: "#4674a8", "stroke-opacity": 0, "fill-opacity": 0, stroke: "" } function _pointerRMDHandler(e){ } function _lineRMDHandler(e){ if (g_preLine) g_preLine.remove(); } //-----------------------------------------------MOUSEMOVE---------------------------------------------------- function mouseMovehandler(e){ switch (g_selected) { case "pointer": _pointerMMHandler(e); break; case "line": _lineMMHandler(e); break; case "segment": break; } } function _pointerMMHandler(e){ if (!g_moveNodeId) return; } var udFlag, g_pY = 0; function _lineMMHandler(e){ //没有点击节点不划线 if (g_lsNodeId == 0) return; var r = e.data.r; rmPreDoodle(e); var x2 = e.pageX, y2 = e.pageY; udFlag = y2 > g_pY ? -1 : 1; g_preLine = r.ai.line(g_lsp.x, g_lsp.y, x2 + udFlag * 20, y2 + udFlag * 20).attr({ stroke: "gray", "stroke-dasharray": "- ", "stroke-width":2 }); g_pY = y2; } //-----------------------------------------------MOUSECLICK---------------------------------------------------- function mouseClickhandler(e){ switch (g_selected) { case "pointer": _pointerMCHandler(e); break; case "line": break; case "segment": crtNode("segment", e.data.r, e); break; case "condition": crtNode("condition", e.data.r, e); break; case "synchro": crtNode("synchro", e.data.r, e); break; } } function _pointerMCHandler(e){ } //---------------------------------------------鼠标右键处理---------------------------------------------------- function contextmenuHandler(e){ e.preventDefault(); rmPreDoodle(e); g_lsNodeId = 0; top.initPaletee(false); } //------------------------------------------创建节点----------------------------------------------------------- function _crtCondition(x, y, r){ return r.path(["M", x + g_sw / 2, y, "L", x + g_sw, y + g_sh / 2, x + g_sw / 2, y + g_sh, x, y + g_sh / 2, "z"]).attr(g_ndsNormal).toBack(); } function _crtSynchro(x, y, r){ var p=["M",x,y,"L",x+g_sw,y,x+g_sw,y+g_sh,x,y+g_sh,x,y,"M",x,y+g_sh/2,"L",x+g_sw,y+g_sh/2] var t=["M",x+g_sw/4,y,"L",x+g_sw/4,y+g_sh/2,x+g_sw/4-5,y+g_sh/2-5,"M",x+g_sw/4,y+g_sh/2,"L",x+g_sw/4+5,y+g_sh/2-5,"M",x+g_sw*3/4,y,"L",x+g_sw*3/4,y+g_sh/2,x+g_sw*3/4-5,y+g_sh/2-5,"M",x+g_sw*3/4,y+g_sh/2,"L",x+g_sw/4+5,y+g_sh/2-5,"M",x+g_sw/2,y+g_sh/2,"L",x+g_sw/2,y+g_sh,x+g_sw/2-5,y+g_sh-5,"M",x+g_sw/2,y+g_sh,"L",x+g_sw/2+5,y+g_sh/-5] return r.path(p.concat(t)).toBack(); } function crtNode(nodeType, r, e, nodeName, fillColor){ var nodeId = new Date().getTime(), c; var x = e.pageX - g_sw / 2; var y = e.pageY - g_sh / 2; var ndStyle = g_ndsNormal; switch (nodeType) { case "beginNode": case "endNode": ndStyle = g_ndsHide; c = r.circle(x + g_sw / 2, y + g_sh / 2, g_sw / 2 - 15).attr(g_ndsNormal); break; case "condition": ndStyle = g_ndsHide; c = _crtCondition(x, y, r); break; case "synchro": ndStyle = g_ndsHide; c = _crtSynchro(x, y, r); break; } var nd = r.rect(x, y, g_sw, g_sh, g_sr).attr(ndStyle).toFront(); if (fillColor) nd.attr("fill", fillColor); var _nodeName = nodeName ? nodeName : "节点名称"; var txt = r.text(e.pageX, e.pageY, _nodeName); g_nodes[nodeId] = { node: [nd, txt, c], il: [], ol: [], fillCollr: fillColor, nodeType: nodeType }; _addNodeHandler(nd, nodeId, r); } //----------------------------------------------------节点事件处理函数-------------------------------------------- function _addNodeHandler(nd, nodeId, r){ $(nd.node).bind("mouseout", { nodeId: nodeId, r: r }, _ndMOHandler); $(nd.node).bind("mouseover", { nodeId: nodeId, r: r }, _ndMIHandler); $(nd.node).bind("click", { nodeId: nodeId, r: r }, _ndMCHandler); $(nd.node).bind("dblclick", { nodeId: nodeId, r: r }, _ndMDCHandler); $(nd.node).bind("mousemove", { nodeId: nodeId, r: r }, _ndMMCHandler); } function showInfo(msg){ alert(msg); } function _ndMOHandler(e){ var nodeId = e.data.nodeId; var no = g_nodes[nodeId]; switch (no.nodeType) { case "beginNode": case "endNode": case "condition": case "synchro": no.node[2].attr(g_ndsNormal); break; default: no.node[0].attr(g_ndsNormal); } } function _ndMIHandler(e){ var nodeId = e.data.nodeId; var no = g_nodes[nodeId]; switch (no.nodeType) { case "beginNode": case "endNode": case "condition": case "synchro": no.node[2].attr(g_ndsSelected); break; default: no.node[0].attr(g_ndsSelected); } } //节点被点击处理时间 function _ndMCHandler(e){ switch (g_selected) { case "line": _ndClickForLine(e); break; case "pointer": _ndClickForMove(e); break; } e.stopPropagation(); } function _lineAsBeginNode(e){ var nodeId = e.data.nodeId; var no = g_nodes[nodeId]; var node = no.node[0]; switch (no.nodeType) { case "beginNode": if (no.ol.length > 0) { showInfo("开始节点只可以有一个输出路径"); return; } case "condition": case "synchro": case "segment": g_lsNodeId = nodeId; g_lsp.x = node.attr("x") + g_sw / 2; g_lsp.y = node.attr("y") + g_sh / 2; break; case "endNode": break; } } function _lineAsEndNode(e){ rmPreDoodle(); var r = e.data.r, leNodeId = e.data.nodeId; //不可以对自己划线 if (!leNodeId || leNodeId == g_lsNodeId || g_lsNodeId == 0 || g_nodes[leNodeId].nodeType == "beginNode") return; var xy = _getLineEndXY(leNodeId); var line = r.ai.lineWithArrow(g_lsp.x, g_lsp.y, xy.x, xy.y).attr({ stroke: "#f88817","stroke-width":2 }); //记录线条信息 var lineId = new Date().getTime(); g_lines[lineId] = { lineId: lineId, bNodeId: g_lsNodeId, eNodeId: leNodeId, line: line }; //修改线条端点对应节点属性 g_nodes[g_lsNodeId].ol.push(lineId); g_nodes[leNodeId].il.push(lineId); g_lsNodeId = 0; } //点击节点来划线 function _ndClickForLine(e){ if (g_lsNodeId) _lineAsEndNode(e); else _lineAsBeginNode(e); } //点击节点来移动节点 function _ndClickForMove(e){ var no = g_nodes[e.data.nodeId]; no.bMoved = !no.bMoved; } function _ndMDCHandler(e){ e.stopPropagation(); } function _ndMMCHandler(e){ if (g_selected != "pointer") return; var nodeId = e.data.nodeId; var no = g_nodes[nodeId] if (!no.bMoved) return; g_moveNodeId = nodeId; _getMskNode(no.nodeType, e); e.stopPropagation(); } function crtMskNode(r){ g_mskNodeSet = r.set(); g_mskNodeSet.push(r.rect(100, 100, g_sw, g_sh, g_sr).hide()); $(g_mskNodeSet[0].node).bind("click", { r: r }, _mskNodeClickHandler).bind("mousemove", { r: r }, _mskNodeMoveHandler); g_mskNodeSet.attr(g_ndsMask); } function _getMskNode(nodeType, e){ switch (nodeType) { case "beginNode": case "endNode": case "segment": case "condition": case "synchro": g_mskNodeObj = g_mskNodeSet[0]; break; } if (!g_mskNodeObj) return; g_mskNodeObj.show(); _moveNode(g_mskNodeObj, e); } function _mskNodeClickHandler(e){ g_mskNodeObj.hide(); //移动原始对象 var no = g_nodes[g_moveNodeId]; no.bMoved = false; _moveNode(no.node[0], e, g_moveNodeId); g_mskNodeObj = 0; g_moveNodeId = 0; } function _mskNodeMoveHandler(e){ _moveNode(g_mskNodeObj, e); } function _moveNode(node, e, realNodeId){ node.attr({ x: e.pageX - g_sw / 2, y: e.pageY - g_sh / 2 }) if (!realNodeId) return; //移动文字 var no = g_nodes[realNodeId]; var node = no.node[0], txt = no.node[1], p = no.node[2]; var x = node.attr("x"), y = node.attr("y"); switch (no.nodeType) { case "beginNode": case "endNode": p.attr({ cx: x + g_sw / 2, cy: y + g_sh / 2 }); break; case "condition": p.remove(); p = _crtCondition(x, y, e.data.r); no.node[2] = p; break; case "synchro": p.remove(); p = _crtSynchro(x, y, e.data.r); no.node[2] = p; case "segment": break; } txt.attr({ x: x + g_sw / 2, y: y + g_sh / 2 }); //移动线条 _moveRltLine(realNodeId, no.il, no.ol, x, y); } function _moveRltLine(nodeId, il, ol, x, y){ var oL = {}, line, bNode; //移动输出直线 for (var i = 0, ii = ol.length; i < ii; i++) { oL = g_lines[ol[i]]; //删除原有的线 oL.line.remove(); g_lsNodeId = nodeId; g_lsp.x = x + g_sw / 2; g_lsp.y = y + g_sh / 2; var xy = _getLineEndXY(oL.eNodeId); line = r.ai.lineWithArrow(g_lsp.x, g_lsp.y, xy.x, xy.y).attr({ stroke: "#f88817","stroke-width":2 }); oL.line = line; g_lsNodeId = 0; } //移动输入直线 for (var i = 0, ii = il.length; i < ii; i++) { oL = g_lines[il[i]]; //删除原有的线 oL.line.remove(); g_lsNodeId = oL.bNodeId; bNode = g_nodes[g_lsNodeId].node[0]; g_lsp.x = bNode.attr("x") + g_sw / 2; g_lsp.y = bNode.attr("y") + g_sh / 2; var xy = _getLineEndXY(nodeId); line = r.ai.lineWithArrow(g_lsp.x, g_lsp.y, xy.x, xy.y).attr({ stroke: "#f88817","stroke-width":2 }); oL.line = line; g_lsNodeId = 0; } } /* * 返回点一相对于点二的位置 关系 * */ function g_getPointsRlt(x1, y1, x2, y2, w, h){ var s = (y1 < y2 && x1 < x2) ? [0, 0] : ((y1 < y2 && x1 > x2 && x1 < x2 + w) ? [0.5, 0] : ((y1 < y2 && x1 > x2 + w) ? [1, 0] : ((y1 > y2 + h && x1 < x2) ? [0, 1] : ((y1 > y2 + h && x1 > x2 && x1 < x2 + w) ? [0.5, 1] : ((y1 > y2 + h && x1 > x2 + w) ? [1, 1] : ((x1 < x2) ? [0, 0.5] : [1, 0.5])))))); return s; } //获取路径的终点坐标 function _getLineEndXY(nodeId){ var xy = {}; xy.x = 0; xy.y = 0; var no = g_nodes[nodeId].node[0]; var cx = no.attr("cx"), cy = no.attr("cy"), cr = no.attr("r"), x = no.attr("x"), y = no.attr("y"); var lType = [], w = g_sw, h = g_sh; switch (g_nodes[nodeId].nodeType) { case "endNode": case "segment": case "condition": case "synchro": lType = g_getPointsRlt(g_lsp.x, g_lsp.y, x, y, g_sw, g_sh); break; } //调整结束点坐标 xy.x = x + lType[0] * w; xy.y = y + lType[1] * h; //调整开始节点坐标 var bsnX = g_lsp.x - w / 2; g_lsp.x = g_lsp.x + w / 2 - lType[0] * w; g_lsp.y = g_lsp.y + h / 2 - lType[1] * h; //调整开始点x g_lsp.x = (xy.x > bsnX && xy.x < bsnX + w) ? xy.x : g_lsp.x; //调整结束点y xy.y = (g_lsp.y > y && g_lsp.y < y + h) ? g_lsp.y : xy.y; return xy; }