首页工作流节点引用多选与保存回显完善:isMultiParameter 全链路透传 + valueSource 数组化/label + 旧 DSL 兼容

- isMultiParameter 节点级开关从节点库透传至引用下拉,支持多选勾选/去重/逐条删除/清空
- valueSource 契约统一为数组并补 label(节点名.字段中文名),覆盖用户引用、表单展示标记与旧 DSL 单对象
- ModelField 递归透传 multi,object/array 嵌套子字段同样支持多选
- 保存/回显兼容旧 DSL 单对象 valueSource,重开自动升级为数组
- modelRequestParamsPath 每项新增 label 字段,后端按 path 重组请求参数

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-19 19:53:06 +08:00
co-authored by Claude
parent b6208b37e4
commit 68bdc63231
10 changed files with 298 additions and 80 deletions
+32 -11
View File
@@ -179,6 +179,7 @@ interface NodeData {
patchLayout?: boolean;
isSaveFile?: boolean;
preTool?: string | null;
postTool?: string | null;
runFormFields?: RunFormField[]; // 仅开始节点使用
// 子流程节点(sub_flow):引入的工作流配置(workflowId + 引入参数)
subFlowConfig?: SubFlowConfig | null;
@@ -205,6 +206,9 @@ const nodeConfigMap = computed(() => {
string,
{
formConfig: any[];
preToolOption: any[];
postToolOption: any[];
isMultiParameter: boolean;
modelConfigOption: boolean;
formConfigOption: boolean;
skillOption: boolean;
@@ -217,6 +221,10 @@ const nodeConfigMap = computed(() => {
group.nodes.forEach((item) => {
map.set(item.key, {
formConfig: item.presetOption || [],
// 前置/后置方法配置定义(后端新增,仅 model 节点提供;值保存到节点顶层 preTool/postTool 字段)
preToolOption: item.preToolOption || [],
postToolOption: item.postToolOption || [],
isMultiParameter: item.isMultiParameter || false,
modelConfigOption: item.modelConfigOption || false,
formConfigOption: item.formConfigOption || false,
skillOption: item.skillOption || false,
@@ -656,7 +664,7 @@ const syncRunFormFields = () => {
value: f.value ?? f.defaultValue ?? '',
defaultValue: f.defaultValue,
fieldConstraint: f.fieldConstraint && typeof f.fieldConstraint === 'object' ? f.fieldConstraint : undefined,
valueSource: f.valueSource && typeof f.valueSource === 'object' ? f.valueSource : undefined,
valueSource: Array.isArray(f.valueSource) ? f.valueSource : undefined,
options: Array.isArray(f.options) ? f.options : undefined,
multiple: isUploadMultiple || f.multiple || undefined,
nodeId: n.id,
@@ -1102,7 +1110,7 @@ const buildOutputConfig = (node: Node<NodeData>, startNodeId = '') => {
const entry = formConfig.find((f: any) => f.field === def.field);
const value = entry?.value ?? '';
// 勾选「表单展示」的字段:值来自开始节点表单,补 valueSource 标记(后端契约)
const valueSource = def.isFormField === true ? { nodeId: startNodeId, field: def.field } : undefined;
const valueSource = def.isFormField === true ? [{ nodeId: startNodeId, field: def.field }] : undefined;
if (def.field === 'responseType') {
const expand = entry?.expand || [];
return {
@@ -1131,7 +1139,7 @@ const buildOutputConfig = (node: Node<NodeData>, startNodeId = '') => {
label: f.label || f.field || '',
value: f.value ?? '',
required: Boolean(f.required),
...(fieldName ? { valueSource: { nodeId: startNodeId, field: fieldName } } : {}),
...(fieldName ? { valueSource: [{ nodeId: startNodeId, field: fieldName }] } : {}),
...(f.type === 'uploadMultiple'
? {
fieldConstraint: {
@@ -1169,7 +1177,7 @@ const buildOutputConfig = (node: Node<NodeData>, startNodeId = '') => {
label: def.label || def.field,
// isFormField 字段值在开始节点运行表单,不重复保存(回显从开始节点反查)
...(isForm ? {} : { value: entry?.value ?? '' }),
...(isForm ? { valueSource: { nodeId: startNodeId, field: def.field } } : {}),
...(isForm ? { valueSource: [{ nodeId: startNodeId, field: def.field }] } : {}),
};
});
};
@@ -1203,9 +1211,9 @@ const serializeSubFlowConfig = (config: SubFlowConfig | null | undefined, node?:
// - 引用上级输出:值来自主工作流上游节点,统一转 field
// - 其余:无引用(静态默认值)
valueSource: f.runtimeShow
? { nodeId: startNodeId, field: f.field }
: f.valueSource && typeof f.valueSource === 'object'
? { nodeId: f.valueSource.nodeId, field: f.valueSource.field }
? [{ nodeId: startNodeId, field: f.field }]
: Array.isArray(f.valueSource)
? f.valueSource.map((vs: any) => ({ nodeId: vs.nodeId, field: vs.field }))
: null,
isFormField: Boolean(f.runtimeShow),
})),
@@ -1232,11 +1240,12 @@ const buildSubFlowConfigFromDsl = (subConfig: any, startNodeId = '') => {
multiple: Boolean(f.multiple) || f.fieldType === 'uploadMultiple',
valueSource: (() => {
const vs = f.valueSource;
// 统一数组:兼容旧 DSL 单对象
const arr = Array.isArray(vs) ? vs : vs && typeof vs === 'object' ? [vs] : [];
// 指向开始节点:值为表单展示(首页可填),编辑器不显示为引用(勾选由 isFormField 恢复)
if (vs && typeof vs === 'object' && vs.nodeId && vs.nodeId === startNodeId) return null;
const refs = arr.filter((r: any) => !(r && r.nodeId && r.nodeId === startNodeId));
// 引用上级:后端 field(兼容旧 DSL 的 fieldName)转回前端 field 结构
if (vs && typeof vs === 'object') return { nodeId: vs.nodeId, field: vs.fieldName ?? vs.field };
return null;
return refs.length ? refs.map((r: any) => ({ nodeId: r.nodeId, field: r.fieldName ?? r.field })) : null;
})(),
runtimeShow: Boolean(f.isFormField),
})),
@@ -1317,6 +1326,7 @@ const loadWorkflowFromDsl = (dsl: any) => {
patchLayout: n.patchLayout || false,
isSaveFile: Boolean(n.isSaveFile),
preTool: n.preTool ?? null,
postTool: n.postTool ?? null,
// 子流程节点:恢复引入的工作流配置(无 subConfig 时返回 null,兼容旧 DSL
...(n.nodeCode === 'sub_flow' ? { subFlowConfig: buildSubFlowConfigFromDsl(n.subConfig, startNodeId) } : {}),
// 开始节点运行表单字段:新格式存 outputConfig;旧 DSL 顶层 runFormFields 兜底兼容
@@ -1403,6 +1413,16 @@ const confirmSaveWorkflow = async () => {
ElMessage.warning(`存在重名运行字段:${dupNames.join('、')},首页表单可能出现冲突`);
}
// 节点 id → 名称与输出字段(field → label)索引,供 modelRequestParamsPath 的 valueSource label 补全
const outputFieldsIndex = new Map<string, { name: string; fields: Map<string, string> }>();
nodes.value.forEach((n) => {
const fields = getNodeOutputFields(n);
outputFieldsIndex.set(n.id, {
name: n.data?.label || n.id,
fields: new Map(fields.map((f: any) => [f.field, f.label || f.field])),
});
});
const workflowDsl = {
version: '1.0.0',
startNodeId: startNode?.id || '',
@@ -1432,6 +1452,7 @@ const confirmSaveWorkflow = async () => {
height: gNode.dimensions?.height || gNode.height || 80,
},
...(n.data?.preTool ? { preTool: n.data.preTool } : {}),
...(n.data?.postTool ? { postTool: n.data.postTool } : {}),
isSaveFile: Boolean(n.data?.isSaveFile),
// 子流程节点:序列化引入的工作流配置(含 workflowId 与 isFormField 标记);其余节点保持 null
subConfig: nodeCode === 'sub_flow' ? serializeSubFlowConfig(n.data?.subFlowConfig, n, startNode?.id || '') : null,
@@ -1444,7 +1465,7 @@ const confirmSaveWorkflow = async () => {
: {}),
modelRequestParams: savedModelRequestParams,
// 全部叶子字段扁平清单(path + 值 + 值来源),后端按 path 重组请求参数;基于编辑器原始嵌套树生成,保留用户配置的引用 valueSource
modelRequestParamsPath: rawModelParams ? buildModelRequestParamsPath(rawModelParams, startNode?.id || '') : null,
modelRequestParamsPath: rawModelParams ? buildModelRequestParamsPath(rawModelParams, startNode?.id || '', outputFieldsIndex) : null,
// 勾选的「表单展示」字段(路径带实例索引),已在上方基于完整结构收集
...(savedModelFormFields && savedModelFormFields.length > 0 ? { modelFormFields: savedModelFormFields } : {}),
// 模型返回参数随工作流保存,保证重开后仍可被下游引用