轻量高效的拓扑图组件
zh

EasyUI + Qunee 组件同步示例

2014-04-25

HTML第三方组件种类繁多,小到按钮,工具栏,大到树图,表格,布局框架,以及各种图表,每种组件都有其优势,比如EasyUI的树和表格,Bootstrap的表单,Qunee的拓扑图等,一个应用需要整合多种组件,本文将以一个示例来介绍EasyUI与Qunee组件的同步使用 Qunee + EasyUI 同步示例

引入相关js和css文件

本例用到jquery, bootstrap, easyui和qunee,分别引入相关文件

<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布局框架

EasyUI支持东南西北中区域布局,类似Java Swing中的BorderLayout,本例中,左侧放置树图,中间为拓扑图

<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>

配置风格样式

本人喜欢简洁风格,故而删除了大部分的边框和背景,并采用了灰色模板

<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格式,数据如下: 节点数据包含编号、名称以及父节点编号等属性,而连线数据则需要起始结束节点的编号

{
    "nodes": [
        {
            "id": 1,
            "name": "001"
        },
        {
            "id": 2,
            "name": "R1",
            "parent": 1
        },
        ...
    ],
    "relations": [
        {
            "from": 1,
            "to": 2
        },
        {
            "from": 1,
            "to": 3
        },
        ...
    ]
}

加载数据

根据json数据,创建对应的图元数据和树节点数据

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;
        }
    }
}

到此时,界面已初步呈现了 Qunee + EasyUI 同步示例

组件状态同步

然后初始化工具栏,对树图和拓扑图填充数据,最后实现两组件的状态同步,包括选中同步和数据同步

首先数据同步

因为树图上不可编辑,所以这里只需要监听Graph组件图元的变化事件,在增加和删除图元时,分别对Tree组件进行处理

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事件派发器,实现双向同步,有了监听器,一切变得容易

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;
    }});
}

代码下载:

treeAndGraph.zip

在线示例:

http://demo.qunee.com/14-4-25/treeAndGraph.html

Next Prev