使用 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 }  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM