MxGraph框架實現web版流程圖
阿新 • • 發佈:2019-01-10
所實現的功能:指定現有節點,實現視覺化節點的【業務演算法】流程配置。
<%@ Page Title="" Language="C#" MasterPageFile="~/MasterPage/Add.Master" AutoEventWireup="true" CodeBehind="AlgGroupAdd.aspx.cs" Inherits="AutoMonitorWeb.Modules.Config.Algorithm.AlgGroup.AlgGroupAdd" %> <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHead" runat="server"> <script type="text/javascript"> mxBasePath = '<%=AutoMonitorWeb.BasePage.BasePage.Url%>Js/MxGraph/src/'; </script> <%=AutoMonitorWeb.Config.StaticFileVersionConfig.GetFileUrl("Js", "MxGraph")%> <style type="text/css"> .ulNode{width:100%;height:auto;} .ulNode li{float:left;border:solid 1px #a9caa1;margin:2px;line-height:18px;padding:2px;white-space:nowrap; } .ulNode li a{color:#333;text-decoration:none;display:block;} .ulNode li:hover{border:solid 1px #f00;font-weight:bolder;} .ulNodeAHover{border:solid 1px #f00 !important;font-weight:bolder;float:left;margin:2px;line-height:18px;padding:2px;background:#d3e9f3;} body div.mxPopupMenu { -webkit-box-shadow: 3px 3px 6px #C0C0C0; -moz-box-shadow: 3px 3px 6px #C0C0C0; box-shadow: 3px 3px 6px #C0C0C0; background: white; position: absolute; border: 3px solid #e7e7e7; padding: 3px; } body table.mxPopupMenu { border-collapse: collapse; margin: 0px; } body tr.mxPopupMenuItem { color: black; cursor: default; } body td.mxPopupMenuItem { padding: 6px 60px 6px 30px; font-family: Arial; font-size: 10pt; } body td.mxPopupMenuIcon { background-color: white; padding: 0px; } body tr.mxPopupMenuItemHover { background-color: #eeeeee; color: black; } table.mxPopupMenu hr { border-top: solid 1px #cccccc; } table.mxPopupMenu tr { font-size: 4pt; } </style> </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceMain" runat="server"> <input type="hidden" id="hdXMLContent" runat="Server" value='' /> <table width="100%" cellpadding="0" cellspacing="0" class="tableEdit"> <tr> <td class="tableField" width="10%"><span class="fontred">*</span>演算法名稱:</td> <td width="20%"> <input id="txtAlgName" type="text" valid="required" errmsg="必填!" runat="server"/> <span id="errMsg_<%=this.txtAlgName.UniqueID%>"></span> </td> <td class="tableField" width="10%"><span class="fontred">*</span>子演算法:</td> <td width="20%"> <asp:DropDownList ID="ddlIsSub" runat="server" valid="custom" custom="CommonJs.CheckSelect" errmsg="必選!"></asp:DropDownList> <span id="errMsg_<%=this.ddlIsSub.UniqueID%>"></span> </td> <td class="tableField" width="10%">備註:</td> <td> <textarea id="txtRemark" runat="server" cols="30" rows="5" style="width:80%;height:40px;"></textarea> </td> </tr> <tr> <td align="center" colspan="6"> <a href="javascript:void(0);" onclick="<%=this.JSVarName%>.SaveForm(this);return false;" class="easyui-linkbutton" data-options="iconCls:'icon-ok'" id="btnSave">儲存</a> </td> </tr> </table> <div style="width:100%;height:20px;text-align:left;line-height:20px;font-weight:bolder;"><a href="javascript:void(0);" id="aShowOrHideNodeInfo" style="color:#08a9b4;">【顯示與隱藏節點資訊】</a></div> <div id="divNode"> <asp:Repeater ID="repNode" runat="server" OnItemDataBound="repDataBound"> <ItemTemplate> <table border="0" width="100%" cellpadding="0" cellspacing="0" class="tableEdit"> <tr> <td width="10%" align="center" style="font-weight:bolder;font-size:14px;color:#68995c;"><%#Eval("DicName")%></td> <td width="90%" align="center"> <ul class="ulNode" sharp="<%#this.GetSharp(Eval("DicName"))%>" stepType="<%#Eval("DicName")%>"> <asp:Repeater ID="repNode" runat="server"> <ItemTemplate> <li> <a href="javascript:void(0);" value="<%#Eval("ID")%>" isSub="0"><%#Eval("StepName")%></a> <div class="divNodeMoreInfo" style="display:none;">節點名稱:<%#Eval("stepName")%><br/>節點型別:<%#Eval("stepType")%><br/>節點子型別:<%#Eval("stepSubType")%><br/>節點方法:<%#Eval("stepMethod")%><br/>告警資訊:<%#Eval("alarmMessage")%><br/>判斷方式:<%#Eval("judgeMode")%><br/>判斷條件:<%#Eval("judgeCondtion")%><br/>獲取資訊:<%#Eval("getInfo")%></div> </li> </ItemTemplate> </asp:Repeater> </ul> </td> </tr> </table> </ItemTemplate> </asp:Repeater> <table border="0" width="100%" cellpadding="0" cellspacing="0" class="tableEdit"> <tr> <td width="10%" align="center" style="font-weight:bolder;font-size:14px;color:#68995c;">子演算法:</td> <td width="90%" align="center"> <ul class="ulNode" sharp="cylinder" stepType="子演算法"> <asp:Repeater ID="repSub" runat="Server"> <ItemTemplate> <li> <a href="javascript:void(0);" value="<%#Eval("ID")%>" isSub="1"><%#Eval("algName")%></a> <div class="divNodeMoreInfo" style="display:none;">名稱:<%#Eval("algName")%><br />備註:<%#Eval("remark")%></div> </li> </ItemTemplate> </asp:Repeater> </ul> </td> </tr> </table> </div> <fieldset> <legend>操作說明:</legend> <ul> <li>1:所有葉子節點必須為【結束】型別,其它型別必須包含“是”和“否”的出口!</li> </ul> </fieldset> <div id="divCon" style="width:100%;height:400px;overflow:auto;position:absolute; background:url('<%=AutoMonitorWeb.BasePage.BasePage.Url%>Js/MxGraph/examples/editors/images/grid.gif');"></div> <script type="text/javascript"> var <%=this.JSVarName%> = { graph:null, model:null, Init: function () { var that=this; $divCon = $("#divCon"); $divNode=$("#divNode"); if (!mxClient.isBrowserSupported()) { mxUtils.error('當前瀏覽器不支援本程式!', 200, false); } else { mxConnectionHandler.prototype.connectImage = new mxImage('<%=AutoMonitorWeb.BasePage.BasePage.Url%>Modules/Config/Algorithm/MxGraphConfig/editors/images/connectorYes.gif', 16, 16); var container = $divCon[0]; if (mxClient.IS_IE) { new mxDivResizer(container); } that.model = new mxGraphModel(); that.graph = new mxGraph(container, that.model); that.graph.setTooltips(true); that.graph.getTooltipForCell = function(cell){ return $divNode.find("ul.ulNode[stepType='"+cell.systemStepType+"']").find("li a[value='"+cell.systemId+"']").parent().find("div.divNodeMoreInfo").html(); } $(".ulNode li a").live("click", function () { $(".ulNode li").removeClass("ulNodeAHover"); $liObj = $(this).closest("li"); $liObj.addClass("ulNodeAHover"); $ulObj = $(this).closest(".ulNode"); var shapV = $.trim($ulObj.attr("sharp")); var shapStr = shapV == "" ? "" : "shape=" + shapV; var parent = that.graph.getDefaultParent(); that.graph.getModel().beginUpdate(); //開始js事務更新 try{ var node = that.graph.insertVertex(parent, null, $(this).text(), 60, 10, $liObj.width() + 30, 30, shapStr); node["systemId"] = $(this).attr("value"); node["systemStepType"] = $ulObj.attr("stepType"); node["isSub"]=$(this).attr("isSub"); }finally{ that.graph.getModel().endUpdate(); } }) // 去鋸齒效果 mxRectangleShape.prototype.crisp = true; // 禁用瀏覽器自帶右鍵選單 mxEvent.disableContextMenu($divCon[0]); // 設定自動擴大滑鼠懸停 that.graph.panningHandler.autoExpand = true; // 覆寫右鍵單擊事件 that.graph.panningHandler.factoryMethod = function (menu, cell, evt) { menu.addItem('刪除已選元素', null, function () { that.graph.removeCells(that.graph.getSelectionCells()); return false; }); menu.addItem('刪除所有元素', null, function () { that.graph.removeCells(that.graph.getChildVertices(that.graph.getDefaultParent())); return false; }); menu.addItem('儲存表單', null, function () { that.SaveForm($("#btnSave")[0]); return false; }); }; var checkParentAndTarget = function (cell) { var flag = true; if (cell!=null&&cell.isEdge()) { if (cell.target == null || cell.source == null) { flag = false; } } if(!flag){ that.graph.removeCells([cell]); art.dialog("起始或結束位置不正確,請重新連線!"); } return flag; } that.graph.addListener(mxEvent.CELLS_ADDED, function (sender, evt) { var cell = evt.getProperty('cells')[0]; if (cell.isEdge()&&checkParentAndTarget(cell)) { art.dialog({ title: "請選擇連線型別", content: "<select id='selXCLEdgeYesNo'><option value='是'>是</option><option value='否'>否</option></select>", ok: function () { var v = $("#selXCLEdgeYesNo").val(); var isUnique=true; var childsCellEdgs=cell.source.edges; for(var i=0;i<childsCellEdgs.length;i++){ if(childsCellEdgs[i].source.id==cell.source.id){ if(childsCellEdgs[i].value!=null &&childsCellEdgs[i].value!=""&& childsCellEdgs[i].value==v){ isUnique=false;break; } } } if(isUnique){ cell.setValue(v); if (v == '是') { cell.setStyle("strokeColor=#0f0"); } else { cell.setStyle("strokeColor=#f00"); } that.graph.refresh(cell); }else{ that.graph.removeCells([cell]); art.dialog('此連線已存在,請重新連線!'); } }, cancel:function () { that.graph.removeCells([cell]); } }); } }); that.graph.addListener(mxEvent.MOVE_CELLS, function (sender, evt) { var cell = evt.getProperty('cells')[0]; checkParentAndTarget(cell); }) that.graph.addListener(mxEvent.CONNECT_CELL, function (sender, evt) {//這個比較特殊 var cell = evt.properties.edge; checkParentAndTarget(cell); }) that.graph.setConnectable(true); that.graph.setMultigraph(false); that.graph.getStylesheet().getDefaultEdgeStyle()['edgeStyle'] = 'orthogonalEdgeStyle'; // Creates the default style for vertices var style = []; style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_RECTANGLE; style[mxConstants.STYLE_PERIMETER] = mxPerimeter.RectanglePerimeter; style[mxConstants.STYLE_STROKECOLOR] = '#99bdc4'; style[mxConstants.STYLE_ROUNDED] = true; style[mxConstants.STYLE_FILLCOLOR] = '#c3f1e9'; style[mxConstants.STYLE_GRADIENTCOLOR] = 'white'; style[mxConstants.STYLE_FONTCOLOR] = '#056685'; style[mxConstants.STYLE_ALIGN] = mxConstants.ALIGN_CENTER; style[mxConstants.STYLE_VERTICAL_ALIGN] = mxConstants.ALIGN_MIDDLE; style[mxConstants.STYLE_FONTSIZE] = '13'; style[mxConstants.STYLE_FONTSTYLE] = 1; that.graph.getStylesheet().putDefaultVertexStyle(style); // Creates the default style for edges style = []; style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_CONNECTOR; style[mxConstants.STYLE_STROKECOLOR] = '#6482B9'; style[mxConstants.STYLE_ALIGN] = mxConstants.ALIGN_CENTER; style[mxConstants.STYLE_VERTICAL_ALIGN] = mxConstants.ALIGN_MIDDLE; style[mxConstants.STYLE_EDGE] = mxEdgeStyle.ElbowConnector; style[mxConstants.STYLE_ENDARROW] = mxConstants.ARROW_CLASSIC; style[mxConstants.STYLE_FONTSIZE] = '12'; that.graph.getStylesheet().putDefaultEdgeStyle(style); var keyHandler = new mxKeyHandler(that.graph); var rubberband = new mxRubberband(that.graph); that.graph.setCellsEditable(false); } $("#aShowOrHideNodeInfo").live("click",function () { $divNode.toggle(); }); $divNode.find("li a").each(function () { var currentATipHtml=$(this).parent().find("div.divNodeMoreInfo").html(); $(this).tooltip({ position: 'bottom', content:currentATipHtml }); }); <%=this.JsMsg%> }, SaveForm: function (obj) { var enc1 = new mxCodec(mxUtils.createXmlDocument()); var node1 = enc1.encode(this.graph.getModel()); var xml1 = mxUtils.getXml(node1); //檢查節點合法性 var errMsg=""; var allCells=this.graph.getChildCells(this.graph.getDefaultParent()); var checkIsLeaf=function(cell){//判斷節點是否為葉子 if(!cell.isVertex()) return false; var flag=true; if(cell.getEdgeCount()>0){ var edgs=cell.edges; for(var i=0;i<edgs.length;i++){ if(edgs[i].source.id==cell.id){ flag=false;break; } } } return flag; } var topCell=[]; for(var i=0;i<allCells.length;i++){ var cell=allCells[i]; if(cell.isEdge()){ //邊的source和target必須有值 if(cell.target==null||cell.source==null){ errMsg+=("邊【"+cell.value+"】必須指定開始和結束節點!<br/>"); } }else{ //判斷該節點是否存在於邊中的target or source if(cell.getEdgeCount()==0){ errMsg+=("節點【"+cell.value+"】必須作為邊的開始或結束位置!<br/>"); } if(checkIsLeaf(cell)){ if(cell.systemStepType!="結束"){ errMsg+=("葉子節點【"+cell.value+"】必須為(結束)型別!<br/>"); } }else{ var isTopCell=true; //非葉子節點不能為【結束】型別 if(cell.systemStepType=="結束"){ errMsg+=("非葉子節點【"+cell.value+"】不能為(結束)型別!<br/>"); } //非葉子節點必須有【是】和【否】出口 var currentVEdge=cell.edges; var currentVEdgeTo=[]; for(var k=0;k<currentVEdge.length;k++){ if(currentVEdge[k].source.id==cell.id){ currentVEdgeTo.push(currentVEdge[k]); } if(currentVEdge[k].target.id==cell.id){ isTopCell=false; } } var edgeCountOk=false; if(currentVEdgeTo.length==2){ if((currentVEdgeTo[0].value=="是"&¤tVEdgeTo[1].value=="否")||(currentVEdgeTo[0].value=="否"&¤tVEdgeTo[1].value=="是")){ edgeCountOk=true; } } if(!edgeCountOk){ errMsg+=("非葉子節點【"+cell.value+"】必須為有兩個出口([是]和[否])!<br/>"); } if(isTopCell){ topCell.push(cell); } } } } if(topCell.length!=1){ errMsg+=("只能存在一個根節點!<br/>"); } if(errMsg!=""){ art.dialog(errMsg); return false; } if (!XCLValid_SubmitForm()) { return false; } $("#<%=this.hdXMLContent.ClientID%>").val(escape(xml1)); $.XCLGoAjaxTab({obj:obj,url:"<%=Request.Url.AbsoluteUri%>"}); }, LoadXml: function (xmlfile) { if(xmlfile!=""){ xmlfile = "<?xml version='1.0' encoding='utf-8'?><mxGraphModel>"+xmlfile+"</mxGraphModel>"; var doc = mxUtils.parseXml(xmlfile); var dec = new mxCodec(doc); dec.decode(doc.documentElement, this.graph.getModel()); } } }; </script> </asp:Content>
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Text; using System.Data; using CommonClass.Enum; using System.Text.RegularExpressions; namespace AutoMonitorWeb.Modules.Config.Algorithm.AlgGroup { /// <summary> /// 演算法視覺化配置 /// </summary> public partial class AlgGroupAdd : BasePage.BasePage { protected string JsMsg = ""; protected int algXMLId = 0; protected void Page_Load(object sender, EventArgs e) { Int32.TryParse(Request.Params["algXMLId"] ?? "", out this.algXMLId); if (IsAjax) { this.AjaxMethod(); return; } if (!IsPostBack) { this.InitData(); } } /// <summary> /// 資料初始化 /// </summary> private void InitData() { this.CommonDataInit(); if (this.PageType == EnumPageType.修改) { AutoMonitorDLL.BLL.AlgXML bll = new AutoMonitorDLL.BLL.AlgXML(); AutoMonitorDLL.Model.AlgXML model = bll.GetModel(this.algXMLId); if (null != model) { this.txtAlgName.Value = model.algName; this.txtRemark.Value = model.remark; this.hdXMLContent.Value = Uri.EscapeDataString(model.xmlContext); this.JsMsg = string.Format("that.LoadXml('{0}');",model.xmlContext); this.ddlIsSub.SelectedValue = Convert.ToString(model.IsSub); } } } /// <summary> /// 頁面載入時已經初始化的資料,不分新增或編輯 /// </summary> private void CommonDataInit() { AutoMonitorDLL.BLL.AlgXML bll = new AutoMonitorDLL.BLL.AlgXML(); PowerMS.BLL.Sys_Common_Dic dicBLL = new PowerMS.BLL.Sys_Common_Dic(); CommonClass.Control.Control.RepDataBind<PowerMS.Model.Sys_Common_Dic>(this.repNode, dicBLL.GetModelChildListByLevelName("配置管理_演算法_演算法節點型別"), false); CommonClass.Control.Control.BindLst<EnumObj>(this.ddlIsSub, EnumObj.GetList(typeof(AutoMonitorDLL.SystemConfig.是否)), "text", "value", "", true); CommonClass.Control.Control.RepDataBind<AutoMonitorDLL.Model.AlgXML>(this.repSub, bll.GetModelList(string.Format("isdel=0 and isSub=1 {0}",this.algXMLId>0?" and id<>"+this.algXMLId:"" )),false); } /// <summary> /// ajax方法 /// </summary> private void AjaxMethod() { AutoMonitorDLL.BLL.AlgXML bll = new AutoMonitorDLL.BLL.AlgXML(); AutoMonitorDLL.Model.AlgXML model = null; StringBuilder strMsg = new StringBuilder(); int isReload = 0;//0:不重新整理父頁面 1:重新整理父頁面 switch (this.PageType) { case EnumPageType.新增: #region 新增 model = new AutoMonitorDLL.Model.AlgXML(); strMsg.Append(this.CommonModel(model)); if (strMsg.Length == 0) { if (bll.AddWithTran(model)) { strMsg.Append("新增成功!"); isReload = 1; } else { strMsg.Append("新增失敗!"); } } #endregion break; case EnumPageType.修改: #region 修改 model = bll.GetModel(this.algXMLId); if (null != model) { strMsg.Append(this.CommonModel(model)); if (strMsg.Length == 0) { if (bll.UpdateWithTran(model)) { strMsg.Append("更新成功!"); isReload = 1; } else { strMsg.Append("更新失敗!"); } } } else { strMsg.Append("該記錄不存在!"); } #endregion break; default: strMsg.Append("頁面操作型別不明確,操作失敗!"); break; } Response.Clear(); Response.Write(string.Format(@"{{""msg"":""{0}"",""isReload"":""{1}""}}", strMsg, isReload)); Response.End(); } /// <summary> /// 公共操作model部分 /// </summary> private string CommonModel(AutoMonitorDLL.Model.AlgXML model) { AutoMonitorDLL.BLL.AlgXML bll = new AutoMonitorDLL.BLL.AlgXML(); StringBuilder str = new StringBuilder(); #region 獲取引數 string algName = CommonClass.StringHander.StringUtil.NoHTML((Request.Params[this.txtAlgName.UniqueID] ?? "")); string xmlContext =CommonClass.StringHander.StringUtil.unescape(Request.Params[this.hdXMLContent.UniqueID] ?? ""); string remark = CommonClass.StringHander.StringUtil.NoHTML((Request.Params[this.txtRemark.UniqueID] ?? "")); int isSub = CommonClass.StringHander.Common.GetInt(Request.Params[this.ddlIsSub.UniqueID] ?? "-1"); #endregion #region 合法性檢測 if (string.IsNullOrEmpty(algName)) { str.Append("【演算法名稱】不能為空!<br/>"); } #region 不能重複 if ((this.PageType == EnumPageType.新增 && bll.Exists(algName)) || (this.PageType == EnumPageType.修改 && !string.Equals(algName, model.algName) && bll.Exists(algName))) { str.AppendFormat("演算法名稱【{0}】已存在!<br/>", algName); } #endregion #endregion #region model賦值 model.algName = algName; model.isDel = false; model.remark = remark; model.xmlContext = xmlContext.Replace("<mxGraphModel>", "").Replace("</mxGraphModel>", ""); model.IsSub = isSub; #endregion return str.ToString(); } protected void repDataBound(object sender, RepeaterItemEventArgs e) { if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem) { PowerMS.Model.Sys_Common_Dic model = (PowerMS.Model.Sys_Common_Dic)e.Item.DataItem; Repeater repNode = e.Item.FindControl("repNode") as Repeater; if (null != repNode) { AutoMonitorDLL.BLL.AlgorithmNode nodeBLL = new AutoMonitorDLL.BLL.AlgorithmNode(); List<AutoMonitorDLL.Model.AlgorithmNode> lst = nodeBLL.GetModelList(string.Format("isdel=0 and stepType='{0}'", model.DicValue)); lst = (null != lst && lst.Count > 0) ? lst.OrderBy(k => k.stepName).ToList() : lst; CommonClass.Control.Control.RepDataBind<AutoMonitorDLL.Model.AlgorithmNode>(repNode, lst, false); } } } protected string GetSharp(object o) { string str = ""; string s = Convert.ToString(o); switch (s) { case "條件判斷": str = "rhombus"; break; case "結束": str = "ellipse"; break; } return str; } } }