237 lines
6.2 KiB
TypeScript
237 lines
6.2 KiB
TypeScript
import { type Edge, type Node, useNodesData, useStore } from '@xyflow/svelte';
|
|
import type { Parameter } from '#types';
|
|
import { getCurrentNodeId, getOptions } from '#components/utils/NodeUtils';
|
|
import { getStartNodeParameterLabel } from '#components/utils/startNodeParameterLabel';
|
|
import { nodeIcons } from '../../consts';
|
|
import {
|
|
buildLoopReferenceParameters,
|
|
buildLoopScopeParameters,
|
|
isArrayDataType,
|
|
projectParameterDataType,
|
|
} from '../../utils/loopScope';
|
|
|
|
const fillRefNodeIds = (
|
|
refNodeIds: string[],
|
|
currentNodeId: string,
|
|
edges: Edge[],
|
|
) => {
|
|
for (const edge of edges) {
|
|
if (edge.target === currentNodeId && edge.source) {
|
|
refNodeIds.push(edge.source);
|
|
fillRefNodeIds(refNodeIds, edge.source, edges);
|
|
}
|
|
}
|
|
};
|
|
|
|
const getChildren = (
|
|
params: any,
|
|
parentId: string,
|
|
nodeType: string,
|
|
parentPathLabel = '',
|
|
ancestorCollectionDepth = 0,
|
|
parentIsCollection = false,
|
|
) => {
|
|
if (!params || params.length === 0) return [];
|
|
return params.map((param: any) => {
|
|
const isCollection =
|
|
isArrayDataType(param.dataType) &&
|
|
param.children &&
|
|
param.children.length > 0;
|
|
const childBaseLabel = param.formLabel || param.displayName || param.name;
|
|
const normalizedChildLabel = String(childBaseLabel || '').trim();
|
|
const pathLabel = !parentPathLabel
|
|
? normalizedChildLabel
|
|
: parentIsCollection
|
|
? `${parentPathLabel}.[].${normalizedChildLabel}`
|
|
: `${parentPathLabel}.${normalizedChildLabel}`;
|
|
const dataType = projectParameterDataType(
|
|
param,
|
|
ancestorCollectionDepth,
|
|
);
|
|
const nextCollectionDepth =
|
|
ancestorCollectionDepth + (isCollection ? 1 : 0);
|
|
return {
|
|
label: pathLabel,
|
|
dataType,
|
|
value: parentId + '.' + param.name,
|
|
selectable: true,
|
|
nodeType: nodeType,
|
|
displayLabel: pathLabel,
|
|
pathLabel,
|
|
itemTypeLabel:
|
|
ancestorCollectionDepth > 0 ? '数组项字段' : undefined,
|
|
isCollection,
|
|
children: getChildren(
|
|
param.children,
|
|
parentId + '.' + param.name,
|
|
nodeType,
|
|
pathLabel,
|
|
nextCollectionDepth,
|
|
isCollection,
|
|
),
|
|
};
|
|
});
|
|
};
|
|
|
|
const nodeToOptions = (
|
|
node: Node,
|
|
currentNode: Node,
|
|
nodes: Node[],
|
|
) => {
|
|
const options = getOptions();
|
|
const nodeType = node.type || '';
|
|
|
|
let icon: string | undefined = nodeIcons[nodeType];
|
|
if (!icon && options?.customNodes && options.customNodes[nodeType]) {
|
|
icon = options.customNodes[nodeType].icon;
|
|
}
|
|
|
|
// 如果仍然获取不到,尝试使用 data.icon (作为回退)
|
|
if (!icon && node.data && node.data.icon) {
|
|
icon = node.data.icon as string;
|
|
}
|
|
|
|
const title = node.data.title;
|
|
|
|
if (nodeType === 'startNode') {
|
|
const parameters = node.data.parameters as Array<Parameter>;
|
|
const children = [];
|
|
if (parameters)
|
|
for (const parameter of parameters) {
|
|
const label = getStartNodeParameterLabel(parameter);
|
|
children.push({
|
|
label,
|
|
dataType: projectParameterDataType(parameter),
|
|
value: node.id + '.' + parameter.name,
|
|
selectable: true,
|
|
nodeType: nodeType,
|
|
});
|
|
}
|
|
return {
|
|
label: title,
|
|
icon: icon,
|
|
value: node.id,
|
|
selectable: false,
|
|
nodeType: nodeType,
|
|
children,
|
|
};
|
|
} else if (nodeType === 'loopNode') {
|
|
const referenceParameters = buildLoopReferenceParameters(
|
|
node,
|
|
currentNode,
|
|
nodes,
|
|
);
|
|
if (!referenceParameters.length) {
|
|
return undefined;
|
|
}
|
|
return {
|
|
label: title,
|
|
icon: icon,
|
|
value: node.id,
|
|
selectable: false,
|
|
nodeType: nodeType,
|
|
children: getChildren(
|
|
referenceParameters,
|
|
node.id,
|
|
nodeType,
|
|
),
|
|
};
|
|
} else {
|
|
const outputDefs = node.data.outputDefs;
|
|
if (outputDefs) {
|
|
return {
|
|
label: title,
|
|
icon: icon,
|
|
value: node.id,
|
|
selectable: false,
|
|
nodeType: nodeType,
|
|
children: getChildren(outputDefs, node.id, nodeType),
|
|
};
|
|
}
|
|
}
|
|
};
|
|
|
|
export const useRefOptions: any = (
|
|
useChildrenOnly: boolean | (() => boolean) = false,
|
|
currentRef: string | (() => string) = '',
|
|
) => {
|
|
const currentNodeId = getCurrentNodeId();
|
|
const currentNode = useNodesData(currentNodeId);
|
|
const { nodes, edges, nodeLookup } = $derived(useStore());
|
|
const isChildrenOnly = () =>
|
|
typeof useChildrenOnly === 'function'
|
|
? useChildrenOnly()
|
|
: useChildrenOnly;
|
|
const getCurrentRef = () =>
|
|
typeof currentRef === 'function' ? currentRef() : currentRef;
|
|
|
|
let selectItems = $derived.by(() => {
|
|
const resultOptions = [];
|
|
if (!currentNode.current) {
|
|
return {
|
|
items: [],
|
|
selected: undefined,
|
|
};
|
|
}
|
|
|
|
//通过 nodeLookup.get 才会得到有 parentId 的 node
|
|
const cNode = nodeLookup.get(currentNodeId)!;
|
|
|
|
if (isChildrenOnly()) {
|
|
for (const node of nodes) {
|
|
if (node.parentId === currentNode.current.id) {
|
|
const nodeOptions = nodeToOptions(
|
|
node,
|
|
cNode,
|
|
nodes,
|
|
);
|
|
nodeOptions && resultOptions.push(nodeOptions);
|
|
}
|
|
}
|
|
} else {
|
|
const refNodeIds: string[] = [];
|
|
fillRefNodeIds(refNodeIds, currentNodeId, edges);
|
|
|
|
for (const node of nodes) {
|
|
const isScopedLoop =
|
|
node.type === 'loopNode' &&
|
|
buildLoopScopeParameters(node, cNode, nodes).length > 0;
|
|
if (refNodeIds.includes(node.id) || isScopedLoop) {
|
|
const nodeOptions = nodeToOptions(
|
|
node,
|
|
cNode,
|
|
nodes,
|
|
);
|
|
nodeOptions && resultOptions.push(nodeOptions);
|
|
}
|
|
}
|
|
}
|
|
|
|
const items = resultOptions;
|
|
const stack = [...items];
|
|
let selected;
|
|
const currentValue = getCurrentRef();
|
|
// 单次深度优先扫描定位当前引用,避免为每个参数额外构建全量索引。
|
|
while (stack.length > 0) {
|
|
const item = stack.pop();
|
|
if (item?.value === currentValue) {
|
|
selected = item;
|
|
break;
|
|
}
|
|
if (item?.children?.length) {
|
|
stack.push(...item.children);
|
|
}
|
|
}
|
|
return { items, selected };
|
|
});
|
|
|
|
return {
|
|
get current() {
|
|
return selectItems.items;
|
|
},
|
|
get selected() {
|
|
return selectItems.selected;
|
|
},
|
|
};
|
|
};
|