HTML第三方組件種類繁多,小到按鈕,工具欄,大到樹圖,表格,佈局框架,以及各類圖表,每種組件都有其優點,好比EasyUI的樹和表格,Bootstrap的表單,Qunee的拓撲圖等,一個應用須要整合多種組件,本文將以一個示例來介紹EasyUI與Qunee組件的同步使用javascript
本例用到jquery, bootstrap, easyui和qunee,分別引入相關文件css
<script type="text/javascript" src="http://demo.qunee.com/js/jquery/jquery.min.js"></script> <script type="text/javascript" src="http://demo.qunee.com/js/bootstrap/bootstrap.min.js?v=1.3"></script> <script type="text/javascript" src="http://demo.qunee.com/jquery-easyui-1.3.6/jquery.easyui.min.js"></script> <script src="http://demo.qunee.com/lib/qunee-min.js"></script> <script src="common.js"></script> <link rel="stylesheet" href="http://demo.qunee.com/js/bootstrap/bootstrap.min.css"/> <link rel="stylesheet" type="text/css" href="http://demo.qunee.com/jquery-easyui-1.3.6/themes/gray/easyui.css"> <link rel="stylesheet" type="text/css" href="http://demo.qunee.com/jquery-easyui-1.3.6/themes/icon.css">
EasyUI支持東南西北中區域佈局,相似Java Swing中的BorderLayout,本例中,左側放置樹圖,中間爲拓撲圖html
<body class="easyui-layout"> <div data-options="region:'west',split:true" border="false" style="width:200px;padding-left: 10px;"> <h3 style="border-bottom:1px solid #ddd;padding:0 0 3px 5px;margin-top: 0px;">拓撲視圖</h3> <ul id="tree" class="easyui-tree"></ul> </div> <div data-options="region:'north'" border="false" style="height:60px;"><h3 style="text-align: center;">Qunee + EasyUI 同步示例</h3></div> <div id="center_panel" data-options="region:'center'" style="padding-right: 10px;"> <div class="easyui-tabs" data-options="fit:true,border:false,plain:true"> <div title="網絡視圖" id="graph_panel" class="q-panel"> <div id="toolbar" class="q-toolbar"></div> <div id="canvas_panel" class="q-content"> <div id="canvas" class="q-canvas"></div> <div id="toolbox"></div> </div> </div> <div title="JSON" style="padding: 10px;" >JSON</div> </div> </div> <div id="footer" data-options="region:'south',border:false">Copyright © 2014 <a href="http://qunee.com">Qunee.com</a></div> </body>
本人喜歡簡潔風格,故而刪除了大部分的邊框和背景,並採用了灰色模板java
<style> #graph_panel { height: 100%; } .tabs-panels .panel-body{ border-left: solid 1px #DDD; border-right: solid 1px #DDD; } .tree-node { height: 20px; } .q-panel { padding-top: 40px; position: relative; } .q-toolbar { padding: 5px; } .q-panel .q-toolbar { position: absolute; top: 0px; height: 40px; width: 100%; z-index: 1; } .q-panel .q-content { height: 100%; background-color: #FFF; overflow: hidden; border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; position: relative; } .q-canvas { height: 100%; } #canvas_panel { position: relative; overflow: hidden; } #canvas { width: 100%; background-color: #FFF; outline: none; overflow: hidden; } #toolbar { background-color: #F8F8F8; border-bottom: solid 1px #DDD; padding: 5px; } #toolbar .btn, #toolbar .btn-group { margin-right: 5px; } #toolbar .btn-group .btn { margin-right: 0px; } #toolbox { position: absolute; top: 0px; background-color: #F8F8F8; padding: 5px; } #toolbox > img, #toolbox > button { display: block; padding: 8px 7px 0 7px; border-radius: 0px; } .layout-split-west { border-right: 5px solid rgba(255, 255, 255, 0); } #center_panel { border: none; } .panel { -webkit-box-shadow: none; box-shadow: none; } #footer { text-align: center; padding: 8px; border-top: solid 1px #DDD; background-color: #EEE; } .node_icon{ background: url('images/node_icon.png') no-repeat; background-size: 18px; background-position:center; } .edge_icon{ background: url('images/edge_icon.png') no-repeat; background-size: 18px; background-position:center; } </style>
這裏採用了模擬數據,使用json格式,數據以下:
節點數據包含編號、名稱以及父節點編號等屬性,而連線數據則須要起始結束節點的編號node
{ "nodes": [ { "id": 1, "name": "001" }, { "id": 2, "name": "R1", "parent": 1 }, ... ], "relations": [ { "from": 1, "to": 2 }, { "from": 1, "to": 3 }, ... ] }
根據json數據,建立對應的圖元數據和樹節點數據jquery
function initDatas(){ Q.loadJSON("testData.json", function(json){ var topoNodes = json.nodes; var relations = json.relations; initTopology(topoNodes,relations); graph.callLater(function(){ var layouter = new Q.TreeLayouter(graph); layouter.doLayout(); graph.moveToCenter(); }) var datas = []; var map = {}; graph.graphModel.forEachByBreadthFirst(function(d){ var name = d.name || d.type; var data = {text: name, id: d.id, iconCls: getTreeIcon(d)}; map[d.id] = data; var parent = d.parent; if(!parent){ datas.push(data); return; } parent = map[parent.id]; var children = parent.children; if(!children){ children = parent.children = []; } children.push(data); }); $('#tree').tree({ data: datas }); syncSelectionTreeAndGraph("tree", graph); syncDataTreeAndGraph("tree", graph); }); } function initTopology(topoNodes,topoRelations) { var map = {}; for(var i=0;i<topoNodes.length;i++) { var node = topoNodes[i]; var qNode = new Q.Node(); qNode.name=node.name; qNode.location = new Q.Point(node.x,node.y); graph.graphModel.add(qNode); map[node.id] = qNode; } for(var i=0;i<topoNodes.length;i++) { var node = topoNodes[i]; var parent = node.parent; if(parent){ parent = map[parent]; if(parent){ map[node.id].parent = parent; } } } for(var i=0;i<topoRelations.length;i++) { var relation = topoRelations[i]; var nodeFrom = map[relation.from]; var nodeTo = map[relation.to]; if(nodeFrom && nodeTo){ var edge = graph.createEdge(nodeFrom, nodeTo); edge.info = relation; } } }
到此時,界面已初步呈現了
web
而後初始化工具欄,對樹圖和拓撲圖填充數據,最後實現兩組件的狀態同步,包括選中同步和數據同步json
由於樹圖上不可編輯,因此這裏只須要監聽Graph組件圖元的變化事件,在增長和刪除圖元時,分別對Tree組件進行處理bootstrap
function syncDataTreeAndGraph(treeId, graph){ treeId = "#" + treeId; graph.listChangeDispatcher.addListener(function(evt){ var data = evt.data; switch (evt.kind) { case Q.ListEvent.KIND_ADD : var treeData = {data:[{id: data.id, text: data.name, iconCls: getTreeIcon(data)}]}; $(treeId).tree('append', treeData); break; case Q.ListEvent.KIND_REMOVE : Q.forEach(data, function(node){ var node = $(treeId).tree('find', node.id); if(node){ $(treeId).tree('remove', node.target); } }); break; case Q.ListEvent.KIND_CLEAR : break; } }); }
須要分別監聽Tree的"onSelect"事件,以及graph的selectionChangeDispatcher事件派發器,實現雙向同步,有了監聽器,一切變得容易canvas
function syncSelectionTreeAndGraph(treeId, graph){ treeId = "#" + treeId; var selectionAjdusting; graph.selectionChangeDispatcher.addListener(function(evt){ if(selectionAjdusting){ return; } selectionAjdusting = true; var selection = []; graph.selectionModel.forEach(function(node){ var node = $(treeId).tree('find', node.id); if(node){ selection.push(node.target); } }); $(treeId).tree('select', selection); selectionAjdusting = false; }); $(treeId).tree({onSelect: function(){ if(selectionAjdusting){ return; } selectionAjdusting = true; var selected = $(treeId).tree("getSelected"); if(selected){ var node = graph.getElement(selected.id); graph.selectionModel.set(node); if(node){ ensureVisible(node); } } selectionAjdusting = false; }}); }