本文实现的方法可以边异步加载数据边绘制拓扑图。 有若干点需要说明一下:
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.