Commit fd9e315f authored by wangqinghua's avatar wangqinghua

重构 jtopo

parent 3aa3544f
<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);
}
}
}
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