|
1
2
3
4
5
6
7
|
// import { Addon, FunctionExt, Graph, Shape } from "@antv/x6";
import { FunctionExt, Graph, Shape } from "@antv/x6";
import { getImg } from "../index.js";
import graphData from "./data/data";
import { customPorts } from "./ports";
import "./shape";
const insertCss = require("insert-css");
|
|
8
9
10
11
12
13
14
|
insertCss(`
@keyframes ant-line {
to {
stroke-dashoffset: -1000
}
}
|
|
15
|
`);
|
|
16
17
|
export default class FlowGraph {
|
|
18
19
|
static graph = null;
static stencil = null;
|
|
20
21
22
23
24
25
26
27
28
29
30
31
32
|
/**
* 初始化方法
* @param {*} dom 画板容器
* @param {*} width 容器宽度
* @param {*} height 容器高度
* @param {*} flag 默认为true,传入false只实例化画板
* @returns
*/
static init(dom, width = 1200, height = 900, flag = true) {
// 初始化 流程图画板
this.graph = new Graph({
background: {
|
|
33
|
color: "#f4f4f4", // 设置画布背景颜色
|
|
34
35
36
37
38
39
40
41
|
},
container: dom,
width: width,
height: height,
autoResize: true,
grid: {
size: 10,
visible: true,
|
|
42
|
type: "doubleMesh",
|
|
43
44
|
args: [
{
|
|
45
46
|
color: "#cccccc",
thickness: 1,
|
|
47
48
|
},
{
|
|
49
|
color: "#5F95FF",
|
|
50
|
thickness: 1,
|
|
51
52
53
|
factor: 4,
},
],
|
|
54
55
56
57
58
|
},
scroller: {
enabled: false,
pageVisible: false,
pageBreak: false,
|
|
59
|
pannable: false,
|
|
60
61
62
63
64
65
66
67
|
},
// 开启画布缩放
mousewheel: {
enabled: true,
// modifiers: ['ctrl', 'meta'],
minScale: 0.1,
maxScale: 10,
// 当鼠标滚轮滚动时触发缩放
|
|
68
|
zoomOnMouseWheel: true,
|
|
69
70
71
72
73
74
75
76
|
},
interacting: {
nodeMovable: false, // 节点是否可以被移动。
edgeMovable: false, // 边是否可以被移动。
edgeLabelMovable: false, // 边的标签是否可以被移动。
arrowheadMovable: false, // 边的起始/终止箭头是否可以被移动
vertexMovable: false, // 边的路径点是否可以被移动。
vertexAddable: false, // 是否可以添加边的路径点。
|
|
77
|
vertexDeletable: false, // 边的路径点是否可以被删除。
|
|
78
79
80
81
82
83
84
85
86
|
},
connecting: {
snap: true, // 是否自动吸附
allowMulti: true, // 是否允许在相同的起始节点和终止之间创建多条边
allowNode: false, // 是否允许边链接到节点(非节点上的链接桩)
allowBlank: false, // 是否允许连接到空白点
allowLoop: false, // 是否允许创建循环连线,即边的起始节点和终止节点为同一节点,
allowEdge: false, // 是否允许边链接到另一个边
highlight: false, // 拖动边时,是否高亮显示所有可用的连接桩或节点
|
|
87
88
|
connectionPoint: "anchor", // 指定连接点
anchor: "center", // 指定被连接的节点的锚点
|
|
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
createEdge() {
// X6 的 Shape 命名空间中内置 Edge、DoubleEdge、ShadowEdge 三种边
return new Shape.DoubleEdge({
attrs: {
// line: {
// // stroke: '#5F95FF',
// // strokeWidth: 4,
// // targetMarker: {
// // name: 'classic',
// // size: 8,
// // },
// stroke: '#1890ff',
// strokeDasharray: 5,
// targetMarker: null,//block classic diamond cross async path circle circlePlus ellipse
// style: {
// animation: 'ant-line 30s infinite linear',
// },
// },
line: {
strokeWidth: 4,
|
|
109
|
stroke: "#cfe7f2",
|
|
110
111
|
strokeDasharray: 0,
style: {
|
|
112
|
animation: "ant-line 0s infinite linear",
|
|
113
|
},
|
|
114
|
targetMarker: null, // 去掉箭头
|
|
115
116
|
},
outline: {
|
|
117
118
119
|
stroke: "#456d89",
strokeWidth: 7,
},
|
|
120
121
|
},
router: {
|
|
122
|
name: "metro",
|
|
123
124
|
args: {
offset: 32,
|
|
125
126
127
128
|
direction: "H",
},
},
});
|
|
129
130
131
132
133
|
},
validateConnection({
sourceView,
targetView,
sourceMagnet,
|
|
134
|
targetMagnet,
|
|
135
136
|
}) {
if (sourceView === targetView) {
|
|
137
|
return false;
|
|
138
139
|
}
if (!sourceMagnet) {
|
|
140
|
return false;
|
|
141
142
|
}
if (!targetMagnet) {
|
|
143
|
return false;
|
|
144
|
}
|
|
145
146
|
return true;
},
|
|
147
148
149
|
},
highlighting: {
magnetAvailable: {
|
|
150
|
name: "stroke",
|
|
151
152
153
154
|
args: {
padding: 4,
attrs: {
strokeWidth: 4,
|
|
155
156
157
158
|
stroke: "rgba(223,234,255)",
},
},
},
|
|
159
160
161
|
},
// 开启拖拽平移(防止冲突,按下修饰键并点击鼠标才能触发画布拖拽)
panning: {
|
|
162
|
enabled: true,
|
|
163
164
165
166
167
168
169
170
171
|
// modifiers: 'shift'
},
resizing: true,
rotating: true,
selecting: {
enabled: true,
multiple: true,
rubberband: true,
movable: true,
|
|
172
|
showNodeSelectionBox: true,
|
|
173
174
175
176
|
},
snapline: true,
history: true,
clipboard: {
|
|
177
|
enabled: true,
|
|
178
179
|
},
keyboard: {
|
|
180
|
enabled: true,
|
|
181
182
183
184
|
},
embedding: {
enabled: true,
findParent({ node }) {
|
|
185
|
const bbox = node.getBBox();
|
|
186
187
|
return this.getNodes().filter((node) => {
// 只有 data.parent 为 true 的节点才是父节点
|
|
188
|
const data = node.getData();
|
|
189
|
if (data && data.parent) {
|
|
190
191
|
const targetBBox = node.getBBox();
return bbox.isIntersectWithRect(targetBBox);
|
|
192
|
}
|
|
193
194
195
196
197
|
return false;
});
},
},
});
|
|
198
199
|
if (!flag) {
// this.graph.centerContent()
|
|
200
201
|
this.graph.hideGrid(); // 返显渲染的时候 隐藏网格
return this.graph;
|
|
202
203
204
205
206
207
208
|
}
// this.initStencil()
// this.initShape()
// this.initGraphShape()
// this.initEvent()
// return this.graph
}
|
|
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
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
|
// // 初始化根节点
// static initStencil() {
// this.stencil = new Addon.Stencil({
// target: this.graph,
// stencilGraphWidth: 280,
// search: { rect: true },
// collapsable: true,
// groups: [
// {
// name: 'basic',
// title: '基础节点',
// graphHeight: 180
// },
// {
// name: 'custom-image',
// title: '系统设计图',
// graphHeight: 600
// }
// // {
// // name: 'combination',
// // title: '组合节点',
// // layoutOptions: {
// // columns: 1,
// // marginX: 60,
// // },
// // graphHeight: 260,
// // },
// // {
// // name: 'group',
// // title: '节点组',
// // graphHeight: 100,
// // layoutOptions: {
// // columns: 1,
// // marginX: 60,
// // },
// // },
// ]
// })
// const stencilContainer = document.querySelector('#stencil')
// stencilContainer?.appendChild(this.stencil.container)
// }
|
|
250
251
|
// 初始化具体每个根节点下不同类型节点
static initShape() {
|
|
252
|
const { graph } = this;
|
|
253
254
|
// 基础节点
const r1 = graph.createNode({
|
|
255
|
shape: "flow-chart-rect",
|
|
256
257
258
|
attrs: {
body: {
rx: 24,
|
|
259
|
ry: 24,
|
|
260
261
|
},
text: {
|
|
262
263
264
265
|
text: "起始节点",
},
},
});
|
|
266
|
const r2 = graph.createNode({
|
|
267
|
shape: "flow-chart-rect",
|
|
268
269
|
attrs: {
text: {
|
|
270
271
272
273
|
text: "流程节点",
},
},
});
|
|
274
|
const r3 = graph.createNode({
|
|
275
|
shape: "flow-chart-rect",
|
|
276
277
278
279
|
width: 52,
height: 52,
angle: 45,
attrs: {
|
|
280
|
"edit-text": {
|
|
281
|
style: {
|
|
282
283
|
transform: "rotate(-45deg)",
},
|
|
284
285
|
},
text: {
|
|
286
287
288
|
text: "判断节点",
transform: "rotate(-45deg)",
},
|
|
289
290
291
292
293
|
},
ports: {
groups: {
top: {
position: {
|
|
294
|
name: "top",
|
|
295
|
args: {
|
|
296
297
298
|
dx: -26,
},
},
|
|
299
300
301
|
},
right: {
position: {
|
|
302
|
name: "right",
|
|
303
|
args: {
|
|
304
305
306
|
dy: -26,
},
},
|
|
307
308
309
|
},
bottom: {
position: {
|
|
310
|
name: "bottom",
|
|
311
|
args: {
|
|
312
313
314
|
dx: 26,
},
},
|
|
315
316
317
|
},
left: {
position: {
|
|
318
|
name: "left",
|
|
319
|
args: {
|
|
320
321
322
323
324
325
326
|
dy: 26,
},
},
},
},
},
});
|
|
327
|
const r4 = graph.createNode({
|
|
328
|
shape: "flow-chart-rect",
|
|
329
330
331
332
333
|
width: 70,
height: 70,
attrs: {
body: {
rx: 35,
|
|
334
|
ry: 35,
|
|
335
336
|
},
text: {
|
|
337
338
339
340
|
text: "链接节点",
},
},
});
|
|
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
|
// // 组合节点
// const c1 = graph.createNode({
// shape: 'flow-chart-image-rect'
// })
// const c2 = graph.createNode({
// shape: 'flow-chart-title-rect'
// })
// const c3 = graph.createNode({
// shape: 'flow-chart-animate-text'
// })
// // 节点组
// const g1 = graph.createNode({
// shape: 'groupNode',
// attrs: {
// text: {
// text: 'Group Name'
// }
// },
// data: {
// parent: true
// }
// })
|
|
363
364
365
|
// 系统设计图
const imgs = [
{
|
|
366
|
image: getImg("ldb.png"),
|
|
367
368
|
},
{
|
|
369
|
image: getImg("wft1.png"),
|
|
370
371
|
},
{
|
|
372
|
image: getImg("wft2.png"),
|
|
373
374
|
},
{
|
|
375
|
image: getImg("wft3.png"),
|
|
376
377
|
},
{
|
|
378
|
image: getImg("wft4.png"),
|
|
379
380
|
},
{
|
|
381
|
image: getImg("wft5.png"),
|
|
382
383
|
},
{
|
|
384
|
image: getImg("lqt.png"),
|
|
385
386
|
},
{
|
|
387
|
image: getImg("lqb-o.png"),
|
|
388
389
|
},
{
|
|
390
|
image: getImg("ldb-o.png"),
|
|
391
392
|
},
{
|
|
393
|
image: getImg("bh.png"),
|
|
394
395
|
},
{
|
|
396
|
image: getImg("f1.png"),
|
|
397
398
|
},
{
|
|
399
|
image: getImg("f2.png"),
|
|
400
401
|
},
{
|
|
402
403
404
|
image: getImg("f3.png"),
},
];
|
|
405
|
const imgNodes = imgs.map((item) => {
|
|
406
407
408
409
410
411
412
|
return graph.createNode({
// shape: 'flow-chart-image-rect-custom',
// attrs: {
// image: {
// 'xlink:href': item.image,
// }
// }
|
|
413
|
shape: "image", // 可选值:Rect Circle Ellipse Polygon Polyline Path Image HTML TextBlock BorderedImage EmbeddedImage InscribedImage Cylinder
|
|
414
415
416
417
|
imageUrl: item.image,
attrs: {
image: {
// fill: 'yellow',
|
|
418
|
},
|
|
419
420
421
|
},
width: 52,
height: 52,
|
|
422
423
424
425
426
|
ports: { ...customPorts },
});
});
this.stencil.load([r1, r2, r3, r4], "basic");
this.stencil.load(imgNodes, "custom-image");
|
|
427
428
429
430
431
432
|
// this.stencil.load([c1, c2, c3], 'combination')
// this.stencil.load([g1], 'group')
}
// 根据json渲染节点和边
static initGraphShape(gd = graphData) {
|
|
433
434
435
|
console.log(gd);
var data = null;
data = gd;
|
|
436
|
// setInterval(() => {
|
|
437
438
439
440
441
|
console.log(data.cells[0].position.x);
data.cells[0].position.x += 1;
this.graph.fromJSON(data);
const nodes = this.graph.getNodes();
console.log(nodes);
|
|
442
443
444
445
446
447
448
|
// }, 100);
}
// 连接桩显示时机
static showPorts(ports, show) {
for (let i = 0, len = ports.length; i < len; i = i + 1) {
|
|
449
|
ports[i].style.visibility = show ? "visible" : "hidden";
|
|
450
451
452
453
454
|
}
}
// 事件相关
static initEvent() {
|
|
455
456
|
const { graph } = this;
const container = document.getElementById("container");
|
|
457
|
// 右键编辑文本
|
|
458
459
460
461
462
|
graph.on("node:contextmenu", ({ cell, view }) => {
console.log(view.container);
const oldText = cell.attr("text/text");
cell.attr("text/style/display", "none");
const elem = view.container.querySelector(".x6-edit-text");
|
|
463
|
if (elem) {
|
|
464
465
|
elem.innerText = oldText;
elem.focus();
|
|
466
467
|
}
const onBlur = () => {
|
|
468
469
470
|
cell.attr("text/text", elem.innerText);
cell.attr("text/style/display", "inline-block");
};
|
|
471
|
if (elem) {
|
|
472
473
474
475
|
elem.addEventListener("blur", () => {
onBlur();
elem.removeEventListener("blur", onBlur);
});
|
|
476
|
}
|
|
477
|
});
|
|
478
|
// 鼠标移入 显示连接桩
|
|
479
|
graph.on(
|
|
480
|
"node:mouseenter",
|
|
481
|
FunctionExt.debounce(() => {
|
|
482
483
|
const ports = container.querySelectorAll(".x6-port-body");
this.showPorts(ports, true);
|
|
484
485
|
}),
500
|
|
486
|
);
|
|
487
|
// 鼠标移出 隐藏连接桩
|
|
488
489
490
491
|
graph.on("node:mouseleave", () => {
const ports = container.querySelectorAll(".x6-port-body");
this.showPorts(ports, false);
});
|
|
492
|
|
|
493
494
495
496
497
|
graph.on("node:collapse", ({ node, e }) => {
e.stopPropagation();
node.toggleCollapse();
const collapsed = node.isCollapsed();
const cells = node.getDescendants();
|
|
498
|
cells.forEach((n) => {
|
|
499
|
if (collapsed) {
|
|
500
|
n.hide();
|
|
501
|
} else {
|
|
502
|
n.show();
|
|
503
|
}
|
|
504
505
|
});
});
|
|
506
|
// backspace
|
|
507
508
|
graph.bindKey("delete", () => {
const cells = graph.getSelectedCells();
|
|
509
|
if (cells.length) {
|
|
510
|
graph.removeCells(cells);
|
|
511
|
}
|
|
512
|
});
|
|
513
|
// 鼠标动态添加/删除小工具。
|
|
514
|
graph.on("edge:mouseenter", ({ cell }) => {
|
|
515
516
517
518
519
520
521
522
523
524
525
526
|
/**
* EdgeTool
* vertices 路径点工具,在路径点位置渲染一个小圆点,拖动小圆点修改路径点位置,双击小圆点删除路径点,在边上单击添加路径点。
* segments 线段工具。在边的每条线段的中心渲染一个工具条,可以拖动工具条调整线段两端的路径点的位置。
* boundary 根据边的包围盒渲染一个包围边的矩形。注意,该工具仅仅渲染一个矩形,不带任何交互。
* button 在指定位置处渲染一个按钮,支持自定义按钮的点击交互。
* button-remove 在指定的位置处,渲染一个删除按钮,点击时删除对应的边。
* source-arrowhead-和-target-arrowhead 在边的起点或终点渲染一个图形(默认是箭头),拖动该图形来修改边的起点或终点。
* edge-editor 提供边上文本编辑功能。
*/
cell.addTools([
{
|
|
527
|
name: "vertices",
|
|
528
|
args: {
|
|
529
|
attrs: { fill: "#007acc" },
|
|
530
531
532
533
534
535
536
537
538
|
// 移动路径点过程中的吸附半径。当路径点与邻近的路径点的某个坐标 (x, y) 距离在半径范围内时,将当前路径点的对应坐标 (x, y) 吸附到邻居路径的路径点。
snapRadius: 20,
// 在边上按下鼠标时,是否可以添加新的路径点。
addable: false,
// 是否可以通过双击移除路径点。
removable: false,
// 是否自动移除冗余的路径点。
removeRedundancies: false,
// 是否阻止工具上的鼠标事件冒泡到边视图上。阻止后鼠标与工具交互时将不会触发边的 mousedown、mousemove 和 mouseup 事件。
|
|
539
540
541
542
543
544
545
546
|
stopPropagation: false,
},
},
]);
});
graph.on("edge:mouseleave", ({ cell }) => {
cell.removeTools();
});
|
|
547
548
|
}
}
|