|
|
|
@ -1,4 +1,5 @@
|
|
|
|
import { Message } from '@arco-design/web-react';
|
|
|
|
import { Message } from '@arco-design/web-react';
|
|
|
|
|
|
|
|
import { Edge } from '@xyflow/react';
|
|
|
|
|
|
|
|
|
|
|
|
export interface ValidationResult {
|
|
|
|
export interface ValidationResult {
|
|
|
|
isValid: boolean;
|
|
|
|
isValid: boolean;
|
|
|
|
@ -365,6 +366,130 @@ export const validateAllNodes = (nodes: any[]): ValidationResult => {
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 校验流程中的所有连接线
|
|
|
|
|
|
|
|
* @param edges 流程中的所有连接线
|
|
|
|
|
|
|
|
* @param nodes 流程中的所有节点(用于验证节点存在性)
|
|
|
|
|
|
|
|
* @returns 校验结果
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
export const validateAllEdges = (edges: Edge[], nodes: any[]): ValidationResult => {
|
|
|
|
|
|
|
|
const allErrors: string[] = [];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 创建节点ID到节点标题的映射,方便错误信息展示
|
|
|
|
|
|
|
|
const nodeTitleMap = new Map<string, string>();
|
|
|
|
|
|
|
|
const nodeMap = new Map<string, any>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
nodes.forEach(node => {
|
|
|
|
|
|
|
|
const nodeTitle = node.data?.title || node.id;
|
|
|
|
|
|
|
|
nodeTitleMap.set(node.id, nodeTitle);
|
|
|
|
|
|
|
|
nodeMap.set(node.id, node);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 检查连接线的基本信息
|
|
|
|
|
|
|
|
edges.forEach((edge, index) => {
|
|
|
|
|
|
|
|
// 检查连接线是否有源节点和目标节点
|
|
|
|
|
|
|
|
if (!edge.source) {
|
|
|
|
|
|
|
|
allErrors.push(`第${index + 1}条连接线缺少源节点`);
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!edge.target) {
|
|
|
|
|
|
|
|
allErrors.push(`第${index + 1}条连接线缺少目标节点`);
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 检查源节点和目标节点是否存在
|
|
|
|
|
|
|
|
if (edge.source && !nodeTitleMap.has(edge.source)) {
|
|
|
|
|
|
|
|
allErrors.push(`第${index + 1}条连接线的源节点"${edge.source}"不存在`);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (edge.target && !nodeTitleMap.has(edge.target)) {
|
|
|
|
|
|
|
|
allErrors.push(`第${index + 1}条连接线的目标节点"${edge.target}"不存在`);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否有未连接的节点(开始节点和结束节点除外)
|
|
|
|
|
|
|
|
const connectedNodeIds = new Set<string>();
|
|
|
|
|
|
|
|
edges.forEach(edge => {
|
|
|
|
|
|
|
|
if (edge.source) connectedNodeIds.add(edge.source);
|
|
|
|
|
|
|
|
if (edge.target) connectedNodeIds.add(edge.target);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
nodes.forEach(node => {
|
|
|
|
|
|
|
|
// 如果节点没有被任何连接线连接
|
|
|
|
|
|
|
|
if (!connectedNodeIds.has(node.id)) {
|
|
|
|
|
|
|
|
const nodeType = node.data?.type || node.type;
|
|
|
|
|
|
|
|
// 开始节点和结束节点可以不连接
|
|
|
|
|
|
|
|
if (nodeType !== 'start' && nodeType !== 'end') {
|
|
|
|
|
|
|
|
const nodeName = node.data?.title || node.id;
|
|
|
|
|
|
|
|
allErrors.push(`节点"${nodeName}"未连接任何连线`);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 检查节点的连接是否符合规则
|
|
|
|
|
|
|
|
const sourceCount = new Map<string, number>(); // 每个节点作为源节点的次数
|
|
|
|
|
|
|
|
const targetCount = new Map<string, number>(); // 每个节点作为目标节点的次数
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
edges.forEach(edge => {
|
|
|
|
|
|
|
|
if (edge.source) {
|
|
|
|
|
|
|
|
sourceCount.set(edge.source, (sourceCount.get(edge.source) || 0) + 1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (edge.target) {
|
|
|
|
|
|
|
|
targetCount.set(edge.target, (targetCount.get(edge.target) || 0) + 1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 检查每个节点的连接情况
|
|
|
|
|
|
|
|
nodes.forEach(node => {
|
|
|
|
|
|
|
|
const nodeId = node.id;
|
|
|
|
|
|
|
|
const nodeType = node.data?.type || node.type;
|
|
|
|
|
|
|
|
const nodeName = node.data?.title || node.id;
|
|
|
|
|
|
|
|
const sourceEdges = sourceCount.get(nodeId) || 0;
|
|
|
|
|
|
|
|
const targetEdges = targetCount.get(nodeId) || 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 特定节点类型可能有特殊的连接规则
|
|
|
|
|
|
|
|
switch (nodeType) {
|
|
|
|
|
|
|
|
case 'SWITCH':
|
|
|
|
|
|
|
|
// 条件节点应该有至少一个输出连接
|
|
|
|
|
|
|
|
if (sourceEdges === 0) {
|
|
|
|
|
|
|
|
allErrors.push(`条件节点"${nodeName}"缺少输出连接`);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case 'start':
|
|
|
|
|
|
|
|
// 开始节点应该有输出连接,但不需要输入连接
|
|
|
|
|
|
|
|
if (sourceEdges === 0) {
|
|
|
|
|
|
|
|
allErrors.push(`开始节点"${nodeName}"缺少输出连接`);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case 'end':
|
|
|
|
|
|
|
|
// 结束节点应该有输入连接,但不需要输出连接
|
|
|
|
|
|
|
|
if (targetEdges === 0) {
|
|
|
|
|
|
|
|
allErrors.push(`结束节点"${nodeName}"缺少输入连接`);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
// 其他节点应该至少有一个输入和一个输出连接
|
|
|
|
|
|
|
|
if (targetEdges === 0) {
|
|
|
|
|
|
|
|
allErrors.push(`节点"${nodeName}"缺少输入连接`);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sourceEdges === 0) {
|
|
|
|
|
|
|
|
allErrors.push(`节点"${nodeName}"缺少输出连接`);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
|
|
isValid: allErrors.length === 0,
|
|
|
|
|
|
|
|
errors: allErrors
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* 显示校验错误信息
|
|
|
|
* 显示校验错误信息
|
|
|
|
* @param errors 错误信息数组
|
|
|
|
* @param errors 错误信息数组
|
|
|
|
|