Newer
Older
/**
* 基于jtopo-editor.js的二次封装
* designed by xwenyuan
* github: https://github.com/xwenyuan/jtopo_topology.git
*/
/**
* 提供拓扑图面板相关操作的函数集,编辑器继承其全部功能
*/
/**
* 保存序列化的拓扑图JSON数据到服务器
*/
TopologyPanel.prototype.saveTopology = function (url) {
var containers = editor.utils.getContainers()
for (var c = 0; c < containers.length; c++) {
var temp = []
var nodes = containers[c].childs
for (var n = 0; n < nodes.length; n++) {
if (nodes[n] instanceof JTopo.Node) {
temp.push(nodes[n].nodeId)
}
}
containers[c].childNodes = temp.join(',')
}
// 获取json
// 保存拓扑图数据
}
/**
* 重置拓扑图
*/
TopologyPanel.prototype.resetTopology = function (url) {
editor.stageMode = 'normal'
this.replaceStage(url)
}
/**
* 加载指定id的拓扑图JSON数据结构
* @param topologyGuid 拓扑 表记录ID
* @param backImg 拓扑图的背景图片
*/
TopologyPanel.prototype.loadTopology = function (response, topologyGuid, backImg) {
'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(topologyGuid, backImg, initTopologyJson, '')
} else {
// 拓扑存在,渲染拓扑图
}
}
/**
* 传入JSON形式的拓扑图数据,绘制拓扑图。如果数据结构不正确,返回空拓扑图
* @param topologyJson json形式的拓扑结构数据
* @param backImg 拓扑图的背景图片
*/
TopologyPanel.prototype.loadTopologyByJson = function (topologyJson, backImg) {
try {
JTopo.replaceStageWithJson(topologyJson)
if (editor.stage && editor.scene && editor.scene.childs && editor.scene.childs.length > 0) {
editor.stage.centerAndZoom()
}
} catch (e) {
console.error(e)
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
'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': []
}
]
}
JTopo.replaceStageWithJson(initTopologyJson)
if (editor.stage && editor.scene && editor.scene.childs && editor.scene.childs.length > 0) {
editor.stage.centerAndZoom()
}
}
}
/**
* 清空所有节点
*/
TopologyPanel.prototype.deleteAllNodes = function () {
editor.stage.childs.forEach(function (s) {
s.clear()
})
// 连线重置
editor.beginNode = null
editor.link = null
// alert('已清空拓扑图')
}
/**
* 编辑器对象,原型继承拓扑图面板对象,提供编辑器的主要功能
*/
function 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', // 节点字体
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
linkArrowsRadius: 0, // 线条箭头半径
linkDefaultWidth: 3, // 连线宽度
linkOffsetGap: 40, // 折线拐角处的长度
linkDirection: '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
// 节点右键菜单DOM对象
this.nodeMenu = $('#node-menu')
// 连线右键菜单DOM对象
this.lineMenu = $('#line-menu')
// 全局右键菜单
this.mainMenu = $('#main-menu')
// 布局管理菜单
this.layoutMenu = $('#layout-menu')
// 节点文字方向
this.nodeTextPosMenu = $('#node-text-pos-menu')
// 节点分组菜单
this.groupMangeMenu = $('#group-mange-menu')
// 节点对齐菜单
this.groupAlignMenu = $('#group-align-menu')
this.alignGroup = $('#align-group')
// 分组的容器管理菜单
this.containerMangeMenu = $('#container-mange-menu')
// 调用构造函数,继承TopologyPanel类
TopologyPanel.call(this)
}
// 原型继承
TopologyEditor.prototype = new TopologyPanel()
/**
* 菜单初始化
*/
TopologyEditor.prototype.initMenus = function () {
// 右键菜单事件处理(右键一级菜单)
self.nodeMenu.on('click', function (event) {
// 菜单文字对应事件
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
if (text === '删除节点(Delete)') {
editor.utils.deleteSelectedNodes()
} else if (text === '复制节点(Shift+C)') {
self.utils.cloneSelectedNodes()
} else if (text === '撤销(Shift+Z)') {
self.utils.cancleNodeAction()
} else if (text === '重做(Shift+R)') {
self.utils.reMakeNodeAction()
} else {
editor.utils.saveNodeInitState()
}
switch (text) {
case '放大(Shift+)':
self.utils.scalingBig()
self.utils.saveNodeNewState()
break
case '缩小(Shift-)':
self.utils.scalingSmall()
self.utils.saveNodeNewState()
break
case '顺时针旋转(Shift+U)':
self.utils.rotateAdd()
self.utils.saveNodeNewState()
break
case '逆时针旋转(Shift+I)':
self.utils.rotateSub()
self.utils.saveNodeNewState()
break
case '节点文字':
return
default :
}
// 关闭菜单
$(this).hide()
})
self.nodeMenu.on('mouseover', function (event) {
// 菜单文字
var text = $.trim($(event.target).text())
var menuX = parseInt(this.style.left) + $(document.getElementById('change-node-text-pos')).width()
// 边界判断
if (menuX + self.nodeTextPosMenu.width() * 2 >= self.stage.width) {
menuX -= (self.nodeTextPosMenu.width() + self.nodeMenu.width())
}
if (text === '文字位置') {
self.layoutMenu.hide()
self.nodeTextPosMenu.css({
top: parseInt(this.style.top) + $(document.getElementById('change-node-text-pos')).height(),
left: menuX
}).show()
} else if (text === '应用布局') {
self.nodeTextPosMenu.hide()
self.layoutMenu.css({
top: parseInt(this.style.top),
left: menuX
}).show()
} else {
self.layoutMenu.hide()
self.nodeTextPosMenu.hide()
}
})
// 修改节点文字位置菜单
self.nodeTextPosMenu.on('click', function (event) {
// 菜单文字
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
if (self.currentNode && self.currentNode instanceof JTopo.Node) {
self.utils.saveNodeInitState()
switch (text) {
case '顶部居左':
self.currentNode.textPosition = 'Top_Left'
self.utils.saveNodeNewState()
break
case '顶部居中':
self.currentNode.textPosition = 'Top_Center'
self.utils.saveNodeNewState()
break
case '顶部居右':
self.currentNode.textPosition = 'Top_Right'
self.utils.saveNodeNewState()
break
case '中间居左':
self.currentNode.textPosition = 'Middle_Left'
self.utils.saveNodeNewState()
break
case '居中':
self.currentNode.textPosition = 'Middle_Center'
self.utils.saveNodeNewState()
break
case '中间居右':
self.currentNode.textPosition = 'Middle_Right'
self.utils.saveNodeNewState()
break
case '底部居左':
self.currentNode.textPosition = 'Bottom_Left'
self.utils.saveNodeNewState()
break
case '底部居中':
self.currentNode.textPosition = 'Bottom_Center'
self.utils.saveNodeNewState()
break
case '底部居右':
self.currentNode.textPosition = 'Bottom_Right'
self.utils.saveNodeNewState()
break
default :
}
$('div[id$=\'-menu\']').hide()
}
})
// 连线菜单
self.lineMenu.on('click', function (event) {
// 关闭菜单
$(this).hide()
switch (text) {
case '连线设置':
// alert('连线设置')
break
case '删除连线':
editor.utils.deleteLine()
break
default :
}
})
// 系统设置菜单
self.mainMenu.on('click', function (event) {
// 关闭菜单
$(this).hide()
})
// 节点分组菜单
self.groupMangeMenu.on('click', function (event) {
$(this).hide()
if (text === '新建分组') {
self.utils.toMerge()
}
})
// 对齐
self.groupAlignMenu.on('click', function (event) {
var currNode = self.currentNode
var selectedNodes = self.utils.getSelectedNodes()
if (!currNode || !selectedNodes || selectedNodes.length === 0) return
$(this).hide()
selectedNodes.forEach(function (n) {
if (n.nodeId === currNode.nodeId) return true
if (text === '水平对齐') {
n.y = currNode.y
} else if (text === '垂直对齐') {
n.x = currNode.x
} else {
}
})
})
self.groupMangeMenu.on('mouseover', function (event) {
var menuX = parseInt(this.style.left) + $(document.getElementById('align-group')).width()
if (menuX + self.alignGroup.width() * 2 >= self.stage.width) {
menuX -= (self.alignGroup.width() + self.groupMangeMenu.width())
}
self.groupAlignMenu.css({
top: parseInt(this.style.top) + $(document.getElementById('align-group')).height(),
left: menuX
}).show()
} else {
self.groupAlignMenu.hide()
}
})
// 容器管理菜单
self.containerMangeMenu.on('click', function (event) {
if (!(cNode instanceof JTopo.Container)) {
return
}
$(this).hide()
if (text === '拆分') {
self.utils.toSplit()
self.utils.deleteNode(cNode)
}
})
// 容器管理菜单
self.layoutMenu.on('click', function (event) {
editor.currentNode.layout = {}
$('div[id$=\'-menu\']').hide()
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
if (text === '取消布局') {
editor.currentNode.layout.on = false
} else if (text === '分组布局') {
editor.currentNode.layout.on = true
editor.currentNode.layout.type = 'auto'
} else if (text === '树形布局') {
editor.currentNode.layout.on = true
editor.currentNode.layout.type = 'tree'
editor.currentNode.layout.direction = 'bottom'
editor.currentNode.layout.width = 80
editor.currentNode.layout.height = 100
JTopo.layout.layoutNode(self.scene, self.currentNode, true)
} else if (text === '圆形布局') {
editor.currentNode.layout.on = true
editor.currentNode.layout.type = 'circle'
editor.currentNode.layout.radius = 200
JTopo.layout.layoutNode(self.scene, self.currentNode, true)
}
})
}
/**
* 替换当前舞台,用于编辑保存后重新加载
* @param topologyGuid
*/
TopologyEditor.prototype.replaceStage = function (url) {
// var self = this
$.ajax({
type: 'GET',
url: url,
async: false,
contentType: 'application/json',
dataType: 'json',
error: function () {
// alert('服务器异常,请稍后重试..')
},
success: function (response) {
// 错误处理
if (response.code !== 200) {
console.error(response.msg)
} else {
JTopo.replaceStageWithJson(topologyJson)
if (editor.stage && editor.scene && editor.scene.childs && editor.scene.childs.length > 0) {
editor.stage.centerAndZoom()
}
}
}
})
}
/**
* 编辑器初始化方法,根据请求返回结果加载空白的或者指定结构的拓扑编辑器
* @param topologyGuid 拓扑记录ID
* @param backImg 背景图片
* @param topologyJson 拓扑JSON结构
*/
TopologyEditor.prototype.init = function (topologyGuid, backImg, topologyJson) {
if (!topologyJson) {
return
}
this.topologyGuid = topologyGuid
// 创建jTopo舞台屏幕对象
canvas.width = $('#topology-body').width()
canvas.height = $('#topology-body').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
if(editor.utils.getAllNodes().length > 0){
editor.utils.getAllNodes()[0].alarm = "告警";
}
// 初始化菜单
this.initMenus()
// 鼠标进入事件
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中的相对位置
var menuY = event.layerY ? event.layerY : event.offsetY
var 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中的相对位置
var menuY = event.layerY ? event.layerY : event.offsetY
var 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) {
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
// 消抖
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') {
// 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.text;
// self.currentNode.textPosition = param.textPosition;
// self.currentNode.setLocation(param.x, param.y);
// $("#node-dialog").css("display","none")
// },
// btn2: function () {
// editor.utils.deleteNode(self.currentNode);
// },
// cancel:function () {
// $("#node-dialog").css("display","none")
// }
// });
} 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){
},
btn2: function () {
editor.utils.deleteNode(self.currentNode);
},
cancel:function () {
$('#link-dialog').css('display', 'none')
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
}
});
}
})
// 清除起始节点不完整的拖放线
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()
var menuY = event.layerY ? event.layerY : event.offsetY
var menuX = event.layerX ? event.layerX : event.offsetX
// 记录鼠标触发位置在canvas中的相对位置
self.xInCanvas = menuX
self.yInCanvas = menuY
if (event.target) {
if (event.target instanceof JTopo.Node) { // 处理节点右键菜单事件
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
// 如果是节点多选状态弹出分组菜单
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) {
// 在终点节点处松开鼠标,则建立连线
// 判断两个节点是否有循环引用
/** ***************** 我这里允许循环引用 *************************
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;
}
}
*****************************************************************/
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
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)
}
}
/**
* 图元拖放功能实现
* @param modeDiv 备选列表中的元素(各种样式的节点)
* @param drawArea 舞台区域
*/
TopologyEditor.prototype.drag = function (modeDiv, drawArea, text) {
if (!text) text = ''
// 拖拽开始,携带必要的参数
modeDiv.ondragstart = function (event) {
event = event || window.event
var dragSrc = this
var backImg = $(dragSrc).find('img').eq(0).attr('data-src')
try {
// IE只允许KEY为text和URL
event.dataTransfer.setData('text', backImg + ';' + text + ';' + nodeType)
} catch (ex) {
console.log(ex)
}