2026-02-08 15:38:50 +08:00
|
|
|
|
/*
|
|
|
|
|
|
* This file is part of the kernelstudio package.
|
|
|
|
|
|
*
|
|
|
|
|
|
* (c) 2014-2025 zlin <admin@kernelstudio.com>
|
|
|
|
|
|
*
|
|
|
|
|
|
* For the full copyright and license information, please view the LICENSE file
|
|
|
|
|
|
* that was distributed with this source code.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2026-02-08 17:57:40 +08:00
|
|
|
|
import { Edge, Graph, Path, Selection } from '@antv/x6';
|
|
|
|
|
|
import type { ModelElement } from './element';
|
2026-02-08 15:38:50 +08:00
|
|
|
|
import type { Connecting } from '@antv/x6/lib/graph/options';
|
2026-02-08 22:31:13 +08:00
|
|
|
|
import { createLineOptions } from './line';
|
2026-02-08 15:38:50 +08:00
|
|
|
|
|
|
|
|
|
|
Graph.registerConnector(
|
|
|
|
|
|
'sequenceFlowConnector',
|
|
|
|
|
|
(s, e) => {
|
|
|
|
|
|
const offset = 4;
|
|
|
|
|
|
const deltaY = Math.abs(e.y - s.y);
|
|
|
|
|
|
const control = Math.floor((deltaY / 3) * 2);
|
|
|
|
|
|
|
|
|
|
|
|
const v1 = { x: s.x, y: s.y + offset + control };
|
|
|
|
|
|
const v2 = { x: e.x, y: e.y - offset - control };
|
|
|
|
|
|
|
|
|
|
|
|
return Path.parse(
|
|
|
|
|
|
`
|
|
|
|
|
|
M ${s.x} ${s.y}
|
|
|
|
|
|
L ${s.x} ${s.y + offset}
|
|
|
|
|
|
C ${v1.x} ${v1.y} ${v2.x} ${v2.y} ${e.x} ${e.y - offset}
|
|
|
|
|
|
L ${e.x} ${e.y}
|
|
|
|
|
|
`,
|
|
|
|
|
|
).serialize();
|
|
|
|
|
|
},
|
|
|
|
|
|
true,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
export const createGraphConnectingAttributes = (): Partial<Connecting> => {
|
2026-02-08 17:57:40 +08:00
|
|
|
|
const lineOptions = createLineOptions();
|
2026-02-08 15:38:50 +08:00
|
|
|
|
return {
|
|
|
|
|
|
snap: true, // 当 snap 设置为 true 时连线的过程中距离节点或者连接桩 50px 时会触发自动吸附
|
|
|
|
|
|
allowBlank: false, // 是否允许连接到画布空白位置的点,默认为 true
|
|
|
|
|
|
allowLoop: false, // 是否允许创建循环连线,即边的起始节点和终止节点为同一节点,默认为 true
|
|
|
|
|
|
highlight: true, // 当连接到节点时,通过 sourceAnchor 来指定源节点的锚点。
|
2026-02-08 17:57:40 +08:00
|
|
|
|
connector: 'smooth',
|
|
|
|
|
|
connectionPoint: 'anchor', // 指定连接点,默认值为 boundary。
|
2026-02-08 15:38:50 +08:00
|
|
|
|
anchor: 'center',
|
|
|
|
|
|
// validateMagnet({ magnet }) {
|
|
|
|
|
|
// return magnet.getAttribute('port-group') !== 'top'
|
|
|
|
|
|
// },
|
|
|
|
|
|
// 验证连接
|
2026-02-08 17:57:40 +08:00
|
|
|
|
createEdge(this: Graph) {
|
|
|
|
|
|
const edge: Edge = this.createEdge({
|
|
|
|
|
|
shape: 'edge',
|
|
|
|
|
|
...lineOptions, // 应用动画配置
|
|
|
|
|
|
attrs: lineOptions.attrs,
|
|
|
|
|
|
animation: lineOptions.animation,
|
|
|
|
|
|
markup: lineOptions.markup,
|
2026-02-08 22:31:13 +08:00
|
|
|
|
});
|
2026-02-08 17:57:40 +08:00
|
|
|
|
return edge;
|
|
|
|
|
|
},
|
2026-02-08 15:38:50 +08:00
|
|
|
|
validateConnection(this: Graph, { sourceCell, targetCell }) {
|
|
|
|
|
|
console.error('validateConnection');
|
|
|
|
|
|
if (!sourceCell || !targetCell) return false;
|
|
|
|
|
|
|
|
|
|
|
|
// 核心逻辑:禁止节点连接自己(自环)
|
|
|
|
|
|
if (sourceCell === targetCell) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// const sourceData = sourceCell.getData() as GraphElement;
|
2026-02-08 17:57:40 +08:00
|
|
|
|
const targetData = targetCell.getData() as ModelElement;
|
2026-02-08 15:38:50 +08:00
|
|
|
|
|
|
|
|
|
|
// 根节点不能作为子节点
|
2026-02-08 17:57:40 +08:00
|
|
|
|
if (targetData.type === 'startEvent') {
|
2026-02-08 15:38:50 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-08 17:57:40 +08:00
|
|
|
|
// 4. 新增核心逻辑:检查源节点是否已有出边(已连接其他节点)
|
|
|
|
|
|
// const hasOutgoingEdge = this.getOutgoingEdges(sourceCell);
|
|
|
|
|
|
// if (hasOutgoingEdge && hasOutgoingEdge.length > 1) {
|
|
|
|
|
|
// return false;
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否已存在相同连接
|
|
|
|
|
|
// const edges: Edge[] = this.getEdges();
|
|
|
|
|
|
// const existingConnection = edges.find(edge =>
|
|
|
|
|
|
// edge.getSourceCell() === sourceCell &&
|
|
|
|
|
|
// edge.getTargetCell() === targetCell,
|
|
|
|
|
|
// );
|
|
|
|
|
|
//
|
|
|
|
|
|
// return !existingConnection;
|
|
|
|
|
|
return true;
|
2026-02-08 15:38:50 +08:00
|
|
|
|
},
|
|
|
|
|
|
};
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export const createGraphCanvas = (container: HTMLDivElement, readonly: boolean = false): Graph => {
|
|
|
|
|
|
const graph = new Graph({
|
|
|
|
|
|
container: container,
|
|
|
|
|
|
grid: {
|
|
|
|
|
|
size: 20,
|
|
|
|
|
|
visible: true,
|
|
|
|
|
|
type: 'dot',
|
|
|
|
|
|
// color: '#e5e7eb'
|
|
|
|
|
|
},
|
|
|
|
|
|
// 确保启用了异步渲染
|
|
|
|
|
|
async: true,
|
|
|
|
|
|
panning: {
|
|
|
|
|
|
enabled: true,
|
|
|
|
|
|
eventTypes: ['leftMouseDown', 'mouseWheel'],
|
|
|
|
|
|
},
|
|
|
|
|
|
mousewheel: {
|
|
|
|
|
|
enabled: true,
|
|
|
|
|
|
modifiers: 'ctrl',
|
2026-02-08 17:57:40 +08:00
|
|
|
|
factor: 1.1,
|
|
|
|
|
|
maxScale: 1.5,
|
2026-02-08 15:38:50 +08:00
|
|
|
|
minScale: 0.5,
|
|
|
|
|
|
},
|
|
|
|
|
|
highlighting: {
|
|
|
|
|
|
magnetAdsorbed: {
|
|
|
|
|
|
name: 'stroke',
|
|
|
|
|
|
args: {
|
|
|
|
|
|
attrs: {
|
|
|
|
|
|
fill: '#fff',
|
|
|
|
|
|
stroke: '#31d0c6',
|
|
|
|
|
|
strokeWidth: 4,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
connecting: createGraphConnectingAttributes(),
|
|
|
|
|
|
|
|
|
|
|
|
scaling: {
|
|
|
|
|
|
min: 0.5,
|
|
|
|
|
|
max: 2,
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 背景配置
|
|
|
|
|
|
background: {},
|
|
|
|
|
|
|
|
|
|
|
|
// 交互配置
|
|
|
|
|
|
interacting: (cellView) => {
|
|
|
|
|
|
if (readonly) {
|
|
|
|
|
|
return false; // 只读模式下禁用所有交互
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 确保边(edge)的顶点交互权限开启
|
|
|
|
|
|
if (cellView.cell.isEdge()) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
vertexAddable: true, // 允许添加顶点
|
|
|
|
|
|
vertexMovable: true, // 允许移动顶点
|
|
|
|
|
|
vertexDeletable: true, // 允许删除顶点
|
|
|
|
|
|
edgeMovable: true, // 允许整体拖动连线
|
|
|
|
|
|
arrowheadMovable: true, // 允许拖动箭头调整端点
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 节点的交互配置(保持不变)
|
|
|
|
|
|
return {
|
|
|
|
|
|
nodeMovable: true,
|
|
|
|
|
|
};
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
graph.use(
|
|
|
|
|
|
new Selection({
|
|
|
|
|
|
multiple: true,
|
|
|
|
|
|
rubberEdge: true,
|
|
|
|
|
|
rubberNode: true,
|
|
|
|
|
|
modifiers: 'shift',
|
|
|
|
|
|
rubberband: true,
|
|
|
|
|
|
}),
|
2026-02-08 17:57:40 +08:00
|
|
|
|
);
|
2026-02-08 15:38:50 +08:00
|
|
|
|
|
|
|
|
|
|
return graph;
|
|
|
|
|
|
|
|
|
|
|
|
};
|