/* * 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 { 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 => { 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; };