Files
auto-solution/modeler/src/views/decision/builder/graph.ts
2026-02-08 22:31:13 +08:00

177 lines
4.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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.
*/
import { Edge, Graph, Path, Selection } from '@antv/x6';
import type { ModelElement } from './element';
import type { Connecting } from '@antv/x6/lib/graph/options';
import { createLineOptions } from './line';
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> => {
const lineOptions = createLineOptions();
return {
snap: true, // 当 snap 设置为 true 时连线的过程中距离节点或者连接桩 50px 时会触发自动吸附
allowBlank: false, // 是否允许连接到画布空白位置的点,默认为 true
allowLoop: false, // 是否允许创建循环连线,即边的起始节点和终止节点为同一节点,默认为 true
highlight: true, // 当连接到节点时,通过 sourceAnchor 来指定源节点的锚点。
connector: 'smooth',
connectionPoint: 'anchor', // 指定连接点,默认值为 boundary。
anchor: 'center',
// validateMagnet({ magnet }) {
// return magnet.getAttribute('port-group') !== 'top'
// },
// 验证连接
createEdge(this: Graph) {
const edge: Edge = this.createEdge({
shape: 'edge',
...lineOptions, // 应用动画配置
attrs: lineOptions.attrs,
animation: lineOptions.animation,
markup: lineOptions.markup,
});
return edge;
},
validateConnection(this: Graph, { sourceCell, targetCell }) {
console.error('validateConnection');
if (!sourceCell || !targetCell) return false;
// 核心逻辑:禁止节点连接自己(自环)
if (sourceCell === targetCell) {
return false;
}
// const sourceData = sourceCell.getData() as GraphElement;
const targetData = targetCell.getData() as ModelElement;
// 根节点不能作为子节点
if (targetData.type === 'startEvent') {
return false;
}
// 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;
},
};
};
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',
factor: 1.1,
maxScale: 1.5,
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,
}),
);
return graph;
};