2026-03-16 15:48:33 +08:00
|
|
|
|
/*
|
|
|
|
|
|
* This file is part of the kernelstudio package.
|
|
|
|
|
|
*
|
|
|
|
|
|
* (c) 2014-2026 zlin <admin@kernelstudio.com>
|
|
|
|
|
|
*
|
|
|
|
|
|
* For the full copyright and license information, please view the LICENSE file
|
|
|
|
|
|
* that was distributed with this source code.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2026-03-27 00:08:48 +08:00
|
|
|
|
import { Edge, Graph, Node } from '@antv/x6';
|
|
|
|
|
|
import type { PlatformRelation } from './types';
|
|
|
|
|
|
import type { Platform, PlatformComponent } from '../types';
|
2026-03-16 15:48:33 +08:00
|
|
|
|
import type { GraphTaskElement } from '../graph';
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 解析节点端口连接关系(去重版)
|
|
|
|
|
|
* @param graph X6 画布实例
|
|
|
|
|
|
* @returns 去重后的端口连接关系列表
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function resolveConnectionRelation(graph: Graph): PlatformRelation[] {
|
|
|
|
|
|
const edges: Edge[] = graph.getEdges();
|
|
|
|
|
|
const items: PlatformRelation[] = [];
|
|
|
|
|
|
const existsKeys: Set<string> = new Set(); // 改用 Set 提升查询性能
|
|
|
|
|
|
const tempEdgeIds: Set<string> = new Set(); // 存储临时边 ID
|
|
|
|
|
|
|
|
|
|
|
|
// 过滤无效/临时边
|
|
|
|
|
|
const validEdges = edges.filter(edge => {
|
|
|
|
|
|
// 过滤临时边(X6 拖拽连线时生成的未完成边)
|
|
|
|
|
|
const isTempEdge = edge?.attr('line/stroke') === 'transparent' || edge.id.includes('temp');
|
|
|
|
|
|
if (isTempEdge) {
|
|
|
|
|
|
tempEdgeIds.add(edge.id);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 过滤未正确关联节点的边
|
|
|
|
|
|
const sourceCell = edge.getSourceCell();
|
|
|
|
|
|
const targetCell = edge.getTargetCell();
|
|
|
|
|
|
if (!sourceCell || !targetCell || !(sourceCell instanceof Node) || !(targetCell instanceof Node)) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 过滤端口 ID 为空的边
|
|
|
|
|
|
const sourcePortId = edge.getSourcePortId();
|
|
|
|
|
|
const targetPortId = edge.getTargetPortId();
|
|
|
|
|
|
if (!sourcePortId || !targetPortId) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
validEdges.forEach(edge => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const sourceCell = edge.getSourceCell() as Node;
|
|
|
|
|
|
const targetCell = edge.getTargetCell() as Node;
|
|
|
|
|
|
|
|
|
|
|
|
const sourcePortId = edge.getSourcePortId()!;
|
|
|
|
|
|
const targetPortId = edge.getTargetPortId()!;
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 获取端口 DOM 元素和数据
|
|
|
|
|
|
const sourceView = graph.findViewByCell(sourceCell);
|
|
|
|
|
|
const targetView = graph.findViewByCell(targetCell);
|
|
|
|
|
|
if (!sourceView || !targetView) return;
|
|
|
|
|
|
|
|
|
|
|
|
const sourcePortEl = sourceView.container.querySelector(`[data-port="${sourcePortId}"]`);
|
|
|
|
|
|
const targetPortEl = targetView.container.querySelector(`[data-port="${targetPortId}"]`);
|
|
|
|
|
|
if (!sourcePortEl || !targetPortEl) return;
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 解析端口数据
|
|
|
|
|
|
let sourcePortData: PlatformComponent | null = null;
|
|
|
|
|
|
let targetPortData: PlatformComponent | null = null;
|
|
|
|
|
|
try {
|
|
|
|
|
|
const sourceDataAttr = sourcePortEl.getAttribute('data-item');
|
|
|
|
|
|
sourcePortData = sourceDataAttr ? JSON.parse(sourceDataAttr) : null;
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.warn(`解析源节点 ${sourceCell.id} 端口 ${sourcePortId} 数据失败`, e);
|
|
|
|
|
|
return; // 数据解析失败直接跳过
|
|
|
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
|
|
|
const targetDataAttr = targetPortEl.getAttribute('data-item');
|
|
|
|
|
|
targetPortData = targetDataAttr ? JSON.parse(targetDataAttr) : null;
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.warn(`解析目标节点 ${targetCell.id} 端口 ${targetPortId} 数据失败`, e);
|
|
|
|
|
|
return; // 数据解析失败直接跳过
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 过滤端口数据为空的情况
|
|
|
|
|
|
if (!sourcePortData || !targetPortData) return;
|
|
|
|
|
|
|
|
|
|
|
|
// 解析节点平台数据
|
|
|
|
|
|
const sourceData = sourceCell.getData() as GraphTaskElement;
|
|
|
|
|
|
const targetData = targetCell.getData() as GraphTaskElement;
|
|
|
|
|
|
|
|
|
|
|
|
// 过滤平台数据不完整的节点
|
|
|
|
|
|
if (!sourceData.platformId || !targetData.platformId) return;
|
|
|
|
|
|
|
|
|
|
|
|
const sourcePlatform: Platform = {
|
|
|
|
|
|
id: sourceData.platformId as number,
|
|
|
|
|
|
key: sourceData.key,
|
|
|
|
|
|
name: sourceData.name,
|
|
|
|
|
|
description: sourceData.description,
|
|
|
|
|
|
scenarioId: sourceData.scenarioId as number,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const targetPlatform: Platform = {
|
|
|
|
|
|
id: targetData.platformId as number,
|
|
|
|
|
|
key: targetData.key,
|
|
|
|
|
|
name: targetData.name,
|
|
|
|
|
|
description: targetData.description,
|
|
|
|
|
|
scenarioId: targetData.scenarioId as number,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 生成唯一标识(支持单向/双向去重
|
|
|
|
|
|
const uniqueKey = `${sourceCell.id}@${sourcePortId}->${targetCell.id}@${targetPortId}`;
|
|
|
|
|
|
|
|
|
|
|
|
if (!existsKeys.has(uniqueKey)) {
|
|
|
|
|
|
existsKeys.add(uniqueKey);
|
|
|
|
|
|
items.push({
|
|
|
|
|
|
sourceId: sourceCell.id,
|
|
|
|
|
|
sourcePort: sourcePortId,
|
|
|
|
|
|
sourcePlatform: sourcePlatform,
|
|
|
|
|
|
sourceComponent: sourcePortData,
|
|
|
|
|
|
targetId: targetCell.id,
|
|
|
|
|
|
targetPort: targetPortId,
|
|
|
|
|
|
targetPlatform: targetPlatform,
|
|
|
|
|
|
targetComponent: targetPortData,
|
|
|
|
|
|
edgeId: edge.id,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error(`解析边 ${edge.id} 连接关系失败`, error);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
return items;
|
|
|
|
|
|
}
|