feat: 完善循环节点配置与作用域输出
- 支持次数与数组独立或组合配置并补齐检查规则 - 统一循环体临时变量与下游正式输出候选 - 稳定知识库对象数组字段并补充前后端测试
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { filterRefOptionsByDataType } from './refOptionFilter';
|
||||
|
||||
describe('filterRefOptionsByDataType', () => {
|
||||
const options = [
|
||||
{
|
||||
label: '知识库',
|
||||
selectable: false,
|
||||
value: 'knowledge',
|
||||
children: [
|
||||
{
|
||||
label: '文档',
|
||||
selectable: true,
|
||||
value: 'knowledge.documents',
|
||||
dataType: 'Array<Object>',
|
||||
},
|
||||
{
|
||||
label: '数量',
|
||||
selectable: true,
|
||||
value: 'knowledge.count',
|
||||
dataType: 'Number',
|
||||
},
|
||||
{
|
||||
label: '标题',
|
||||
selectable: true,
|
||||
value: 'knowledge.title',
|
||||
dataType: 'String',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
it('数组输入接受 Array 及 Array 泛型', () => {
|
||||
const filtered = filterRefOptionsByDataType(options, ['Array']);
|
||||
|
||||
expect(filtered[0]?.children.map((item: any) => item.value)).toEqual([
|
||||
'knowledge.documents',
|
||||
]);
|
||||
});
|
||||
|
||||
it('次数输入只展示 Number,并保留当前旧引用', () => {
|
||||
const filtered = filterRefOptionsByDataType(
|
||||
options,
|
||||
['Number'],
|
||||
'knowledge.title',
|
||||
);
|
||||
|
||||
expect(filtered[0]?.children.map((item: any) => item.value)).toEqual([
|
||||
'knowledge.count',
|
||||
'knowledge.title',
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
function isAcceptedDataType(dataType: string, acceptedDataTypes: string[]) {
|
||||
const normalized = dataType.trim().toLowerCase();
|
||||
return acceptedDataTypes.some((acceptedDataType) => {
|
||||
const accepted = acceptedDataType.trim().toLowerCase();
|
||||
return accepted === 'array'
|
||||
? normalized === 'array' || normalized.startsWith('array<')
|
||||
: normalized === accepted;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 按数据类型过滤引用选项,并保留当前已选择的旧引用。
|
||||
*/
|
||||
export function filterRefOptionsByDataType(
|
||||
options: any[],
|
||||
acceptedDataTypes: string[],
|
||||
currentRef = '',
|
||||
): any[] {
|
||||
if (!acceptedDataTypes.length) {
|
||||
return options;
|
||||
}
|
||||
return options
|
||||
.map((option) => {
|
||||
const children = Array.isArray(option.children)
|
||||
? filterRefOptionsByDataType(
|
||||
option.children,
|
||||
acceptedDataTypes,
|
||||
currentRef,
|
||||
)
|
||||
: [];
|
||||
const selectable =
|
||||
option.selectable === true &&
|
||||
(isAcceptedDataType(
|
||||
String(option.dataType || ''),
|
||||
acceptedDataTypes,
|
||||
) ||
|
||||
String(option.value || '') === currentRef);
|
||||
if (!selectable && children.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
...option,
|
||||
selectable,
|
||||
children,
|
||||
};
|
||||
})
|
||||
.filter(Boolean);
|
||||
}
|
||||
@@ -3,6 +3,11 @@ 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,
|
||||
} from '../../utils/loopScope';
|
||||
|
||||
const fillRefNodeIds = (
|
||||
refNodeIds: string[],
|
||||
@@ -27,7 +32,10 @@ const getChildren = (
|
||||
) => {
|
||||
if (!params || params.length === 0) return [];
|
||||
return params.map((param: any) => {
|
||||
const isCollection = param.dataType === 'Array' && param.children && param.children.length > 0;
|
||||
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
|
||||
@@ -64,6 +72,7 @@ const nodeToOptions = (
|
||||
node: Node,
|
||||
nodeIsChildren: boolean,
|
||||
currentNode: Node,
|
||||
nodes: Node[],
|
||||
) => {
|
||||
const options = getOptions();
|
||||
const nodeType = node.type || '';
|
||||
@@ -105,29 +114,27 @@ const nodeToOptions = (
|
||||
nodeType: nodeType,
|
||||
children,
|
||||
};
|
||||
} else if (nodeType === 'loopNode' && currentNode.parentId) {
|
||||
} 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: [
|
||||
{
|
||||
label: 'loopItem',
|
||||
dataType: 'Any',
|
||||
value: node.id + '.loopItem',
|
||||
selectable: true,
|
||||
nodeType: nodeType,
|
||||
},
|
||||
{
|
||||
label: 'index',
|
||||
dataType: 'Number',
|
||||
value: node.id + '.index',
|
||||
selectable: true,
|
||||
nodeType: nodeType,
|
||||
},
|
||||
],
|
||||
children: getChildren(
|
||||
referenceParameters,
|
||||
node.id,
|
||||
false,
|
||||
nodeType,
|
||||
),
|
||||
};
|
||||
} else {
|
||||
const outputDefs = node.data.outputDefs;
|
||||
@@ -168,7 +175,12 @@ export const useRefOptions: any = (
|
||||
for (const node of nodes) {
|
||||
const nodeIsChildren = node.parentId === currentNode.current.id;
|
||||
if (nodeIsChildren) {
|
||||
const nodeOptions = nodeToOptions(node, nodeIsChildren, cNode);
|
||||
const nodeOptions = nodeToOptions(
|
||||
node,
|
||||
nodeIsChildren,
|
||||
cNode,
|
||||
nodes,
|
||||
);
|
||||
nodeOptions && resultOptions.push(nodeOptions);
|
||||
}
|
||||
}
|
||||
@@ -177,9 +189,17 @@ export const useRefOptions: any = (
|
||||
fillRefNodeIds(refNodeIds, currentNodeId, edges);
|
||||
|
||||
for (const node of nodes) {
|
||||
if (refNodeIds.includes(node.id)) {
|
||||
const isScopedLoop =
|
||||
node.type === 'loopNode' &&
|
||||
buildLoopScopeParameters(node, cNode, nodes).length > 0;
|
||||
if (refNodeIds.includes(node.id) || isScopedLoop) {
|
||||
const nodeIsChildren = node.parentId === currentNode.current.id;
|
||||
const nodeOptions = nodeToOptions(node, nodeIsChildren, cNode);
|
||||
const nodeOptions = nodeToOptions(
|
||||
node,
|
||||
nodeIsChildren,
|
||||
cNode,
|
||||
nodes,
|
||||
);
|
||||
nodeOptions && resultOptions.push(nodeOptions);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user