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



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

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

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

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

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

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

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

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

            8.  代码:   

      1 /**
      2  * 使用 jsPlumb 根据指定的拓扑数据结构绘制拓扑图
      3  * 使用 drawTopo_asyn(vmName, regionNo, parentDivId) 方法
      4  */
      5 /**
      6  * 初始化拓扑图实例及外观设置
      7  */
      8 (function() {
      9     
     10     jsPlumb.importDefaults({
     11         
     12         DragOptions : { cursor: 'pointer', zIndex:2000 },
     13     
     14         EndpointStyles : [{ fillStyle:'#225588' }, { fillStyle:'#558822' }],
     15     
     16         Endpoints : [ [ "Dot", { radius:2 } ], [ "Dot", { radius: 2 } ]],
     17     
     18         ConnectionOverlays : [
     19             [ "Label", { location:1 } ],
     20             [ "Label", { 
     21                 location:0.1,
     22                 id:"label",
     23                 cssClass:"aLabel"
     24             }]
     25         ]
     26     });
     27     
     28     var connectorPaintStyle = {
     29         lineWidth: 1,
     30         strokeStyle: "#096EBB",
     31         joinstyle:"round",
     32         outlineColor: "#096EBB",
     33         outlineWidth: 1
     34     };
     35     
     36     var connectorHoverStyle = {
     37         lineWidth: 2,
     38         strokeStyle: "#5C96BC",
     39         outlineWidth: 2,
     40         outlineColor:"white"
     41     };
     42     
     43     var endpointHoverStyle = {
     44         fillStyle:"#5C96BC"
     45     };
     46     
     47     window.topoDrawUtil = {
     48             
     49         sourceEndpoint: {
     50             endpoint:"Dot",
     51             paintStyle:{ 
     52                 strokeStyle:"#1e8151",
     53                 fillStyle:"transparent",
     54                 radius: 4,
     55                 lineWidth:2 
     56             },                
     57             isSource:true,
     58             maxConnections:-1,
     59             connector:[ "Flowchart", { stub:[40, 60], gap:1, cornerRadius:5, alwaysRespectStubs:true } ],                                                
     60             connectorStyle: connectorPaintStyle,
     61             hoverPaintStyle: endpointHoverStyle,
     62             connectorHoverStyle: connectorHoverStyle,
     63             dragOptions:{},
     64             overlays:[
     65                 [ "Label", { 
     66                     location:[0.5, 1.5], 
     67                     label:"",
     68                     cssClass:"endpointSourceLabel" 
     69                 } ]
     70             ]
     71         },
     72         
     73         targetEndpoint: {
     74             endpoint: "Dot",                    
     75             paintStyle: { fillStyle:"#1e8151",radius: 2 },
     76             hoverPaintStyle: endpointHoverStyle,
     77             maxConnections:-1,
     78             dropOptions:{ hoverClass:"hover", activeClass:"active" },
     79             isTarget:true,
     80             overlays:[
     81                 [ "Label", { location:[0.5, -0.5], label:"", cssClass:"endpointTargetLabel" } ]
     82             ]
     83         },
     84         
     85         initConnection: function(connection) {
     86             connection.getOverlay("label").setLabel(connection.sourceId + "-" + connection.targetId);
     87             connection.bind("editCompleted", function(o) {
     88                 if (typeof console != "undefined")
     89                     console.log("connection edited. path is now ", o.path);
     90             });
     91         },    
     92         
     93         removeAllEndPoints: function(nodeDivId) {
     94             jsPlumb.removeAllEndpoints($('#'+nodeDivId)); 
     95         },
     96         addEndpoints: function(toId, sourceAnchors, targetAnchors) {
     97             for (var i = 0; i < sourceAnchors.length; i++) {
     98                 var sourceUUID = toId + sourceAnchors[i];
     99                 var endPoint = jsPlumb.addEndpoint(toId, this.sourceEndpoint, { anchor:sourceAnchors[i], uuid:sourceUUID });    
    100                 endPoint.bind("click", function(endpoint) {
    101                     var anchorType = endpoint.anchor.type;
    102                     var nodeType = toId.split('_')[0];
    103                     var content = toId.split('_')[1];
    104                     if (nodeType == VM_TYPE) {
    105                         switch (anchorType) {
    106                             case 'Right':
    107                                 cacheKey = 'VM-DEVICE-'+ vmNodeData.key;
    108                                 cacheConnectionData[cacheKey] = null;
    109                                 linkDevices(vmNodeData, vmNodeData.key);
    110                                 break;
    111                             case 'Top':
    112                                 cacheKey = 'VM-ACCOUNT-'+ vmNodeData.key;
    113                                 cacheConnectionData[cacheKey] = null;
    114                                 vmName = vmNodeData.key;
    115                                 regionNo = vmNodeData.data.region_no;
    116                                 linkAccount(vmNodeData, vmName, regionNo);
    117                                 break;
    118                             case 'Bottom':
    119                                 cacheKey = 'VM-NC-'+ vmNodeData.key;
    120                                 cacheConnectionData[cacheKey] = null;
    121                                 ncId = vmNodeData.data.nc_id;
    122                                 regionNo = vmNodeData.data.region_no;
    123                                 linkNc(vmNodeData, ncId, regionNo);
    124                                 break;
    125                             case 'Left':
    126                                 cacheKey = 'VM-VIP-'+ vmNodeData.key;
    127                                 cacheConnectionData[cacheKey] = null;
    128                                 vmInnerIp = vmNodeData.data.vm_inner_ip;
    129                                 linkVips(vmNodeData, vmInnerIp);
    130                                 break;
    131                             default:
    132                                 break;
    133                         }
    134                     }
    135                     else if (nodeType == DEVICE_TYPE) {
    136                         if (anchorType == 'Bottom') {
    137                             cacheKey = 'DEVICE-SNAPSHOT-'+ content;
    138                             cacheConnectionData[cacheKey] = null;
    139                             deviceNodeData = deviceNodeDataMapping[content];
    140                             linkSnapshot(deviceNodeData.data.aliUid, content, deviceNodeData);
    141                         }
    142                     }
    143                 });
    144             }
    145             for (var j = 0; j < targetAnchors.length; j++) {
    146                 var targetUUID = toId + targetAnchors[j];
    147                 jsPlumb.addEndpoint(toId, this.targetEndpoint, { anchor:targetAnchors[j], uuid:targetUUID });                        
    148             }
    149         }
    150     };
    151 })();
    152 //////////////////////////////////////////////////////////////////////////////
    153 // 这里将所有用到的数据结构汇聚在这里, 避免修改时需要在代码中穿行, 浪费时间
    154 /**
    155  * 重新刷新VM关联实体时需要使用到VM的信息,这里进行全局缓存,避免重复查询
    156  * 重新刷新VM关联实体时VM必定存在, vmNodeData 也必定是最近一次查询的结果
    157  */
    158 var vmNodeData = {};
    159 /**
    160  * 重新刷新磁盘关联快照实体时需要使用到磁盘的信息,这里进行全局缓存,避免重复查询
    161  * 重新刷新磁盘关联快照实体时磁盘必定存在,且必定是最近一次查询的结果
    162  * eg. {'instanceId': { "ecsInstanceId": "vmName", "houyiDiskId": "102-80012003",
    163                         "aliUid": aliUidNum,  "instanceId": "d-28ilj8rsf", ... }}
    164  */
    165 var deviceNodeDataMapping = {};
    166 /**
    167  * 拓扑图中的节点类型
    168  */
    169 var nodeTypeArray = ['VM', 'DEVICE', 'NC', 'VIP', 'SNAPSHOT', 'CLUSTER', 'AVZ', 'ACCOUNT'];
    170 var VM_TYPE = nodeTypeArray[0];
    171 var DEVICE_TYPE = nodeTypeArray[1];
    172 var NC_TYPE = nodeTypeArray[2];
    173 var VIP_TYPE = nodeTypeArray[3];
    174 var SNAPSHOT_TYPE= nodeTypeArray[4];
    175 var CLUSTER_TYPE= nodeTypeArray[5];
    176 var AVZ_TYPE= nodeTypeArray[6];
    177 var ACCOUNT_TYPE= nodeTypeArray[7];
    178 /**
    179  * cacheConnectionData 节点之间的已有连接数目缓存, 在计算节点位置及布局方法 computeLayout 中用到
    180  * eg. {
    181  *    'VM-DEVICE-vmkey': 2,  'VM-NC-vmkey':1,  'VM-VIP-vmkey':2 , 'VM-ACCOUNT-vmkey': 1,
    182  * } 
    183  * 表示已经有2磁盘/1NC/2VIP/1ACCOUNT 与VM(key 为 vmkey)连接, 这些信息用于计算与VM相连接的同类型的下一个实体的相对位置
    184  */
    185 var cacheConnectionData = {};
    186 /**
    187  * 连接关系的存储, 在 cacheConnections , reconnectAll 方法中用到
    188  * 由于重复节点会移动到新的位置,原有连接会出现断连现象, 因此采用"每次刷新拉取新实体时重连所有连线" 的策略, 可以保证实时性, 只要连接数不多重复连接的开销是可以接受的.
    189  * connections = [[startPoint1, endPoint1], [startPoint2, endPoint2], ..., [startPointN, endPointN]];
    190  */
    191 var connections = [];
    192 /**
    193  * 实体与实体上附着点方向的设置
    194  * DEVICE_TYPE: [['Right', 'Bottom'], ['Left']] 的含义是:
    195  * 对于DEVICE实体: 作为起始节点时, 附着点可以在右方中点(连接CLUSTER), 下方中点(连接快照); 作为终止节点时, 附着点仅在左方中点(连接VM) 
    196  */
    197 var entityEndPointsMapping = {
    198     "VM": [['Top', 'Bottom', 'Right', 'Left'], []],
    199     "DEVICE": [['Right', 'Bottom'], ['Left']],
    200     "NC": [['Bottom'], ['Top']],
    201     "VIP": [[], ['Right']],
    202     "SNAPSHOT": [[], ['Top']],
    203     "CLUSTER": [[], ['Left', 'Top']],
    204     "AVZ": [['Bottom'], ['Top']],
    205     "ACCOUNT": [[], ['Bottom']]
    206 };
    207 /**
    208  * 连接线附着点方向设置
    209  * "VM-ACCOUNT": ['Top', 'Bottom'] 的含义是:
    210  * VM 的上方附着点 与 ACCOUNT 的下方附着点的连接
    211  */
    212 var connectionDirectionMapping = {
    213     "VM-ACCOUNT": ['Top', 'Bottom'],
    214     "VM-NC": ['Bottom', 'Top'],
    215     "NC-CLUSTER": ['Bottom', 'Top'],
    216     "VM-DEVICE": ['Right', 'Left'],
    217     "DEVICE-CLUSTER": ['Right', 'Left'],
    218     "VM-VIP": ['Left', 'Right'],
    219     "DEVICE-SNAPSHOT": ['Bottom', 'Top']
    220 }
    221 /**
    222  * 节点之间的水平与垂直相对位置
    223  */
    224 var largeVerticalDistance = 270;
    225 var verticalDistance = 220;
    226 var horizontalDistance = 300;
    227 var shortVerticalDistance = 50;
    228 var shortHorizontalDistance = 220;
    229 /**
    230  * 节点之间的水平或垂直相对位置和距离的设置
    231  * "VM-DEVICE": [largeVerticalDistance, horizontalDistance] 
    232  */
    233 var connectionDistanceMapping = {
    234     "VM-ACCOUNT": [-verticalDistance, 0],
    235     "VM-NC": [shortVerticalDistance, 0],
    236     "NC-CLUSTER": [shortVerticalDistance, 0],
    237     "VM-DEVICE": [largeVerticalDistance, horizontalDistance],
    238     "DEVICE-CLUSTER": [-108, horizontalDistance],
    239     "VM-VIP": [verticalDistance, -horizontalDistance],
    240     "DEVICE-SNAPSHOT": [shortVerticalDistance, shortHorizontalDistance]
    241 }
    242 /**
    243  * 根节点位置
    244  */
    245 rootPosition = [220, 360];
    246 rootTop = rootPosition[0];
    247 rootLeft = rootPosition[1];
    248 var parentDiv = null;
    249 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    250 function drawtopo_asyn(vmName, regionNo, parentDivId) {
    251     
    252     parentDiv = $('#'+parentDivId);
    253     
    254     var vmInfoReq = {
    255         'url':     httpPrefix + '/controllers/vm/obtainVmData',
    256         'params' : {
    257             'vm_name' : vmName,
    258             'region_no': regionNo
    259         }
    260     };
    261     var vmjq = doAjaxRequest(vmInfoReq);
    262     var vmInfoLoadedAfter = function(resultJson) {
    263         
    264         vmNodeData = resultJson.result.data;
    265         if (vmNodeData == null) {
    266             alert('没有找到VM的相关信息!');
    267             return ;
    268         }
    269         vmNode = createDiv(vmNodeData);
    270         
    271         vmDivId = obtainNodeDivId(vmNodeData);
    272         $('#'+vmDivId).css('top', rootTop + 'px');
    273         $('#'+vmDivId).css('left', rootLeft + 'px');
    274         
    275         linkAccount(vmNodeData, vmName, regionNo);
    276         
    277         ncId = vmNodeData.data.nc_id;
    278         linkNc(vmNodeData, ncId, regionNo);
    279         
    280         // vmName = 'ATX-28n2dhdq8';
    281         linkDevices(vmNodeData, vmName);
    282         
    283         vmInnerIp = vmNodeData.data.vm_inner_ip;
    284         linkVips(vmNodeData, vmInnerIp);
    285         
    286     };
    287     vmjq.done(vmInfoLoadedAfter);
    288 }
    289 function linkAccount(vmNodeData, vmName, regionNo) {
    290     var accountInfoReq = {
    291         'url': httpPrefix + '/controllers/vm/obtainAliyunAccountInfo',
    292         'params': {
    293             'vm_name' : vmName,
    294             'region_no': regionNo
    295         }
    296     };
    297     var accountjq = doAjaxRequest(accountInfoReq);
    298     accountjq.done(function(resultJson) {
    299         
    300         // for test
    301         // resultJson = {"result":{"msg":"successful","code":200,"data":{"errorCode":null,"errorMsg":null,"aliyunID":"it-cloudpc@alibaba-inc.com","kp":null},"success":true}};
    302         
    303         if (resultJson.result.success) {
    304             accountData = resultJson.result.data;
    305             accountNodeData = createAccountData(accountData);
    306             accountNode = createDiv(accountNodeData);
    307             cacheConnections(vmNodeData, accountNodeData, obtainConnectionDirections(vmNodeData, accountNodeData));
    308             reconnectAll(connections);
    309         }
    310         else {
    311             $('#error').append('获取关联云账号信息失败!');
    312         }
    313     }).fail(function() {
    314         $('#error').append('获取关联云账号信息失败! ');
    315     });
    316     
    317 }
    318 function linkNc(vmNodeData, ncId, regionNo) {
    319     var ncInfoReq = {
    320         'url':     httpPrefix + '/controllers/nc/listNc',
    321         'params': {
    322             'region_no': regionNo,
    323             'nc_id': ncId,
    324             'start': 0,
    325             'page': 1,
    326             'limit': 1
    327         }
    328     };
    329     var ncjq = doAjaxRequest(ncInfoReq);
    330     ncjq.done(function(resultJson) {
    331         ncDataList = resultJson.data;
    332         if (ncDataList.length > 0) {
    333             ncData = ncDataList[0];
    334             ncNodeData = createNcData(ncData);
    335             ncNode = createDiv(ncNodeData);
    336             cacheConnections(vmNodeData, ncNodeData, obtainConnectionDirections(vmNodeData, ncNodeData));
    337             
    338             ncClusterNodeData = createNcClusterData(ncData);
    339             ncClusterNode = createDiv(ncClusterNodeData);
    340             cacheConnections(ncNodeData, ncClusterNodeData, obtainConnectionDirections(ncNodeData, ncClusterNodeData));
    341             reconnectAll(connections);
    342         }
    343         else {
    344             $('#error').append('获取关联NC实体失败!');
    345         }
    346     }).fail(function() {
    347         $('#error').append('获取关联NC实体失败!');
    348     });
    349 }
    350 function linkDevices(vmNodeData, vmName) {
    351     var deviceInfoReq = {
    352         'url' :  httpPrefix + '/controllers/disk/search',
    353         'params': {
    354             'vmName': vmName
    355         }
    356     }
    357     var regionPeNickName = vmNodeData.data.region_pe_nickname;
    358     var devicejq = doAjaxRequest(deviceInfoReq);
    359     devicejq.done(function(resultJson) {
    360         
    361         total = resultJson.data.total;
    362         if (total > 0) {
    363             devices = resultJson.data.list;
    364             
    365             for (var i=0; i<total; i++) {
    366                 
    367                 deviceData = devices[i];
    368                 deviceData['regionPeNickName'] = regionPeNickName;
    369                 deviceNodeData = createDeviceData(deviceData);
    370                 deviceNodeDataMapping[deviceData.instanceId] = deviceNodeData;
    371                 deviceNode = createDiv(deviceNodeData);
    372                 cacheConnections(vmNodeData, deviceNodeData, obtainConnectionDirections(vmNodeData, deviceNodeData));
    373                 
    374                 deviceClusterNodeData = createDeviceClusterData(deviceData);
    375                 deviceClusterNode = createDiv(deviceClusterNodeData);
    376                 cacheConnections(deviceNodeData, deviceClusterNodeData, obtainConnectionDirections(deviceNodeData,deviceClusterNodeData));
    377                 linkSnapshot(devices[i].aliUid, devices[i].instanceId, deviceNodeData);
    378             }
    379             reconnectAll(connections);
    380         }
    381         else {
    382             $('#error').append('该VM没有关联的磁盘实体!');
    383         }
    384     }).fail(function() {
    385         $('#error').append('获取关联磁盘实体失败!');
    386     });
    387 }
    388 function linkVips(vmNodeData, vmInnerIp) {
    389     
    390     var vipInfoReq = {
    391         'url': httpPrefix + '/controllers/slbvip/listVip',
    392         'params': {
    393             'realserver_param': vmInnerIp,
    394             'start': 0,
    395             'page': 1,
    396             'limit': 100
    397         }
    398     };
    399     var vipjq = doAjaxRequest(vipInfoReq);
    400     vipjq.done(function(resultJson) {
    401         
    402         total = resultJson.total;
    403         vips = resultJson.data;
    404         if (total > 0) {
    405             for (j=0; j<total; j++) {
    406                 var vipInfo = vips[j];
    407                 vipNodeData = createVipData(vipInfo);
    408                 vipNode = createDiv(vipNodeData);
    409                 cacheConnections(vmNodeData, vipNodeData, obtainConnectionDirections(vmNodeData,vipNodeData));
    410             }
    411             reconnectAll(connections);
    412         }
    413         else {
    414             $('#error').append('该VM没有关联的VIP实体!');
    415         }
    416     }).fail(function() {
    417         $('#error').append('获取关联VIP实体失败!');
    418     });
    419     
    420 }
    421 function linkSnapshot(aliUid, diskId, deviceNodeData) {
    422     
    423     var snapshotInfoReq = {
    424         'url': httpPrefix + '/controllers/snapshot/search',
    425         'params': {
    426             'aliUid': aliUid,
    427             'diskId': diskId
    428         }
    429     };
    430     var snapshotjq = doAjaxRequest(snapshotInfoReq);
    431     snapshotjq.done(function(resultJson) {
    432         
    433         total = resultJson.total;
    434         if (total > 0) {
    435             snapshotDataList = resultJson.list;
    436             for (k=0; k<total; k++) {
    437                 snapshotData = snapshotDataList[k];
    438                 snapshotNodeData = createSnapshotData(snapshotData);
    439                 snapshotNode = createDiv(snapshotNodeData);
    440                 cacheConnections(deviceNodeData, snapshotNodeData, obtainConnectionDirections(deviceNodeData, snapshotNodeData));
    441             }
    442             reconnectAll(connections);
    443         }
    444         else {
    445             $('#error').append('磁盘 ' + diskId + ' 没有关联的快照实体!');
    446         }
    447     }).fail(function() {
    448         $('#error').append('磁盘' + diskId + ' 获取关联快照实体失败!');
    449     });
    450 }
    451 /**
    452  * createXXXData
    453  * 创建拓扑图所使用的节点数据  
    454  */
    455 function createVmData(vmData) {
    456     return {
    457         'type': 'VM',
    458         'key': vmData.vm_name,
    459         'data': vmData
    460     }
    461 }
    462 function createNcData(ncData) {
    463     return {
    464         'type': 'NC',
    465         'key': ncData.ip,
    466         'data': ncData
    467     }
    468 }
    469 function createNcClusterData(ncData) {
    470     return {
    471         'type': 'CLUSTER',
    472         'key': ncData.regionPeNickName,
    473         'data': {
    474             'regionNo': ncData.regionNo,
    475             'regionNickName': ncData.regionNickName,
    476             'regionPeNickName': ncData.regionPeNickName
    477         }
    478     }
    479 }
    480 function createDeviceData(deviceData) {
    481     return {
    482         'type': 'DEVICE',
    483         'key': deviceData.instanceId,
    484         'data': deviceData
    485     }
    486 }
    487 function createDeviceClusterData(deviceData) {
    488     return {
    489         'type': 'CLUSTER',
    490         'key': deviceData.regionNo,
    491         'data': {
    492             'regionNo': deviceData.regionNo
    493         }
    494     }
    495 }
    496 function createSnapshotData(snapshotData) {
    497     return {
    498         'type': 'SNAPSHOT',
    499         'key': snapshotData.snapshotId,
    500         'data': snapshotData 
    501     }
    502 }
    503 function createSnapshotClusterData(snapshotData) {
    504     return {
    505         'type': 'CLUSTER',
    506         'key': snapshotData.regionNo,
    507         'data': {
    508             'regionNo': snapshotData.regionNo
    509         }
    510     }
    511 }
    512 function createVipData(vipData) {
    513     return {
    514         'type': 'VIP',
    515         'key': vipData.vipAddress,
    516         'data': vipData
    517     }
    518 }
    519 function createAccountData(accountData) {
    520     return {
    521         'type': 'ACCOUNT',
    522         'key': accountData.aliyunID,
    523         'data': accountData
    524     }
    525 }
    526 /**
    527  * 缓存起始节点 beginNode 和终止节点 endNode 的连接关系 
    528  */
    529 function cacheConnections(beginNode, endNode, directions) {
    530     
    531     computeLayout(beginNode, endNode);
    532     
    533     var startPoint = obtainNodeDivId(beginNode) + directions[0];
    534     var endPoint = obtainNodeDivId(endNode) + directions[1];
    535     connections.push([startPoint, endPoint]);
    536 }
    537 /**
    538  * 计算节点位置及布局
    539  */
    540 function computeLayout(beginNode, endNode) {
    541     
    542     var beginDivId = obtainNodeDivId(beginNode);
    543     var endDivId = obtainNodeDivId(endNode);
    544     var beginNodeType = beginNode.type;
    545     var endNodeType = endNode.type;
    546     
    547     beginNodeTop = $('#'+beginDivId).offset().top;
    548     beginNodeLeft = $('#'+beginDivId).offset().left;
    549     
    550     var key = beginNodeType + '-' + endNodeType + '-' + beginNode.key;
    551     if (cacheConnectionData[key] == null) {
    552         cacheConnectionData[key] = -1;
    553     }
    554     else {
    555         cacheConnectionData[key] = cacheConnectionData[key]+1;
    556     }
    557     connNum = cacheConnectionData[key];
    558     
    559     var typeKey = beginNodeType + '-' + endNodeType;
    560     var relDistance = connectionDistanceMapping[typeKey];
    561     var relVertiDistance = relDistance[0];
    562     var relHoriDistance = relDistance[1];
    563     
    564     switch (beginNodeType) {
    565         case VM_TYPE:
    566             if (endNodeType == VIP_TYPE) {
    567                 endNodePosition = [beginNodeTop+connNum*relVertiDistance, beginNodeLeft+relHoriDistance];
    568             }
    569             else if (endNodeType == DEVICE_TYPE) {
    570                 endNodePosition = [beginNodeTop+connNum*relVertiDistance, beginNodeLeft+relHoriDistance];
    571             }
    572             else if (endNodeType == NC_TYPE) {
    573                 endNodePosition = [beginNodeTop+relVertiDistance, beginNodeLeft+relHoriDistance];
    574             }
    575             else if (endNodeType == ACCOUNT_TYPE) {
    576                 endNodePosition = [beginNodeTop+relVertiDistance, beginNodeLeft+relHoriDistance];
    577             }
    578             break;
    579         case DEVICE_TYPE:
    580             if (endNodeType == CLUSTER_TYPE) {
    581                 endNodePosition = [beginNodeTop+relVertiDistance, beginNodeLeft+relHoriDistance];
    582             }
    583             else if (endNodeType == SNAPSHOT_TYPE) {
    584                 endNodePosition = [beginNodeTop+relVertiDistance, beginNodeLeft+(connNum+1)*relHoriDistance];
    585             }
    586             break;
    587         case VIP_TYPE:
    588             break;
    589         case NC_TYPE:
    590             if (endNodeType == CLUSTER_TYPE) {
    591                 endNodePosition = [beginNodeTop+relVertiDistance, beginNodeLeft+relHoriDistance];
    592             }
    593             break;
    594         case SNAPSHOT_TYPE:
    595         default: 
    596             break;
    597     }
    598     
    599     $('#'+endDivId).css('top', endNodePosition[0] + 'px');
    600     $('#'+endDivId).css('left', endNodePosition[1] + 'px');
    601     
    602     addEndPoints(beginDivId, beginNodeType);
    603     addEndPoints(endDivId, endNodeType);
    604 }
    605 /**
    606  * 为节点添加用于连线的附着点
    607  * @param nodeDivId  节点的 DIV ID
    608  * @param type  节点类型
    609  */
    610 function addEndPoints(nodeDivId, type) {
    611     var startAttachedPoints = entityEndPointsMapping[type][0];
    612     var endAttachedPoints = entityEndPointsMapping[type][1];
    613     topoDrawUtil.addEndpoints(nodeDivId, startAttachedPoints, endAttachedPoints);
    614 }
    615 /**
    616  * 连接所有连线
    617  */
    618 function reconnectAll(connections) {
    619     
    620     var i=0;
    621     for (i=0; i<connections.length; i++) {
    622         jsPlumb.connect({uuids:connections[i], editable: false}); 
    623     }
    624     // 使所有拓扑节点均为可拉拽的                    
    625     jsPlumb.draggable(jsPlumb.getSelector(".node"), { grid: [5, 5] });
    626 }
    627 /**
    628  * div id cache , avoid duplicated div.
    629  * {'divId': 'divStr'}
    630  */
    631 divIdCache = {};
    632 /**
    633  * 为节点数据创建节点附着点并返回节点的DIV
    634  */
    635 function createDiv(metaNode) {
    636     var clickUrl = '';
    637     var display = '';
    638     var type = metaNode.type;
    639     var regionPeNickname = '';
    640     if (metaNode.data != null) {
    641         regionPeNickname = metaNode.data.regionPeNickName;
    642     }
    643     
    644     nodeDivId = obtainNodeDivId(metaNode);
    645     
    646     if (divIdCache[nodeDivId] != null) {
    647         // 该节点要移动到新的位置, 因此原来的附着点要去掉
    648         topoDrawUtil.removeAllEndPoints(nodeDivId);
    649         return divIdCache[nodeDivId];
    650     }
    651     
    652     switch(type.toUpperCase()) {
    653         case VM_TYPE:
    654             clickUrl = httpPrefix + '/framehtml/vm_monitor.html?vm_name=' + metaNode.key + '&data='+JSON.stringify(metaNode.data).replace(/"/g,"'");
    655             display = metaNode.key;
    656             break;
    657         case DEVICE_TYPE:
    658             displayDevice1 = metaNode.data.instanceId;
    659             clickDeviceUrl2 = httpPrefix + '/framehtml/device_monitor.html?device_id=' + metaNode.data.houyiDiskId + '®ion_pe_nickname='+regionPeNickname;
    660             displayDevice2 = metaNode.data.houyiDiskId;
    661             break;
    662         case NC_TYPE:
    663             var regionNo = metaNode.data.regionNo;
    664             clickUrl = httpPrefix + '/framehtml/nc_monitor.html?nc_ip=' + metaNode.key + '®ion_pe_nickname='+regionPeNickname + '®ion_no='+regionNo;
    665             display = metaNode.key;
    666             break;
    667         case VIP_TYPE:
    668             display = metaNode.key + ':' + metaNode.data.port;
    669             clickUrl = httpPrefix + '/framehtml/vip_monitor.html?vip=' + display + '®ion_pe_nickname='+regionPeNickname + '&slbdb_configId='+metaNode.data.slbdb_configId;
    670             break;
    671         case SNAPSHOT_TYPE:
    672             display = metaNode.key + '<br/>' + metaNode.data.houyiSnapshotId + '<br/><span style="color:green">'+ metaNode.data.regionNo + '</span>';
    673             break;
    674         case CLUSTER_TYPE:
    675         case AVZ_TYPE:
    676         case ACCOUNT_TYPE:
    677             display = metaNode.key;
    678             break;
    679         default:
    680             break;
    681     } 
    682     
    683     if (type == VM_TYPE || type == NC_TYPE || type == VIP_TYPE || type == ACCOUNT_TYPE ) {
    684         divStr =  '<div class="node biggerNode" id="' + nodeDivId + '"><strong>' 
    685                 + metaNode.type + '<br/><a href="' + clickUrl + '" target="_blank">' + display + '</a><br/></strong></div>';
    686     }
    687     else if (type == DEVICE_TYPE){
    688         divStr =  '<div class="node biggerNode" id="' + nodeDivId + '"><strong>' 
    689         + metaNode.type + '<br/>' + displayDevice1 + '<br/><a href="' + clickDeviceUrl2 + '" target="_blank">' + displayDevice2 + '</a><br/></strong></div>';
    690     }
    691     else {
    692         divStr = '<div class="node biggerNode" id="' + nodeDivId + '"><strong>' 
    693                 + metaNode.type + '<br/>' + display + '<br/></strong></div>';
    694     }
    695     parentDiv.append(divStr);
    696     
    697     divIdCache[nodeDivId] = divStr;
    698     return divStr;
    699 }
    700 function obtainConnectionDirections(srcNodeData, destNodeData) {
    701     var key = srcNodeData.type + '-' + destNodeData.type;
    702     var startDirection = connectionDirectionMapping[key][0];
    703     var endDirection = connectionDirectionMapping[key][1];
    704     return [startDirection, endDirection];
    705 }
    706 /**
    707  * 生成节点的 DIV id
    708  * divId = nodeType.toUpperCase + "_" + key
    709  * key 可能为 IP , 其中的 . 将被替换成 ZZZ , 因为 jquery id 选择器中 . 属于转义字符.
    710  * eg. {type: 'VM', key: '1.1.1.1' }, divId = 'VM_1ZZZ1ZZZ1ZZZ1'
    711  */
    712 function obtainNodeDivId(metaNode) {
    713     if (metaNode.type == VIP_TYPE) {
    714         return metaNode.type.toUpperCase() + '_' + transferKey(metaNode.key) + '_' + metaNode.data.port;
    715     }
    716     return metaNode.type.toUpperCase() + '_' + transferKey(metaNode.key);
    717 }
    718 function transferKey(key) {
    719     return key.replace(/./g, 'ZZZ').replace(/@/g,'YYY');
    720 }
    721 function revTransferKey(value) {
    722     return value.replace(/ZZZ/g, '.').replace('/YYY/g','@');
    723 }  
  • 相关阅读:
    tp5.1接入支付宝网站支付
    go类型转换
    mysql常见sql练习题
    php 获取当前时间的 前一小时、一天、一个月、一年
    php如何检测是否有环
    Go的切片:长度和容量
    go实现小项目
    知乎网页版不登录如何浏览内容
    图挂了
    centos搭建php环境
  • 原文地址:https://www.cnblogs.com/lovesqcc/p/4037689.html
Copyright © 2020-2023  润新知