/* * This file is part of the kernelstudio package. * * (c) 2014-2025 zlin * * For the full copyright and license information, please view the LICENSE file * that was distributed with this source code. */ import { Clipboard, Edge, Graph, History, Keyboard, Path, Selection, Snapline, Transform } from '@antv/x6'; import type { BaseElement } from '../types'; import type { Connecting } from '@antv/x6/lib/graph/options'; 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 => { return { snap: true, // 当 snap 设置为 true 时连线的过程中距离节点或者连接桩 50px 时会触发自动吸附 allowBlank: false, // 是否允许连接到画布空白位置的点,默认为 true allowLoop: false, // 是否允许创建循环连线,即边的起始节点和终止节点为同一节点,默认为 true highlight: true, // 当连接到节点时,通过 sourceAnchor 来指定源节点的锚点。 connector: 'sequenceFlowConnector', connectionPoint: 'boundary', // 指定连接点,默认值为 boundary。 anchor: 'center', router: 'manhattan', // connector: { // name: 'rounded', // args: { // radius: 8, // }, // }, // connectionPoint: 'anchor', // validateMagnet({ magnet }) { // return magnet.getAttribute('port-group') !== 'top' // }, // 验证连接 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 BaseElement; // 根节点不能作为子节点 if (targetData.type === 'root') { return false; } // 检查是否已存在相同连接(保留原有逻辑) const edges: Edge[] = this.getEdges(); const existingConnection = edges.find(edge => edge.getSourceCell() === sourceCell && edge.getTargetCell() === targetCell, ); return !existingConnection; }, }; }; 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, zoomAtMousePosition: true, modifiers: 'ctrl', minScale: 0.5, maxScale: 3, }, 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, }), ).use( new Transform({ resizing: false, rotating: false, }), ) .use(new Snapline()) .use(new Keyboard()) .use(new Clipboard()) .use(new History()); return graph; };