修正版gooflow流程解决方案(源码分享+在线演示+U…

2018-06-22 06:14:52来源:未知 阅读 ()

新老客户大回馈,云服务器低至5折

一、功能简介

gooflow功能清单
1、自定义流程绘制
2、自定义属性添加
3、支持3种步骤类型
普通审批步骤
自动决策步骤
手动决策步骤

4、决策方式(支持js决策,sql语句决策)

5、审批人员参与方式,可以自定配置,系统目前自带(员工,部门,岗位,sql语句4种方式)
6、响应方式(支持多人审批通过,和单人审批通过)
7、事件执行方式(审批通过后可以触发配置事件)
8、消息提醒(支持实时提醒)
9、流程决策条件,自定义配置

10、版本控制

11、后台管理

二、流程审批功能
1、提交
2、退回
3、退回上一步
4、退回到开始节点
5、表单退回
6、审批不通过
7、附件上传
8、传阅、转办、撤回、查看流程图。

9、提供WebAPI接口

10、可配置审批参与者数据源

三、接口说明

作用

接口名称

参数

返回值

获取审批列表

GetApprovalList

1.(string)userdata

2.(int)page

3.(int)pagesize

4.(int)isOver

5.(string)

flowLevel

6.(string)where

Dynamic->JsonData

 

 

获取审批列表汇总信息

LoadApprovalListSummary

1.(string)userData

Dynamic->JsonData

 

初始化流程状态

InitFlowState

1.string userData

2.string flowNo

3.int appID

4.string formHtml

5.string formData

6.string formUrl

Dynamic->

status = ?

flowID = ?

获取当前步骤信息

GetCurrentStepInfo

1.string userData

2.int approvalID = 0

3.int? flowID = 0

4.int? appID = 0

Dynamic ->

{

status = ?

stepData =?

isBack = ?

isTurnRead =? isTurnRead=?

isTurnDo = ?

};

获取审批参与者

FindFlowApprovers

1.string userData

2.int flowID

3.int approvalID

4. int appID

5. string toNodeID = ""

Dynamic ->{

status = ?

data = ?

}

获取审批意见

GetApprovalOpinions

  1. int flowID
  2. int appID

Dynamic ->

{

data = ?

}

获取下个节点(所有)

GetNextFlowNodes

1.int flowID

2.int appID

3.int approvalID

Dynamic ->

{

status = ?

 data = ?

 }

获取决策方案

GetFlowDecisionModes

  1. int flowID
  2. int appID
  3. int approvalID

Dynamic ->

{

status = ?

 data = ?

 }

流转主要功能接口

FlowAction

1.string userData

2.string actionType

3.int stateID

4.int approvalID

5.int appID;

6.bool isPass

7.string opinion

8.string formName

9.string participantJson                10.string participantValue                11.int level                12.int turnToDoID = 0

Dynamic ->

{

status = ?

 data = ?

 }

获取待阅

列表数据

GetTurnToReadPageList

1.string userData

2.int page                3.int pagesize

4.int isRead

5.string where

Dynamic->JsonData

查找传阅参与者

FindFlowTurnToReaders

1.string userData,

2.int flowID,

3.string nodeID

Dynamic ->

{

status = ?,

data = ?

 }

浏览传阅表单

ReadApprovalForm

1.tring userData

2.int approvalID

Dynamic ->

{

status = ?

}

获取传阅数据

GetTurnToReadList

int stateID

Dynamic->JsonData

获取代办参与者数据

FindFlowTurnToApprovers

  1. string userData
  2.  int flowID
  3.  string nodeID

Dynamic ->

{

status = ?,

data=?

 }

获取代办数据

GetTurnToDoPageList

1.string userData

2.int pag                3.int pagesize                 4.int isOver                 5.string where

Dynamic->JsonData

获取流程图流转标记

GetFlowElementMarked

1.int flowID

2. int appID

Dynamic ->

{

status = ?,

data=?

 }

 

 

四、流转界面

   1 function currentDate() {
   2     //日期
   3     var date = new Date();
   4     var y = date.getYear();
   5     var m = date.getMonth() + 1;
   6     var d = date.getDate();
   7     var hh = date.getHours();
   8     var mm = date.getMinutes();
   9     var ss = date.getMilliseconds();
  10     return y + (m < 10 ? ('0' + m) : m) + (d < 10 ? ('0' + d) : d) + (hh < 10 ? ('0' + hh) : hh) + (mm < 10 ? ('0' + mm) : mm) + (ss < 100 ? ('00' + ss) : ss)
  11 }
  12 //定义一个区域图类:
  13 function GooFlow(bgDiv, property) {
  14     if (navigator.userAgent.indexOf("MSIE 8.0") > 0 || navigator.userAgent.indexOf("MSIE 7.0") > 0 || navigator.userAgent.indexOf("MSIE 6.0") > 0)
  15         GooFlow.prototype.useSVG = "";
  16     else GooFlow.prototype.useSVG = "1";
  17     //初始化区域图的对象
  18     this.$id = bgDiv.attr("id");
  19     this.$bgDiv = bgDiv;//最父框架的DIV
  20     this.$bgDiv.addClass("GooFlow");
  21     if (GooFlow.prototype.color.font) {
  22         this.$bgDiv.css("color", GooFlow.prototype.color.font);
  23     }
  24     var width = (property.width || 800) - 2;
  25     var height = (property.height || 500) - 2;
  26     this.$bgDiv.css({ width: width + "px", height: height + "px" });
  27     this.$tool = null;//左侧工具栏对象
  28     this.$head = null;//顶部标签及工具栏按钮
  29     this.$title = "newFlow_1";//流程图的名称
  30     this.$nodeRemark = {};//每一种结点或按钮的说明文字,JSON格式,key为类名,value为用户自定义文字说明
  31     this.$nowType = "cursor";//当前要绘制的对象类型
  32     this.$lineData = {};
  33     this.$lineCount = 0;
  34     this.$nodeData = {};
  35     this.$nodeCount = 0;
  36     this.$areaData = {};
  37     this.$areaCount = 0;
  38     this.$lineDom = {};
  39     this.$nodeDom = {};
  40     this.$areaDom = {};
  41     this.$max = property.initNum || 1;//计算默认ID值的起始SEQUENCE
  42     this.$focus = "";//当前被选定的结点/转换线ID,如果没选中或者工作区被清空,则为""
  43     this.$cursor = "default";//鼠标指针在工作区内的样式
  44     this.$editable = false;//工作区是否可编辑
  45     this.$deletedItem = {};//在流程图的编辑操作中被删除掉的元素ID集合,元素ID为KEY,元素类型(node,line.area)为VALUE
  46     var headHeight = 0;
  47     var tmp = "";
  48     if (property.haveHead) {
  49         tmp = "<div class='GooFlow_head' " + (GooFlow.prototype.color.main ? "style='border-bottom-color:" + GooFlow.prototype.color.main + "'" : "")
  50         + ">";
  51         if (property.headLabel) {
  52             tmp += "<label title='" + (property.initLabelText || "newFlow_1") + "' "
  53               + (GooFlow.prototype.color.main ? "style='background:" + GooFlow.prototype.color.main + "'" : "") + ">" + (property.initLabelText || "newFlow_1") + "</label>";
  54         }
  55         for (var x = 0; x < property.headBtns.length; ++x) {
  56             tmp += "<a href='javascript:void(0)' class='GooFlow_head_btn'><i class='ico_" + property.headBtns[x] + "'></i></a>"
  57         }
  58         tmp += "</div>";
  59         this.$head = $(tmp);
  60         this.$bgDiv.append(this.$head);
  61         headHeight = 24;
  62         //以下是当工具栏按钮被点击时触发的事件自定义(虚函数),格式为function(),因为可直接用THIS操作对象本身,不用传参;用户可自行重定义:
  63         this.onBtnNewClick = null;//新建流程图按钮被点中
  64         this.onBtnOpenClick = null;//打开流程图按钮定义
  65         this.onBtnSaveClick = null;//保存流程图按钮定义
  66         this.onFreshClick = null;//重载流程图按钮定义
  67         this.onCloseClick = null;
  68         if (property.headBtns)
  69             this.$head.on("click", { inthis: this }, function (e) {
  70                 if (!e) e = window.event;
  71                 var tar = e.target;
  72                 if (tar.tagName == "DIV" || tar.tagName == "SPAN") return;
  73                 else if (tar.tagName == "a") tar = tar.childNode[0];
  74                 var This = e.data.inthis;
  75                 //定义顶部操作栏按钮的事件
  76                 switch ($(tar).attr("class")) {
  77                     case "ico_new": if (This.onBtnNewClick != null) This.onBtnNewClick(); break;
  78                     case "ico_open": if (This.onBtnOpenClick != null) This.onBtnOpenClick(); break;
  79                     case "ico_save": if (This.onBtnSaveClick != null) This.onBtnSaveClick(); break;
  80                     case "ico_undo": This.undo(); break;
  81                     case "ico_redo": This.redo(); break;
  82                     case "ico_reload": if (This.onFreshClick != null) This.onFreshClick(); break;
  83                     case "ico_close": if (This.onCloseClick != null) This.onCloseClick(); break;
  84                 }
  85             });
  86     }
  87     var toolWidth = 0;
  88     if (property.haveTool) {
  89         this.$bgDiv.append("<div class='GooFlow_tool'" + (property.haveHead ? "" : " style='margin-top:3px'") + "><div style='height:" + (height - headHeight - (property.haveHead ? 7 : 10)) + "px' class='GooFlow_tool_div'></div></div>");
  90         this.$tool = this.$bgDiv.find(".GooFlow_tool div");
  91         //未加代码:加入绘图工具按钮
  92         this.$tool.append(
  93       "<a href='javascript:void(0)' type='cursor' class='GooFlow_tool_btndown' id='" + this.$id + "_btn_cursor'><i class='ico_cursor'/></a>"
  94      //+ "<a href='javascript:void(0)' type='mutiselect' class='GooFlow_tool_btn' id='" + this.$id + "_btn_mutiselect'><i class='ico_mutiselect'/></a>"
  95      + "<a href='javascript:void(0)' type='direct' class='GooFlow_tool_btn' id='" + this.$id + "_btn_direct'><i class='ico_direct'/></a>"
  96     );
  97         if (property.toolBtns && property.toolBtns.length > 0) {
  98             tmp = "<span/>";
  99             for (var i = 0; i < property.toolBtns.length; ++i) {
 100                 tmp += "<a href='javascript:void(0)' type='" + property.toolBtns[i] + "' id='" + this.$id + "_btn_" + property.toolBtns[i].split(" ")[0] + "' class='GooFlow_tool_btn'><i class='ico_" + property.toolBtns[i] + "'/></a>";//加入自定义按钮
 101             }
 102             this.$tool.append(tmp);
 103         }
 104         //加入区域划分框工具开关按钮
 105         if (property.haveGroup)
 106             this.$tool.append("<span/><a href='javascript:void(0)' type='group' class='GooFlow_tool_btn' id='" + this.$id + "_btn_group'><i class='ico_group'/></a>");
 107         toolWidth = 35;
 108         this.$nowType = "cursor";
 109         //绑定各个按钮的点击事件
 110         this.$tool.on("click", { inthis: this }, function (e) {
 111             if (!e) e = window.event;
 112             var tar;
 113             switch (e.target.tagName) {
 114                 case "SPAN": return false;
 115                 case "DIV": return false;
 116                 case "I": tar = e.target.parentNode; break;
 117                 case "A": tar = e.target;
 118             };
 119             var type = $(tar).attr("type");
 120             e.data.inthis.switchToolBtn(type);
 121             return false;
 122         });
 123         this.$editable = true;//只有具有工具栏时可编辑
 124     }
 125     width = width - toolWidth - 9;
 126     height = height - headHeight - (property.haveHead ? 5 : 8);
 127     this.$bgDiv.append("<div class='GooFlow_work' style='width:" + (width) + "px;height:" + (height) + "px;" + (property.haveHead ? "" : "margin-top:3px") + "'></div>");
 128     this.$workArea = $("<div class='GooFlow_work_inner' style='width:" + width * 3 + "px;height:" + height * 3 + "px'></div>")
 129         .attr({ "unselectable": "on", "onselectstart": 'return false', "onselect": 'document.selection.empty()' });
 130     this.$bgDiv.children(".GooFlow_work").append(this.$workArea);
 131     this.$draw = null;//画矢量线条的容器
 132     this.initDraw("draw_" + this.$id, width, height);
 133     this.$group = null;
 134     if (property.haveGroup)
 135         this.initGroup(width, height);
 136     if (this.$editable) {
 137         this.$workArea.on("click", { inthis: this }, function (e) {
 138             if (!e) e = window.event;
 139             var This = e.data.inthis;
 140             if (!This.$editable) return;
 141             var type = This.$nowType;
 142             if (type == "cursor") {
 143                 var t = $(e.target);
 144                 var n = t.prop("tagName");
 145                 //alert(n);
 146                 if (n == "svg" || (n == "DIV" && t.prop("class").indexOf("GooFlow_work") > -1) || n == "LABEL") {
 147                     if (This.$lineOper.data("tid")) {
 148                         This.focusItem(This.$lineOper.data("tid"), false);
 149                         //This.$mpFrom.removeData("p");
 150                     }
 151                     else { This.blurItem(); }
 152                 }
 153                 return;
 154             }
 155             else if (type == "direct" || type == "group") return;
 156             var X, Y;
 157             var ev = mousePosition(e), t = getElCoordinate(this);
 158             X = ev.x - t.left + this.parentNode.scrollLeft - 1;
 159             Y = ev.y - t.top + this.parentNode.scrollTop - 1;
 160 
 161             //2015-01-13 TXF
 162             //This.addNode(This.$id + "_node_" + This.$max, { name: "node_" + This.$max, left: X, top: Y, type: This.$nowType });
 163             //This.$max++;
 164             This.addNode(This.$id + "_node_" + currentDate(), { name: "node_" + currentDate(), left: X, top: Y, type: e.data.inthis.$nowType, width: 135, height: 50 });
 165         });
 166         //划线或改线时用的绑定
 167         this.$workArea.mousemove({ inthis: this }, function (e) {
 168             if (e.data.inthis.$nowType != "direct" && !e.data.inthis.$mpTo.data("p")) return;
 169             var lineStart = $(this).data("lineStart");
 170             var lineEnd = $(this).data("lineEnd");
 171             if (!lineStart && !lineEnd) return;
 172 
 173             var ev = mousePosition(e), t = getElCoordinate(this);
 174             var X, Y;
 175             X = ev.x - t.left + this.parentNode.scrollLeft;
 176             Y = ev.y - t.top + this.parentNode.scrollTop;
 177             var line = document.getElementById("GooFlow_tmp_line");
 178             if (lineStart) {
 179                 if (GooFlow.prototype.useSVG != "") {
 180                     line.childNodes[0].setAttribute("d", "M " + lineStart.x + " " + lineStart.y + " L " + X + " " + Y);
 181                     line.childNodes[1].setAttribute("d", "M " + lineStart.x + " " + lineStart.y + " L " + X + " " + Y);
 182                     if (line.childNodes[1].getAttribute("marker-end") == "url(\"#arrow2\")")
 183                         line.childNodes[1].setAttribute("marker-end", "url(#arrow3)");
 184                     else line.childNodes[1].setAttribute("marker-end", "url(#arrow2)");
 185                 }
 186                 else line.points.value = lineStart.x + "," + lineStart.y + " " + X + "," + Y;
 187             } else if (lineEnd) {
 188                 if (GooFlow.prototype.useSVG != "") {
 189                     line.childNodes[0].setAttribute("d", "M " + X + " " + Y + " L " + lineEnd.x + " " + lineEnd.y);
 190                     line.childNodes[1].setAttribute("d", "M " + X + " " + Y + " L " + lineEnd.x + " " + lineEnd.y);
 191                     if (line.childNodes[1].getAttribute("marker-end") == "url(\"#arrow2\")")
 192                         line.childNodes[1].setAttribute("marker-end", "url(#arrow3)");
 193                     else line.childNodes[1].setAttribute("marker-end", "url(#arrow2)");
 194                 }
 195                 else line.points.value = X + "," + Y + " " + lineEnd.x + "," + lineEnd.y;
 196             }
 197         });
 198         this.$workArea.mouseup({ inthis: this }, function (e) {
 199             var This = e.data.inthis;
 200             if (This.$nowType != "direct" && !This.$mpTo.data("p")) return;
 201             var tmp = document.getElementById("GooFlow_tmp_line");
 202             if (tmp) {
 203                 $(this).css("cursor", "auto").removeData("lineStart").removeData("lineEnd");
 204                 This.$mpTo.hide().removeData("p");
 205                 This.$mpFrom.hide().removeData("p");
 206                 This.$draw.removeChild(tmp);
 207                 This.focusItem(This.$focus, false);
 208             } else {
 209                 This.$lineOper.removeData("tid");
 210             }
 211         });
 212         //为了结点而增加的一些集体delegate绑定
 213         this.initWorkForNode();
 214         //对结点进行移动或者RESIZE时用来显示的遮罩层
 215         this.$ghost = $("<div class='rs_ghost'></div>").attr({ "unselectable": "on", "onselectstart": 'return false', "onselect": 'document.selection.empty()' });
 216         this.$bgDiv.append(this.$ghost);
 217         this.$textArea = $("<textarea></textarea>");
 218         this.$bgDiv.append(this.$textArea);
 219         this.$lineMove = $("<div class='GooFlow_line_move' style='display:none'></div>");//操作折线时的移动框
 220         this.$workArea.append(this.$lineMove);
 221         this.$lineMove.on("mousedown", { inthis: this }, function (e) {
 222             if (e.button == 2) return false;
 223             var lm = $(this);
 224             lm.css({ "background-color": GooFlow.prototype.color.font || "#333" });
 225             var This = e.data.inthis;
 226             var ev = mousePosition(e), t = getElCoordinate(This.$workArea[0]);
 227             var X, Y;
 228             X = ev.x - t.left + This.$workArea[0].parentNode.scrollLeft;
 229             Y = ev.y - t.top + This.$workArea[0].parentNode.scrollTop;
 230             var p = This.$lineMove.position();
 231             var vX = X - p.left, vY = Y - p.top;
 232             var isMove = false;
 233             document.onmousemove = function (e) {
 234                 if (!e) e = window.event;
 235                 var ev = mousePosition(e);
 236                 var ps = This.$lineMove.position();
 237                 X = ev.x - t.left + This.$workArea[0].parentNode.scrollLeft;
 238                 Y = ev.y - t.top + This.$workArea[0].parentNode.scrollTop;
 239                 if (This.$lineMove.data("type") == "lr") {
 240                     X = X - vX;
 241                     if (X < 0) X = 0;
 242                     else if (X > This.$workArea.width())
 243                         X = This.$workArea.width();
 244                     This.$lineMove.css({ left: X + "px" });
 245                 }
 246                 else if (This.$lineMove.data("type") == "tb") {
 247                     Y = Y - vY;
 248                     if (Y < 0) Y = 0;
 249                     else if (Y > This.$workArea.height())
 250                         Y = This.$workArea.height();
 251                     This.$lineMove.css({ top: Y + "px" });
 252                 }
 253                 isMove = true;
 254             }
 255             document.onmouseup = function (e) {
 256                 if (isMove) {
 257                     var p = This.$lineMove.position();
 258                     if (This.$lineMove.data("type") == "lr")
 259                         This.setLineM(This.$lineMove.data("tid"), p.left + 3);
 260                     else if (This.$lineMove.data("type") == "tb")
 261                         This.setLineM(This.$lineMove.data("tid"), p.top + 3);
 262                 }
 263                 This.$lineMove.css({ "background-color": "transparent" });
 264                 if (This.$focus == This.$lineMove.data("tid")) {
 265                     This.focusItem(This.$lineMove.data("tid"));
 266                 }
 267                 document.onmousemove = null;
 268                 document.onmouseup = null;
 269             }
 270         });
 271         //选定一条转换线后出现的浮动操作栏,有改变线的样式和删除线等按钮。
 272         this.$lineOper = $("<div class='GooFlow_line_oper' style='display:none'><i class='b_l1'></i><i class='b_l2'></i><i class='b_l3'></i><i class='b_x'></i></div>");//选定线时显示的操作框
 273         this.$workArea.parent().append(this.$lineOper);
 274         this.$lineOper.on("click", { inthis: this }, function (e) {
 275             if (!e) e = window.event;
 276             if (e.target.tagName != "I") return;
 277             var This = e.data.inthis;
 278             var id = $(this).data("tid");
 279             switch ($(e.target).attr("class")) {
 280                 case "b_x":
 281                     This.delLine(id);
 282                     this.style.display = "none"; break;
 283                 case "b_l1":
 284                     This.setLineType(id, "lr"); break;
 285                 case "b_l2":
 286                     This.setLineType(id, "tb"); break;
 287                 case "b_l3":
 288                     This.setLineType(id, "sl"); break;
 289             }
 290         });
 291         //新增移动线两个端点至新的结点功能移动功能,这里要提供移动用的DOM
 292         this.$mpFrom = $("<div class='GooFlow_line_mp' style='display:none'></div>");
 293         this.$mpTo = $("<div class='GooFlow_line_mp' style='display:none'></div>");
 294         this.$workArea.append(this.$mpFrom).append(this.$mpTo);
 295         this.initLinePointsChg();
 296 
 297         //下面绑定当结点/线/分组块的一些操作事件,这些事件可直接通过this访问对象本身
 298         //当操作某个单元(结点/线/分组块)被添加时,触发的方法,返回FALSE可阻止添加事件的发生
 299         //格式function(id,type,json):id是单元的唯一标识ID,type是单元的种类,有"node","line","area"三种取值,json即addNode,addLine或addArea方法的第二个传参json.
 300         this.onItemAdd = null;
 301         //当操作某个单元(结点/线/分组块)被删除时,触发的方法,返回FALSE可阻止删除事件的发生
 302         //格式function(id,type):id是单元的唯一标识ID,type是单元的种类,有"node","line","area"三种取值
 303         this.onItemDel = null;
 304         //当操作某个单元(结点/分组块)被移动时,触发的方法,返回FALSE可阻止移动事件的发生
 305         //格式function(id,type,left,top):id是单元的唯一标识ID,type是单元的种类,有"node","area"两种取值,线line不支持移动,left是新的左边距坐标,top是新的顶边距坐标
 306         this.onItemMove = null;
 307         //当操作某个单元(结点/线/分组块)被重命名时,触发的方法,返回FALSE可阻止重命名事件的发生
 308         //格式function(id,name,type):id是单元的唯一标识ID,type是单元的种类,有"node","line","area"三种取值,name是新的名称
 309         this.onItemRename = null;
 310         //当操作某个单元(结点/线)被由不选中变成选中时,触发的方法,返回FALSE可阻止选中事件的发生
 311         //格式function(id,type):id是单元的唯一标识ID,type是单元的种类,有"node","line"两种取值,"area"不支持被选中
 312         this.onItemFocus = null;
 313         //当操作某个单元(结点/线)被由选中变成不选中时,触发的方法,返回FALSE可阻止取消选中事件的发生
 314         //格式function(id,type):id是单元的唯一标识ID,type是单元的种类,有"node","line"两种取值,"area"不支持被取消选中
 315         this.onItemBlur = null;
 316         //当操作某个单元(结点/分组块)被重定义大小或造型时,触发的方法,返回FALSE可阻止重定大小/造型事件的发生
 317         //格式function(id,type,width,height):id是单元的唯一标识ID,type是单元的种类,有"node","line","area"三种取值;width是新的宽度,height是新的高度
 318         this.onItemResize = null;
 319         //当移动某条折线中段的位置,触发的方法,返回FALSE可阻止重定大小/造型事件的发生
 320         //格式function(id,M):id是单元的唯一标识ID,M是中段的新X(或Y)的坐标
 321         this.onLineMove = null;
 322         //当变换某条连接线的类型,触发的方法,返回FALSE可阻止重定大小/造型事件的发生
 323         //格式function(id,type):id是单元的唯一标识ID,type是连接线的新类型,"sl":直线,"lr":中段可左右移动的折线,"tb":中段可上下移动的折线
 324         this.onLineSetType = null;
 325         //当变换某条连接线的端点变更连接的结点时,触发的方法,返回FALSE可阻止重定大小/造型事件的发生
 326         //格式function(id,newStart,newEnd):id是连线单元的唯一标识ID,newStart,newEnd分别是起始结点的ID和到达结点的ID
 327         this.onLinePointMove = null;
 328         //当用重色标注某个结点/转换线时触发的方法,返回FALSE可阻止重定大小/造型事件的发生
 329         //格式function(id,type,mark):id是单元的唯一标识ID,type是单元类型("node"结点,"line"转换线),mark为布尔值,表示是要标注TRUE还是取消标注FALSE
 330         this.onItemMark = null;
 331         //分组区域颜色改变事件
 332         this.onAreaSetColor = null;
 333 
 334         if (property.useOperStack && this.$editable) {//如果要使用堆栈记录操作并提供“撤销/重做”的功能,只在编辑状态下有效
 335             this.$undoStack = [];
 336             this.$redoStack = [];
 337             this.$isUndo = 0;
 338             ///////////////以下是构造撤销操作/重做操作的方法
 339             //为了节省浏览器内存空间,undo/redo中的操作缓存栈,最多只可放40步操作;超过40步时,将自动删掉最旧的一个缓存
 340             this.pushOper = function (funcName, paras) {
 341                 var len = this.$undoStack.length;
 342                 if (this.$isUndo == 1) {
 343                     this.$redoStack.push([funcName, paras]);
 344                     this.$isUndo = false;
 345                     if (this.$redoStack.length > 40) this.$redoStack.shift();
 346                 } else {
 347                     this.$undoStack.push([funcName, paras]);
 348                     if (this.$undoStack.length > 40) this.$undoStack.shift();
 349                     if (this.$isUndo == 0) {
 350                         this.$redoStack.splice(0, this.$redoStack.length);
 351                     }
 352                     this.$isUndo = 0;
 353                 }
 354             };
 355             //将外部的方法加入到GooFlow对象的事务操作堆栈中,在过后的undo/redo操作中可以进行控制,一般用于对流程图以外的附加信息进行编辑的事务撤销/重做控制;
 356             //传参func为要执行方法对象,jsonPara为外部方法仅有的一个面向字面的JSON传参,由JSON对象带入所有要传的信息;
 357             //提示:为了让外部方法能够被UNDO/REDO,需要在编写这些外部方法实现时,加入对该方法执行后效果回退的另一个执行方法的pushExternalOper
 358             this.pushExternalOper = function (func, jsonPara) {
 359                 this.pushOper("externalFunc", [func, jsonPara]);
 360             };
 361             //撤销上一步操作
 362             this.undo = function () {
 363                 if (this.$undoStack.length == 0) return;
 364                 this.blurItem();
 365                 var tmp = this.$undoStack.pop();
 366                 this.$isUndo = 1;
 367                 if (tmp[0] == "externalFunc") {
 368                     tmp[1][0](tmp[1][1]);
 369                 }
 370                 else {
 371                     //传参的数量,最多支持6个.
 372                     switch (tmp[1].length) {
 373                         case 0: this[tmp[0]](); break;
 374                         case 1: this[tmp[0]](tmp[1][0]); break;
 375                         case 2: this[tmp[0]](tmp[1][0], tmp[1][1]); break;
 376                         case 3: this[tmp[0]](tmp[1][0], tmp[1][1], tmp[1][2]); break;
 377                         case 4: this[tmp[0]](tmp[1][0], tmp[1][1], tmp[1][2], tmp[1][3]); break;
 378                         case 5: this[tmp[0]](tmp[1][0], tmp[1][1], tmp[1][2], tmp[1][3], tmp[1][4]); break;
 379                         case 6: this[tmp[0]](tmp[1][0], tmp[1][1], tmp[1][2], tmp[1][3], tmp[1][4], tmp[1][5]); break;
 380                     }
 381                 }
 382             };
 383             //重做最近一次被撤销的操作
 384             this.redo = function () {
 385                 if (this.$redoStack.length == 0) return;
 386                 this.blurItem();
 387                 var tmp = this.$redoStack.pop();
 388                 this.$isUndo = 2;
 389                 if (tmp[0] == "externalFunc") {
 390                     tmp[1][0](tmp[1][1]);
 391                 }
 392                 else {
 393                     //传参的数量,最多支持6个.
 394                     switch (tmp[1].length) {
 395                         case 0: this[tmp[0]](); break;
 396                         case 1: this[tmp[0]](tmp[1][0]); break;
 397                         case 2: this[tmp[0]](tmp[1][0], tmp[1][1]); break;
 398                         case 3: this[tmp[0]](tmp[1][0], tmp[1][1], tmp[1][2]); break;
 399                         case 4: this[tmp[0]](tmp[1][0], tmp[1][1], tmp[1][2], tmp[1][3]); break;
 400                         case 5: this[tmp[0]](tmp[1][0], tmp[1][1], tmp[1][2], tmp[1][3], tmp[1][4]); break;
 401                         case 6: this[tmp[0]](tmp[1][0], tmp[1][1], tmp[1][2], tmp[1][3], tmp[1][4], tmp[1][5]); break;
 402                     }
 403                 }
 404             };
 405         }
 406         $(document).keydown({ inthis: this }, function (e) {
 407             //绑定键盘操作
 408             var This = e.data.inthis;
 409             if (This.$focus == "") return;
 410             switch (e.keyCode) {
 411                 case 46://删除
 412                     This.delNode(This.$focus, true);
 413                     This.delLine(This.$focus);
 414                     break;
 415             }
 416         });
 417     }
 418 }
 419 GooFlow.prototype = {
 420     useSVG: "",
 421     getSvgMarker: function (id, color) {
 422         var m = document.createElementNS("http://www.w3.org/2000/svg", "marker");
 423         m.setAttribute("id", id);
 424         m.setAttribute("viewBox", "0 0 6 6");
 425         m.setAttribute("refX", 5);
 426         m.setAttribute("refY", 3);
 427         m.setAttribute("markerUnits", "strokeWidth");
 428         m.setAttribute("markerWidth", 6);
 429         m.setAttribute("markerHeight", 6);
 430         m.setAttribute("orient", "auto");
 431         var path = document.createElementNS("http://www.w3.org/2000/svg", "path");
 432         path.setAttribute("d", "M 0 0 L 6 3 L 0 6 z");
 433         path.setAttribute("fill", color);
 434         path.setAttribute("stroke-width", 0);
 435         m.appendChild(path);
 436         return m;
 437     },
 438     initDraw: function (id, width, height) {
 439         var elem;
 440         if (GooFlow.prototype.useSVG != "") {
 441             this.$draw = document.createElementNS("http://www.w3.org/2000/svg", "svg");//可创建带有指定命名空间的元素节点
 442             this.$workArea.prepend(this.$draw);
 443             var defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
 444             this.$draw.appendChild(defs);
 445             defs.appendChild(GooFlow.prototype.getSvgMarker("arrow1", GooFlow.prototype.color.line || "#3892D3"));
 446             defs.appendChild(GooFlow.prototype.getSvgMarker("arrow2", GooFlow.prototype.color.mark || "#ff3300"));
 447             defs.appendChild(GooFlow.prototype.getSvgMarker("arrow3", GooFlow.prototype.color.mark || "#ff3300"));
 448         }
 449         else {
 450             this.$draw = document.createElement("v:group");
 451             this.$draw.coordsize = width * 3 + "," + height * 3;
 452             this.$workArea.prepend("<div class='GooFlow_work_vml' style='position:relative;width:" + width * 3 + "px;height:" + height * 3 + "px'></div>");
 453             this.$workArea.children("div")[0].insertBefore(this.$draw, null);
 454         }
 455         this.$draw.id = id;
 456         this.$draw.style.width = width * 3 + "px";
 457         this.$draw.style.height = +height * 3 + "px";
 458         //绑定连线的点击选中以及双击编辑事件
 459         var tmpClk = null;
 460         if (GooFlow.prototype.useSVG != "") tmpClk = "g";
 461         else tmpClk = "PolyLine";
 462         if (!this.$editable) return;
 463 
 464         $(this.$draw).delegate(tmpClk, "click", { inthis: this }, function (e) {
 465             e.data.inthis.focusItem(this.id, true);
 466         });
 467         $(this.$draw).delegate(tmpClk, "dblclick", { inthis: this }, function (e) {
 468             var oldTxt, x, y, from, to;
 469             var This = e.data.inthis;
 470             if (GooFlow.prototype.useSVG != "") {
 471                 oldTxt = this.childNodes[2].textContent;
 472                 from = this.getAttribute("from").split(",");
 473                 to = this.getAttribute("to").split(",");
 474             } else {
 475                 oldTxt = this.childNodes[1].innerHTML;
 476                 var n = this.getAttribute("fromTo").split(",");
 477                 from = [n[0], n[1]];
 478                 to = [n[2], n[3]];
 479             }
 480             if (This.$lineData[this.id].type == "lr") {
 481                 from[0] = This.$lineData[this.id].M;
 482                 to[0] = from[0];
 483             }
 484             else if (This.$lineData[this.id].type == "tb") {
 485                 from[1] = This.$lineData[this.id].M;
 486                 to[1] = from[1];
 487             }
 488             x = (parseInt(from[0], 10) + parseInt(to[0], 10)) / 2 - 60;
 489             y = (parseInt(from[1], 10) + parseInt(to[1], 10)) / 2 - 12;
 490             var t = getElCoordinate(This.$workArea[0]);
 491             This.$textArea.val(oldTxt).css({
 492                 display: "block", width: 120, height: 14,
 493                 left: t.left + x - This.$workArea[0].parentNode.scrollLeft,
 494                 top: t.top + y - This.$workArea[0].parentNode.scrollTop
 495             }).data("id", This.$focus).focus();
 496             This.$workArea.parent().one("mousedown", function (e) {
 497                 if (e.button == 2) return false;
 498                 This.setName(This.$textArea.data("id"), This.$textArea.val(), "line");
 499                 This.$textArea.val("").removeData("id").hide();
 500             });
 501         });
 502     },
 503     initGroup: function (width, height) {
 504         this.$group = $("<div class='GooFlow_work_group' style='width:" + width * 3 + "px;height:" + height * 3 + "px'></div>");//存放背景区域的容器
 505         this.$workArea.prepend(this.$group);
 506         if (!this.$editable) return;
 507         //区域划分框操作区的事件绑定
 508         this.$group.on("mousedown", { inthis: this }, function (e) {//绑定RESIZE功能以及移动功能
 509             if (e.button == 2) return false;
 510             var This = e.data.inthis;
 511             if (This.$nowType != "group") return;
 512             if (This.$textArea.css("display") == "block") {
 513                 This.setName(This.$textArea.data("id"), This.$textArea.val(), "area");
 514                 This.$textArea.val("").removeData("id").hide();
 515                 return false;
 516             };
 517             if (!e) e = window.event;
 518             var cursor = $(e.target).css("cursor");
 519             var id = e.target.parentNode;
 520             switch (cursor) {
 521                 case "nw-resize": id = id.parentNode; break;
 522                 case "w-resize": id = id.parentNode; break;
 523                 case "n-resize": id = id.parentNode; break;
 524                 case "move": break;
 525                 default: return;
 526             }
 527             id = id.id;
 528             var hack = 1;
 529             if (navigator.userAgent.indexOf("8.0") != -1) hack = 0;
 530             var ev = mousePosition(e), t = getElCoordinate(This.$workArea[0]);
 531 
 532             var X, Y;
 533             X = ev.x - t.left + This.$workArea[0].parentNode.scrollLeft;
 534             Y = ev.y - t.top + This.$workArea[0].parentNode.scrollTop;
 535             if (cursor != "move") {
 536                 This.$ghost.css({
 537                     display: "block",
 538                     width: This.$areaData[id].width - 2 + "px", height: This.$areaData[id].height - 2 + "px",
 539                     top: This.$areaData[id].top + t.top - This.$workArea[0].parentNode.scrollTop + hack + "px",
 540                     left: This.$areaData[id].left + t.left - This.$workArea[0].parentNode.scrollLeft + hack + "px", cursor: cursor
 541                 });
 542                 var vX = (This.$areaData[id].left + This.$areaData[id].width) - X;
 543                 var vY = (This.$areaData[id].top + This.$areaData[id].height) - Y;
 544             }
 545             else {
 546                 var vX = X - This.$areaData[id].left;
 547                 var vY = Y - This.$areaData[id].top;
 548             }
 549             var isMove = false;
 550             This.$ghost.css("cursor", cursor);
 551             document.onmousemove = function (e) {
 552                 if (!e) e = window.event;
 553                 var ev = mousePosition(e);
 554                 if (cursor != "move") {
 555                     X = ev.x - t.left + This.$workArea[0].parentNode.scrollLeft - This.$areaData[id].left + vX;
 556                     Y = ev.y - t.top + This.$workArea[0].parentNode.scrollTop - This.$areaData[id].top + vY;
 557                     if (X < 200) X = 200;
 558                     if (Y < 100) Y = 100;
 559                     switch (cursor) {
 560                         case "nw-resize": This.$ghost.css({ width: X - 2 + "px", height: Y - 2 + "px" }); break;
 561                         case "w-resize": This.$ghost.css({ width: X - 2 + "px" }); break;
 562                         case "n-resize": This.$ghost.css({ height: Y - 2 + "px" }); break;
 563                     }
 564                 }
 565                 else {
 566                     if (This.$ghost.css("display") == "none") {
 567                         This.$ghost.css({
 568                             display: "block",
 569                             width: This.$areaData[id].width - 2 + "px", height: This.$areaData[id].height - 2 + "px",
 570                             top: This.$areaData[id].top + t.top - This.$workArea[0].parentNode.scrollTop + hack + "px",
 571                             left: This.$areaData[id].left + t.left - This.$workArea[0].parentNode.scrollLeft + hack + "px", cursor: cursor
 572                         });
 573                     }
 574                     X = ev.x - vX; Y = ev.y - vY;
 575                     if (X < t.left - This.$workArea[0].parentNode.scrollLeft)
 576                         X = t.left - This.$workArea[0].parentNode.scrollLeft;
 577                     else if (X + This.$workArea[0].parentNode.scrollLeft + This.$areaData[id].width > t.left + This.$workArea.width())
 578                         X = t.left + This.$workArea.width() - This.$workArea[0].parentNode.scrollLeft - This.$areaData[id].width;
 579                     if (Y < t.top - This.$workArea[0].parentNode.scrollTop)
 580                         Y = t.top - This.$workArea[0].parentNode.scrollTop;
 581                     else if (Y + This.$workArea[0].parentNode.scrollTop + This.$areaData[id].height > t.top + This.$workArea.height())
 582                         Y = t.top + This.$workArea.height() - This.$workArea[0].parentNode.scrollTop - This.$areaData[id].height;
 583                     This.$ghost.css({ left: X + hack + "px", top: Y + hack + "px" });
 584                 }
 585                 isMove = true;
 586             }
 587             document.onmouseup = function (e) {
 588                 This.$ghost.empty().hide();
 589                 document.onmousemove = null;
 590                 document.onmouseup = null;
 591                 if (!isMove) return;
 592                 if (cursor != "move")
 593                     This.resizeArea(id, This.$ghost.outerWidth(), This.$ghost.outerHeight());
 594                 else
 595                     This.moveArea(id, X + This.$workArea[0].parentNode.scrollLeft - t.left, Y + This.$workArea[0].parentNode.scrollTop - t.top);
 596                 return false;
 597             }
 598         });
 599         //绑定修改文字说明功能
 600         this.$group.on("dblclick", { inthis: this }, function (e) {
 601             var This = e.data.inthis;
 602             if (This.$nowType != "group") return;
 603             if (!e) e = window.event;
 604             if (e.target.tagName != "LABEL") return false;
 605             var oldTxt = e.target.innerHTML;
 606             var p = e.target.parentNode;
 607             var x = parseInt(p.style.left, 10) + 18, y = parseInt(p.style.top, 10) + 1;
 608             var t = getElCoordinate(This.$workArea[0]);
 609             This.$textArea.val(oldTxt).css({
 610                 display: "block", width: 100, height: 14,
 611                 left: t.left + x - This.$workArea[0].parentNode.scrollLeft,
 612                 top: t.top + y - This.$workArea[0].parentNode.scrollTop
 613             }).data("id", p.id).focus();
 614             This.$workArea.parent().one("mousedown", function (e) {
 615                 if (e.button == 2) return false;
 616                 if (This.$textArea.css("display") == "block") {
 617                     This.setName(This.$textArea.data("id"), This.$textArea.val(), "area");
 618                     This.$textArea.val("").removeData("id").hide();
 619                 }
 620             });
 621             return false;
 622         });
 623         //绑定点击事件
 624         this.$group.mouseup({ inthis: this }, function (e) {
 625 
 626             var This = e.data.inthis;
 627             if (This.$nowType != "group") return;
 628             if (!e) e = window.event;
 629             switch ($(e.target).attr("class")) {
 630                 case "rs_close": This.delArea(e.target.parentNode.parentNode.id); return false;//删除该分组区域
 631                 case "bg": return;
 632             }
 633             switch (e.target.tagName) {
 634                 case "LABEL": return false;
 635                 case "I"://绑定变色功能
 636                     var id = e.target.parentNode.id;
 637                     switch (This.$areaData[id].color) {
 638                         case "red": This.setAreaColor(id, "yellow"); break;
 639                         case "yellow": This.setAreaColor(id, "blue"); break;
 640                         case "blue": This.setAreaColor(id, "green"); break;
 641                         case "green": This.setAreaColor(id, "red"); break;
 642                     }
 643                     return false;
 644             }
 645             if (e.data.inthis.$ghost.css("display") == "none") {
 646                 var X, Y;
 647                 var ev = mousePosition(e), t = getElCoordinate(this);
 648                 X = ev.x - t.left + this.parentNode.parentNode.scrollLeft - 1;
 649                 Y = ev.y - t.top + this.parentNode.parentNode.scrollTop - 1;
 650                 var color = ["red", "yellow", "blue", "green"];
 651                 //TXF 2015-01-13
 652                 //e.data.inthis.addArea(e.data.inthis.$id + "_area_" + e.data.inthis.$max, { name: "area_" + e.data.inthis.$max, left: X, top: Y, color: color[e.data.inthis.$max % 4], width: 200, height: 100 });
 653                 //e.data.inthis.$max++;
 654                 e.data.inthis.addArea(e.data.inthis.$id + "_area_" + currentDate(), { name: "area_" + currentDate(), left: X, top: Y, color: color[e.data.inthis.$max % 4], width: 200, height: 100 });
 655                 return false;
 656             }
 657         });
 658     },
 659     //初始化用来改变连线的连接端点的两个小方块的操作事件
 660     initLinePointsChg: function () {
 661         this.$mpFrom.on("mousedown", { inthis: this }, function (e) {
 662             var This = e.data.inthis;
 663             This.switchToolBtn("cursor");
 664             var ps = This.$mpFrom.data("p").split(",");
 665             var pe = This.$mpTo.data("p").split(",");
 666             $(this).hide();
 667             This.$workArea.data("lineEnd", { "x": pe[0], "y": pe[1], "id": This.$lineData[This.$lineOper.data("tid")].to }).css("cursor", "crosshair");
 668             var line = GooFlow.prototype.drawLine("GooFlow_tmp_line", [ps[0], ps[1]], [pe[0], pe[1]], true, true);
 669             This.$draw.appendChild(line);
 670             return false;
 671         });
 672         this.$mpTo.on("mousedown", { inthis: this }, function (e) {
 673             var This = e.data.inthis;
 674             This.switchToolBtn("cursor");
 675             var ps = This.$mpFrom.data("p").split(",");
 676             var pe = This.$mpTo.data("p").split(",");
 677             $(this).hide();
 678             This.$workArea.data("lineStart", { "x": ps[0], "y": ps[1], "id": This.$lineData[This.$lineOper.data("tid")].from }).css("cursor", "crosshair");
 679             var line = GooFlow.prototype.drawLine("GooFlow_tmp_line", [ps[0], ps[1]], [pe[0], pe[1]], true, true);
 680             This.$draw.appendChild(line);
 681             return false;
 682         });
 683     },
 684     //每一种类型结点及其按钮的说明文字
 685     setNodeRemarks: function (remark) {
 686         if (this.$tool == null) return;
 687         this.$tool.children("a").each(function () {
 688             this.title = remark[$(this).attr("id").split("btn_")[1]];
 689         });
 690         this.$nodeRemark = remark;
 691     },
 692 
 693     //切换左边工具栏按钮,传参TYPE表示切换成哪种类型的按钮
 694     switchToolBtn: function (type) {
 695         if (!this.$tool) return;
 696         this.$tool.children("#" + this.$id + "_btn_" + this.$nowType.split(" ")[0]).attr("class", "GooFlow_tool_btn");
 697         if (this.$nowType == "group") {
 698             this.$workArea.prepend(this.$group);
 699             for (var key in this.$areaDom) this.$areaDom[key].addClass("lock").children("div:eq(1)").css("display", "none");
 700         }
 701         this.$nowType = type;
 702         this.$tool.children("#" + this.$id + "_btn_" + type.split(" ")[0]).attr("class", "GooFlow_tool_btndown");
 703         if (this.$nowType == "group") {
 704             this.blurItem();
 705             this.$workArea.append(this.$group);
 706             for (var key in this.$areaDom) this.$areaDom[key].removeClass("lock").children("div:eq(1)").css("display", "");
 707         } else if (this.$nowType == "direct") {
 708             this.blurItem();
 709         }
 710         if (this.$textArea.css("display") == "none") this.$textArea.removeData("id").val("").hide();
 711     },
 712     //增加一个流程结点,传参为一个JSON,有id,name,top,left,width,height,type(结点类型)等属性
 713     //2015-07-28 TXF
 714     addNode: function (id, json, disabled) {
 715         if (!disabled)
 716             if (this.onItemAdd != null && !this.onItemAdd(id, "node", json)) return;
 717         if (this.$undoStack && this.$editable) {
 718             this.pushOper("delNode", [id]);
 719         }
 720         var mark = json.marked ? " item_mark" : "";
 721         if (json.type != "start" && json.type != "end") {
 722             if (!json.width || json.width < 135) json.width = 135;
 723             if (!json.height || json.height < 50) json.height = 50;
 724             if (!json.top || json.top < 0) json.top = 0;
 725             if (!json.left || json.left < 0) json.left = 0;
 726             var hack = 0;
 727             if (navigator.userAgent.indexOf("8.0") != -1) hack = 2;
 728             this.$nodeDom[id] = $("<div class='GooFlow_item" + mark + "' id='" + id + "' style='top:" + json.top + "px;left:" + json.left + "px'><table cellspacing='1' style='width:" + (json.width - 2) + "px;height:" + (json.height - 2) + "px;'><tr><td class='ico'><i class='ico_" + json.type + "'></i></td><td>" + json.name + "</td></tr></table><div style='display:none'><div class='rs_bottom'></div><div class='rs_right'></div><div class='rs_rb'></div><div class='rs_close'></div></div></div>");
 729         }
 730         else {
 731             json.width = 24; json.height = 24;
 732             //2015-07-27 TXF
 733             if (json.type == "start")
 734                 json.name = "开始";
 735             else if (json.type == "end")
 736                 json.name = "结束";
 737             this.$nodeDom[id] = $("<div class='GooFlow_item item_round" + mark + "' id='" + id + "' style='top:" + json.top + "px;left:" + json.left + "px'><table cellspacing='0'><tr><td class='ico'><i class='ico_" + json.type + "'></i></td></tr></table><div  style='display:none'><div class='rs_close'></div></div><div class='span'>" + json.name + "</div></div>");
 738         }
 739         if (GooFlow.prototype.color.node) {
 740             if (json.type.indexOf(" mix") > -1) {
 741                 this.$nodeDom[id].css({ "background-color": GooFlow.prototype.color.mix, "border-color": GooFlow.prototype.color.mix });
 742             } else {
 743                 this.$nodeDom[id].css({ "background-color": GooFlow.prototype.color.node, "border-color": GooFlow.prototype.color.node });
 744             }
 745             if (mark && GooFlow.prototype.color.mark) {
 746                 this.$nodeDom[id].css({ "border-color": GooFlow.prototype.color.mark });
 747             }
 748         }
 749         if (json.type.indexOf(" mix") > -1) {
 750             this.$nodeDom[id].addClass("item_mix");
 751         }
 752 
 753         var ua = navigator.userAgent.toLowerCase();
 754         if (ua.indexOf('msie') != -1 && ua.indexOf('8.0') != -1)
 755             this.$nodeDom[id].css("filter", "progid:DXImageTransform.Microsoft.Shadow(color=#94AAC2,direction=135,strength=2)");
 756         this.$workArea.append(this.$nodeDom[id]);
 757         this.$nodeData[id] = json;
 758         ++this.$nodeCount;
 759         if (this.$editable) {
 760             this.$nodeData[id].alt = true;
 761             if (this.$deletedItem[id]) delete this.$deletedItem[id];//在回退删除操作时,去掉该元素的删除记录
 762         }
 763         //2017-07-15 Taffy添加
 764         this.focusItem(id, true);
 765     },
 766     initWorkForNode: function () {
 767         //绑定点击事件
 768         this.$workArea.delegate(".GooFlow_item", "click", { inthis: this }, function (e) {
 769             e.data.inthis.focusItem(this.id, true);
 770             $(this).removeClass("item_mark");
 771         });
 772         //绑定用鼠标移动事件
 773         this.$workArea.delegate(".ico", "mousedown", { inthis: this }, function (e) {
 774             if (!e) e = window.event;
 775             if (e.button == 2) return false;
 776             var This = e.data.inthis;
 777             if (This.$nowType == "direct") return;
 778             var Dom = $(this).parents(".GooFlow_item");
 779             var id = Dom.attr("id");
 780             This.focusItem(id, true);
 781             var hack = 1;
 782             if (navigator.userAgent.indexOf("8.0") != -1) hack = 0;
 783             var ev = mousePosition(e), t = getElCoordinate(This.$workArea[0]);
 784 
 785             Dom.children("table").clone().prependTo(This.$ghost);
 786             var X, Y;
 787             X = ev.x - t.left + This.$workArea[0].parentNode.scrollLeft;
 788             Y = ev.y - t.top + This.$workArea[0].parentNode.scrollTop;
 789             var vX = X - This.$nodeData[id].left, vY = Y - This.$nodeData[id].top;
 790             var isMove = false;
 791             document.onmousemove = function (e) {
 792                 if (!e) e = window.event;
 793                 var ev = mousePosition(e);
 794                 if (X == ev.x - vX && Y == ev.y - vY) return false;
 795                 X = ev.x - vX; Y = ev.y - vY;
 796 
 797                 if (isMove && This.$ghost.css("display") == "none") {
 798                     This.$ghost.css({
 799                         display: "block",
 800                         width: This.$nodeData[id].width - 2 + "px", height: This.$nodeData[id].height - 2 + "px",
 801                         top: This.$nodeData[id].top + t.top - This.$workArea[0].parentNode.scrollTop + hack + "px",
 802                         left: This.$nodeData[id].left + t.left - This.$workArea[0].parentNode.scrollLeft + hack + "px", cursor: "move"
 803                     });
 804                 }
 805 
 806                 if (X < t.left - This.$workArea[0].parentNode.scrollLeft)
 807                     X = t.left - This.$workArea[0].parentNode.scrollLeft;
 808                 else if (X + This.$workArea[0].parentNode.scrollLeft + This.$nodeData[id].width > t.left + This.$workArea.width())
 809                     X = t.left + This.$workArea.width() - This.$workArea[0].parentNode.scrollLeft - This.$nodeData[id].width;
 810                 if (Y < t.top - This.$workArea[0].parentNode.scrollTop)
 811                     Y = t.top - This.$workArea[0].parentNode.scrollTop;
 812                 else if (Y + This.$workArea[0].parentNode.scrollTop + This.$nodeData[id].height > t.top + This.$workArea.height())
 813                     Y = t.top + This.$workArea.height() - This.$workArea[0].parentNode.scrollTop - This.$nodeData[id].height;
 814                 This.$ghost.css({ left: X + hack + "px", top: Y + hack + "px" });
 815                 isMove = true;
 816             }
 817             document.onmouseup = function (e) {
 818                 if (isMove) This.moveNode(id, X + This.$workArea[0].parentNode.scrollLeft - t.left, Y + This.$workArea[0].parentNode.scrollTop - t.top);
 819                 This.$ghost.empty().hide();
 820                 document.onmousemove = null;
 821                 document.onmouseup = null;
 822             }
 823         });
 824         if (!this.$editable) return;
 825         //绑定鼠标覆盖/移出事件
 826         this.$workArea.delegate(".GooFlow_item", "mouseenter", { inthis: this }, function (e) {
 827             if (e.data.inthis.$nowType != "direct" && !document.getElementById("GooFlow_tmp_line")) return;
 828             $(this).addClass("item_mark").addClass("crosshair").css("border-color", GooFlow.prototype.color.mark || "#ff3300");
 829         });
 830         this.$workArea.delegate(".GooFlow_item", "mouseleave", { inthis: this }, function (e) {
 831             if (e.data.inthis.$nowType != "direct" && !document.getElementById("GooFlow_tmp_line")) return;
 832             $(this).removeClass("item_mark").removeClass("crosshair");
 833             if (this.id == e.data.inthis.$focus) {
 834                 $(this).css("border-color", GooFlow.prototype.color.line || "#3892D3");
 835             } else {
 836                 $(this).css("border-color", GooFlow.prototype.color.node || "#A1DCEB");
 837             }
 838         });
 839         //绑定连线时确定初始点
 840         this.$workArea.delegate(".GooFlow_item", "mousedown", { inthis: this }, function (e) {
 841             if (e.button == 2) return false;
 842             var This = e.data.inthis;
 843             if (This.$nowType != "direct") return;
 844             var ev = mousePosition(e), t = getElCoordinate(This.$workArea[0]);
 845             var X, Y;
 846             X = ev.x - t.left + This.$workArea[0].parentNode.scrollLeft;
 847             Y = ev.y - t.top + This.$workArea[0].parentNode.scrollTop;
 848             This.$workArea.data("lineStart", { "x": X, "y": Y, "id": this.id }).css("cursor", "crosshair");
 849             var line = GooFlow.prototype.drawLine("GooFlow_tmp_line", [X, Y], [X, Y], true, true);
 850             This.$draw.appendChild(line);
 851         });
 852         //绑定连线时确定结束点
 853         this.$workArea.delegate(".GooFlow_item", "mouseup", { inthis: this }, function (e) {
 854             var This = e.data.inthis;
 855             if (This.$nowType != "direct" && !This.$mpTo.data("p")) return;
 856             var lineStart = This.$workArea.data("lineStart");
 857             var lineEnd = This.$workArea.data("lineEnd");
 858             if (lineStart && !This.$mpTo.data("p")) {
 859                 //2015-01-13 TXF
 860                 //This.addLine(This.$id + "_line_" + This.$max, { from: lineStart.id, to: this.id, name: "" });
 861                 //This.$max++;
 862                 if (lineStart) This.addLine(This.$id + "_line_" + currentDate(), { from: lineStart.id, to: this.id, name: "", type: "sl" });
 863             }
 864             else {
 865                 if (lineStart) {
 866                     This.moveLinePoints(This.$focus, lineStart.id, this.id);
 867                 } else if (lineEnd) {
 868                     This.moveLinePoints(This.$focus, this.id, lineEnd.id);
 869                 }
 870                 if (!This.$nodeData[this.id].marked) {
 871                     $(this).removeClass("item_mark");
 872                     if (this.id != This.$focus) {
 873                         $(this).css("border-color", GooFlow.prototype.color.node);
 874                     }
 875                     else {
 876                         $(this).css("border-color", GooFlow.prototype.color.line);
 877                     }
 878                 }
 879             }
 880         });
 881         //绑定双击编辑事件
 882         this.$workArea.delegate(".GooFlow_item > .span", "dblclick", { inthis: this }, function (e) {
 883             var oldTxt = this.innerHTML;
 884             var This = e.data.inthis;
 885             var id = this.parentNode.id;
 886             var t = getElCoordinate(This.$workArea[0]);
 887             This.$textArea.val(oldTxt).css({
 888                 display: "block", height: $(this).height(), width: 100,
 889                 left: t.left + This.$nodeData[id].left - This.$workArea[0].parentNode.scrollLeft - 24,
 890                 top: t.top + This.$nodeData[id].top - This.$workArea[0].parentNode.scrollTop + 26
 891             })
 892                 .data("id", This.$focus).focus();
 893             This.$workArea.parent().one("mousedown", function (e) {
 894                 if (e.button == 2) return false;
 895                 This.setName(This.$textArea.data("id"), This.$textArea.val(), "node");
 896                 This.$textArea.val("").removeData("id").hide();
 897             });
 898         });
 899         this.$workArea.delegate(".ico + td", "dblclick", { inthis: this }, function (e) {
 900             var oldTxt = this.innerHTML;
 901             var This = e.data.inthis;
 902             var id = $(this).parents(".GooFlow_item").attr("id");
 903             var t = getElCoordinate(This.$workArea[0]);
 904             This.$textArea.val(oldTxt).css({
 905                 display: "block", width: $(this).width() + 24, height: $(this).height(),
 906                 left: t.left + 24 + This.$nodeData[id].left - This.$workArea[0].parentNode.scrollLeft,
 907                 top: t.top + 2 + This.$nodeData[id].top - This.$workArea[0].parentNode.scrollTop
 908             })
 909                 .data("id", This.$focus).focus();
 910             This.$workArea.parent().one("mousedown", function (e) {
 911                 if (e.button == 2) return false;
 912                 This.setName(This.$textArea.data("id"), This.$textArea.val(), "node");
 913                 This.$textArea.val("").removeData("id").hide();
 914             });
 915         });
 916         //绑定结点的删除功能
 917         this.$workArea.delegate(".rs_close", "click", { inthis: this }, function (e) {
 918             if (!e) e = window.event;
 919             e.data.inthis.delNode(e.data.inthis.$focus);
 920             return false;
 921         });
 922         //绑定结点的RESIZE功能
 923         this.$workArea.delegate(".GooFlow_item > div > div[class!=rs_close]", "mousedown", { inthis: this }, function (e) {
 924             if (!e) e = window.event;
 925             if (e.button == 2) return false;
 926             var cursor = $(this).css("cursor");
 927             if (cursor == "pointer") { return; }
 928             var This = e.data.inthis;
 929             var id = This.$focus;
 930             This.switchToolBtn("cursor");
 931             e.cancelBubble = true;
 932             e.stopPropagation();
 933             var hack = 1;
 934             if (navigator.userAgent.indexOf("8.0") != -1) hack = 0;
 935             var ev = mousePosition(e), t = getElCoordinate(This.$workArea[0]);
 936             This.$ghost.css({
 937                 display: "block",
 938                 width: This.$nodeData[id].width - 2 + "px", height: This.$nodeData[id].height - 2 + "px",
 939                 top: This.$nodeData[id].top + t.top - This.$workArea[0].parentNode.scrollTop + hack + "px",
 940                 left: This.$nodeData[id].left + t.left - This.$workArea[0].parentNode.scrollLeft + hack + "px", cursor: cursor
 941             });
 942             var X, Y;
 943             X = ev.x - t.left + This.$workArea[0].parentNode.scrollLeft;
 944             Y = ev.y - t.top + This.$workArea[0].parentNode.scrollTop;
 945             var vX = (This.$nodeData[id].left + This.$nodeData[id].width) - X;
 946             var vY = (This.$nodeData[id].top + This.$nodeData[id].height) - Y;
 947             var isMove = false;
 948             This.$ghost.css("cursor", cursor);
 949             document.onmousemove = function (e) {
 950                 if (!e) e = window.event;
 951                 var ev = mousePosition(e);
 952                 X = ev.x - t.left + This.$workArea[0].parentNode.scrollLeft - This.$nodeData[id].left + vX;
 953                 Y = ev.y - t.top + This.$workArea[0].parentNode.scrollTop - This.$nodeData[id].top + vY;
 954                 if (X < 100) X = 100;
 955                 if (Y < 24) Y = 24;
 956                 isMove = true;
 957                 switch (cursor) {
 958                     case "nw-resize": This.$ghost.css({ width: X - 2 + "px", height: Y - 2 + "px" }); break;
 959                     case "w-resize": This.$ghost.css({ width: X - 2 + "px" }); break;
 960                     case "n-resize": This.$ghost.css({ height: Y - 2 + "px" }); break;
 961                 }
 962             }
 963             document.onmouseup = function (e) {
 964                 This.$ghost.hide();
 965                 if (!isMove) return;
 966                 if (!e) e = window.event;
 967                 This.resizeNode(id, This.$ghost.outerWidth(), This.$ghost.outerHeight());
 968                 document.onmousemove = null;
 969                 document.onmouseup = null;
 970             }
 971         });
 972     },
 973     //获取结点/连线/分组区域的详细信息
 974     getItemInfo: function (id, type) {
 975         switch (type) {
 976             case "node": return this.$nodeData[id] || null;
 977             case "line": return this.$lineData[id] || null;
 978             case "area": return this.$areaData[id] || null;
 979         }
 980     },
 981     //取消所有结点/连线被选定的状态
 982     blurItem: function () {
 983         if (this.$focus != "") {
 984             var jq = $("#" + this.$focus);
 985             if (jq.prop("tagName") == "DIV") {
 986                 if (this.onItemBlur != null && !this.onItemBlur(this.$focus, "node")) return false;
 987                 jq.removeClass("item_focus").children("div:eq(0)").css("display", "none");
 988                 if (GooFlow.prototype.color.line) {
 989                     if (this.$nodeData[this.$focus].marked) {
 990                         jq.css("border-color", GooFlow.prototype.color.mark || "#ff3300");
 991                     }
 992                     else {
 993                         jq.css("border-color", GooFlow.prototype.color.node || "#A1DCEB");
 994                     }
 995                 }
 996             }
 997             else {
 998                 if (this.onItemBlur != null && !this.onItemBlur(this.$focus, "line")) return false;
 999                 if (GooFlow.prototype.useSVG != "") {
1000                     if (!this.$lineData[this.$focus].marked) {
1001                         jq[0].childNodes[1].setAttribute("stroke", GooFlow.prototype.color.line || "#3892D3");
1002                         jq[0].childNodes[1].setAttribute("marker-end", "url(#arrow1)");
1003                     }
1004                 }
1005                 else {
1006                     if (!this.$lineData[this.$focus].marked) jq[0].strokeColor = GooFlow.prototype.color.line || "#3892D3";
1007                 }
1008                 this.$lineMove.hide().removeData("type").removeData("tid");
1009                 if (this.$editable) {
1010                     this.$lineOper.hide().removeData("tid");
1011                     this.$mpFrom.hide().removeData("p");
1012                     this.$mpTo.hide().removeData("p");
1013                 }
1014             }
1015         }
1016         this.$focus = "";
1017         return true;
1018     },
1019     //选定某个结点/转换线 bool:TRUE决定了要触发选中事件,FALSE则不触发选中事件,多用在程序内部调用。
1020     focusItem: function (id, bool) {
1021         var jq = $("#" + id);
1022         if (jq.length == 0) return;
1023         if (!this.blurItem()) return;//先执行"取消选中",如果返回FLASE,则也会阻止选定事件继续进行.
1024         if (jq.prop("tagName") == "DIV") {
1025             if (bool && this.onItemFocus != null && !this.onItemFocus(id, "node")) return;
1026             jq.addClass("item_focus");
1027             if (GooFlow.prototype.color.line) {
1028                 jq.css("border-color", GooFlow.prototype.color.line);
1029             }
1030             if (this.$editable) jq.children("div:eq(0)").css("display", "block");
1031             this.$workArea.append(jq);
1032         }
1033         else {//如果是连接线
1034             if (this.onItemFocus != null && !this.onItemFocus(id, "line")) return;
1035             if (GooFlow.prototype.useSVG != "") {
1036                 jq[0].childNodes[1].setAttribute("stroke", GooFlow.prototype.color.mark || "#ff3300");
1037                 jq[0].childNodes[1].setAttribute("marker-end", "url(#arrow2)");
1038             }
1039             else jq[0].strokeColor = GooFlow.prototype.color.mark || "#ff3300";
1040             if (!this.$editable) return;
1041             var x, y, from, to, n;
1042             if (GooFlow.prototype.useSVG != "") {
1043                 from = jq.attr("from").split(",");
1044                 to = jq.attr("to").split(",");
1045                 n = [from[0], from[1], to[0], to[1]];
1046             } else {
1047                 n = jq[0].getAttribute("fromTo").split(",");
1048                 from = [n[0], n[1]];
1049                 to = [n[2], n[3]];
1050             }
1051             from[0] = parseInt(from[0], 10);
1052             from[1] = parseInt(from[1], 10);
1053             to[0] = parseInt(to[0], 10);
1054             to[1] = parseInt(to[1], 10);
1055             //var t=getElCoordinate(this.$workArea[0]);
1056             if (this.$lineData[id].type == "lr") {
1057                 from[0] = this.$lineData[id].M;
1058                 to[0] = from[0];
1059 
1060                 this.$lineMove.css({
1061                     width: "5px", height: (to[1] - from[1]) * (to[1] > from[1] ? 1 : -1) + "px",
1062                     left: from[0] - 3 + "px",
1063                     top: (to[1] > from[1] ? from[1] : to[1]) + 1 + "px",
1064                     cursor: "e-resize", display: "block"
1065                 }).data({ "type": "lr", "tid": id });
1066             }
1067             else if (this.$lineData[id].type == "tb") {
1068                 from[1] = this.$lineData[id].M;
1069                 to[1] = from[1];
1070                 this.$lineMove.css({
1071                     width: (to[0] - from[0]) * (to[0] > from[0] ? 1 : -1) + "px", height: "5px",
1072                     left: (to[0] > from[0] ? from[0] : to[0]) + 1 + "px",
1073                     top: from[1] - 3 + "px",
1074                     cursor: "s-resize", display: "block"
1075                 }).data({ "type": "tb", "tid": id });
1076             }
1077             x = (from[0] + to[0]) / 2 - 35;
1078             y = (from[1] + to[1]) / 2 + 6;
1079             this.$lineOper.css({ display: "block", left: x + "px", top: y + "px" }).data("tid", id);
1080             if (this.$editable) {
1081                 this.$mpFrom.css({ display: "block", left: n[0] - 4 + "px", top: n[1] - 4 + "px" }).data("p", n[0] + "," + n[1]);
1082                 this.$mpTo.css({ display: "block", left: n[2] - 4 + "px", top: n[3] - 4 + "px" }).data("p", n[2] + "," + n[3]);
1083             }
1084             this.$draw.appendChild(jq[0]);
1085         }
1086         this.$focus = id;
1087         this.switchToolBtn("cursor");
1088     },
1089     //移动结点到一个新的位置
1090     moveNode: function (id, left, top) {
1091         if (!this.$nodeData[id]) return;
1092         if (this.onItemMove != null && !this.onItemMove(id, "node", left, top)) return;
1093         if (this.$undoStack) {
1094             var paras = [id, this.$nodeData[id].left, this.$nodeData[id].top];
1095             this.pushOper("moveNode", paras);
1096         }
1097         if (left < 0) left = 0;
1098         if (top < 0) top = 0;
1099         $("#" + id).css({ left: left + "px", top: top + "px" });
1100         this.$nodeData[id].left = left;
1101         this.$nodeData[id].top = top;
1102         //重画转换线
1103         this.resetLines(id, this.$nodeData[id]);
1104         if (this.$editable) {
1105             this.$nodeData[id].alt = true;
1106         }
1107     },
1108     //设置结点/连线/分组区域的文字信息
1109     setName: function (id, name, type) {
1110         var oldName;
1111         if (type == "node") {//如果是结点
1112             if (!this.$nodeData[id]) return;
1113             if (this.$nodeData[id].name == name) return;
1114             if (this.onItemRename != null && !this.onItemRename(id, name, "node")) return;
1115             oldName = this.$nodeData[id].name;
1116             this.$nodeData[id].name = name;
1117             if (this.$nodeData[id].type.indexOf("round") > 1) {
1118                 this.$nodeDom[id].children(".span").text(name);
1119             }
1120             else {
1121                 this.$nodeDom[id].find("td:eq(1)").text(name);
1122                 var hack = 0;
1123                 if (navigator.userAgent.indexOf("8.0") != -1) hack = 2;
1124                 var width = this.$nodeDom[id].outerWidth();
1125                 var height = this.$nodeDom[id].outerHeight();
1126                 this.$nodeDom[id].children("table").css({ width: width - 2 + "px", height: height - 2 + "px" });
1127                 this.$nodeData[id].width = width;
1128                 this.$nodeData[id].height = height;
1129             }
1130             if (this.$editable) {
1131                 this.$nodeData[id].alt = true;
1132             }
1133             //重画转换线
1134             this.resetLines(id, this.$nodeData[id]);
1135         }
1136         else if (type == "line") {//如果是线
1137             if (!this.$lineData[id]) return;
1138             if (this.$lineData[id].name == name) return;
1139             if (this.onItemRename != null && !this.onItemRename(id, name, "line")) return;
1140             oldName = this.$lineData[id].name;
1141             this.$lineData[id].name = name;
1142             if (GooFlow.prototype.useSVG != "") {
1143                 this.$lineDom[id].childNodes[2].textContent = name;
1144             }
1145             else {
1146                 this.$lineDom[id].childNodes[1].innerHTML = name;
1147                 var n = this.$lineDom[id].getAttribute("fromTo").split(",");
1148                 var x;
1149                 if (this.$lineData[id].type != "lr") {
1150                     x = (n[2] - n[0]) / 2;
1151                 }
1152                 else {
1153                     var Min = n[2] > n[0] ? n[0] : n[2];
1154                     if (Min > this.$lineData[id].M) Min = this.$lineData[id].M;
1155                     x = this.$lineData[id].M - Min;
1156                 }
1157                 if (x < 0) x = x * -1;
1158                 this.$lineDom[id].childNodes[1].style.left = x - this.$lineDom[id].childNodes[1].offsetWidth / 2 + 4 + "px";
1159             }
1160             if (this.$editable) {
1161                 this.$lineData[id].alt = true;
1162             }
1163         }
1164         else if (type == "area") {//如果是分组区域
1165             if (!this.$areaData[id]) return;
1166             if (this.$areaData[id].name == name) return;
1167             if (this.onItemRename != null && !this.onItemRename(id, name, "area")) return;
1168             oldName = this.$areaData[id].name;
1169             this.$areaData[id].name = name;
1170             this.$areaDom[id].children("label").text(name);
1171             if (this.$editable) {
1172                 this.$areaData[id].alt = true;
1173             }
1174         }
1175         if (this.$undoStack) {
1176             var paras = [id, oldName, type];
1177             this.pushOper("setName", paras);
1178         }
1179     },
1180     //设置结点的尺寸,仅支持非开始/结束结点
1181     resizeNode: function (id, width, height) {
1182         if (!this.$nodeData[id]) return;
1183         if (this.onItemResize != null && !this.onItemResize(id, "node", width, height)) return;
1184         if (this.$nodeData[id].type == "start" || this.$nodeData[id].type == "end") return;
1185         if (this.$undoStack) {
1186             var paras = [id, this.$nodeData[id].width, this.$nodeData[id].height];
1187             this.pushOper("resizeNode", paras);
1188         }
1189         var hack = 0;
1190         if (navigator.userAgent.indexOf("8.0") != -1) hack = 2;
1191         this.$nodeDom[id].children("table").css({ width: width - 2 + "px", height: height - 2 + "px" });
1192         width = this.$nodeDom[id].outerWidth() - hack;
1193         height = this.$nodeDom[id].outerHeight() - hack;
1194         this.$nodeDom[id].children("table").css({ width: width - 2 + "px", height: height - 2 + "px" });
1195         this.$nodeData[id].width = width;
1196         this.$nodeData[id].height = height;
1197         if (this.$editable) {
1198             this.$nodeData[id].alt = true;
1199         }
1200         //重画转换线
1201         this.resetLines(id, this.$nodeData[id]);
1202     },
1203     //删除结点
1204     delNode: function (id) {
1205         if (!this.$nodeData[id]) return;
1206         if (this.onItemDel != null && !this.onItemDel(id, "node")) return;
1207         //先删除可能的连线
1208         for (var k in this.$lineData) {
1209             if (this.$lineData[k].from == id || this.$lineData[k].to == id) {
1210                 //this.$draw.removeChild(this.$lineDom[k]);
1211                 //delete this.$lineData[k];
1212                 //delete this.$lineDom[k];
1213                 this.delLine(k);
1214             }
1215         }
1216         //再删除结点本身
1217         if (this.$undoStack) {
1218             var paras = [id, this.$nodeData[id]];
1219             this.pushOper("addNode", paras);
1220         }
1221         delete this.$nodeData[id];
1222         this.$nodeDom[id].remove();
1223         delete this.$nodeDom[id];
1224         --this.$nodeCount;
1225         if (this.$focus == id) this.$focus = "";
1226 
1227         if (this.$editable) {
1228             //在回退新增操作时,如果节点ID以this.$id+"_node_"开头,则表示为本次编辑时新加入的节点,这些节点的删除不用加入到$deletedItem中
1229             if (id.indexOf(this.$id + "_node_") < 0)
1230                 this.$deletedItem[id] = "node";
1231         }
1232     },
1233     //设置流程图的名称
1234     setTitle: function (text) {
1235         this.$title = text;
1236         if (this.$head) this.$head.children("label").attr("title", text).text(text);
1237     },
1238     //载入一组数据
1239     loadData: function (data) {
1240         var t = this.$editable;
1241         this.$editable = false;
1242         if (data.title) this.setTitle(data.title);
1243         if (data.initNum) this.$max = data.initNum;
1244         for (var i in data.nodes)
1245             this.addNode(i, data.nodes[i], true);//2015-07-28 加载不要触发onItemAdd事件
1246         for (var j in data.lines)
1247             this.addLine(j, data.lines[j], true);
1248         for (var k in data.areas)
1249             this.addArea(k, data.areas[k], true);
1250         this.$editable = t;
1251         this.$deletedItem = {};
1252     },
1253     //用AJAX方式,远程读取一组数据
1254     //参数para为JSON结构,与JQUERY中$.ajax()方法的传参一样
1255     //TXF 2015-07-29
1256     loadDataAjax: function (param) {
1257         var This = this;
1258         $.ajax({
1259             type: param.type,
1260             url: param.url,
1261             dataType: "json",
1262             data: param.data,
1263             cache: false,
1264             success: function (obj) {
1265                 if (param.dataFilter) param.dataFilter(obj.flow_elements, "json");
1266                 This.loadData($.parseJSON(obj.flow_elements));
1267                 if (param.success) param.success(obj);
1268             },
1269             error: function (XMLHttpRequest, textStatus, errorThrown) {
1270                 if (param.error) param.error(textStatus, errorThrown);
1271             }
1272         })
1273     },
1274     //把画好的整个流程图导出到一个变量中(其实也可以直接访问GooFlow对象的$nodeData,$lineData,$areaData这三个JSON属性)
1275     exportData: function () {
1276         var ret = { title: this.$title, nodes: this.$nodeData, lines: this.$lineData, areas: this.$areaData, initNum: this.$max };
1277         for (var k1 in ret.nodes) {
1278             if (!ret.nodes[k1].marked) {
1279                 delete ret.nodes[k1]["marked"];
1280             }
1281         }
1282         for (var k2 in ret.lines) {
1283             if (!ret.lines[k2].marked) {
1284                 delete ret.lines[k2]["marked"];
1285             }
1286         }
1287         return ret;
1288     },
1289     //只把本次编辑流程图中作了变更(包括增删改)的元素导出到一个变量中,以方便用户每次编辑载入的流程图后只获取变更过的数据
1290     exportAlter: function () {
1291         var ret = { nodes: {}, lines: {}, areas: {} };
1292         for (var k1 in this.$nodeData) {
1293             if (this.$nodeData[k1].alt) {
1294                 ret.nodes[k1] = this.$nodeData[k1];
1295             }
1296         }
1297         for (var k2 in this.$lineData) {
1298             if (this.$lineData[k2].alt) {
1299                 ret.lines[k2] = this.$lineData[k2];
1300             }
1301         }
1302         for (var k3 in this.$areaData) {
1303             if (this.$areaData[k3].alt) {
1304                 ret.areas[k3] = this.$areaData[k3];
1305             }
1306         }
1307         ret.deletedItem = this.$deletedItem;
1308         return ret;
1309     },
1310     //变更元素的ID,一般用于快速保存后,将后台返回新元素的ID更新到页面中;type为元素类型(节点,连线,区块)
1311     transNewId: function (oldId, newId, type) {
1312         var tmp;
1313         switch (type) {
1314             case "node":
1315                 if (this.$nodeData[oldId]) {
1316                     tmp = this.$nodeData[oldId];
1317                     delete this.$nodeData[oldId];
1318                     this.$nodeData[newId] = tmp;
1319                     tmp = this.$nodeDom[oldId].attr("id", newId);
1320                     delete this.$nodeDom[oldId];
1321                     this.$nodeDom[newId] = tmp;
1322                 }
1323                 break;
1324             case "line":
1325                 if (this.$lineData[oldId]) {
1326                     tmp = this.$lineData[oldId];
1327                     delete this.$lineData[oldId];
1328                     this.$lineData[newId] = tmp;
1329                     tmp = this.$lineDom[oldId].attr("id", newId);
1330                     delete this.$lineDom[oldId];
1331                     this.$lineDom[newId] = tmp;
1332                 }
1333                 break;
1334             case "area":
1335                 if (this.$areaData[oldId]) {
1336                     tmp = this.$areaData[oldId];
1337                     delete this.$areaData[oldId];
1338                     this.$areaData[newId] = tmp;
1339                     tmp = this.$areaDom[oldId].attr("id", newId);
1340                     delete this.$areaDom[oldId];
1341                     this.$areaDom[newId] = tmp;
1342                 }
1343                 break;
1344         }
1345     },
1346     //清空工作区及已载入的数据
1347     clearData: function () {
1348         for (var key in this.$nodeData) {
1349             this.delNode(key);
1350         }
1351         for (var key in this.$lineData) {
1352             this.delLine(key);
1353         }
1354         for (var key in this.$areaData) {
1355             this.delArea(key);
1356         }
1357         this.$deletedItem = {};
1358     },
1359     //销毁自己
1360     destrory: function () {
1361         this.$bgDiv.empty();
1362         this.$lineData = null;
1363         this.$nodeData = null;
1364         this.$lineDom = null;
1365         this.$nodeDom = null;
1366         this.$areaDom = null;
1367         this.$areaData = null;
1368         this.$nodeCount = 0;
1369         this.$areaCount = 0;
1370         this.$areaCount = 0;
1371         this.$deletedItem = {};
1372     },
1373     ///////////以下为有关画线的方法
1374     //绘制一条箭头线,并返回线的DOM
1375     drawLine: function (id, sp, ep, mark, dash) {
1376         var line;
1377         if (GooFlow.prototype.useSVG != "") {
1378             line = document.createElementNS("http://www.w3.org/2000/svg", "g");
1379             var hi = document.createElementNS("http://www.w3.org/2000/svg", "path");
1380             var path = document.createElementNS("http://www.w3.org/2000/svg", "path");
1381 
1382             if (id != "") line.setAttribute("id", id);
1383             line.setAttribute("from", sp[0] + "," + sp[1]);
1384             line.setAttribute("to", ep[0] + "," + ep[1]);
1385             hi.setAttribute("visibility", "hidden");
1386             hi.setAttribute("stroke-width", 9);
1387             hi.setAttribute("fill", "none");
1388             hi.setAttribute("stroke", "white");
1389             hi.setAttribute("d", "M " + sp[0] + " " + sp[1] + " L " + ep[0] + " " + ep[1]);
1390             hi.setAttribute("pointer-events", "stroke");
1391             path.setAttribute("d", "M " + sp[0] + " " + sp[1] + " L " + ep[0] + " " + ep[1]);
1392             path.setAttribute("stroke-width", 1.4);
1393             path.setAttribute("stroke-linecap", "round");
1394             path.setAttribute("fill", "none");
1395             if (dash) path.setAttribute("style", "stroke-dasharray:6,5");
1396             if (mark) {
1397                 path.setAttribute("stroke", GooFlow.prototype.color.mark || "#ff3300");
1398                 path.setAttribute("marker-end", "url(#arrow2)");
1399             }
1400             else {
1401                 path.setAttribute("stroke", GooFlow.prototype.color.line || "#3892D3");
1402                 path.setAttribute("marker-end", "url(#arrow1)");
1403             }
1404             line.appendChild(hi);
1405             line.appendChild(path);
1406             line.style.cursor = "crosshair";
1407             if (id != "" && id != "GooFlow_tmp_line") {
1408                 var text = document.createElementNS("http://www.w3.org/2000/svg", "text");
1409                 text.setAttribute("fill", GooFlow.prototype.color.font || "#333");
1410                 line.appendChild(text);
1411                 var x = (ep[0] + sp[0]) / 2;
1412                 var y = (ep[1] + sp[1]) / 2;
1413                 text.setAttribute("text-anchor", "middle");
1414                 text.setAttribute("x", x);
1415                 text.setAttribute("y", y);
1416                 line.style.cursor = "pointer";
1417                 text.style.cursor = "text";
1418             }
1419         } else {
1420             line = document.createElement("v:polyline");
1421             if (id != "") line.id = id;
1422             //line.style.position="absolute";
1423             line.points.value = sp[0] + "," + sp[1] + " " + ep[0] + "," + ep[1];
1424             line.setAttribute("fromTo", sp[0] + "," + sp[1] + "," + ep[0] + "," + ep[1]);
1425             line.strokeWeight = "1.2";
1426             line.stroke.EndArrow = "Block";
1427             line.style.cursor = "crosshair";
1428             if (id != "" && id != "GooFlow_tmp_line") {
1429                 var text = document.createElement("div");
1430                 //text.innerHTML=id;
1431                 line.appendChild(text);
1432                 var x = (ep[0] - sp[0]) / 2;
1433                 var y = (ep[1] - sp[1]) / 2;
1434                 if (x < 0) x = x * -1;
1435                 if (y < 0) y = y * -1;
1436                 text.style.left = x + "px";
1437                 text.style.top = y - 6 + "px";
1438                 line.style.cursor = "pointer";
1439             }
1440             if (dash) line.stroke.dashstyle = "Dash";
1441             if (mark) line.strokeColor = GooFlow.prototype.color.mark || "#ff3300";
1442             else line.strokeColor = GooFlow.prototype.color.line || "#3892D3";
1443             line.fillColor = GooFlow.prototype.color.line || "#3892D3";
1444         }
1445         return line;
1446     },
1447     //画一条只有两个中点的折线
1448     drawPoly: function (id, sp, m1, m2, ep, mark) {
1449         var poly, strPath;
1450         if (GooFlow.prototype.useSVG != "") {
1451             poly = document.createElementNS("http://www.w3.org/2000/svg", "g");
1452             var hi = document.createElementNS("http://www.w3.org/2000/svg", "path");
1453             var path = document.createElementNS("http://www.w3.org/2000/svg", "path");
1454             if (id != "") poly.setAttribute("id", id);
1455             poly.setAttribute("from", sp[0] + "," + sp[1]);
1456             poly.setAttribute("to", ep[0] + "," + ep[1]);
1457             hi.setAttribute("visibility", "hidden");
1458             hi.setAttribute("stroke-width", 9);
1459             hi.setAttribute("fill", "none");
1460             hi.setAttribute("stroke", "white");
1461             strPath = "M " + sp[0] + " " + sp[1];
1462             if (m1[0] != sp[0] || m1[1] != sp[1])
1463                 strPath += " L " + m1[0] + " " + m1[1];
1464             if (m2[0] != ep[0] || m2[1] != ep[1])
1465                 strPath += " L " + m2[0] + " " + m2[1];
1466             strPath += " L " + ep[0] + " " + ep[1];
1467             hi.setAttribute("d", strPath);
1468             hi.setAttribute("pointer-events", "stroke");
1469             path.setAttribute("d", strPath);
1470             path.setAttribute("stroke-width", 1.4);
1471             path.setAttribute("stroke-linecap", "round");
1472             path.setAttribute("fill", "none");
1473             if (mark) {
1474                 path.setAttribute("stroke", GooFlow.prototype.color.mark || "#ff3300");
1475                 path.setAttribute("marker-end", "url(#arrow2)");
1476             }
1477             else {
1478                 path.setAttribute("stroke", GooFlow.prototype.color.line || "#3892D3");
1479                 path.setAttribute("marker-end", "url(#arrow1)");
1480             }
1481             poly.appendChild(hi);
1482             poly.appendChild(path);
1483             var text = document.createElementNS("http://www.w3.org/2000/svg", "text");
1484             text.setAttribute("fill", GooFlow.prototype.color.font || "#333");
1485             poly.appendChild(text);
1486             var x = (m2[0] + m1[0]) / 2;
1487             var y = (m2[1] + m1[1]) / 2;
1488             text.setAttribute("text-anchor", "middle");
1489             text.setAttribute("x", x);
1490             text.setAttribute("y", y);
1491             text.style.cursor = "text";
1492             poly.style.cursor = "pointer";
1493         }
1494         else {
1495             poly = document.createElement("v:Polyline");
1496             if (id != "") poly.id = id;
1497             poly.filled = "false";
1498             strPath = sp[0] + "," + sp[1];
1499             if (m1[0] != sp[0] || m1[1] != sp[1])
1500                 strPath += " " + m1[0] + "," + m1[1];
1501             if (m2[0] != ep[0] || m2[1] != ep[1])
1502                 strPath += " " + m2[0] + "," + m2[1];
1503             strPath += " " + ep[0] + "," + ep[1];
1504             poly.points.value = strPath;
1505             poly.setAttribute("fromTo", sp[0] + "," + sp[1] + "," + ep[0] + "," + ep[1]);
1506             poly.strokeWeight = "1.2";
1507             poly.stroke.EndArrow = "Block";
1508             var text = document.createElement("div");
1509             //text.innerHTML=id;
1510             poly.appendChild(text);
1511             var x = (m2[0] - m1[0]) / 2;
1512             var y = (m2[1] - m1[1]) / 2;
1513             if (x < 0) x = x * -1;
1514             if (y < 0) y = y * -1;
1515             text.style.left = x + "px";
1516             text.style.top = y - 4 + "px";
1517             poly.style.cursor = "pointer";
1518             if (mark) poly.strokeColor = GooFlow.prototype.color.mark || "#ff3300";
1519             else poly.strokeColor = GooFlow.prototype.color.line || "#3892D3";
1520         }
1521         return poly;
1522     },
1523     //计算两个结点间要连直线的话,连线的开始坐标和结束坐标
1524     calcStartEnd: function (n1, n2) {
1525         var X_1, Y_1, X_2, Y_2;
1526         //X判断:
1527         var x11 = n1.left, x12 = n1.left + n1.width, x21 = n2.left, x22 = n2.left + n2.width;
1528         //结点2在结点1左边
1529         if (x11 >= x22) {
1530             X_1 = x11; X_2 = x22;
1531         }
1532             //结点2在结点1右边
1533         else if (x12 <= x21) {
1534             X_1 = x12; X_2 = x21;
1535         }
1536             //结点2在结点1水平部分重合
1537         else if (x11 <= x21 && x12 >= x21 && x12 <= x22) {
1538             X_1 = (x12 + x21) / 2; X_2 = X_1;
1539         }
1540         else if (x11 >= x21 && x12 <= x22) {
1541             X_1 = (x11 + x12) / 2; X_2 = X_1;
1542         }
1543         else if (x21 >= x11 && x22 <= x12) {
1544             X_1 = (x21 + x22) / 2; X_2 = X_1;
1545         }
1546         else if (x11 <= x22 && x12 >= x22) {
1547             X_1 = (x11 + x22) / 2; X_2 = X_1;
1548         }
1549 
1550         //Y判断:
1551         var y11 = n1.top, y12 = n1.top + n1.height, y21 = n2.top, y22 = n2.top + n2.height;
1552         //结点2在结点1上边
1553         if (y11 >= y22) {
1554             Y_1 = y11; Y_2 = y22;
1555         }
1556             //结点2在结点1下边
1557         else if (y12 <= y21) {
1558             Y_1 = y12; Y_2 = y21;
1559         }
1560             //结点2在结点1垂直部分重合
1561         else if (y11 <= y21 && y12 >= y21 && y12 <= y22) {
1562             Y_1 = (y12 + y21) / 2; Y_2 = Y_1;
1563         }
1564         else if (y11 >= y21 && y12 <= y22) {
1565             Y_1 = (y11 + y12) / 2; Y_2 = Y_1;
1566         }
1567         else if (y21 >= y11 && y22 <= y12) {
1568             Y_1 = (y21 + y22) / 2; Y_2 = Y_1;
1569         }
1570         else if (y11 <= y22 && y12 >= y22) {
1571             Y_1 = (y11 + y22) / 2; Y_2 = Y_1;
1572         }
1573         return { "start": [X_1, Y_1], "end": [X_2, Y_2] };
1574     },
1575     //计算两个结点间要连折线的话,连线的所有坐标
1576     calcPolyPoints: function (n1, n2, type, M) {
1577         //开始/结束两个结点的中心
1578         var SP = { x: n1.left + n1.width / 2, y: n1.top + n1.height / 2 };
1579         var EP = { x: n2.left + n2.width / 2, y: n2.top + n2.height / 2 };
1580         var sp = [], m1 = [], m2 = [], ep = [];
1581         //如果是允许中段可左右移动的折线,则参数M为可移动中段线的X坐标
1582         //粗略计算起始点
1583         sp = [SP.x, SP.y];
1584         ep = [EP.x, EP.y];
1585         if (type == "lr") {
1586             //粗略计算2个中点
1587             m1 = [M, SP.y];
1588             m2 = [M, EP.y];
1589             //再具体分析修改开始点和中点1
1590             if (m1[0] > n1.left && m1[0] < n1.left + n1.width) {
1591                 m1[1] = (SP.y > EP.y ? n1.top : n1.top + n1.height);
1592                 sp[0] = m1[0]; sp[1] = m1[1];
1593             }
1594             else {
1595                 sp[0] = (m1[0] < n1.left ? n1.left : n1.left + n1.width)
1596             }
1597             //再具体分析中点2和结束点
1598             if (m2[0] > n2.left && m2[0] < n2.left + n2.width) {
1599                 m2[1] = (SP.y > EP.y ? n2.top + n2.height : n2.top);
1600                 ep[0] = m2[0]; ep[1] = m2[1];
1601             }
1602             else {
1603                 ep[0] = (m2[0] < n2.left ? n2.left : n2.left + n2.width)
1604             }
1605         }
1606             //如果是允许中段可上下移动的折线,则参数M为可移动中段线的Y坐标
1607         else if (type == "tb") {
1608             //粗略计算2个中点
1609             m1 = [SP.x, M];
1610             m2 = [EP.x, M];
1611             //再具体分析修改开始点和中点1
1612             if (m1[1] > n1.top && m1[1] < n1.top + n1.height) {
1613                 m1[0] = (SP.x > EP.x ? n1.left : n1.left + n1.width);
1614                 sp[0] = m1[0]; sp[1] = m1[1];
1615             }
1616             else {
1617                 sp[1] = (m1[1] < n1.top ? n1.top : n1.top + n1.height)
1618             }
1619             //再具体分析中点2和结束点
1620             if (m2[1] > n2.top && m2[1] < n2.top + n2.height) {
1621                 m2[0] = (SP.x > EP.x ? n2.left + n2.width : n2.left);
1622                 ep[0] = m2[0]; ep[1] = m2[1];
1623             }
1624             else {
1625                 ep[1] = (m2[1] < n2.top ? n2.top : n2.top + n2.height);
1626             }
1627         }
1628         return { start: sp, m1: m1, m2: m2, end: ep };
1629     },
1630     //初始化折线中段的X/Y坐标,mType='rb'时为X坐标,mType='tb'时为Y坐标
1631     getMValue: function (n1, n2, mType) {
1632         if (mType == "lr") {
1633             return (n1.left + n1.width / 2 + n2.left + n2.width / 2) / 2;
1634         }
1635         else if (mType == "tb") {
1636             return (n1.top + n1.height / 2 + n2.top + n2.height / 2) / 2;
1637         }
1638     },
1639     //原lineData已经设定好的情况下,只在绘图工作区画一条线的页面元素
1640     addLineDom: function (id, lineData) {
1641         var n1 = this.$nodeData[lineData.from], n2 = this.$nodeData[lineData.to];//获取开始/结束结点的数据
1642         if (!n1 || !n2) return;
1643         //开始计算线端点坐标
1644         var res;
1645         if (lineData.type && lineData.type != "sl")
1646             res = GooFlow.prototype.calcPolyPoints(n1, n2, lineData.type, lineData.M);
1647         else
1648             res = GooFlow.prototype.calcStartEnd(n1, n2);
1649         if (!res) return;
1650 
1651         if (lineData.type == "sl")
1652             this.$lineDom[id] = GooFlow.prototype.drawLine(id, res.start, res.end, lineData.marked);
1653         else
1654             this.$lineDom[id] = GooFlow.prototype.drawPoly(id, res.start, res.m1, res.m2, res.end, lineData.marked);
1655         this.$draw.appendChild(this.$lineDom[id]);
1656         if (GooFlow.prototype.useSVG == "") {
1657             this.$lineDom[id].childNodes[1].innerHTML = lineData.name;
1658             if (lineData.type != "sl") {
1659                 var Min = (res.start[0] > res.end[0] ? res.end[0] : res.start[0]);
1660                 if (Min > res.m2[0]) Min = res.m2[0];
1661                 if (Min > res.m1[0]) Min = res.m1[0];
1662                 this.$lineDom[id].childNodes[1].style.left = (res.m2[0] + res.m1[0]) / 2 - Min - this.$lineDom[id].childNodes[1].offsetWidth / 2 + 4;
1663                 Min = (res.start[1] > res.end[1] ? res.end[1] : res.start[1]);
1664                 if (Min > res.m2[1]) Min = res.m2[1];
1665                 if (Min > res.m1[1]) Min = res.m1[1];
1666                 this.$lineDom[id].childNodes[1].style.top = (res.m2[1] + res.m1[1]) / 2 - Min - this.$lineDom[id].childNodes[1].offsetHeight / 2;
1667             } else
1668                 this.$lineDom[id].childNodes[1].style.left =
1669                 ((res.end[0] - res.start[0]) * (res.end[0] > res.start[0] ? 1 : -1) - this.$lineDom[id].childNodes[1].offsetWidth) / 2 + 4;
1670         }
1671         else this.$lineDom[id].childNodes[2].textContent = lineData.name;
1672     },
1673     //增加一条线
1674     addLine: function (id, json, disabled) {
1675         if (!disabled)
1676             if (this.onItemAdd != null && !this.onItemAdd(id, "line", json)) return;
1677         if (this.$undoStack && this.$editable) {
1678             this.pushOper("delLine", [id]);
1679         }
1680         if (json.from == json.to) return;
1681         var n1 = this.$nodeData[json.from], n2 = this.$nodeData[json.to];//获取开始/结束结点的数据
1682         if (!n1 || !n2) return;
1683         //避免两个节点间不能有一条以上同向接连线
1684         for (var k in this.$lineData) {
1685             if ((json.from == this.$lineData[k].from && json.to == this.$lineData[k].to))
1686                 return;
1687         }
1688         //设置$lineData[id]
1689         this.$lineData[id] = {};
1690         if (json.type) {
1691             this.$lineData[id].type = json.type;
1692             this.$lineData[id].M = json.M;
1693         }
1694         else this.$lineData[id].type = "sl";//默认为直线
1695         this.$lineData[id].from = json.from;
1696         this.$lineData[id].to = json.to;
1697         this.$lineData[id].name = json.name;
1698         if (json.marked) this.$lineData[id].marked = json.marked;
1699         else this.$lineData[id].marked = false;
1700         //设置$lineData[id]完毕
1701 
1702         this.addLineDom(id, this.$lineData[id]);
1703 
1704         ++this.$lineCount;
1705         if (this.$editable) {
1706             this.$lineData[id].alt = true;
1707             if (this.$deletedItem[id]) delete this.$deletedItem[id];//在回退删除操作时,去掉该元素的删除记录
1708         }
1709     },
1710     //重构所有连向某个结点的线的显示,传参结构为$nodeData数组的一个单元结构
1711     resetLines: function (id, node) {
1712         for (var i in this.$lineData) {
1713             var other = null;//获取结束/开始结点的数据
1714             var res;
1715             if (this.$lineData[i].from == id) {//找结束点
1716                 other = this.$nodeData[this.$lineData[i].to] || null;
1717                 if (other == null) continue;
1718                 if (this.$lineData[i].type == "sl")
1719                     res = GooFlow.prototype.calcStartEnd(node, other);
1720                 else
1721                     res = GooFlow.prototype.calcPolyPoints(node, other, this.$lineData[i].type, this.$lineData[i].M)
1722                 if (!res) break;
1723             }
1724             else if (this.$lineData[i].to == id) {//找开始点
1725                 other = this.$nodeData[this.$lineData[i].from] || null;
1726                 if (other == null) continue;
1727                 if (this.$lineData[i].type == "sl")
1728                     res = GooFlow.prototype.calcStartEnd(other, node);
1729                 else
1730                     res = GooFlow.prototype.calcPolyPoints(other, node, this.$lineData[i].type, this.$lineData[i].M);
1731                 if (!res) break;
1732             }
1733             if (other == null) continue;
1734             this.$draw.removeChild(this.$lineDom[i]);
1735             if (this.$lineData[i].type == "sl") {
1736                 this.$lineDom[i] = GooFlow.prototype.drawLine(i, res.start, res.end, this.$lineData[i].marked);
1737             }
1738             else {
1739                 this.$lineDom[i] = GooFlow.prototype.drawPoly(i, res.start, res.m1, res.m2, res.end, this.$lineData[i].marked);
1740             }
1741             this.$draw.appendChild(this.$lineDom[i]);
1742             if (GooFlow.prototype.useSVG == "") {
1743                 this.$lineDom[i].childNodes[1].innerHTML = this.$lineData[i].name;
1744                 if (this.$lineData[i].type != "sl") {
1745                     var Min = (res.start[0] > res.end[0] ? res.end[0] : res.start[0]);
1746                     if (Min > res.m2[0]) Min = res.m2[0];
1747                     if (Min > res.m1[0]) Min = res.m1[0];
1748                     this.$lineDom[i].childNodes[1].style.left = (res.m2[0] + res.m1[0]) / 2 - Min - this.$lineDom[i].childNodes[1].offsetWidth / 2 + 4;
1749                     Min = (res.start[1] > res.end[1] ? res.end[1] : res.start[1]);
1750                     if (Min > res.m2[1]) Min = res.m2[1];
1751                     if (Min > res.m1[1]) Min = res.m1[1];
1752                     this.$lineDom[i].childNodes[1].style.top = (res.m2[1] + res.m1[1]) / 2 - Min - this.$lineDom[i].childNodes[1].offsetHeight / 2 - 4;
1753                 } else
1754                     this.$lineDom[i].childNodes[1].style.left =
1755                     ((res.end[0] - res.start[0]) * (res.end[0] > res.start[0] ? 1 : -1) - this.$lineDom[i].childNodes[1].offsetWidth) / 2 + 4;
1756             }
1757             else this.$lineDom[i].childNodes[2].textContent = this.$lineData[i].name;
1758         }
1759     },
1760     //重新设置连线的样式 newType= "sl":直线, "lr":中段可左右移动型折线, "tb":中段可上下移动型折线
1761     setLineType: function (id, newType, M) {
1762         if (!newType || newType == null || newType == "" || newType == this.$lineData[id].type) return false;
1763         if (this.onLineSetType != null && !this.onLineSetType(id, newType)) return;
1764         if (this.$undoStack) {
1765             var paras = [id, this.$lineData[id].type, this.$lineData[id].M];
1766             this.pushOper("setLineType", paras);
1767         }
1768         var from = this.$lineData[id].from;
1769         var to = this.$lineData[id].to;
1770         this.$lineData[id].type = newType;
1771         var res;
1772         //如果是变成折线
1773         if (newType != "sl") {
1774             var res = GooFlow.prototype.calcPolyPoints(this.$nodeData[from], this.$nodeData[to], this.$lineData[id].type, this.$lineData[id].M);
1775             if (M) {
1776                 this.setLineM(id, M, true);
1777             } else {
1778                 this.setLineM(id, this.getMValue(this.$nodeData[from], this.$nodeData[to], newType), true);
1779             }
1780         }
1781             //如果是变回直线
1782         else {
1783             delete this.$lineData[id].M;
1784             this.$lineMove.hide().removeData("type").removeData("tid");
1785             res = GooFlow.prototype.calcStartEnd(this.$nodeData[from], this.$nodeData[to]);
1786             if (!res) return;
1787             this.$draw.removeChild(this.$lineDom[id]);
1788             this.$lineDom[id] = GooFlow.prototype.drawLine(id, res.start, res.end, this.$lineData[id].marked || this.$focus == id);
1789             this.$draw.appendChild(this.$lineDom[id]);
1790             if (GooFlow.prototype.useSVG == "") {
1791                 this.$lineDom[id].childNodes[1].innerHTML = this.$lineData[id].name;
1792                 this.$lineDom[id].childNodes[1].style.left =
1793                 ((res.end[0] - res.start[0]) * (res.end[0] > res.start[0] ? 1 : -1) - this.$lineDom[id].childNodes[1].offsetWidth) / 2 + 4;
1794             }
1795             else
1796                 this.$lineDom[id].childNodes[2].textContent = this.$lineData[id].name;
1797         }
1798         if (this.$focus == id) {
1799             this.focusItem(id);
1800         }
1801         if (this.$editable) {
1802             this.$lineData[id].alt = true;
1803         }
1804     },
1805     //设置折线中段的X坐标值(可左右移动时)或Y坐标值(可上下移动时)
1806     setLineM: function (id, M, noStack) {
1807         if (!this.$lineData[id] || M < 0 || !this.$lineData[id].type || this.$lineData[id].type == "sl") return false;
1808         if (this.onLineMove != null && !this.onLineMove(id, M)) return false;
1809         if (this.$undoStack && !noStack) {
1810             var paras = [id, this.$lineData[id].M];
1811             this.pushOper("setLineM", paras);
1812         }
1813         var from = this.$lineData[id].from;
1814         var to = this.$lineData[id].to;
1815         this.$lineData[id].M = M;
1816         var ps = GooFlow.prototype.calcPolyPoints(this.$nodeData[from], this.$nodeData[to], this.$lineData[id].type, this.$lineData[id].M);
1817         this.$draw.removeChild(this.$lineDom[id]);
1818         this.$lineDom[id] = GooFlow.prototype.drawPoly(id, ps.start, ps.m1, ps.m2, ps.end, this.$lineData[id].marked || this.$focus == id);
1819         this.$draw.appendChild(this.$lineDom[id]);
1820         if (GooFlow.prototype.useSVG == "") {
1821             this.$lineDom[id].childNodes[1].innerHTML = this.$lineData[id].name;
1822             var Min = (ps.start[0] > ps.end[0] ? ps.end[0] : ps.start[0]);
1823             if (Min > ps.m2[0]) Min = ps.m2[0];
1824             if (Min > ps.m1[0]) Min = ps.m1[0];
1825             this.$lineDom[id].childNodes[1].style.left = (ps.m2[0] + ps.m1[0]) / 2 - Min - this.$lineDom[id].childNodes[1].offsetWidth / 2 + 4;
1826             Min = (ps.start[1] > ps.end[1] ? ps.end[1] : ps.start[1]);
1827             if (Min > ps.m2[1]) Min = ps.m2[1];
1828             if (Min > ps.m1[1]) Min = ps.m1[1];
1829             this.$lineDom[id].childNodes[1].style.top = (ps.m2[1] + ps.m1[1]) / 2 - Min - this.$lineDom[id].childNodes[1].offsetHeight / 2 - 4;
1830         }
1831         else this.$lineDom[id].childNodes[2].textContent = this.$lineData[id].name;
1832         if (this.$editable) {
1833             this.$lineData[id].alt = true;
1834         }
1835     },
1836     //删除转换线
1837     delLine: function (id) {
1838         if (!this.$lineData[id]) return;
1839         if (this.onItemDel != null && !this.onItemDel(id, "line")) return;
1840         if (this.$undoStack) {
1841             var paras = [id, this.$lineData[id]];
1842             this.pushOper("addLine", paras);
1843         }
1844         this.$draw.removeChild(this.$lineDom[id]);
1845         delete this.$lineData[id];
1846         delete this.$lineDom[id];
1847         if (this.$focus == id) this.$focus = "";
1848         --this.$lineCount;
1849         if (this.$editable) {
1850             //在回退新增操作时,如果节点ID以this.$id+"_line_"开头,则表示为本次编辑时新加入的节点,这些节点的删除不用加入到$deletedItem中
1851             if (id.indexOf(this.$id + "_line_") < 0)
1852                 this.$deletedItem[id] = "line";
1853             this.$mpFrom.hide().removeData("p");
1854             this.$mpTo.hide().removeData("p");
1855         }
1856         this.$lineOper.hide().removeData("tid");
1857     },
1858     //变更连线两个端点所连的结点
1859     //参数:要变更端点的连线ID,新的开始结点ID、新的结束结点ID;如果开始/结束结点ID是传入null或者"",则表示原端点不变
1860     moveLinePoints: function (lineId, newStart, newEnd, noStack) {
1861         if (newStart == newEnd) return;
1862         if (!lineId || !this.$lineData[lineId]) return;
1863         if (newStart == null || newStart == "")
1864             newStart = this.$lineData[lineId].from;
1865         if (newEnd == null || newEnd == "")
1866             newEnd = this.$lineData[lineId].to;
1867 
1868         //避免两个节点间不能有一条以上同向接连线
1869         for (var k in this.$lineData) {
1870             if ((newStart == this.$lineData[k].from && newEnd == this.$lineData[k].to))
1871                 return;
1872         }
1873         if (this.onLinePointMove != null && !this.onLinePointMove(lineId, newStart, newEnd)) return;
1874         if (this.$undoStack && !noStack) {
1875             var paras = [lineId, this.$lineData[lineId].from, this.$lineData[lineId].to];
1876             this.pushOper("moveLinePoints", paras);
1877         }
1878         if (newStart != null && newStart != "") {
1879             this.$lineData[lineId].from = newStart;
1880         }
1881         if (newEnd != null && newEnd != "") {
1882             this.$lineData[lineId].to = newEnd;
1883         }
1884         //重建转换线
1885         this.$draw.removeChild(this.$lineDom[lineId]);
1886         this.addLineDom(lineId, this.$lineData[lineId]);
1887         if (this.$editable) {
1888             this.$lineData[lineId].alt = true;
1889         }
1890     },
1891 
1892     //用颜色标注/取消标注一个结点或转换线,常用于显示重点或流程的进度。
1893     //这是一个在编辑模式中无用,但是在纯浏览模式中非常有用的方法,实际运用中可用于跟踪流程的进度。
1894     markItem: function (id, type, mark) {
1895         if (type == "node") {
1896             if (!this.$nodeData[id]) return;
1897             if (this.onItemMark != null && !this.onItemMark(id, "node", mark)) return;
1898             this.$nodeData[id].marked = mark || false;
1899             if (mark) {
1900                 this.$nodeDom[id].addClass("item_mark");
1901                 this.$nodeDom[id].css("border-color", GooFlow.prototype.color.mark);
1902             }
1903             else {
1904                 this.$nodeDom[id].removeClass("item_mark");
1905                 if (id != this.$focus) jq.css("border-color", "transparent");
1906             }
1907 
1908         } else if (type == "line") {
1909             if (!this.$lineData[id]) return;
1910             if (this.onItemMark != null && !this.onItemMark(id, "line", mark)) return;
1911             this.$lineData[id].marked = mark || false;
1912             if (GooFlow.prototype.useSVG != "") {
1913                 if (mark) {
1914                     this.$lineDom[id].childNodes[1].setAttribute("stroke", GooFlow.prototype.color.mark || "#ff3300");
1915                     this.$lineDom[id].childNodes[1].setAttribute("marker-end", "url(#arrow2)");
1916                 } else {
1917                     this.$lineDom[id].childNodes[1].setAttribute("stroke", GooFlow.prototype.color.line || "#3892D3");
1918                     this.$lineDom[id].childNodes[1].setAttribute("marker-end", "url(#arrow1)");
1919                 }
1920             } else {
1921                 if (mark) this.$lineDom[id].strokeColor = GooFlow.prototype.color.mark || "#ff3300";
1922                 else this.$lineDom[id].strokeColor = GooFlow.prototype.color.line || "#3892D3"
1923             }
1924         }
1925         if (this.$undoStatck) {
1926             var paras = [id, type, !mark];
1927             this.pushOper("markItem", paras);
1928         }
1929     },
1930     ////////////////////////以下为区域分组块操作
1931     moveArea: function (id, left, top) {
1932         if (!this.$areaData[id]) return;
1933         if (this.onItemMove != null && !this.onItemMove(id, "area", left, top)) return;
1934         if (this.$undoStack) {
1935             var paras = [id, this.$areaData[id].left, this.$areaData[id].top];
1936             this.pushOper("moveNode", paras);
1937         }
1938         if (left < 0) left = 0;
1939         if (top < 0) top = 0;
1940         $("#" + id).css({ left: left + "px", top: top + "px" });
1941         this.$areaData[id].left = left;
1942         this.$areaData[id].top = top;
1943         if (this.$editable) {
1944             this.$areaData[id].alt = true;
1945         }
1946     },
1947     //删除区域分组
1948     delArea: function (id) {
1949         if (!this.$areaData[id]) return;
1950         if (this.$undoStack) {
1951             var paras = [id, this.$areaData[id]];
1952             this.pushOper("addArea", paras);
1953         }
1954         if (this.onItemDel != null && !this.onItemDel(id, "area")) return;
1955         delete this.$areaData[id];
1956         this.$areaDom[id].remove();
1957         delete this.$areaDom[id];
1958         --this.$areaCount;
1959         if (this.$editable) {
1960             //在回退新增操作时,如果节点ID以this.$id+"_area_"开头,则表示为本次编辑时新加入的节点,这些节点的删除不用加入到$deletedItem中
1961             if (id.indexOf(this.$id + "_area_") < 0)
1962                 this.$deletedItem[id] = "area";
1963         }
1964     },
1965     //设置区域分组的颜色
1966     setAreaColor: function (id, color) {
1967         if (!this.$areaData[id]) return;
1968         if (this.onAreaSetColor != null && !this.onAreaSetColor(id, color)) return;
1969         if (this.$undoStack) {
1970             var paras = [id, this.$areaData[id].color];
1971             this.pushOper("setAreaColor", paras);
1972         }
1973         if (color == "red" || color == "yellow" || color == "blue" || color == "green") {
1974             this.$areaDom[id].removeClass("area_" + this.$areaData[id].color).addClass("area_" + color);
1975             this.$areaData[id].color = color;
1976         }
1977         if (this.$editable) {
1978             this.$areaData[id].alt = true;
1979         }
1980     },
1981     //设置区域分块的尺寸
1982     resizeArea: function (id, width, height) {
1983         if (!this.$areaData[id]) return;
1984         if (this.onItemResize != null && !this.onItemResize(id, "area", width, height)) return;
1985         if (this.$undoStack) {
1986             var paras = [id, this.$areaData[id].width, this.$areaData[id].height];
1987             this.pushOper("resizeArea", paras);
1988         }
1989         var hack = 0;
1990         if (navigator.userAgent.indexOf("8.0") != -1) hack = 2;
1991         this.$areaDom[id].children(".bg").css({ width: width - 2 + "px", height: height - 2 + "px" });
1992         width = this.$areaDom[id].outerWidth();
1993         height = this.$areaDom[id].outerHeight();
1994         this.$areaDom[id].children("bg").css({ width: width - 2 + "px", height: height - 2 + "px" });
1995         this.$areaData[id].width = width;
1996         this.$areaData[id].height = height;
1997         if (this.$editable) {
1998             this.$areaData[id].alt = true;
1999         }
2000     },
2001     addArea: function (id, json, disabled) {
2002         if (!disabled)
2003             if (this.onItemAdd != null && !this.onItemAdd(id, "area", json)) return;
2004         if (this.$undoStack && this.$editable) {
2005             this.pushOper("delArea", [id]);
2006         }
2007         this.$areaDom[id] = $("<div id='" + id + "' class='GooFlow_area area_" + json.color + "' style='top:" + json.top + "px;left:" + json.left + "px'><div class='bg' style='width:" + (json.width - 2) + "px;height:" + (json.height - 2) + "px'></div>"
2008         + "<label>" + json.name + "</label><i></i><div><div class='rs_bottom'></div><div class='rs_right'></div><div class='rs_rb'></div><div class='rs_close'></div></div></div>");
2009         this.$areaData[id] = json;
2010         this.$group.append(this.$areaDom[id]);
2011         if (this.$nowType != "group") this.$areaDom[id].children("div:eq(1)").css("display", "none");
2012         ++this.$areaCount;
2013         if (this.$editable) {
2014             this.$areaData[id].alt = true;
2015             if (this.$deletedItem[id]) delete this.$deletedItem[id];//在回退删除操作时,去掉该元素的删除记录
2016         }
2017     },
2018     //重构整个流程图设计器的宽高
2019     reinitSize: function (width, height) {
2020         var w = (width || 800) - 2;
2021         var h = (height || 500) - 2;
2022         this.$bgDiv.css({ height: h + "px", width: w + "px" });
2023         var headHeight = 0, hack = 10;
2024         if (this.$head != null) {
2025             headHeight = 24;
2026             hack = 7;
2027         }
2028         if (this.$tool != null) {
2029             this.$tool.css({ height: h - headHeight - hack + "px" });
2030         }
2031         w -= 51;
2032         h = h - headHeight - (this.$head != null ? 5 : 8);
2033         this.$workArea.parent().css({ height: h + "px", width: w + "px" });
2034         this.$workArea.css({ height: h * 3 + "px", width: w * 3 + "px" });
2035         if (GooFlow.prototype.useSVG == "") {
2036             this.$draw.coordsize = w * 3 + "," + h * 3;
2037         }
2038         this.$draw.style.width = w * 3 + "px";
2039         this.$draw.style.height = +h * 3 + "px";
2040         if (this.$group) {
2041             this.$group.css({ height: h * 3 + "px", width: w * 3 + "px" });
2042         }
2043     }
2044 }
2045 GooFlow.prototype.color = {};
2046 //将此类的构造函数加入至JQUERY对象中
2047 jQuery.extend({
2048     createGooFlow: function (bgDiv, property) {
2049         return new GooFlow(bgDiv, property);
2050     }
2051 });
gooflow开源版js
  1 v\:group,v\:rect,v\:imagedata,v\:oval,v\:line,v\:polyline,v\:stroke,v\:textbox { display:inline-block;background:transparent }
  2 .GooFlow{
  3     background:#F5F5F5;border:1px solid #ccc;font: 0.8em Microsoft Yahei;
  4     -moz-user-select:none;-webkit-user-select:none;border-radius:4px;color:#333
  5 }
  6 .GooFlow i{font: 1em}
  7 .GooFlow_head{clear:both;height:28px;border-bottom: 2px solid #00B4E1;}
  8 .GooFlow_head label{
  9     font-weight:bold;display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;height:26px;line-height:26px;padding:2px;width:170px;text-align:center;
 10     background:#00B4E1;float:left;color:#fff;border-radius:3px 0px 0px 0px;overflow:hidden;margin:-1px 4px 0px -1px;
 11 }
 12 .GooFlow_head span{float:left;height:22px;width:0px;overflow:hidden;border-left:1px solid #3892D3;margin:0px 3px;}
 13 .GooFlow_head_btn{display:block;border:0px;height:28px;width:28px;cursor:default;margin:0px 3px;float:left;outline:none;blr:expression(this.onFocus=this.blur());}
 14 .GooFlow_head_btn i{display:inline-block;overflow:hidden;width:28px;height:28px;border:0px;}
 15 .GooFlow_head_btn:hover{border-radius:2px;background:#fff}
 16 .GooFlow_head_btn:hover i{}
 17 
 18 .GooFlow_tool{float:left;padding:0px 3px;overflow:hidden;clear:left;border-right:1px solid #ccc}
 19 .GooFlow_tool_div{overflow:hidden;border-radius:3px;width:35px;padding:2px}
 20 
 21 .GooFlow_tool span{height:0px;overflow:hidden;border-top:#ccc 1px solid;margin:5px 0;clear:both;display:block;}
 22 .GooFlow_tool_btn{display:block;border:0px;cursor:default;margin:5px 0;outline:none;blr:expression(this.onFocus=this.blur()); background:#00B4E1; border:3px solid #00B4E1;}
 23 .GooFlow_tool_btn i{display:block;overflow:hidden;width:28px;height:28px;border:0px}
 24 .GooFlow_tool_btn:hover{padding:0px;background:#438eb9}
 25 
 26 .GooFlow_tool_btndown{cursor:default;margin:5px 0;outline:none;blr:expression(this.onFocus=this.blur());
 27                       display:block; border:3px solid #00B4E1; background:#438eb9;}
 28 .GooFlow_tool_btndown i{display:block; width:28px; height:28px;}
 29 
 30 .GooFlow_work{float:left;margin:0px 3px 3px 0px;border:#F5F5F5 1px solid;position:relative;overflow:scroll;}
 31 .GooFlow_work .GooFlow_work_inner{background-image:url(img/gooflow_blank2.gif);position:relative;overflow:hidden;}
 32 .GooFlow_work .GooFlow_work_group{cursor:default;position:absolute;overflow:hidden;top:0px;left:0px}
 33 .GooFlow_work text{color:#fff}
 34 .GooFlow_area {cursor: default;position: absolute;overflow: hidden;}
 35 .GooFlow_area .lock{cursor:default;}
 36 .GooFlow_area .bg{cursor:move;filter:Alpha(Opacity=30);-moz-opacity:0.3;opacity: 0.3;}
 37 .GooFlow_work .lock .bg{cursor:default;}
 38 .GooFlow_area label{cursor:text;top:1px;left:1px;position:absolute;display:block;font-size:12px;text-indent:18px;height:18px;line-height:18px}
 39 .GooFlow_work .lock label{cursor:default;}
 40 .GooFlow_area i{display:block;height:18px;width:18px;top:0px;left:0px;position:absolute;cursor:pointer}
 41 .GooFlow_work .area_red .bg{border:1px solid red;background-color:#FF7865}
 42 .GooFlow_work .area_red label{color:red;background:url(img/gooflow_bullet.png) no-repeat 1px 1px}
 43 .GooFlow_work .area_yellow .bg{border:1px solid #CD925A;background-color:#FFD564}
 44 .GooFlow_work .area_yellow label{color:#FFBA1D;background:url(img/gooflow_bullet.png) no-repeat 1px -16px}
 45 .GooFlow_work .area_blue .bg{border:1px solid #347BB1;background-color:#549CDE}
 46 .GooFlow_work .area_blue label{color:#347BB1;background:url(img/gooflow_bullet.png) no-repeat 1px -33px}
 47 .GooFlow_work .area_green .bg{border:1px solid green;background-color:#84CA04}
 48 .GooFlow_work .area_green label{color:green;background:url(img/gooflow_bullet.png) no-repeat 1px -50px}
 49 
 50 .GooFlow_work svg{display:block;position:absolute}
 51 .GooFlow_work v\:group{position:relative;display:block}
 52 .GooFlow_work v\:group v\:line{overflow:visible}
 53 .GooFlow_work v\:group v\:polyline{overflow:visible}
 54 .GooFlow_work v\:group div{cursor:text;position:absolute;overflow:visible;display:inline;float:left;white-space: nowrap}
 55 .GooFlow_work .draw{color:#ff3300}
 56 
 57 .GooFlow_item{
 58     position:absolute;background:#A1DCEB;border:#A1DCEB solid 1px;
 59     border-radius:3px;background-color:#C1DCFC;box-shadow:1px 1px 2px rgba(99,99,99,2);
 60 }
 61 .GooFlow table{padding:1px;border-radius:2px}
 62 .GooFlow td{ vertical-align:middle;text-align:center;padding:0px;cursor:default;word-wrap:break-word;word-break:break-all}
 63 .GooFlow .ico{width:28px;cursor:move}
 64 .GooFlow i{display:block;width:28px;height:28px;overflow:hidden}
 65 
 66 .GooFlow .item_round{border-radius:11px;border:#C1DCFC solid 1px;width:35px;height:35px; overflow:visible}
 67 .GooFlow .item_round table{border:0px;padding:2px;width:35px;height:35px}
 68 .GooFlow .item_round .span{
 69     display:block;text-align:center; position:absolute;top:35px;left:-20px;width:80px;overflow:visible;text-align:center;
 70     padding:0px;cursor:default;word-wrap: break-word;word-break:break-all
 71 }
 72 .GooFlow .item_mix{background:#B6F700;border-color:#C2DB4E;color:#fff}
 73 .GooFlow div .rs_right{overflow:hidden;position:absolute;right:-1px;top:-1px;height:100%;width:6px;cursor:w-resize}
 74 .GooFlow div .rs_bottom{overflow:hidden;position:absolute;left:-1px;bottom:-1px;width:100%;height:6px;cursor:n-resize}
 75 .GooFlow div .rs_rb{
 76     position:absolute;right:-1px;bottom:-1px;width:9px;height:9px;overflow:hidden;cursor:nw-resize;background:url(img/gooflow_tip.png) no-repeat 0px -8px;
 77 }
 78 .GooFlow div .rs_close{
 79     position:absolute;right:1px;top:1px;width:7px;height:7px;overflow:hidden;cursor:pointer;background:url(img/gooflow_tip.png) no-repeat 0px 0px
 80 }
 81 .GooFlow .rs_ghost{
 82     position:absolute;display:none;overflow:hidden;border:#8EA4C1 1px dashed; background:#D9E8FB;
 83     filter:Alpha(Opacity=60);-moz-opacity:0.6;opacity: 0.6;z-index:10
 84 }
 85 .GooFlow .item_focus{border:#3892D3 1px solid}
 86 .GooFlow .item_mark{border:#ff3300 1px solid}
 87 .GooFlow .item_mark td{cursor:crosshair}
 88 .GooFlow textarea{position:absolute;border:#3892D3 1px solid;display:none;font: 1em Microsoft Yahei;overflow-y:visible;width:100px;z-index:1000}
 89 
 90 .GooFlow .GooFlow_line_oper{
 91     width:70px;height:15px;background-color:#D8E8FC;border:#7DA2CE 1px solid;position:absolute;
 92     filter:Alpha(Opacity=50);-moz-opacity:0.5;opacity: 0.5;z-index:1000;
 93 }
 94 
 95 .GooFlow .GooFlow_line_mp{
 96     width:9px;height:9px;filter:Alpha(Opacity=40);-moz-opacity:0.4;opacity:0.4;overflow:hidden;
 97     position:absolute;z-index:1000;background:#333;cursor:crosshair
 98 }
 99 
100 .GooFlow .GooFlow_line_move{filter:Alpha(Opacity=50);-moz-opacity:0.5;opacity:0.5;overflow:hidden;position:absolute;z-index:1000;}
101 .GooFlow .GooFlow_line_oper i{display:inline-block;width:15px;height:15px;margin-left:2px;cursor:pointer}
102 .GooFlow .b_l1{background:url(img/GooFlow_line_oper.png) no-repeat 1px 1px}
103 .GooFlow .b_l2{background:url(img/GooFlow_line_oper.png) no-repeat 1px -14px}
104 .GooFlow .b_l3{background:url(img/GooFlow_line_oper.png) no-repeat 1px -29px}
105 .GooFlow .b_x{background:url(img/GooFlow_line_oper.png) no-repeat 1px -44px;margin-left:10px}
106 
107 .GooFlow .ico_cursor{background:url(img/gooflow_icon.png) no-repeat -1px -41px}
108 .GooFlow .ico_start{background:url(img/gooflow_icon.png) no-repeat -121px -41px}
109 .GooFlow .ico_end{background:url(img/gooflow_icon.png) no-repeat -161px -41px}
110 .GooFlow .ico_fork{background:url(img/gooflow_icon.png) no-repeat -401px -41px}
111 .GooFlow .ico_join{background:url(img/gooflow_icon.png) no-repeat -441px -41px}
112 .GooFlow .ico_direct{background:url(img/gooflow_icon.png) no-repeat -81px -41px}
113 .GooFlow .ico_group{background:url(img/gooflow_icon.png) no-repeat -41px -81px}
114 .GooFlow .ico_complex{background:url(img/gooflow_icon.png) no-repeat -1px -81px}
115 .GooFlow .ico_node{background:url(img/gooflow_icon.png) no-repeat -241px -41px}
116 .GooFlow .ico_task{background:url(img/gooflow_icon.png) no-repeat -201px -41px}
117 .GooFlow .ico_chat{background:url(img/gooflow_icon.png) no-repeat -281px -41px}
118 .GooFlow .ico_state{background:url(img/gooflow_icon.png) no-repeat -321px -41px}
119 .GooFlow .ico_plug{background:url(img/gooflow_icon.png) no-repeat -361px -41px}
120 .GooFlow .ico_menu{background:url(img/gooflow_icon.png) no-repeat 0px -66px}
121 .GooFlow .ico_sound{background:url(img/gooflow_icon.png) no-repeat -19px -66px}
122 .GooFlow .ico_topo{background:url(img/gooflow_icon.png) no-repeat -118px -46px}
123 
124 .GooFlow .ico_open{background:url(img/gooflow_icon.png) no-repeat -40px 0}
125 .GooFlow .ico_new{background:url(img/gooflow_icon.png) no-repeat 0 0}
126 .GooFlow .ico_reload{background:url(img/gooflow_icon.png) no-repeat -200px 0}
127 .GooFlow .ico_save{background:url(img/gooflow_icon.png) no-repeat -80px 0}
128 .GooFlow .ico_undo{background:url(img/gooflow_icon.png) no-repeat -120px 0}
129 .GooFlow .ico_redo{background:url(img/gooflow_icon.png) no-repeat -160px 0}
130 .GooFlow .ico_close{background:url(img/gooflow_icon.png) no-repeat -320px 0}
131 
132 .GooFlow .ico_mutiselect{background:url(img/gooflow_icon.png) no-repeat -40px -40px}
gooflow开源版css
 1 @{
 2     ViewBag.Title = "流程图";
 3 }
 4 @section HeadCss{
 5     <style type="text/css">
 6         #id-div-east tr td div{
 7             line-height: 25px;
 8         }
 9     </style>
10 }
11 <div class="easyui-layout" id="id-div-layout" style="width:100%;height:100%">
12     <div data-options="region:'center'" style="overflow: hidden; border-width: 0px;">
13         <div id="flow"></div>
14         <div id="div-dialog-choose-condition" style="padding: 2px; overflow: hidden;"></div>
15         <div id="div-dialog-edit" style="padding:2px;"></div>
16     </div>
17     <div data-options="region:'east',split:true,title:'属性'" id="id-div-east" style="width: 350px; border-width: 0px 0px 0px 1px; overflow-x:hidden">
18         <table id="tt-grid" border="0"></table>
19     </div>
20 </div>
21 
22 <script type="text/html" id="participant-template">
23     @Html.Partial("FlowParticipantSetting")
24 </script>
25 <script type="text/html" id="scheme-template">
26     @Html.Partial("FlowSchemeSetting")
27 </script>
28 
29 
30 @section scripts{
31     <script type="text/javascript">
32         var picPage, myLayer;
33         seajs.use("flow/flow.picture", function (page) {
34             this.picPage = Class.create(page.PageClass);
35             this.myLayer = page.myLayer;
36         });
37     </script>
38 }
流程图设计界面
  1 define(function (require, exports, module) {
  2     require("utils");
  3     exports.myLayer = require("layer.extend");
  4     exports.PageClass = {
  5         initialize: function () {
  6             this.myGooFlow = require("flow/flow.core");
  7             this.flowID = utils.getUrlParam("flowID") || 0;
  8             this.focusElement = {};
  9         },
 10         initializeDOM: function () {
 11             this.$grid = $("#tt-grid");
 12         },
 13         initializeControl: function () {
 14             var that = this;
 15             var title = utils.getUrlParam1("title");
 16             var center = $('#id-div-layout').layout('panel', 'center');
 17             that.myGooFlow.initFlowPic($("#flow"), {
 18                 "title": title,
 19                 "flowID": that.flowID,
 20                 "container": center,
 21                 "grid": that.$grid,
 22                 "onClose": function () {
 23                     parent.myLayer.extend.closeAll();
 24                 }
 25             });
 26             center.panel({
 27                 onResize: function (w, h) {
 28                     gooFlow.reinitSize(w, h);
 29                 }
 30             });
 31 
 32             //#region 初始化属性菜单
 33             that.propertygrid = that.$grid.propertygrid({
 34                 showGroup: true,
 35                 scrollbarSize: 0,
 36                 //fit:true,
 37                 columns: [[
 38                     { field: 'name', title: '名称' },
 39                     {
 40                         field: 'value', title: '值', resizable: false, width: 150, formatter: function (value, row, index) {
 41                             return "<span title=" + value + ">" + value + "</span>"
 42                         }
 43                     }
 44                 ]],
 45                 onBeginEdit: function (rowIndex, rowData) {
 46                     var ed = that.$grid.datagrid('getEditor', { index: rowIndex, field: 'value' });
 47                     var options = $(ed.target).textbox("options");
 48                     if (ed.type == "combobox") {
 49                         $(ed.target).combobox("setValue", rowData.value);
 50                     }
 51                     else if (ed.type == "textbox") {
 52                         var txt = $(ed.target).textbox("textbox");
 53                         $(txt).keydown(function () { return false });
 54                         $(txt).click(function () {
 55                             if (options.title == "设置参与者") {
 56                                 var layerIndex = myLayer.extend.open({
 57                                     title: options.title,
 58                                     maxmin: false,
 59                                     area: ["700px","320px"],
 60                                     content: $("#participant-template").html() + "</" + "script>",
 61                                     success: function (layero, index) {
 62                                         picPage.participantLayer = layero;
 63                                         $("#id-hd-ParticipantType").attr("name", options.typeField);
 64                                         $("#id-hd-ParticipantText").attr("name", options.textField);
 65                                         $("#id-hd-ParticipantValue").attr("name", options.valueField);
 66                                     },
 67                                     yes: function (index) {
 68                                         saveForm(function () {
 69                                             var params = participantPage.$form.serializeArray();
 70                                             $(params).each(function (i, obj) {
 71                                                 var row = picPage.focusElement.rowData.where("o.field=='" + obj.name + "'")[0];
 72                                                 if (row) {
 73                                                     row.value = obj.value;
 74                                                 }
 75                                             });
 76 
 77                                             myLayer.extend.close(layerIndex);
 78                                             picPage.$grid.datagrid("refreshRow", rowIndex - 1);
 79                                             picPage.$grid.datagrid("refreshRow", rowIndex);
 80                                         });
 81                                     }
 82                                 });
 83                             }
 84                             else if (options.title == "设置方案") {
 85                                 var layerIndex = myLayer.extend.open({
 86                                     title: options.title,
 87                                     maxmin: false,
 88                                     area: ["700px", "320px"],
 89                                     content: $("#scheme-template").html() + "</" + "script>",
 90                                     success: function (layero, index) {
 91                                         var row = picPage.focusElement.rowData.where("o.field=='" + options.valueField + "'")[0];
 92                                         if (row && row.value) {
 93                                             var schemeValue = eval("(" + row.value + ")");
 94                                             $("#id-hd-schemeType").val(schemeValue.id);
 95                                         }
 96                                     },
 97                                     yes: function (index) {
 98                                         saveForm(options, function () {
 99                                             myLayer.extend.close(layerIndex);
100                                             picPage.$grid.datagrid("refreshRow", rowIndex);
101                                         });
102                                     }
103                                 });
104                             }
105                         });
106                     }
107                     else if (ed.type == "checkbox") {
108                         $(ed.target).attr("checked", rowData.value == "是");
109                     }
110                 },
111                 onEndEdit: function (rowIndex, rowData, changes) {
112 
113                 },
114                 onLoadSuccess: function () {
115                     $("#id-div-east").animate({ scrollTop: 0 }, 100);
116                 }
117             });
118 
119             that.propertygrid.loadData = function (rows) {
120                 var rowData = rows.where("!o.hidden");
121                 that.$grid.propertygrid("loadData", rowData);
122             }
123             //#endregion
124         },
125         initializeEvent: function () {
126         },
127         pageLoad: function () {
128             $("#id-div-layout").layout('panel', 'east').panel({
129                 onResize: function (w, h) {
130                     PropertygridFit($("#id-div-east .datagrid-view").height());
131                 }
132             });
133             var originalHeight;
134             setInterval(function () {
135                 var gridHeight = $("#id-div-east .datagrid-view").height();
136                 if (originalHeight != gridHeight) {
137                     PropertygridFit(gridHeight);
138                     originalHeight = gridHeight;
139                 }
140             }, 1000);
141         }
142     }
143 
144     function PropertygridFit(gridHeight) {
145         var east = $('#id-div-layout').layout('panel', 'east');
146         var h = east.height(), w = east.width();
147         if (gridHeight > h)
148             picPage.$grid.propertygrid("resize", { width: w - 18 });
149         else
150             picPage.$grid.propertygrid("resize", { width: w });
151     }
152 });
流程图设计js
  1 define(function (require, exports, module) {
  2     require("../../plugins/GooFlow/Gooflow.css");
  3     //require("../../plugins/GooFlow/default.css");
  4     require("../../plugins/GooFlow/GooFunc.js");
  5     require("../../plugins/GooFlow/GooFlow.js");
  6 
  7     var nodeRows = [], lineRows = [], areaRows = [],
  8         node_extra_attributes = [],
  9         line_extra_attributes = [],
 10         area_extra_attributes=[],
 11         gooflow;
 12     GooFlow.prototype.color = {
 13         main: "#00B4E1",
 14         node: "#A1DCEB",
 15         line: "#3892D3",
 16         mark: "#ff3300",
 17         mix: "#B6F700",
 18         font: "#15428B"
 19     };
 20     module.exports = {
 21         initFlowMap: function (jq, params) {
 22             var property = {
 23                 haveHead: true,
 24                 headLabel: true,
 25                 headBtns: [], //如果haveHead=true,则定义HEAD区的按钮
 26                 haveTool: false,
 27                 haveGroup: true,
 28                 useOperStack: false
 29             };
 30             gooFlow = $.createGooFlow(jq, property);
 31             gooFlow.setTitle(params.title + "·流程绘制");
 32             gooFlow.$editable = false;
 33             $(window).on("resize", function () {
 34                 gooFlow.reinitSize(params.container.width() - 5, params.container.height());
 35             });
 36             $(window).trigger('resize');
 37             //加载数据
 38             utils.showLoading(gooFlow.$bgDiv, "图形正在加载中,请稍候……");
 39             var queryParam = {
 40                 "type": "get",
 41                 "url": "/Flow/LoadWorkSpace?flowID=" + flowID,
 42                 "success": function (result) {
 43                     utils.hideLoading(gooFlow.$bgDiv);
 44                     params.callBack();
 45                 },
 46                 "error": function () {
 47                     myLayer.extend.alert('图形加载失败:' + result, 8, function () { utils.hideLoading(gooFlow.$bgDiv); });
 48                 }
 49             }
 50             gooFlow.loadDataAjax(queryParam);
 51         },
 52         initFlowPic: function (jq, params) {
 53             var remark = {
 54                 "cursor": "选择指针",
 55                 "direct": "节点连线",
 56                 "start": "开始节点",
 57                 "end": "结束节点",
 58                 "task": "普通节点",
 59                 "chat": "决策结点",
 60                 "group": "区域"
 61             }
 62             var property = {
 63                 toolBtns: ["start", "end", "task", "chat"],
 64                 headLabel: true,
 65                 haveHead: true,
 66                 headBtns: ["new", "save", "undo", "redo", "reload", "close"], //如果haveHead=true,则定义HEAD区的按钮
 67                 headBtnTitles: ["新增", "保存", "撤销", "撤回", "刷新", "退出"], //如果haveHead=true,则定义HEAD区的按钮
 68                 haveTool: true,
 69                 haveGroup: true,
 70                 useOperStack: true
 71             };
 72             gooFlow = $.createGooFlow(jq, property);
 73             gooFlow.setTitle(params.title + "·流程绘制");
 74             gooFlow.reinitSize(params.container.width(), params.container.height());
 75             //设定左侧工具栏中每一种节点或按钮的说明文字
 76             gooFlow.setNodeRemarks(remark);
 77             //加载gooflow数据
 78             utils.showLoading(gooFlow.$bgDiv, "图形正在加载中,请稍候……");
 79             gooFlow.loadDataAjax({
 80                 "type": "get",
 81                 "url": "/Flow/LoadWorkSpace?flowID=" + params.flowID,
 82                 "success": function (result) {
 83                     node_extra_attributes = result.node_extra_attributes;
 84                     line_extra_attributes = result.line_extra_attributes;
 85                     area_extra_attributes = result.area_extra_attributes;
 86                     var flowElement = $.parseJSON(result.flow_elements);
 87                     var rows = [];
 88                     //节点属性
 89                     for (var nodeID in flowElement.nodes) {
 90                         var node_extra_attrValues = result.extra_attribute_values.where("o.ElementID=='" + nodeID + "'");
 91                         rows = createAttributeData("node", nodeID, flowElement.nodes[nodeID], node_extra_attrValues);
 92                         nodeRows.push({ "elementID": nodeID, "rowData": rows, "ID": flowElement.nodes[nodeID].ID });
 93                     }
 94                     //连接线属性
 95                     for (var lineID in flowElement.lines) {
 96                         var line_extra_attrValues = result.extra_attribute_values.where("o.ElementID=='" + lineID + "'");
 97                         rows = createAttributeData("line", lineID, flowElement.lines[lineID], line_extra_attrValues);
 98                         lineRows.push({ "elementID": lineID, "rowData": rows, "ID": flowElement.lines[lineID].ID });
 99                     }
100                     //块属性
101                     for (var areaID in flowElement.areas) {
102                         var area_extra_attrValues = result.extra_attribute_values.where("o.ElementID=='" + areaID + "'");
103                         rows = createAttributeData("area", areaID, flowElement.areas[areaID], area_extra_attrValues);
104                         areaRows.push({ "elementID": areaID, "rowData": rows, "ID": flowElement.areas[areaID].ID });
105                     }
106 
107                     //加载流程属性
108                     var form_attributes = [
109                                { "name": "流程名称", "value": "" + result.flowData.FlowName + "", "group": "流程属性" },
110                                { "name": "表单地址", "value": "" + (result.flowData.FormUrl || "") + "", "group": "流程属性" },
111                                { "name": "是否禁用", "value": result.flowData.IsDisabled, "group": "流程属性" },
112                                { "name": "创建时间", "value": utils.dateFormatter(result.flowData.CreateOn, "yyyy-MM-dd hh:mm:ss"), "group": "流程属性" },
113                                { "name": "创建人", "value": result.flowData.CreateBy, "group": "流程属性" },
114                                { "name": "修改时间", "value": utils.dateFormatter(result.flowData.ModifiedOn, "yyyy-MM-dd hh:mm:ss"), "group": "流程属性" },
115                                { "name": "修改人", "value": result.flowData.ModifiedBy, "group": "流程属性" }
116                     ];
117                     params.grid.propertygrid("loadData", { 'total': form_attributes.length, "rows": form_attributes });
118                     utils.hideLoading(gooFlow.$bgDiv);
119                 },
120                 "error": function () {
121                     myLayer.extend.alert('图形加载失败:' + status, 8, function () { utils.hideLoading(gooFlow.$bgDiv); });
122                 }
123             });
124 
125             //#region 头部工具条事件
126             //新建流程
127             gooFlow.onBtnNewClick = function () {
128                 gooFlow.clearData();
129             }
130             //保存流程
131             gooFlow.onBtnSaveClick = function () {
132                 var flowData = [], n = 0;
133                 $(nodeRows).each(function (i, item) {
134                     flowData.push({ "name": "nodes[" + i + "].ID", "value": item.ID });
135                     $(item.rowData).each(function (j, row) {
136                         if (!row.attrID)
137                             flowData.push({ "name": "nodes[" + i + "]." + row.field, "value": row.value });
138                         else {
139                             flowData.push({ "name": "attrValues[" + n + "].ID", "value": row.attrValueID });
140                             flowData.push({ "name": "attrValues[" + n + "].ElementID", "value": item.elementID });
141                             flowData.push({ "name": "attrValues[" + n + "].AttrID", "value": row.attrID });
142                             flowData.push({ "name": "attrValues[" + n + "].AttrName", "value": row.field });
143                             flowData.push({ "name": "attrValues[" + n + "].AttrValue", "value": row.value });
144                             n++;
145                         }
146                     });
147                 });
148                 //连接线
149                 $(lineRows).each(function (i, item) {
150                     flowData.push({ "name": "lines[" + i + "].ID", "value": item.ID });
151                     $(item.rowData).each(function (j, row) {
152                         if (!row.attrID)
153                             flowData.push({ "name": "lines[" + i + "]." + row.field, "value": row.value });
154                         else {
155                             flowData.push({ "name": "attrValues[" + n + "].ID", "value": row.attrValueID });
156                             flowData.push({ "name": "attrValues[" + n + "].ElementID", "value": item.elementID });
157                             flowData.push({ "name": "attrValues[" + n + "].AttrID", "value": row.attrID });
158                             flowData.push({ "name": "attrValues[" + n + "].AttrName", "value": row.field });
159                             flowData.push({ "name": "attrValues[" + n + "].AttrValue", "value": row.value });
160                             n++;
161                         }
162                     });
163                 });
164                 //区域
165                 $(areaRows).each(function (i, item) {
166                     flowData.push({ "name": "areas[" + i + "].ID", "value": item.ID });
167                     $(item.rowData).each(function (j, row) {
168                         flowData.push({ "name": "areas[" + i + "]." + row.field, "value": row.value });
169                     });
170                 });
171                 utils.showLoading(gooFlow.$bgDiv, "数据正在保存中,请稍候……");
172                 flowData.push({ "name": "flowID", "value": params.flowID });
173                 $.ajaxPost({
174                     url: "/Flow/SaveFlowPicture",
175                     data: flowData,
176                     success: function (msg) {
177                         if (msg == "success") {
178                             myLayer.extend.alert('保存成功.', 9, function () { location.reload() });
179                         }
180                         else
181                             myLayer.extend.alert('保存失败:' + msg, 8);
182                         utils.hideLoading(gooFlow.$bgDiv);
183                     }
184                 });
185             }
186             //刷新
187             gooFlow.onFreshClick = function () {
188                 location.reload();
189             }
190             //关闭
191             gooFlow.onCloseClick = function () {
192                 params.onClose();
193             }
194             //#endregion
195 
196             //#region 流程元素事件
197             gooFlow.onItemAdd = function (id, type, element) {
198                 if (type == "line") {
199                     if (element.from == element.to) return;
200                     //防止添加重复的连接线
201                     var flag = true;
202                     $(lineRows).each(function (i, item) {
203                         var n = 0;
204                         $(item.rows).each(function (j, row) {
205                             if (row.field == "LineFrom" && row.value == element.from)
206                                 n += 1;
207                             if (row.field == "LineTo" && row.value == element.to)
208                                 n += 1;
209                         });
210                         if (n == 2) {
211                             flag = false;
212                             return false;
213                         }
214                     });
215                     if (!flag) return;
216                 }
217                 //获取并创建流程元素属性
218                 var rows = [];
219                 if (type == "node") {
220                     rows = createAttributeData(type, id, element);
221                     picPage.focusElement = { "type": "node", "elementID": id, "rowData": rows, "ID": "0" };
222                     nodeRows.push(picPage.focusElement);
223                 }
224                 else if (type == "line") {
225                     rows = createAttributeData(type, id, element);
226                     picPage.focusElement = { "type": "line", "elementID": id, "rowData": rows, "ID": "0" };
227                     lineRows.push(picPage.focusElement);
228                 }
229                 else if (type == "area") {
230                     rows = createAttributeData(type, id, element);
231                     picPage.focusElement = { "type": "area", "elementID": id, "rowData": rows, "ID": "0" };
232                     areaRows.push(picPage.focusElement);
233                 }
234                 //picPage.propertygrid.loadData(rows);
235                 return true;
236             }
237             gooFlow.onItemFocus = function (id, type) {
238                 FlowElementAction(id, type, "focus");
239                 return true;
240             }
241             //流程元素移动事件
242             gooFlow.onItemMove = function (id, type, left, top) {
243                 if (type == "node")
244                     FlowElementAction(id, type, "modify", { "NodeLeft": left, "NodeTop": top });
245                 else if (type == "area")
246                     FlowElementAction(id, type, "modify", { "AreaLeft": left, "AreaTop": top });
247                 return true;
248             }
249             //修改元素大小
250             gooFlow.onItemResize = function (id, type, width, height) {
251                 if (type == "node")
252                     FlowElementAction(id, type, "modify", { "NodeWidth": width, "NodeHeight": height });
253                 else if (type == "area")
254                     FlowElementAction(id, type, "modify", { "AreaWidth": width, "AreaHeight": height });
255                 return true;
256             }
257             gooFlow.onItemRename = function (id, name, type) {
258                 if (type == "node")
259                     FlowElementAction(id, type, "modify", { "NodeName": name });
260                 else if (type == "line")
261                     FlowElementAction(id, type, "modify", { "LineName": name });
262                 else if (type == "area")
263                     FlowElementAction(id, type, "modify", { "AreaName": name });
264                 return true;
265             }
266             //操作单元删除事件
267             gooFlow.onItemDel = function (id, type) {
268                 FlowElementAction(id, type, "delete");
269                 return true;
270             }
271             //连接线跟换类型事件
272             gooFlow.onLineSetType = function (id, type) {
273                 FlowElementAction(id, "line", "modify", { "LineType": type, "LineM": 0 });
274                 return true;
275             }
276             //移动某条折线中段
277             gooFlow.onLineMove = function (id, M) {
278                 FlowElementAction(id, "line", "modify", { "LineM": M });
279                 return true;
280             }
281             //更换线条节点事件
282             gooFlow.onLinePointMove = function (id, newStart, newEnd) {
283                 FlowElementAction(id, "line", "modify", { "LineFrom": newStart, "LineTo": newEnd });
284                 return true;
285             }
286             //区域修改颜色
287             gooFlow.onAreaSetColor = function (id, color) {
288                 FlowElementAction(id, "area", "modify", { "AreaColor": color });
289                 return true;
290             }
291             //#endregion
292         }
293     }
294 
295     //#region 创建gooflow元素的属性
296     function createAttributeData(type, id, element,attrValues) {
297         var rows = [];
298         if (type == "node") {
299             rows = [{ "field": "NodeID", "name": "节点ID", "value": "" + id + "", "group": "节点属性" },
300                      { "field": "NodeName", "name": "节点名称", "value": "" + element.name + "", "group": "节点属性" },
301                      { "field": "NodeType", "name": "节点类型", "value": "" + element.type + "", "group": "节点属性" },
302                      { "field": "NodeLeft", "name": "X坐标", "value": element.left, "group": "节点属性" },
303                      { "field": "NodeTop", "name": "Y坐标", "value": element.top, "group": "节点属性" },
304                      { "field": "NodeWidth", "name": "宽度", "value": element.width, "group": "节点属性" },
305                      { "field": "NodeHeight", "name": "高度", "value": element.height, "group": "节点属性" }];
306             $(node_extra_attributes).each(function (index, obj) {
307                 var editor = null;
308                 if (obj.Editor) {
309                     editor = eval('(' + obj.Editor + ')');
310                     if (editor.type == "combobox") {
311                         $.extend(editor.options, {
312                             valueField: 'text',
313                             textField: 'text',
314                             editable: false,
315                             panelHeight: 'auto'
316                         });
317                         obj.AttrValue = editor.options.defaultValue;
318                     }
319                     else if (editor.type == "textbox") {
320                         $.extend(editor.options, {
321                             buttonIcon: 'icon-clear',
322                             onClickButton: function () {
323                                 $(this).textbox('clear');
324                             }
325                         });
326                     }
327                 }
328                 var attrValue = obj.AttrValue || "";
329                 var attrValueID = obj.AttrValueID || 0;
330                 if (attrValues) {
331                     var attr = attrValues.where("o.ElementID=='"+id+"' && o.AttrID==" + obj.ID)[0];
332                     if (attr) {
333                         attrValue = attr.AttrValue;
334                         attrValueID = attr.ID;
335                     }
336                 }
337                 rows.push({
338                     "field": "" + obj.AttrName + "",
339                     "name": "" + obj.AttrTitle + "",
340                     "value": attrValue,
341                     "group": "" + obj.GroupName + "",
342                     "editor": editor,
343                     "hidden": !obj.ShowInList,
344                     "attrID": obj.ID,
345                     "attrValueID": attrValueID
346                 });
347             })
348         }
349         else if (type == "line") {
350             rows = [{ "field": "LineID", "name": "连接线ID", "value": "" + id + "", "group": "线条属性" },
351                     { "field": "LineName", "name": "连接线名称", "value": "" + element.name + "", "group": "线条属性" },
352                     { "field": "LineType", "name": "连接线类型", "value": "" + element.type + "", "group": "线条属性" },
353                     { "field": "LineFrom", "name": "开始节点ID", "value": element.from, "group": "线条属性" },
354                     { "field": "LineTo", "name": "结束节点ID", "value": element.to, "group": "线条属性" },
355                     { "field": "LineM", "name": "M值", "value": element.M || 0, "group": "线条属性" }];
356             $(line_extra_attributes).each(function (index, obj) {
357                 var editor = null;
358                 if (obj.Editor) {
359                     editor = eval('(' + obj.Editor + ')');
360                     if (editor.type == "textbox") {
361                         $.extend(editor.options, {
362                             buttonIcon: 'icon-clear',
363                             onClickButton: function () {
364                                 $(this).textbox('clear');
365                                 var row = picPage.focusElement.rowData.where("o.field=='" + editor.options.valueField + "'")[0];
366                                 if (row) row.value = "";
367                             }
368                         });
369                     }
370                 }
371                 var attrValue = obj.AttrValue || "";
372                 var attrValueID = obj.AttrValueID || 0;
373                 if (attrValues) {
374                     var attr = attrValues.where("o.ElementID=='" + id + "' && o.AttrID==" + obj.ID)[0];
375                     if (attr) {
376                         attrValue = attr.AttrValue;
377                         attrValueID = attr.ID;
378                     }
379                 }
380                 rows.push({
381                     "field": "" + obj.AttrName + "",
382                     "name": "" + obj.AttrTitle + "",
383                     "value": attrValue || "",
384                     "group": "" + obj.GroupName + "",
385                     "editor": editor,
386                     "hidden": !obj.ShowInList,
387                     "attrID": obj.ID,
388                     "attrValueID": attrValueID
389                 });
390             })
391         }
392         else if (type == "area") {
393             rows = [{ "field": "AreaID", "name": "分组区ID", "value": "" + id + "", "group": "分组区域属性" },
394                     { "field": "AreaName", "name": "分组区名称", "value": "" + element.name + "", "group": "分组区域属性" },
395                     { "field": "AreaLeft", "name": "X坐标", "value": element.left, "group": "分组区域属性" },
396                     { "field": "AreaTop", "name": "Y坐标", "value": element.top, "group": "分组区域属性" },
397                     { "field": "AreaWidth", "name": "宽度", "value": element.width, "group": "分组区域属性" },
398                     { "field": "AreaHeight", "name": "高度", "value": element.height, "group": "分组区域属性" },
399                     { "field": "AreaColor", "name": "颜色", "value": element.color, "group": "分组区域属性" }];
400         }
401         return rows;
402     }
403     //#endregion
404 
405     //#region 流程元素的操作公共方法
406     function FlowElementAction(id, type, action, changes) {
407         if (action == "focus") {
408             if (type == "node")
409                 picPage.focusElement = nodeRows.where("o.elementID=='" + id + "'")[0];
410             else if (type == "line")
411                 picPage.focusElement = lineRows.where("o.elementID=='" + id + "'")[0];
412             if (picPage.focusElement)
413                 picPage.propertygrid.loadData(picPage.focusElement.rowData);
414         }
415         else if (action == "modify") {
416             if (type == "area") {
417                 picPage.focusElement = areaRows.where("o.elementID=='" + id + "'")[0];
418                 picPage.propertygrid.loadData(picPage.focusElement.rowData);
419             }
420             for (var prop in changes) {
421                 var row = picPage.focusElement.rowData.where("o.field=='" + prop + "'")[0]
422                 row.value = changes[prop];
423                 var rowIndex = picPage.$grid.datagrid("getRowIndex", row);
424                 picPage.$grid.datagrid("refreshRow", rowIndex);
425             }
426         }
427         else if (action == "delete") {
428             if (type == "node")
429                 nodeRows.splice($.inArray(picPage.focusElement, nodeRows), 1);
430             else if (type == "line")
431                 lineRows.splice($.inArray(picPage.focusElement, lineRows), 1);
432             else if (type == "area")
433                 areaRows.splice($.inArray(picPage.focusElement, areaRows), 1);
434             picPage.propertygrid.loadData([]);
435 
436         }
437     }
438     //#endregion
439 
440 
441 
442     //propertygrid【checkbox】扩展方法
443 
444     $.extend($.fn.datagrid.defaults.editors, {
445         checkbox: {
446             init: function (container, options) {
447                 var input = $('<input type="checkbox">').appendTo(container);
448                 input.val(options.on);
449                 input.attr("offval", options.off);
450                 return input;
451             },
452             destroy: function (target) {
453                 $(target).remove();
454             },
455             getValue: function (target) {
456                 if ($(target).is(":checked")) {
457                     return $(target).val();
458                 } else {
459                     return $(target).attr("offval");
460                 }
461             },
462             setValue: function (target, value) {
463                 return $(target).val() == value ? true : false;
464             },
465             resize: function (target, width) {
466                 $(target)._outerWidth(width);
467             }
468         }
469     });
470 });
gooflow.core

暂时分享这么多把。

点击右下角【推荐】=================》为您提供演示地址。

联系方式:(QQ:512948935)

Gooflow0.6 UI界面下载

GooFlow0.8 ui界面下载

GooFlow1.1 ui界面下载

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:爱上MVC~Web.Config的Debug和Release版本介绍

下一篇:ASP.NET MVC 使用Remote特性实现远程属性验证