Commit 392f36a1 authored by wangqinghua's avatar wangqinghua

重构 jtopo

parent 636c4984
...@@ -96,7 +96,8 @@ import {ModifyPasswordComponent} from './modal/modify-password/modify-password.c ...@@ -96,7 +96,8 @@ import {ModifyPasswordComponent} from './modal/modify-password/modify-password.c
import {FullScreenComponent} from './modal/full-screen/full-screen.component'; import {FullScreenComponent} from './modal/full-screen/full-screen.component';
import {LookRoleComponent} from './look-role/look-role.component'; import {LookRoleComponent} from './look-role/look-role.component';
import {ThresholdComponent} from './modal/threshold/threshold.component'; import {ThresholdComponent} from './modal/threshold/threshold.component';
import {JtopoComponent} from './jtopo/jtopo.component'; import {LineComponent} from './netTopology/model/line/line.component';
import {NodeComponent} from './netTopology/model/node/node.component';
@NgModule({ @NgModule({
imports: [ imports: [
...@@ -195,7 +196,8 @@ import {JtopoComponent} from './jtopo/jtopo.component'; ...@@ -195,7 +196,8 @@ import {JtopoComponent} from './jtopo/jtopo.component';
LookPlanComponent, LookPlanComponent,
LookRoleComponent, LookRoleComponent,
ThresholdComponent, ThresholdComponent,
JtopoComponent, LineComponent,
NodeComponent
], ],
providers:[ providers:[
OverAllService, OverAllService,
......
<div class="layui-layout layui-layout-admin">
<div class="layui-header header-bar">
<span (click)="addDevice()">添加设备</span>
<span (click)="addCheck()">添加监测点</span>
<span (click)="showAddImg()">添加图片</span>
<span onClick="editor.utils.deleteSelectedNodes()">移除</span>
<!-- 顶部工具栏 -->
<div class="layui-nav layui-layout-right" style="color: #6097b7" >
<span aria-hidden="true" title="全屏查看"
onClick="editor.utils.showInFullScreen(editor.stage.canvas,'RequestFullScreen')">全屏查看</span>
<span aria-hidden="true" title="居中显示" onClick="editor.utils.showInCenter()">居中显示</span>
<span aria-hidden="true" title="保存" (click)="update()">保存</span>
<span aria-hidden="true" title="清空" onClick="editor.utils.clearTopology()">清空</span>
<span aria-hidden="true" title="放大" onClick="editor.utils.scalingBig()">放大</span>
<span caria-hidden="true" title="缩小" onClick="editor.utils.scalingSmall()">缩小</span>
</div>
</div>
<div class="container">
<div class="layui-row">
<div class="layui-col-md12">
<div #topologyBody id="topology-body" class="topology-context">
<!-- 鼠标悬浮显示节点信息 -->
<div class="node-infobox" style="display: none;">
<span>节点名称:<label name="node_name"></label></span>
<span>当前时间:<label name="current_time"></label></span>
</div>
<canvas class="topology-context" id="topology-canvas" #topologyCanvas
style="height: 580px;">
您的浏览器不支持HTML5!
</canvas>
<div class="checkList">
<p *ngFor="let item of checkJson;let i = index;">
<span [style.fontSize]="item.fontSize +'px'">{{item.name}}</span><i (click)="deleteCheck(i)" class="minus anticon anticon-minus-circle-o"></i>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
<!--添加图片-->
<nz-modal [(nzVisible)]="isDevice" nzTitle="添加图片" (nzOnCancel)="handleCancel()" (nzOnOk)="handleOk()">
<form nz-form>
<nz-form-item>
<nz-form-label [nzSpan]="7" nzRequired nzFor="group">选择图片</nz-form-label>
<nz-form-control [nzSpan]="12">
<input nz-input type="text">
</nz-form-control>
</nz-form-item>
</form>
</nz-modal>
<!--添加拓扑图-->
<smart-topology #smartTopology (done)="getList()"></smart-topology>
<!--添加监测点-->
<smart-check #smartCheck (done)="setCheckList($event)"></smart-check>
<!--选择资源-->
<smart-select-group #smartSelectGroup (done)="setImg($event)"></smart-select-group>
\ No newline at end of file
import {Component, ElementRef, OnInit, ViewChild} from '@angular/core';
declare let editor: any;
declare let JTopo: any;
@Component({
selector: 'smart-jtopo',
templateUrl: './jtopo.component.html',
styles: [
`
.layui-side-scroll {
width: initial;
}
.layui-layout-right {
color: #666666;
}
.layui-header span {
margin-right: 15px;
cursor: pointer;
}
.header-bar {
line-height: 60px;
color: #6097b7;
padding: 0 20px;
background-color: #ddd;
}
.tips {
position: absolute;
top: 10px;
left: 20px;
color: #ca0814;
font-size: 20px;
}
.checkList {
position: absolute;
top: 10px;
right: 20px;
color: #666666;
font-size: 20px;
}
.layui-form-label {
width: 110px;
}
.minus {
vertical-align: middle;
color: red;
font-size: 15px;
margin-left: 12px;
cursor: pointer;
}
`
]
})
export class JtopoComponent implements OnInit {
@ViewChild('topologyCanvas') topologyCanvas: ElementRef;
@ViewChild('topologyBody') topologyBody: ElementRef;
isDevice = false;
config;
// 布局参数
layout = {};
// 绘图区属性
stage = null;
scene = null;
// 当前模式
stageMode = 'normal';
// 默认连线类型
lineType = 'line';
// 当前选择的节点对象
currentNode = null;
// 当前选择的连线对象
currentLink = null;
topologyGuid;
tempNodeA;
tempNodeZ;
beginNode;
link;
constructor() {
}
ngOnInit() {
this.initTopologyPanel();
}
initTopologyPanel() {
let initTopologyJson = {
'version': '0.4.8',
'wheelZoom': 0.95,
'width': 972,
'height': 569,
'id': 'ST172.19.105.52015100809430700001',
'childs': [
{
'elementType': 'scene',
'id': 'S172.19.105.52015100809430700002',
'translateX': -121.82,
'translateY': 306.72,
'scaleX': 1.26,
'scaleY': 1.26,
'childs': []
}
]
};
editor.init('', 'img/backimg.png', initTopologyJson, '');
}
TopologyEditor() {
this.config = {
// Stage属性
stageFrames: 500, // 舞台播放的帧数/秒
defaultScal: 0.95, // 鼠标滚轮缩放比例
eagleEyeVsibleDefault: false, // 是否显示鹰眼对象
// Node属性
nodeAlpha: 1, // 节点透明度,取值范围[0-1]
nodeStrokeColor: '22,124,255', // 节点描边的颜色
nodeFillColor: '22,124,255', // 节点填充颜色
nodeShadow: false, // 节点是否显示阴影
nodeShadowColor: 'rgba(0,0,0,0.5)', // 节点阴影的颜色
nodeFont: '12px Consolas', // 节点字体
nodeFontColor: 'black', // 节点文字颜色,如"255,255,0"
nodeDefaultWidth: 32, // 新建节点默认宽
nodeDefaultHeight: 32, // 新建节点默认高
nodeBorderColor: 'black', // 节点容器边框颜色,如"255,255,0"
nodeBorderRadius: 30, // 节点半径,非圆节点有此属性会变形
nodeRotateValue: 0.5, // 节点旋转的角度(弧度)
nodeScale: 0.2, // 节点缩放幅度(此处保证X和Y均等缩放)
// Link属性
linkAlpha: 1, // 连线透明度,取值范围[0-1]
linkStrokeColor: '123,165,241', // 连线的颜色
linkFillColor: '123,165,241',
linkShadow: false, // 是否显示连线阴影
linkShadowColor: 'rgba(0,0,0,0.5)',
linkFont: '12px Consolas', // 节点字体
linkFontColor: 'black', // 连线文字颜色,如"255,255,0"
linkArrowsRadius: 0, // 线条箭头半径
linkDefaultWidth: 3, // 连线宽度
linkOffsetGap: 40, // 折线拐角处的长度
linkDirection: 'horizontal', // 折线的方向 竖直 vertical 水平 horizontal
// Container属性
containerAlpha: 1,
containerStrokeColor: '22,124,255',
containerFillColor: '22,124,255',
containerShadow: false,
containerShadowColor: 'rgba(0,0,0,0.5)',
containerFont: '12px Consolas',
containerFontColor: 'black',
containerBorderColor: 'black',
containerBorderRadius: 30
};
// 布局参数
this.layout = {};
// 绘图区属性
this.stage = null;
this.scene = null;
// 当前模式
this.stageMode = 'normal';
// 默认连线类型
this.lineType = 'line';
// 当前选择的节点对象
this.currentNode = null;
// 当前选择的连线对象
this.currentLink = null;
}
// 拓扑图初始化
initCanvas(topologyGuid, backImg, topologyJson) {
if (!topologyJson) {
// alert('加载拓扑编辑器失败!')
return;
}
this.topologyGuid = topologyGuid;
// 创建jTopo舞台屏幕对象
let canvas = this.topologyCanvas.nativeElement;
canvas.width = this.topologyBody.nativeElement.width();
canvas.height = this.topologyBody.nativeElement.height();
// 加载空白的编辑器
if (topologyJson === '-1') {
this.stage = new JTopo.Stage(canvas); // 定义舞台对象
this.scene = new JTopo.Scene(this.stage); // 定义场景对象
} else {
this.stage = JTopo.createStageFromJson(topologyJson, canvas); // 根据保存好的jsonStr(拓扑结构)创建舞台对象
this.scene = this.stage.childs[0]; // 场景对象列表,childs是舞台的属性
}
// 滚轮缩放
this.stage.frames = this.config.stageFrames; // 设置当前舞台播放的帧数/秒
this.stage.wheelZoom = this.config.defaultScal; // 鼠标滚轮缩放操作比例
this.stage.eagleEye.visible = this.config.eagleEyeVsibleDefault; // 是否开启鹰眼
this.stage.mode = this.stageMode;
// 设置舞台模式
// 背景由样式指定
// this.scene.background = backImg;
// 用来连线的两个节点
this.tempNodeA = new JTopo.Node('tempA');
this.tempNodeA.setSize(1, 1);
this.tempNodeZ = new JTopo.Node('tempZ');
this.tempNodeZ.setSize(1, 1);
this.beginNode = null;
this.link = null;
let self = this;
// 模拟告警
// editor.utils.getAllNodes()[0].alarm = '告警';
// 鼠标进入事件
this.scene.mouseover(function(event) {
Timer.start();
// 进入某个节点
if (event.target != null && event.target instanceof JTopo.Node && event.target.nodeTooltip && editor.stageMode !== 'edit') {
// $('.node-tooltip span').html(event.target.nodeTooltip);
// 记录鼠标触发位置在canvas中的相对位置
let menuY = event.layerY ? event.layerY : event.offsetY;
let menuX = event.layerX ? event.layerX : event.offsetX;
// 判断边界出是否能完整显示弹出菜单
if (menuX + $('.node-tooltip').width() >= self.stage.width) {
menuX -= $('.node-tooltip').width();
}
if (menuY + $('.node-tooltip').height() >= self.stage.height) {
menuY -= $('.node-tooltip').height();
}
$('.link-tooltip').css('display', 'none');
$('.node-tooltip').css({
'display': 'block',
'margin-top': menuY,
'margin-left': menuX,
'cursor': 'pointer'
});
// 进入某个连线
} else if (event.target != null && event.target instanceof JTopo.Link && event.target.linkTooltip && editor.stageMode !== 'edit') {
$('.link-tooltip span').html(event.target.linkTooltip);
// 记录鼠标触发位置在canvas中的相对位置
let menuY = event.layerY ? event.layerY : event.offsetY;
let menuX = event.layerX ? event.layerX : event.offsetX;
// 判断边界出是否能完整显示弹出菜单
if (menuX + $('.link-tooltip').width() >= self.stage.width) {
menuX -= $('.link-tooltip').width();
}
if (menuY + $('.link-tooltip').height() >= self.stage.height) {
menuY -= $('.link-tooltip').height();
}
$('.node-tooltip').css('display', 'none');
$('.link-tooltip').css({
'display': 'block',
'margin-top': menuY,
'margin-left': menuX,
'cursor': 'pointer'
});
} else {
// 鼠标进入别的地方
}
});
// 鼠标离开事件
this.scene.mouseout(function(event) {
let timeSpan = Timer.pause();
// 消抖
if (timeSpan > 100) {
$('.node-tooltip').css('display', 'none');
$('.link-tooltip').css('display', 'none');
Timer.stop();
}
});
// 鼠标单击节点事件
this.scene.click(function(event) {
if (!event.target) {
// 单击舞台空白处
$('.node-tooltip').css('display', 'none');
return;
}
self.currentNode = event.target;
// 只读模式下单击节点
if (event.target instanceof JTopo.Node && editor.stageMode === 'normal') {
console.log(self.currentNode);
} else if (event.target instanceof JTopo.Node && editor.stageMode === 'edit') {
console.log(self.currentNode);
} else if (event.target instanceof JTopo.Link && editor.stageMode === 'normal') {
console.log(self.currentNode);
} else if (event.target instanceof JTopo.Link && editor.stageMode === 'edit') {
console.log(self.currentNode);
} else {
// 单击别的地方
$('.node-tooltip').css('display', 'none');
}
});
// 双击编辑事件
this.scene.dbclick(function(event) {
if (!event.target) {
// 单击舞台空白处
$('.node-tooltip').css('display', 'none');
return;
}
self.currentNode = event.target;
// 只处理双击节点事件
if (event.target instanceof JTopo.Node && editor.stageMode === 'edit') {
console.log(event.target);
// alert('双击了节点')
// 查询单个节点的信息, 回填数据
layer.open({
title: '编辑设备',
type: 1,
area: ['600px', '500px'],
content: $('#node-dialog'),
btn: ['应用', '删除', '取消'],
yes: function(index, layero) {
let param = getParam('node-form');
self.currentNode.text = param.text;
self.currentNode.textPosition = param.textPosition;
self.currentNode.setLocation(param.x, param.y);
},
btn2: function() {
editor.utils.deleteNode(self.currentNode);
}
});
} else if (event.target instanceof JTopo.Link && editor.stageMode === 'edit') {
layer.open({
title: '编辑线',
type: 1,
area: ['600px', '500px'],
content: $('#link-dialog'),
btn: ['应用', '删除', '取消'],
yes: function(index, layero) {
let param = getParam('link-form');
self.currentNode.text = param.text;
self.currentNode.textPosition = param.textPosition;
self.currentNode.setLocation(param.x, param.y);
},
btn2: function() {
editor.utils.deleteNode(self.currentNode);
}
});
}
});
// 清除起始节点不完整的拖放线
this.scene.mousedown(function(e) {
if (self.link && !self.isSelectedMode && (e.target == null || e.target === self.beginNode || e.target === self.link)) {
this.remove(self.link);
}
});
// 监听鼠标松开事件
// 处理右键菜单、左键连线
// event.button: 0-左键 1-中键 2-右键
this.scene.mouseup(function(event) {
if (event.target && event.target instanceof JTopo.Node) {
self.currentNode = event.target;
} else if (event.target && event.target instanceof JTopo.Link) {
self.currentLink = event.target;
}
if (event.target && event.target instanceof JTopo.Node && event.target.layout && event.target.layout.on && event.target.layout.type && event.target.layout.type !== 'auto') {
JTopo.layout.layoutNode(this, event.target, true, event);
}
if (event.button === 2) { // 右键菜单
$('div[id$=\'-menu\']').hide();
let menuY = event.layerY ? event.layerY : event.offsetY;
let menuX = event.layerX ? event.layerX : event.offsetX;
// 记录鼠标触发位置在canvas中的相对位置
self.xInCanvas = menuX;
self.yInCanvas = menuY;
if (event.target) {
if (event.target instanceof JTopo.Node) { // 处理节点右键菜单事件
let selectedNodes = self.utils.getSelectedNodes();
// 如果是节点多选状态弹出分组菜单
if (selectedNodes && selectedNodes.length > 1) {
// 判断边界出是否能完整显示弹出菜单
if (menuX + self.groupMangeMenu.width() >= self.stage.width) {
menuX -= self.groupMangeMenu.width();
}
if (menuY + self.groupMangeMenu.height() >= self.stage.height) {
menuY -= self.groupMangeMenu.height();
}
self.groupMangeMenu.css({
top: menuY,
left: menuX
}).show();
} else {
// 判断边界出是否能完整显示弹出菜单
if (menuX + self.nodeMenu.width() >= self.stage.width) {
menuX -= self.nodeMenu.width();
}
if (menuY + self.nodeMenu.height() >= self.stage.height) {
menuY -= self.nodeMenu.height();
}
self.nodeMenu.css({
top: menuY,
left: menuX
}).show();
}
} else if (event.target instanceof JTopo.Link) { // 连线右键菜单
self.lineMenu.css({
top: event.layerY ? event.layerY : event.offsetY,
left: event.layerX ? event.layerX : event.offsetX
}).show();
} else if (event.target instanceof JTopo.Container) { // 容器右键菜单
self.containerMangeMenu.css({
top: event.layerY ? event.layerY : event.offsetY,
left: event.layerX ? event.layerX : event.offsetX
}).show();
}
} else {
// 判断边界出是否能完整显示弹出菜单
if (menuX + self.mainMenu.width() >= self.stage.width) {
menuX -= self.mainMenu.width();
}
if (menuY + self.mainMenu.height() >= self.stage.height) {
menuY -= self.mainMenu.height();
}
self.mainMenu.css({
top: menuY,
left: menuX
}).show();
}
} else if (event.button === 1) { // 中键
} else if (event.button === 0) { // 左键松开事件
if (event.target != null && event.target instanceof JTopo.Node && !self.isSelectedMode && editor.stageMode === 'edit') {
if (self.beginNode == null) {
// 在起始节点处松开鼠标,创建动态的线条(临时节点A-Z)
self.beginNode = event.target;
if (self.lineType === 'line') {
// 直线
self.link = new JTopo.Link(self.tempNodeA, self.tempNodeZ);
self.link.lineType = 'line';
} else if (self.lineType === 'foldLine') {
// 折线
self.link = new JTopo.FoldLink(self.tempNodeA, self.tempNodeZ);
self.link.lineType = 'foldLine';
self.link.direction = self.config.linkDirection;
} else if (self.lineType === 'flexLine') {
// 二次折线
self.link = new JTopo.FlexionalLink(self.tempNodeA, self.tempNodeZ);
self.link.direction = self.config.linkDirection;
self.link.lineType = 'flexLine';
} else if (self.lineType === 'curveLine') {
// 曲线
self.link = new JTopo.CurveLink(self.tempNodeA, self.tempNodeZ);
self.link.lineType = 'curveLine';
}
self.link.dashedPattern = 2;
self.link.lineWidth = self.config.linkDefaultWidth;
self.link.shadow = self.config.linkShadow;
self.link.strokeColor = JTopo.util.randomColor();
this.add(self.link);
self.tempNodeA.setLocation(event.x, event.y);
self.tempNodeZ.setLocation(event.x, event.y);
} else if (event.target && event.target instanceof JTopo.Node && self.beginNode !== event.target) {
// 在终点节点处松开鼠标,则建立连线
let endNode = event.target;
// 判断两个节点是否有循环引用
/** ***************** 我这里允许循环引用 *************************
for (var el = 0; el < endNode.outLinks.length; el++) {
// 存在循环引用,线条变红
if (endNode.outLinks[el].nodeZ === self.beginNode) {
if (self.link)
this.remove(self.link);
self.beginNode = null;
return;
}
}
*****************************************************************/
// 判断节点间是否有重复连线,即起点到终点有两条以上连线
/** ***************** 我这里允许它有两条连线 *************************
for (var el2 = 0; el2 < self.beginNode.outLinks.length; el2++) {
// 起始节点已经有一条线指向目标节点
if (self.beginNode.outLinks[el2].nodeZ === endNode) {
if (self.link)
this.remove(self.link);
self.beginNode = null;
return;
}
}
*****************************************************************/
let link;
if (self.lineType === 'line') {
link = new JTopo.Link(self.beginNode, endNode);
link.lineType = 'line';
} else if (self.lineType === 'foldLine') {
link = new JTopo.FoldLink(self.beginNode, endNode);
link.direction = self.config.linkDirection;
link.bundleOffset = self.config.linkOffsetGap; // 折线拐角处的长度
link.lineType = 'foldLine';
} else if (self.lineType === 'flexLine') {
link = new JTopo.FlexionalLink(self.beginNode, endNode);
link.direction = self.config.linkDirection;
link.lineType = 'flexLine';
link.offsetGap = self.config.linkOffsetGap;
} else if (self.lineType === 'curveLine') {
link = new JTopo.CurveLink(self.beginNode, endNode);
link.lineType = 'curveLine';
}
// 保存线条所连接的两个节点ID
link.nodeSrc = self.beginNode.nodeId;
link.nodeDst = endNode.nodeId;
if (self.lineType !== 'curveLine') {
link.arrowsRadius = self.config.arrowsRadius;
}
link.strokeColor = self.config.linkStrokeColor;
link.lineWidth = self.config.linkDefaultWidth;
this.add(link);
self.beginNode = null;
this.remove(self.link);
self.link = null;
} else {
self.beginNode = null;
}
} else {
if (self.link) {
this.remove(self.link);
}
self.beginNode = null;
}
}
});
// 动态更新连线坐标(创建连线时的临时节点A-Z)
this.scene.mousemove(function(event) {
if (!self.isSelectedMode && self.beginNode) {
self.tempNodeZ.setLocation(event.x, event.y);
}
});
this.scene.mousedrag(function(event) {
if (!self.isSelectedMode && self.beginNode) {
self.tempNodeZ.setLocation(event.x, event.y);
}
});
// 单击编辑器隐藏右键菜单
this.stage.click(function(event) {
if (event.button === 0) {
// 关闭弹出菜单(div)
$('div[id$=\'-menu\']').hide();
}
});
// 鼠标移出舞台
this.stage.mouseout(function(event) {
// 删掉节点带出来的连线
if (self.link && !self.isSelectedMode && (event.target == null || event.target === self.beginNode || event.target === self.link)) {
self.scene.remove(self.link);
}
});
// 按下ctrl进入多选模式,此时选择节点不能画线
$(document).keydown(function(e) {
if (e.shiftKey) { // 组合键模式
switch (e.which) {
// 放大 ctrl+=
case 187:
case 61:
// 单个节点可以撤销操作
if (editor.currentNode instanceof JTopo.Node) {
// 保存初始状态
editor.utils.saveNodeInitState();
editor.utils.scalingBig();
editor.utils.saveNodeNewState();
} else {
editor.utils.scalingBig();
}
break;
// 缩小 ctrl+-
case 189:
case 173:
if (editor.currentNode instanceof JTopo.Node) {
// 保存初始状态
editor.utils.saveNodeInitState();
editor.utils.scalingSmall();
editor.utils.saveNodeNewState();
} else {
editor.utils.scalingSmall();
}
break;
case 70:
// ctrl+f 全屏显示
editor.utils.showInFullScreen(editor.stage.canvas, 'RequestFullScreen');
break;
case 72:
// h 帮助
// alert('帮助文档')
break;
case 71:
// ctrl+g 居中显示
editor.utils.showInCenter();
break;
case 73:
// shif+I 逆时针旋转
if (editor.currentNode instanceof JTopo.Node) {
editor.utils.saveNodeInitState();
editor.utils.rotateSub();
editor.utils.saveNodeNewState();
}
break;
case 67:
editor.utils.cloneSelectedNodes();
break;
case 80:
// ctrl + p
editor.utils.showPic();
break;
case 82:
// 单个节点重做
if (editor.currentNode instanceof JTopo.Node) {
editor.utils.reMakeNodeAction();
}
break;
case 83:
// ctrl+s 保存
editor.saveTopology(true);
break;
case 85:
// shif+U 顺时针旋转
if (editor.currentNode instanceof JTopo.Node) {
editor.utils.saveNodeInitState();
editor.utils.rotateAdd();
editor.utils.saveNodeNewState();
}
break;
case 87:
// alert('ctrl + w 另存为')
break;
case 89:
// ctrl+y
editor.utils.clearTopology();
break;
case 90:
// 单个节点撤销
if (editor.currentNode instanceof JTopo.Node) {
editor.utils.cancleNodeAction();
}
break;
}
} else if (e.which === 46) { // 单独按下delete
editor.utils.deleteSelectedNodes();
} else if (e.which === 17) { // 单独按下ctrl
self.isSelectedMode = true;
}
});
$(document).keyup(function(e) {
if (e.which === 17) {
self.isSelectedMode = false;
return false;
}
});
// 第一次进入拓扑编辑器,生成stage和scene对象
if (topologyJson === '-1') {
this.saveTopology(false);
}
}
}
...@@ -34,4 +34,81 @@ ...@@ -34,4 +34,81 @@
</div> </div>
<smart-jtopo></smart-jtopo> <div class="layui-layout layui-layout-admin">
\ No newline at end of file <div class="layui-header header-bar">
<span (click)="addDevice()">添加设备</span>
<span (click)="addCheck()">添加监测点</span>
<span (click)="showAddImg()">添加图片</span>
<span onClick="editor.utils.deleteSelectedNodes()">移除</span>
<!-- 顶部工具栏 -->
<div class="layui-nav layui-layout-right" style="color: #6097b7" >
<span aria-hidden="true" title="全屏查看"
onClick="editor.utils.showInFullScreen(editor.stage.canvas,'RequestFullScreen')">全屏查看</span>
<span aria-hidden="true" title="居中显示" onClick="editor.utils.showInCenter()">居中显示</span>
<span aria-hidden="true" title="保存" (click)="update()">保存</span>
<span aria-hidden="true" title="清空" onClick="editor.utils.clearTopology()">清空</span>
<span aria-hidden="true" title="放大" onClick="editor.utils.scalingBig()">放大</span>
<span caria-hidden="true" title="缩小" onClick="editor.utils.scalingSmall()">缩小</span>
</div>
</div>
<div class="container">
<div class="layui-row">
<div class="layui-col-md12">
<div #topologyBody id="topology-body" class="topology-context">
<!-- 鼠标悬浮显示节点信息 -->
<div class="node-infobox" style="display: none;">
<span>节点名称:<label name="node_name"></label></span>
<span>当前时间:<label name="current_time"></label></span>
</div>
<canvas class="topology-context" id="topology-canvas" #topologyCanvas style="height: 580px;">
您的浏览器不支持HTML5!
</canvas>
<div class="lineList">
<p onclick="editor.lineType='line'">连线</p>
<p onclick="editor.lineType='foldLine';editor.config.linkDirection='horizontal';">折线(横向)</p>
<p onclick="editor.lineType='foldLine';editor.config.linkDirection='Vertical';">折线(纵向)</p>
<p onclick="editor.lineType='flexLine';editor.config.linkDirection='horizontal';">二次折线(横向)</p>
<p onclick="editor.lineType='flexLine';editor.config.linkDirection='Vertical';">二次折线(纵向)</p>
</div>
<div class="checkList">
<p *ngFor="let item of checkJson;let i = index;">
<span [style.fontSize]="item.fontSize +'px'">{{item.name}}</span><i (click)="deleteCheck(i)" class="minus anticon anticon-minus-circle-o"></i>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
<!--添加图片-->
<nz-modal [(nzVisible)]="isDevice" nzTitle="添加图片" (nzOnCancel)="handleCancel()" (nzOnOk)="handleOk()">
<form nz-form>
<nz-form-item>
<nz-form-label [nzSpan]="7" nzRequired nzFor="group">选择图片</nz-form-label>
<nz-form-control [nzSpan]="12">
<input nz-input type="text">
</nz-form-control>
</nz-form-item>
</form>
</nz-modal>
<!--流量配置-->
<smart-line #smartLine></smart-line>
<!--设备配置-->
<smart-node #smartNode></smart-node>
<!--添加拓扑图-->
<smart-topology #smartTopology (done)="getList()"></smart-topology>
<!--添加监测点-->
<smart-check #smartCheck (done)="setCheckList($event)"></smart-check>
<!--选择资源-->
<smart-select-group #smartSelectGroup (done)="setImg($event)"></smart-select-group>
\ No newline at end of file
import {AfterViewChecked, AfterViewInit, Component, ElementRef, OnInit, ViewChild} from '@angular/core'; import {AfterViewChecked, AfterViewInit, Component, ElementRef, KeyValueDiffer, OnInit, ViewChild, KeyValueDiffers, DoCheck} from '@angular/core';
import {TopologyService} from '../topology.service'; import {TopologyService} from '../topology.service';
import {NzMessageService, NzModalService, UploadFile} from 'ng-zorro-antd'; import {NzMessageService, NzModalService, UploadFile} from 'ng-zorro-antd';
import {DomSanitizer} from '@angular/platform-browser'; import {DomSanitizer} from '@angular/platform-browser';
import * as $ from 'jquery';
import {TopologyComponent} from '../model/topology/topology.component'; import {TopologyComponent} from '../model/topology/topology.component';
import {CheckComponent} from '../model/check/check.component'; import {CheckComponent} from '../model/check/check.component';
import {DeviceComponent} from '../model/device/device.component'; import {DeviceComponent} from '../model/device/device.component';
import {SelectGroupComponent} from '../../modal/select-group/select-group.component'; import {SelectGroupComponent} from '../../modal/select-group/select-group.component';
import {LocalStorageService} from 'ngx-webstorage';
import {numberOfBytes} from 'ng-jhipster/src/directive/number-of-bytes';
import {LineComponent} from '../model/line/line.component';
import {NodeComponent} from '../model/node/node.component';
declare let editor: any; declare let editor: any;
declare var layui: any; declare var layui: any;
...@@ -50,6 +53,13 @@ declare var layui: any; ...@@ -50,6 +53,13 @@ declare var layui: any;
color: #666666; color: #666666;
font-size: 20px; font-size: 20px;
} }
.lineList{
position: absolute;
top: 5px;
left: 20px;
color: #6097b7;
cursor: pointer;
}
.layui-form-label { .layui-form-label {
width: 110px; width: 110px;
...@@ -65,11 +75,13 @@ declare var layui: any; ...@@ -65,11 +75,13 @@ declare var layui: any;
` `
] ]
}) })
export class NeTopologyComponent implements OnInit, AfterViewInit { export class NeTopologyComponent implements OnInit, DoCheck, AfterViewInit {
@ViewChild('topologyCanvas') topologyCanvas: ElementRef; @ViewChild('topologyCanvas') topologyCanvas: ElementRef;
@ViewChild('smartTopology') smartTopology: TopologyComponent; @ViewChild('smartTopology') smartTopology: TopologyComponent;
@ViewChild('smartCheck') smartCheck: CheckComponent; @ViewChild('smartCheck') smartCheck: CheckComponent;
@ViewChild('smartSelectGroup') smartSelectGroup: SelectGroupComponent; @ViewChild('smartSelectGroup') smartSelectGroup: SelectGroupComponent;
@ViewChild('smartLine') smartLine: LineComponent;
@ViewChild('smartNode') smartNode: NodeComponent;
dataSet; dataSet;
isVisible = false; isVisible = false;
...@@ -82,14 +94,37 @@ export class NeTopologyComponent implements OnInit, AfterViewInit { ...@@ -82,14 +94,37 @@ export class NeTopologyComponent implements OnInit, AfterViewInit {
checkJson = []; checkJson = [];
fileList: UploadFile[] = []; fileList: UploadFile[] = [];
isDevice;
private customerDiffer: KeyValueDiffer<string, any>;
local = {
node:'',
link:''
};
constructor(private topologySer: TopologyService, private message: NzMessageService, constructor(private topologySer: TopologyService, private message: NzMessageService, private localStorage:LocalStorageService,private differs: KeyValueDiffers,
private sanitizer: DomSanitizer, private modalSer: NzModalService) { private sanitizer: DomSanitizer, private modalSer: NzModalService) {
} }
ngOnInit() { ngOnInit() {
this.getList(); this.getList();
this.getTypeList(); this.getTypeList();
this.customerDiffer = this.differs.find(this.local).create();
}
ngAfterViewInit() {
this.loadCanvas();
}
ngDoCheck(){
this.local.node = localStorage.getItem("node");
this.local.link = localStorage.getItem("link");
if (this.local.node == 'true') {
this.smartNode.showModal();
}
if (this.local.link == 'true') {
this.smartLine.showModal(null);
}
} }
//一级分类 //一级分类
...@@ -98,10 +133,6 @@ export class NeTopologyComponent implements OnInit, AfterViewInit { ...@@ -98,10 +133,6 @@ export class NeTopologyComponent implements OnInit, AfterViewInit {
(res)=>{ (res)=>{
if(res.errCode == 10000){ if(res.errCode == 10000){
this.options = res.data; this.options = res.data;
layui.use('form', function() {
let form = layui.form;
form.render("select");
});
}else{ }else{
this.message.error(res.errMSg); this.message.error(res.errMSg);
} }
...@@ -109,15 +140,6 @@ export class NeTopologyComponent implements OnInit, AfterViewInit { ...@@ -109,15 +140,6 @@ export class NeTopologyComponent implements OnInit, AfterViewInit {
) )
} }
ngAfterViewInit() {
layui.use('element', () => {
let element = layui.element;
element.init();
});
this.loadCanvas();
}
//新增拓扑图 //新增拓扑图
addTopo() { addTopo() {
this.smartTopology.showAddMOodal(); this.smartTopology.showAddMOodal();
...@@ -157,7 +179,7 @@ export class NeTopologyComponent implements OnInit, AfterViewInit { ...@@ -157,7 +179,7 @@ export class NeTopologyComponent implements OnInit, AfterViewInit {
} }
this.topologySer.findByHostIdOrWeb(data).subscribe( this.topologySer.findByHostIdOrWeb(data).subscribe(
(res)=>{ (res)=>{
editor.utils.addNode(res.data[0].url, '图片'); editor.utils.addNode(res.data[0].url, '图片',e[0]);
} }
) )
} }
...@@ -200,6 +222,11 @@ export class NeTopologyComponent implements OnInit, AfterViewInit { ...@@ -200,6 +222,11 @@ export class NeTopologyComponent implements OnInit, AfterViewInit {
); );
} }
//load
loadCanvas() {
editor.loadTopology('','', 'img/backimg.png');
}
//查询单个 //查询单个
getDetail(id) { getDetail(id) {
this.topologySer.findItem(id).subscribe( this.topologySer.findItem(id).subscribe(
...@@ -209,6 +236,7 @@ export class NeTopologyComponent implements OnInit, AfterViewInit { ...@@ -209,6 +236,7 @@ export class NeTopologyComponent implements OnInit, AfterViewInit {
let json = JSON.parse(res.data.json); let json = JSON.parse(res.data.json);
if(json.topology){ if(json.topology){
editor.loadTopologyByJson(JSON.parse(json.topology), 'img/backimg.png'); editor.loadTopologyByJson(JSON.parse(json.topology), 'img/backimg.png');
// this.smartJtopo.loadTopologyByJson(JSON.parse(json.topology), 'img/backimg.png');
this.checkJson = json.check; this.checkJson = json.check;
}else{ }else{
editor.utils.clearTopology(); editor.utils.clearTopology();
...@@ -221,9 +249,9 @@ export class NeTopologyComponent implements OnInit, AfterViewInit { ...@@ -221,9 +249,9 @@ export class NeTopologyComponent implements OnInit, AfterViewInit {
this.name = res.data.name; this.name = res.data.name;
this.refreshRete = res.data.refreshRete; this.refreshRete = res.data.refreshRete;
this.topoId = res.data.id; this.topoId = res.data.id;
editor.utils.editTopology();
this.findItemStatus(); this.findItemStatus();
} }
editor.utils.editTopology();
} }
); );
} }
...@@ -286,18 +314,6 @@ export class NeTopologyComponent implements OnInit, AfterViewInit { ...@@ -286,18 +314,6 @@ export class NeTopologyComponent implements OnInit, AfterViewInit {
this.checkJson.splice(index, 1); this.checkJson.splice(index, 1);
} }
//load
loadCanvas() {
// 节点树中每个节点的拖放动作定义给拓扑图编辑器
let nodes = $('[topo-div-type=\'topo-node\']');
let nodeLength = nodes.length;
for (let i = 0; i < nodeLength; i++) {
let text = $(nodes[i]).find('span').eq(0).text();
editor.drag(nodes[i], document.getElementById('topology-canvas'), text);
}
editor.loadTopology('', 'img/backimg.png');
}
//添加图片-----start //添加图片-----start
showAddImg() { showAddImg() {
if (!this.topoId) { if (!this.topoId) {
...@@ -344,6 +360,4 @@ export class NeTopologyComponent implements OnInit, AfterViewInit { ...@@ -344,6 +360,4 @@ export class NeTopologyComponent implements OnInit, AfterViewInit {
); );
} }
// end
} }
...@@ -98,4 +98,14 @@ export class TopologyService { ...@@ -98,4 +98,14 @@ export class TopologyService {
findElementStatus(data): Observable<any> { findElementStatus(data): Observable<any> {
return this.http.post(SERVER_API_URL + '/sysmapJson/findElementStatus',data); return this.http.post(SERVER_API_URL + '/sysmapJson/findElementStatus',data);
} }
//查询进出口流量
findFlow(data): Observable<any> {
return this.http.post(SERVER_API_URL + '/sysmapJson/flow',data);
}
//根据主机id查询网卡进出口监控项
findFlowItemByHost(data): Observable<any> {
return this.http.post(SERVER_API_URL + '/sysmapJson/findFlowItemByHost',data);
}
} }
...@@ -56,7 +56,7 @@ ...@@ -56,7 +56,7 @@
<td></td> <td></td>
<td>总计</td> <td>总计</td>
<td></td> <td></td>
<td>{{totalNum}}{{(nowStatus[0].response_units).toFixed(4)}}</td> <td>{{totalNum.toFixed(4)}}s</td>
<td></td> <td></td>
<td></td> <td></td>
</tr> </tr>
...@@ -125,9 +125,9 @@ ...@@ -125,9 +125,9 @@
<div nz-col [nzSpan]="6" > <div nz-col [nzSpan]="6" >
<div #chartSpeedLeft class="chart-left" *ngFor="let item of speed,let i = index;"> <div #chartSpeedLeft class="chart-left" *ngFor="let item of speed,let i = index;">
<p class="tag-form"> <nz-tag [nzColor]="colorArr[i]"></nz-tag> <span>{{item.name}}</span> </p> <p class="tag-form"> <nz-tag [nzColor]="colorArr[i]"></nz-tag> <span>{{item.name}}</span> </p>
<p>平均值 {{item.average}}</p> <p>平均值 {{item.average.toFixed(4)}}</p>
<p>最大值 {{item.max}}</p> <p>最大值 {{item.max.toFixed(4)}}</p>
<p>最小值 {{item.min}}</p> <p>最小值 {{item.min.toFixed(4)}}</p>
</div> </div>
</div> </div>
<div nz-col [nzSpan]="18"> <div nz-col [nzSpan]="18">
...@@ -138,7 +138,7 @@ ...@@ -138,7 +138,7 @@
<div nz-col [nzSpan]="6" > <div nz-col [nzSpan]="6" >
<div #chartTimeLeft class="chart-left" *ngFor="let item of time,let i = index;"> <div #chartTimeLeft class="chart-left" *ngFor="let item of time,let i = index;">
<p class="tag-form"> <nz-tag [nzColor]="colorArr[i]"></nz-tag> <span>{{item.name}}</span> </p> <p class="tag-form"> <nz-tag [nzColor]="colorArr[i]"></nz-tag> <span>{{item.name}}</span> </p>
<p>平均值 {{(item.average.toFixed(4))}}</p> <p>平均值 {{(item.average).toFixed(4)}}</p>
<p>最大值 {{(item.max).toFixed(4)}}</p> <p>最大值 {{(item.max).toFixed(4)}}</p>
<p>最小值 {{(item.min).toFixed(4)}}</p> <p>最小值 {{(item.min).toFixed(4)}}</p>
</div> </div>
......
...@@ -125,36 +125,4 @@ export class CommonService implements OnInit { ...@@ -125,36 +125,4 @@ export class CommonService implements OnInit {
return usablePrefixMethod; return usablePrefixMethod;
} }
/**
* 计算程序执行时间
* @type {{startTime: {}, timeSpan: number, start: Timer.start, stop: Timer.stop, getTimeSpan: Timer.getTimeSpan}}
*/
Timer = {
startTime: {},
stoppedStatus: true,
start: ()=> {
if (this.stoppedStatus) {
this.startTime = new Date()
this.stoppedStatus = false
}
},
pause: ()=> {
var startTime = this.startTime
if (startTime) {
return new Date() - startTime
} else {
return -1
}
},
stop: function () {
var startTime = this.startTime
if (startTime) {
this.stoppedStatus = true
return new Date() - startTime
} else {
this.stoppedStatus = true
return -1
}
}
}
} }
...@@ -18,6 +18,7 @@ import { ...@@ -18,6 +18,7 @@ import {
} from './'; } from './';
import {ReactiveFormsModule} from '@angular/forms'; import {ReactiveFormsModule} from '@angular/forms';
import {CommonService} from './common/common.service'; import {CommonService} from './common/common.service';
import {UtilService} from './common/util.service';
@NgModule({ @NgModule({
imports: [ imports: [
...@@ -42,6 +43,7 @@ import {CommonService} from './common/common.service'; ...@@ -42,6 +43,7 @@ import {CommonService} from './common/common.service';
EmitService, EmitService,
NgbActiveModal, NgbActiveModal,
CommonService, CommonService,
UtilService,
], ],
entryComponents: [JhiLoginModalComponent], entryComponents: [JhiLoginModalComponent],
exports: [ exports: [
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
* designed by xwenyuan * designed by xwenyuan
* github: https://github.com/xwenyuan/jtopo_topology.git * github: https://github.com/xwenyuan/jtopo_topology.git
*/ */
/** /**
* 提供拓扑图面板相关操作的函数集,编辑器继承其全部功能 * 提供拓扑图面板相关操作的函数集,编辑器继承其全部功能
*/ */
...@@ -86,6 +85,8 @@ TopologyPanel.prototype.loadTopologyByJson = function (topologyJson, backImg) { ...@@ -86,6 +85,8 @@ TopologyPanel.prototype.loadTopologyByJson = function (topologyJson, backImg) {
JTopo.replaceStageWithJson(topologyJson) JTopo.replaceStageWithJson(topologyJson)
if (editor.stage && editor.scene && editor.scene.childs && editor.scene.childs.length > 0) { if (editor.stage && editor.scene && editor.scene.childs && editor.scene.childs.length > 0) {
editor.stage.centerAndZoom() editor.stage.centerAndZoom()
editor.utils.getAllNodes()[0].alarm = "告警";
console.log(editor.utils.getAllNodes()[0]);
} }
} catch (e) { } catch (e) {
console.error(e) console.error(e)
...@@ -161,7 +162,7 @@ function TopologyEditor() { ...@@ -161,7 +162,7 @@ function TopologyEditor() {
linkFontColor: 'red', // 连线文字颜色,如"255,255,0" linkFontColor: 'red', // 连线文字颜色,如"255,255,0"
linkArrowsRadius: 0, // 线条箭头半径 linkArrowsRadius: 0, // 线条箭头半径
linkDefaultWidth: 3, // 连线宽度 linkDefaultWidth: 3, // 连线宽度
linkOffsetGap: 40, // 折线拐角处的长度 linkOffsetGap: 80, // 折线拐角处的长度
linkDirection: 'horizontal', // 折线的方向 linkDirection: 'horizontal', // 折线的方向
// Container属性 // Container属性
containerAlpha: 1, containerAlpha: 1,
...@@ -172,8 +173,11 @@ function TopologyEditor() { ...@@ -172,8 +173,11 @@ function TopologyEditor() {
containerFont: '12px Consolas', containerFont: '12px Consolas',
containerFontColor: 'black', containerFontColor: 'black',
containerBorderColor: 'black', containerBorderColor: 'black',
containerBorderRadius: 30 containerBorderRadius: 30,
}
//主机属性
hostId:null,
};
// 布局参数 // 布局参数
this.layout = {} this.layout = {}
// 绘图区属性 // 绘图区属性
...@@ -507,6 +511,7 @@ TopologyEditor.prototype.init = function (topologyGuid, backImg, topologyJson) { ...@@ -507,6 +511,7 @@ TopologyEditor.prototype.init = function (topologyGuid, backImg, topologyJson) {
// 模拟告警 // 模拟告警
if(editor.utils.getAllNodes().length > 0){ if(editor.utils.getAllNodes().length > 0){
debugger
editor.utils.getAllNodes()[0].alarm = "告警"; editor.utils.getAllNodes()[0].alarm = "告警";
} }
...@@ -610,52 +615,13 @@ TopologyEditor.prototype.init = function (topologyGuid, backImg, topologyJson) { ...@@ -610,52 +615,13 @@ TopologyEditor.prototype.init = function (topologyGuid, backImg, topologyJson) {
// 只处理双击节点事件 // 只处理双击节点事件
if (event.target instanceof JTopo.Node && editor.stageMode === 'edit') { if (event.target instanceof JTopo.Node && editor.stageMode === 'edit') {
console.log(event.target)
// alert('双击了节点') localStorage.setItem("node",'true');
// 查询单个节点的信息, 回填数据
layer.open({
title: "编辑设备",
type: 1,
area: ['600px', '500px'],
content: $("#node-dialog"),
btn: ["应用", "删除", "取消"],
yes: function(index, layero){
var param = getParam("node-form");
self.currentNode.text = param.deviceName; //设置文字
$("#node-dialog").css("display","none");
layer.close(index);
},
btn2: function () {
editor.utils.deleteNode(self.currentNode);
},
cancel:function () {
$("#node-dialog").css("display","none")
}
});
} else if (event.target instanceof JTopo.Link && editor.stageMode === 'edit') { } else if (event.target instanceof JTopo.Link && editor.stageMode === 'edit') {
layer.open({ console.log(event.target);
title: "编辑线", localStorage.setItem("link",'true');
type: 1,
area: ['600px', '500px'],
content: $("#link-dialog"),
btn: ["应用", "删除", "取消"],
yes: function(index, layero){
var param = getParam("link-form");
console.log(param);
self.currentNode.text = param.lineName;
self.currentNode.fontColor = param.lineColor;
$('#link-dialog').css('display', 'none')
layer.close(index);
},
btn2: function () {
editor.utils.deleteNode(self.currentNode);
},
cancel:function () {
$('#link-dialog').css('display', 'none')
}
});
} }
}) });
// 清除起始节点不完整的拖放线 // 清除起始节点不完整的拖放线
this.scene.mousedown(function (e) { this.scene.mousedown(function (e) {
...@@ -1056,8 +1022,7 @@ editor.utils = { ...@@ -1056,8 +1022,7 @@ editor.utils = {
editor.utils.setNormalMode(); editor.utils.setNormalMode();
}, },
// 添加节点, 编辑模式下才能添加 // 添加节点, 编辑模式下才能添加
addNode: function (img, text) { addNode: function (img, text, hostId) {
console.log(editor.stageMode);
if (editor.stageMode === "normal") { if (editor.stageMode === "normal") {
return; return;
} }
...@@ -1071,15 +1036,17 @@ editor.utils = { ...@@ -1071,15 +1036,17 @@ editor.utils = {
node.fontColor = editor.config.nodeFontColor node.fontColor = editor.config.nodeFontColor
// 节点坐标 // 节点坐标
node.setBound(320, -200, 50, 50); node.setBound(320, -200, 50, 50);
console.log(node);
// 默认节点图片 // 默认节点图片
node.setImage(topoImgPath + img) node.setImage(topoImgPath + img);
// 节点数据 // 节点数据
node.nodeId = generateUUID() node.nodeId = generateUUID()
node.text = text node.text = text
node.nodeImage = img node.nodeImage = img;
node.hostId = hostId;
editor.scene.add(node) editor.scene.add(node)
editor.currentNode = node editor.currentNode = node;
console.log(node);
}, },
// 重新渲染节点 // 重新渲染节点
reloadNode: function (node) { reloadNode: function (node) {
...@@ -1385,5 +1352,8 @@ editor.utils = { ...@@ -1385,5 +1352,8 @@ editor.utils = {
} }
}) })
}) })
},
update:function (config) {
editor.config = config;
} }
} }
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
"variables-before-functions" "variables-before-functions"
], ],
"no-arg": true, "no-arg": true,
"no-bitwise": true, "no-bitwise": false,
"no-console": [ "no-console": [
true, true,
"debug", "debug",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment