diff --git a/nezha-fronted/package-lock.json b/nezha-fronted/package-lock.json index aeb4e0641..2c356909e 100644 --- a/nezha-fronted/package-lock.json +++ b/nezha-fronted/package-lock.json @@ -4716,8 +4716,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -4738,14 +4737,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4760,20 +4757,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -4890,8 +4884,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -4903,7 +4896,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -4918,7 +4910,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -4926,14 +4917,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -4952,7 +4941,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -5033,8 +5021,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -5046,7 +5033,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -5132,8 +5118,7 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -5169,7 +5154,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -5189,7 +5173,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -5233,14 +5216,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, diff --git a/nezha-fronted/src/components/charts/addLine.vue b/nezha-fronted/src/components/charts/addLine.vue new file mode 100644 index 000000000..1507550f5 --- /dev/null +++ b/nezha-fronted/src/components/charts/addLine.vue @@ -0,0 +1,72 @@ + + + + + diff --git a/nezha-fronted/src/components/charts/addNode.vue b/nezha-fronted/src/components/charts/addNode.vue new file mode 100644 index 000000000..6f9363bc4 --- /dev/null +++ b/nezha-fronted/src/components/charts/addNode.vue @@ -0,0 +1,62 @@ + + + + + diff --git a/nezha-fronted/src/components/charts/chart-list.vue b/nezha-fronted/src/components/charts/chart-list.vue index c2ada986a..7b57b1101 100644 --- a/nezha-fronted/src/components/charts/chart-list.vue +++ b/nezha-fronted/src/components/charts/chart-list.vue @@ -660,7 +660,7 @@ panelId: 0, title: this.$t("alert.config.chart.alertNumTrend"), span: 8, - height: 350, + height: 800, type: "topology", prev: -11, next: -1, diff --git a/nezha-fronted/src/components/charts/visNetwork.vue b/nezha-fronted/src/components/charts/visNetwork.vue index 472b46dc1..4d80c180b 100644 --- a/nezha-fronted/src/components/charts/visNetwork.vue +++ b/nezha-fronted/src/components/charts/visNetwork.vue @@ -1,5 +1,5 @@ @@ -67,11 +94,15 @@ import chartDataFormat from './chartDataFormat' import loading from "../common/loading"; import timePicker from '../common/timePicker' + import addNode from './addNode' + import addLine from './addLine' export default { name: 'home', components: { 'loading': loading, - 'time-picker':timePicker + 'time-picker':timePicker, + 'add-model':addNode, + 'add-line':addLine, }, props:{ chartIndex:{ @@ -83,6 +114,12 @@ }, }, watch:{ + NodeArr(n){ + if(n.length==2){ + this.addLineShow=true; + this.selectNodeTitle=false; + } + } }, data () { return { @@ -129,6 +166,7 @@ dragTitleShow:false, dropdownMenuShow:false, divFirstShow:false, + selectNodeTitle:false, columns: [ { title:'Element', @@ -150,39 +188,46 @@ searchTime: [new Date().setHours(new Date().getHours() - 1), new Date()],//全屏显示的时间 oldSearchTime: [], - + // 拓扑图工具 + networkPopTop:0, + networkPopLeft:0, + networkPopShow:false, + addNodeShow:false, + addLineShow:false, + cursorMove:false, + NodeArr:[], + selNodeId:'', + lineData:{}, + nodeData:{}, + //viewsCenter + viewsCenter:{ + x:0,y:0 + }, // 拓扑图数据 - nodes:[], edges:[], // network:null, container:null, nodesArray:[ - { id: 0, label: "model", - image: 'https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1906469856,4113625838&fm=26&gp=0.jpg', shape:'image',shapeProperties:{useImageSize:false}, - chosen:{ - node:(values, id, selected, hovering)=>{ - console.log(values, id, selected, hovering); - } - }, - physics:false, - x:0,y:0 + { id: 1, label: "model", + image: 'https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1906469856,4113625838&fm=26&gp=0.jpg', + shape:'image',shapeProperties:{useImageSize:false}, + x:0,y:0, }, - {id: 1, label: "model", x:0,y:10, physics:false,image: png, shape:'image',shapeProperties:{useImageSize:false}}, - {id: 2, label: "model", x:10,y:20, physics:false,image: png, shape:'image',shapeProperties:{useImageSize:false}}, - {id: 3, label: "model", x:10,y:30, physics:false,image: png, shape:'image',shapeProperties:{useImageSize:false}}, - {id: 4, label: "model", x:0,y:40, physics:false,image: png, shape:'image',shapeProperties:{useImageSize:false}}, - {id: 5, label: "model", x:0,y:50, physics:false,image: png, shape:'image',shapeProperties:{useImageSize:false}}, - {id: 6, label: "model", x:10,y:50, physics:false,image: png, shape:'image',shapeProperties:{useImageSize:false}}, + {id: 2, label: "model", x:-254,y:-59, image: png, shape:'image',shapeProperties:{useImageSize:false}}, + {id: 3, label: "model", x:114,y:-36, image: png, shape:'image',shapeProperties:{useImageSize:false}}, + {id: 4, label: "model", x:-126,y:54, image: png, shape:'image',shapeProperties:{useImageSize:false}}, + {id: 5, label: "model", x:125,y:67, image: png, shape:'image',shapeProperties:{useImageSize:false}}, + {id: 6, label: "model", x:-211,y:0, image: png, shape:'image',shapeProperties:{useImageSize:false}}, + {id: 7, label: "model", x:200,y:100, image: png, shape:'image',shapeProperties:{useImageSize:false}}, ], - edgesArray:[ - {id: 0,from: 1, to: 0,label:"hahah",arrows:'from;to'}, - {id: 1,from: 2, to: 0}, - {id: 2,from: 4, to: 3}, - {id: 3,from: 3, to: 0}, - {id: 4,from: 3, to: 5}, - {id: 5,from: 0, to: 6}, + {id: 0,from: 2, to: 1,label:"hahah",arrows:'from;to', color: {color:'red',highlight:'red',hover:'red',opacity:1.0},}, + {id: 1,from: 3, to: 1,label:"hahah",arrows:'from'}, + {id: 2,from: 5, to: 4,label:"hahah",arrows:'to'}, + {id: 3,from: 4, to: 1,label:"hahah",arrows:'from;to'}, + {id: 4,from: 4, to: 6,label:"hahah",arrows:'from;to'}, + {id: 5,from: 1, to: 7,label:"hahah",arrows:'from;to'}, ], options:{}, data:{} @@ -193,17 +238,20 @@ methods:{ //拓扑图方法 - init(){ + init(type){ let this_ = this; this_.nodes = new Vis.DataSet(this_.nodesArray); this_.edges = new Vis.DataSet(this_.edgesArray); this_.container = document.getElementById('network_id'); + this_.containermodal = document.getElementById('network_id2'); + this_.networkPop = document.getElementById('networkPop'); this_.data = { nodes: this_.nodes, edges: this_.edges }; this_.options = { autoResize: true, + clickToUse:true, groups:{ useDefaultGroups: true, myGroupId:{ @@ -212,18 +260,26 @@ }, nodes: { shape: 'dot', - size: 30, + size: 40, font: { - size: 32, + size: 16, }, - borderWidth: 2 + borderWidth: 2, + // chosen:{ + // node:(values, id, selected, hovering)=>{ + // console.log(values, id, selected, hovering); + // if(hovering){ + // this_.cursorMove=true; + // } + // } + // } }, edges: { width: 2, smooth:{ //设置两个节点之前的连线的状态 enabled: false //默认是true,设置为false之后,两个节点之前的连线始终为直线,不会出现贝塞尔曲线 - } + }, }, layout:{ randomSeed: 666, @@ -242,13 +298,13 @@ interaction:{ dragNodes: true, //是否能拖动节点 - dragView: false, //是否能拖动画布 + dragView: true, //是否能拖动画布 hover: true, //鼠标移过后加粗该节点和连接线 multiselect: false, //按 ctrl 多选 selectable: true, //是否可以点击选择 selectConnectedEdges: false, //选择节点后是否显示连接线 hoverConnectedEdges: true, //鼠标滑动节点后是否显示连接线 - zoomView: true, //是否能缩放画布 + zoomView: false, //是否能缩放画布 navigationButtons:true, }, @@ -256,9 +312,28 @@ enabled: false, }, }; - this_.network = new Vis.Network(this_.container, this_.data, this_.options); + if(type==='modal'){ + this_.network = new Vis.Network(this_.container, this_.data, this_.options); + this_.network.moveTo({ + position: this_.viewsCenter, + scale: 1, + offset: {x:0, y:0}, + }) + } + if(type=== 'screenModal'){ + if(!this_.networkScreenModal){ + this_.networkScreenModal= new Vis.Network(this_.containermodal, this_.data, this_.options); + }else{ + this_.nodes = new Vis.DataSet(this_.nodesArray); + this_.edges = new Vis.DataSet(this_.edgesArray); + this_.networkScreenModal.setData({ + nodes:this_.nodes, + edges: this_.edges + }); + }; + } + this_.containerCanvas=document.querySelectorAll("#network_id canvas")[0]; }, - resetAllNodes() { let this_ = this; this_.nodes.clear(); @@ -286,44 +361,76 @@ nodes:this_.nodes, edges: this_.edges }); + this_.network.moveTo({ + position: this_.viewsCenter, + scale: 1, + offset: {x:0, y:0}, + }) }, - addModel(){ // 添加model - console.log(this.network); - let model={ - id: 7, - label: "model", - group: 0, - image: 'https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1906469856,4113625838&fm=26&gp=0.jpg', - shape:'image', - shapeProperties:{useImageSize:false}, - chosen:{ - node:(values, id, selected, hovering)=>{ - console.log(values, id, selected, hovering); - } - }, - x:-100, - y:-100, - physics:false, - }; + addModel(model){ // 添加model + this.addNodeShow=false; + if(!model){return} + model={...model,...this.network.DOMtoCanvas({x:model.x,y:model.y})}; + if(!model.image){ + model.image=png + } this.nodesArray.push(model); - // this.resetAllNodes(); + this.setNetworkData(this.nodesArray,this.edgesArray); }, - addLine(){ // 添加line - let edges= {id: 7,from: 7, to: 0,label:"hahah",arrows:'from;to'}; + addModelShow(){ // 显示添加节点弹窗 + this.addNodeShow=true; + }, + addLine(edges){ // 添加line + this.addLineShow=false; + console.log(edges); + if(!edges){return} + edges.from=this.NodeArr[0]; + edges.to=this.NodeArr[1]; this.edgesArray.push(edges); this.setNetworkData(this.nodesArray,this.edgesArray); + this.NodeArr=[]; + }, + addLineTitleShow(){ // 显示添加线弹窗 + // this.addLineShow=true; + this.selectNodeTitle=true; + }, + closeAddLine(){ + this.selectNodeTitle=false; + this.NodeArr=[]; }, - setNodePosition(selId){ // 移动节点后 设置节点坐标 let position = this.network.getPositions([selId]); let selItem = this.nodesArray.find((item)=>item.id===selId); - console.log(selItem); + this.nodeData = selItem; selItem.x=position[selId].x; selItem.y=position[selId].y; }, - - + setPopPosition(selId,params){//设置节点工具栏位置 + let position=this.network.canvasToDOM(this.network.getPositions(params.nodes)[selId]); + this.networkPop.style.top = position.y - 70 +'px'; + this.networkPop.style.left = position.x - 30 +'px'; + this.networkPopShow=true; + }, + networkPopClose(){//关闭节点工具栏 + this.networkPopShow=false; + }, + //工具栏 + nodeDel(){ + console.log(this.selNodeId) + this.nodesArray=this.nodesArray.filter((item)=>item.id!==this.selNodeId); + this.edgesArray=this.edgesArray.filter((item)=>item.from!==this.selNodeId || this.to!==this.selNodeId); + this.setNetworkData(this.nodesArray, this.edgesArray); + }, + nodeEdit(){ + console.log('edit') + }, + lineDel(){ + if(!this.lineData.id){return} + this.edgesArray=this.edgesArray.filter((item)=>item.id!==this.lineData.id); + console.log(this.lineData.id); + this.setNetworkData(this.nodesArray, this.edgesArray); + }, // 其他 filterShowData(source,pageObj){ return source.slice((pageObj.pageNo-1)*pageObj.pageSize,pageObj.pageNo*pageObj.pageSize) @@ -441,6 +548,9 @@ this.seriesItemScreen = this.seriesItem; this.screenModal = true; + this.$nextTick(()=>{ + this.init('screenModal'); + }) }, // 设置数据, filter区分 setData(chartItem, seriesItem, panelId, filter,area,errorMsg) { @@ -540,22 +650,61 @@ mounted(){ this.firstLoad = false; setTimeout(()=>{ - let this_=this - this.init(); + let this_=this; + this.init('modal'); this.network.on("click", function (params) { // params.event = "[original event]"; // document.getElementById('eventSpan').innerHTML = '

Click event:

' + JSON.stringify(params, null, 4); - console.log('click event, getNodeAt returns: ' + this.getNodeAt(params.pointer.DOM),params); + console.log(params); + let selId=params.nodes[0]; + this_.networkPopClose(); + if(selId){ + this_.selNodeId=selId; + this_.cursorMove=true; + this_.nodeData=this_.nodesArray.find((item)=>item.id==selId); + this_.setPopPosition(selId,params); + if(this_.selectNodeTitle){ + this_.NodeArr.push(selId); + this_.network.selectNodes(this_.NodeArr,true) + } + } }); - this.network.on("dragEnd", function (params) { + + this.network.on("selectEdge", function (params) { + this_.lineData.id=params.edges[0]; + console.log(this_.lineData.id); + }); + + this.network.on("dragStart", function (params) {//节点移动开始 // params.event = "[original event]"; // document.getElementById('eventSpan').innerHTML = '

Click event:

' + JSON.stringify(params, null, 4); - console.log('click event, getNodeAt returns: ' + this.getNodeAt(params.pointer.DOM),params); - console.log( this_.network.getPositions()); - let selId=params.nodes[0]; - this_.setNodePosition(selId) + // console.log('click event, getNodeAt returns: ' + this.getNodeAt(params.pointer.DOM),params); + // console.log( this_.network.getPositions()); + this_.networkPopShow=false; }); - }) + + this.network.on("dragging", function (params,event) {//节点移动中 + + }); + + this.network.on("dragEnd", function (params) {//节点移动结束 + this_.viewsCenter=this_.network.getViewPosition(); + let selId=params.nodes[0]; + if(selId){ + this_.setPopPosition(selId,params) + this_.setNodePosition(selId) + } + }); + this.network.on("hoverNode", function (params) {//hoverNode + this_.cursorMove=true; + console.log(params); + }); + this.network.on("blurNode", function (params) {//blurNode + this_.cursorMove=false; + // console.log(params); + }); + }); + } } @@ -569,6 +718,62 @@ height: calc(100% - 30px); } .network{ + display: flex; height: calc(100% - 30px); + position: relative; + } + .networkPop{ + position: absolute; + z-index: 10; + border: 1px solid #e6e6e6; + border-radius: 5px; + height: 32px; + background: #fff; + padding: 0 3px; + line-height: 32px; + } + .btmTriangle{ + position: absolute; + width: 0; + height: 0; + border-width: 10px; + border-style: solid; + border-color:#e6e6e6 transparent transparent transparent; + bottom: -20px; + left: 50%; + transform: translateX(-50%); + } + .btmTriangle:after{ + content: ''; + display:block; + width:0; + height:0; + border-width: 10px; + border-style:solid; + border-color:#fff transparent transparent transparent; + position:absolute; + bottom: -7px; + left: -9px; + } + #network_id{ + width: 60%; + } + .cursorMove{ + cursor: move; + } + #network_id2{ + height: 100%; + } + .networkContent{ + width: 40%; + } + .nz-icon-delete{ + cursor: pointer; + color: #ee6723; + margin-left: 10px; + } + .nz-icon-edit{ + font-size: 14px; + cursor: pointer; }