首页 > 代码库 > 使用 jsPlumb 绘制拓扑图 —— 异步加载与绘制的实现

使用 jsPlumb 绘制拓扑图 —— 异步加载与绘制的实现


        本文实现的方法可以边异步加载数据边绘制拓扑图。 有若干点需要说明一下:

        1.  一次性获取所有数据并绘制拓扑图, 请参见文章: <使用 JsPlumb 绘制拓扑图的通用方法> ; 本文实现的最终显示效果与之类似, 所使用的基本方法与之类似。

        2.  在此次实现中, 可以一边异步加载数据一边绘制拓扑图, 是动态可扩展的; 

        3.  所有影响节点位置、布局的配置均放置在最前面, 便于修改, 避免在代码中穿梭, 浪费时间;

        4.  布局算法比之前的实现更加完善;

        5.  此实现由于与业务逻辑绑得比较紧, 可复用的部分不多, 但是可以作为一个模板, 用在读者自己的场景中, 自行修改相应的节点类型、URL等。

        6.  添加了附着点的点击事件处理, 可以刷新显示关联实体;

        7.  主流程很简单:  发送 AJAX 请求获取数据 ---> 创建节点(实际上就是DIV) ---> 计算节点位置、布局 ---> 添加节点附着点 ---> 缓存节点连接 ---> 连接所有现有的缓存节点连接。  多个 AJAX 请求的处理是异步的, 顺序没有控制。

        8.  代码:

        

/**
 * 使用 jsPlumb 根据指定的拓扑数据结构绘制拓扑图
 * 使用 drawTopo_asyn(vmName, regionNo, parentDivId) 方法
 */
/**
 * 初始化拓扑图实例及外观设置
 */
(function() {
    
    jsPlumb.importDefaults({
        
        DragOptions : { cursor: 'pointer', zIndex:2000 },
    
        EndpointStyles : [{ fillStyle:'#225588' }, { fillStyle:'#558822' }],
    
        Endpoints : [ [ "Dot", { radius:2 } ], [ "Dot", { radius: 2 } ]],
    
        ConnectionOverlays : [
            [ "Label", { location:1 } ],
            [ "Label", { 
                location:0.1,
                id:"label",
                cssClass:"aLabel"
            }]
        ]
    });
    
    var connectorPaintStyle = {
        lineWidth: 1,
        strokeStyle: "#096EBB",
        joinstyle:"round",
        outlineColor: "#096EBB",
        outlineWidth: 1
    };
    
    var connectorHoverStyle = {
        lineWidth: 2,
        strokeStyle: "#5C96BC",
        outlineWidth: 2,
        outlineColor:"white"
    };
    
    var endpointHoverStyle = {
        fillStyle:"#5C96BC"
    };
    
    window.topoDrawUtil = {
            
        sourceEndpoint: {
            endpoint:"Dot",
            paintStyle:{ 
                strokeStyle:"#1e8151",
                fillStyle:"transparent",
                radius: 4,
                lineWidth:2 
            },                
            isSource:true,
            maxConnections:-1,
            connector:[ "Flowchart", { stub:[40, 60], gap:1, cornerRadius:5, alwaysRespectStubs:true } ],                                                
            connectorStyle: connectorPaintStyle,
            hoverPaintStyle: endpointHoverStyle,
            connectorHoverStyle: connectorHoverStyle,
            dragOptions:{},
            overlays:[
                [ "Label", { 
                    location:[0.5, 1.5], 
                    label:"",
                    cssClass:"endpointSourceLabel" 
                } ]
            ]
        },
        
        targetEndpoint: {
            endpoint: "Dot",                    
            paintStyle: { fillStyle:"#1e8151",radius: 2 },
            hoverPaintStyle: endpointHoverStyle,
            maxConnections:-1,
            dropOptions:{ hoverClass:"hover", activeClass:"active" },
            isTarget:true,
            overlays:[
                [ "Label", { location:[0.5, -0.5], label:"", cssClass:"endpointTargetLabel" } ]
            ]
        },
        
        initConnection: function(connection) {
            connection.getOverlay("label").setLabel(connection.sourceId + "-" + connection.targetId);
            connection.bind("editCompleted", function(o) {
                if (typeof console != "undefined")
                    console.log("connection edited. path is now ", o.path);
            });
        },    
        
        removeAllEndPoints: function(nodeDivId) {
            jsPlumb.removeAllEndpoints($('#'+nodeDivId)); 
        },
        addEndpoints: function(toId, sourceAnchors, targetAnchors) {
            for (var i = 0; i < sourceAnchors.length; i++) {
                var sourceUUID = toId + sourceAnchors[i];
                var endPoint = jsPlumb.addEndpoint(toId, this.sourceEndpoint, { anchor:sourceAnchors[i], uuid:sourceUUID });    
                endPoint.bind("click", function(endpoint) {
                    var anchorType = endpoint.anchor.type;
                    var nodeType = toId.split('_')[0];
                    var content = toId.split('_')[1];
                    if (nodeType == VM_TYPE) {
                        switch (anchorType) {
                            case 'Right':
                                cacheKey = 'VM-DEVICE-'+ vmNodeData.key;
                                cacheConnectionData[cacheKey] = null;
                                linkDevices(vmNodeData, vmNodeData.key);
                                break;
                            case 'Top':
                                cacheKey = 'VM-ACCOUNT-'+ vmNodeData.key;
                                cacheConnectionData[cacheKey] = null;
                                vmName = vmNodeData.key;
                                regionNo = vmNodeData.data.region_no;
                                linkAccount(vmNodeData, vmName, regionNo);
                                break;
                            case 'Bottom':
                                cacheKey = 'VM-NC-'+ vmNodeData.key;
                                cacheConnectionData[cacheKey] = null;
                                ncId = vmNodeData.data.nc_id;
                                regionNo = vmNodeData.data.region_no;
                                linkNc(vmNodeData, ncId, regionNo);
                                break;
                            case 'Left':
                                cacheKey = 'VM-VIP-'+ vmNodeData.key;
                                cacheConnectionData[cacheKey] = null;
                                vmInnerIp = vmNodeData.data.vm_inner_ip;
                                linkVips(vmNodeData, vmInnerIp);
                                break;
                            default:
                                break;
                        }
                    }
                    else if (nodeType == DEVICE_TYPE) {
                        if (anchorType == 'Bottom') {
                            cacheKey = 'DEVICE-SNAPSHOT-'+ content;
                            cacheConnectionData[cacheKey] = null;
                            deviceNodeData = http://www.mamicode.com/deviceNodeDataMapping[content];>