使用jTopo生成拓扑图
后端数据的获取
public void dashboard() { Record user = getSessionAttr("user"); //获得设备数据 List<Map<String, Object>> roomAndDeviceList = IndexService.service.getRoomAndDevice(); setAttr("roomAndDeviceListJson", JsonKit.toJson(roomAndDeviceList)); render("dashboard.html"); }
获取列表数据roomAndDeviceList内的每个map的keys(机房room和该机房下的设备devices)
public List<Map<String, Object>> getRoomAndDevice(){ List<Map<String, Object>> list = new ArrayList<>(); List<Record> rooms = Db.find(Db.getSql("room.queryPageList")); for(Record room : rooms){ Map<String, Object> map = new HashMap<>(); String roomId = room.getStr("room_id"); List<Record> devices = Db.find(Db.getSqlPara("device.getDeviceByRoomId", roomId)); map.put("room", room); map.put("devices", devices); list.add(map); } return list; }
前段页面的数据渲染
目前还双击根节点后隐藏/显示 所有下级的连线和节点 还没完全弄好,待改善
#define room_topology(id) <script type="text/javascript" src="assets/lib/jtopo/jtopo-0.4.8-min.js" ></script> <div style="height:100%;width:100%;"> <canvas id="topology_#(id)" :width="canvasWidth" :height="canvasHeight" style="border: 1px solid rgb(68, 68, 68);background: rgb(32, 95, 181); cursor: default;"></canvas> <ul id="contextmenu" style="display:none;"> <li><a>顺时针旋转</a></li> <li><a>逆时针旋转</a></li> <li><a>更改颜色</a></li> <li><a>放大</a></li> <li><a>缩小</a></li> <li><a>删除该节点</a></li> </ul> </div> <script> $(function(){ var dtVue = new Vue({ el: "#topology_#(id)", data: { canvasWidth: 0, canvasHeight: 0 }, computed: { }, mounted: function (){ var $parent = $(this.$el.parentNode); this.canvasWidth = $parent.width(); this.canvasHeight = $parent.height(); $parent.css("overflow", "hidden"); }, methods: { renderTopology: function () { var vThis = this; var canvas = document.getElementById('topology_#(id)'); var stage = new JTopo.Stage(canvas); //显示工具栏 var scene = new JTopo.Scene(stage); var root = new JTopo.Node("政务数据安全管制系统管控中心"); root.font = '14px 微软雅黑'; // 字体 var x = this.canvasWidth/2-50; var y = this.canvasHeight/2-50;; root.setLocation(x, y); root.setSize(100, 100); root.setImage("assets/lib/jtopo/icon/vr-selfdefined.png"); root.layout = {type: 'circle', radius: 250}; root.addEventListener('mouseup', function(event){ if(event.button == 2){// 右键 // 当前位置弹出菜单(div) $("#contextmenu").css({ top: event.pageY, left: event.pageX }).show(); } }); //root节点双击后将所有连线和子节点设置显示/隐藏 TODO 二级节点device还未设置 root.dbclick(function(event){ if (root.layout.radius == 0) { root.layout.radius = 250; root.outLinks.forEach(function(item){ item.visible = true; item.nodeZ.visible = true; }) } else { root.layout.radius = 0; root.outLinks.forEach(function(item){ item.visible = false; item.nodeZ.visible = false; }) } JTopo.layout.layoutNode(scene, root, true); }) scene.add(root); //一级节点 rooms var roomAndDeviceList = #(roomAndDeviceListJson); for(var i=0; i<roomAndDeviceList.length; i++){ var room = roomAndDeviceList[i].room; var node = new JTopo.Node(room.room_name); node.setImage("assets/lib/jtopo/icon/mypc.png"); node.layout = {type: 'circle', radius: 100}; node.setSize(50, 50); node.setLocation(scene.width * Math.random(), scene.height * Math.random()); scene.add(node); var link = new JTopo.Link(root, node); scene.add(link); //二级节点 devices var devices = roomAndDeviceList[i].devices; for(var j=0; j<devices.length; j++){ var device = devices[j]; var deviceNode = new JTopo.Node(device.device_name); deviceNode.setImage("assets/lib/jtopo/icon/py_VM.png"); deviceNode.layout = {type: 'circle', radius: 50}; deviceNode.setSize(30, 30); deviceNode.setLocation(scene.width * Math.random(), scene.height * Math.random()); scene.add(deviceNode); scene.add(new JTopo.Link(node, deviceNode)); (function(device){ deviceNode.mouseover(function(deviceTarget){ vThis.showDeviceTip(deviceTarget, device) }); deviceNode.mouseout(function(){ $("#"+device.device_id).remove(); layer.closeAll(); }) })(device) } (function(room){ node.mouseover(function(event){ vThis.showRoomTip(event, room) }); node.mouseout(function(){ $("#"+room.room_id).remove(); layer.closeAll(); }) })(room) } // 重新渲染拓扑图布局 JTopo.layout.layoutNode(scene, root, true); scene.addEventListener('mouseup', function(e){ if(e.target && e.target.layout){ JTopo.layout.layoutNode(scene, e.target, true); } }); }, showRoomTip: function (event, room){ var target = $("<div style='width:50px;height:50px;position:fixed;'></div>").attr("id", room.room_id) .css({ top: event.target.y + 160, left: event.target.x + 260 }); $("body").append(target); var content = `<div style="min-width:200px;"> 服务器名: ${room.room_name} <br> 省: ${room.province_name} <br> 市: ${room.city_name} <br> 区: ${room.area_name} <br> 具体地址: ${room.room_address} <br> 负责人姓名: ${room.room_contacts} <br> 负责人电话: ${room.room_contacts_phone} <br> </div>`; layer.tips(content, "#"+room.room_id, { tips: [1, '#3595CC'], time: 0 }); }, showDeviceTip: function (deviceTarget, device){ var deviceTarget = $("<div style='width:50px;height:50px;position:fixed;'></div>").attr("id", device.device_id) .css({ top: deviceTarget.target.y + 160, left: deviceTarget.target.x + 260 }); $("body").append(deviceTarget); var content = `<div style="min-width:200px;"> 设备名: ${device.device_name} <br> 设备类型: ${device.device_type_tag} <br> 设备IP: ${device.ip} <br> 设备MAC: ${device.mac} <br> 维护人员姓名: ${device.maintainer_name} <br> 维护人员电话: ${device.maintainer_phone} <br> 所属单位: ${device.maintainer_org_name} <br> </div>`; layer.tips(content, "#"+device.device_id, { tips: [1, '#3595CC'], time: 0 }); } }, watch: { canvasheight: function(){ this.renderTopology(); }, canvasWidth: function(){ this.renderTopology(); } } }) }) </script> #end
页面效果
鼠标悬停到具体设备上时会出现一个小窗体展示详细信息
jTopo参考网址:http://www.jtopo.cn/demo/layout-circle.html
具体相关的jTopo用法和文档可到上面官网查看