From 998e13e6be7982fa5633bc4e9791f7ce2092fb53 Mon Sep 17 00:00:00 2001 From: 2910410219 <2910410219@qq.com> Date: Wed, 19 Aug 2026 23:18:03 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B7=A5=E4=BD=9C=E6=B5=81=20valueSource=20lab?= =?UTF-8?q?el=20=E5=A5=91=E7=BA=A6=E7=BB=9F=E4=B8=80=EF=BC=9A=E5=8E=BB?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E5=90=8D=E5=89=8D=E7=BC=80=20+=20outputConfi?= =?UTF-8?q?g/subConfig=20=E8=A1=A5=E5=85=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - resolveRefLabel 去掉「节点名.」前缀,valueSource label 只保留字段中文名 - ModelField 引用 tag 展示同步去前缀 - buildOutputConfig(http/form/其它节点)与 serializeSubFlowConfig(subConfig)的 valueSource 补 label,subConfig 引用上游分支经 outputIndex 查上游字段中文名 - 取消引用改用 delete(避免写空数组),enumValues 按 __originIndex 统一索引 Co-Authored-By: Claude --- .../workflow/component/MfReferenceButton.vue | 6 ++-- .../workflow/component/ModelField.vue | 4 +-- .../workflow/component/modelParamUtils.ts | 34 +++++++++++++------ src/views/settings/workflow/index.vue | 20 +++++++---- 4 files changed, 42 insertions(+), 22 deletions(-) diff --git a/src/views/settings/workflow/component/MfReferenceButton.vue b/src/views/settings/workflow/component/MfReferenceButton.vue index 98d6302..bcf702a 100644 --- a/src/views/settings/workflow/component/MfReferenceButton.vue +++ b/src/views/settings/workflow/component/MfReferenceButton.vue @@ -94,14 +94,16 @@ const toggleValueSource = (node: UpstreamNodeInfo, of: UpstreamField) => { if (props.multi) { // 多选:勾选/取消切换,同字段去重 if (isSelected(node, of)) { - props.fieldDef.valueSource = cur.filter((r) => !(r && r.nodeId === node.id && r.field === of.field)); + const keep = cur.filter((r) => !(r && r.nodeId === node.id && r.field === of.field)); + if (keep.length === 0) delete props.fieldDef.valueSource; + else props.fieldDef.valueSource = keep; } else { props.fieldDef.valueSource = [...cur, { nodeId: node.id, field: of.field }]; } } else { // 单选:点已选字段取消,否则替换为单元素数组 if (cur.length === 1 && isSelected(node, of)) { - props.fieldDef.valueSource = []; + delete props.fieldDef.valueSource; } else { props.fieldDef.valueSource = [{ nodeId: node.id, field: of.field }]; } diff --git a/src/views/settings/workflow/component/ModelField.vue b/src/views/settings/workflow/component/ModelField.vue index 902032b..5cb8d0e 100644 --- a/src/views/settings/workflow/component/ModelField.vue +++ b/src/views/settings/workflow/component/ModelField.vue @@ -187,10 +187,10 @@ const valueSourceList = computed(() => { const arr = Array.isArray(vs) ? vs : [vs]; return arr.map((r: any) => { const node = props.upstreamNodes?.find((n) => n.id === r?.nodeId); - const nodeName = node?.label || r?.nodeId || ''; + // 引用标签只展示字段中文名(不带「节点名.」前缀) const fieldLabel = node?.outputFields.find((f) => f.field === r?.field)?.label || r?.field || ''; const invalid = !node || !node.outputFields.some((f) => f.field === r?.field); - return { nodeId: r?.nodeId, field: r?.field, label: `${nodeName}.${fieldLabel}`, invalid }; + return { nodeId: r?.nodeId, field: r?.field, label: fieldLabel, invalid }; }); }); const hasValueSource = computed(() => valueSourceList.value.length > 0); diff --git a/src/views/settings/workflow/component/modelParamUtils.ts b/src/views/settings/workflow/component/modelParamUtils.ts index 807c243..9a70e33 100644 --- a/src/views/settings/workflow/component/modelParamUtils.ts +++ b/src/views/settings/workflow/component/modelParamUtils.ts @@ -217,7 +217,10 @@ export function collectExposedFields(params: any, prefix = ''): ExposedField[] { const templates = arrayTemplatesOf(def); templates.forEach((tpl: any, i: number) => { if (tpl && typeof tpl === 'object' && tpl.attrs && typeof tpl.attrs === 'object' && !Array.isArray(tpl.attrs)) { - result.push(...collectExposedFields(tpl.attrs, `${path}.enumValues[${i}].attrs`)); + // 索引与 collectAllLeafFields 一致:优先用 strip 时记录的原始模板索引 __originIndex, + // 保证 modelFormFields 与 modelRequestParamsPath 的 path 索引对齐后端模型完整定义 + const originIdx = typeof tpl.__originIndex === 'number' ? tpl.__originIndex : i; + result.push(...collectExposedFields(tpl.attrs, `${path}.enumValues[${originIdx}].attrs`)); } }); } else if (def.runtimeShow === true) { @@ -232,7 +235,11 @@ export function collectExposedFields(params: any, prefix = ''): ExposedField[] { value: def.value, defaultValue: def.defaultValue, fieldConstraint: def.fieldConstraint && typeof def.fieldConstraint === 'object' ? def.fieldConstraint : undefined, - valueSource: Array.isArray(def.valueSource) ? def.valueSource : undefined, + valueSource: (() => { + const vs = def.valueSource; + // 契约统一数组;兼容旧 DSL 单对象 { nodeId, field } + return Array.isArray(vs) ? vs : vs && typeof vs === 'object' ? [vs] : undefined; + })(), multiple: def.multiple || def.fieldType === 'uploadMultiple' || undefined, }); } @@ -299,12 +306,11 @@ export function buildModelRequestParamsPath( outputIndex?: Map }> ): ModelRequestParamsPathItem[] { const fields = collectAllLeafFields(params); - // 引用来源 label:节点名.字段label(无索引/未命中时兜底 field) + // 引用来源 label:字段中文名(不带「节点名.」前缀;无索引/未命中时兜底 field) const resolveRefLabel = (nodeId: string, field: string): string => { const info = outputIndex?.get(nodeId); if (!info) return field; - const fieldLabel = info.fields.get(field) || field; - return info.name ? `${info.name}.${fieldLabel}` : fieldLabel; + return info.fields.get(field) || field; }; return fields.map(({ path, def }) => { let valueSource: any; @@ -348,10 +354,12 @@ export function restoreRuntimeShow(params: any, fields: ExposedField[] | null | const cur = resolvePath(params, f.path); if (cur && typeof cur === 'object' && !Array.isArray(cur)) { cur.runtimeShow = true; - if (startNodeId && Array.isArray(cur.valueSource)) { - // 移除指向开始节点(表单展示标记)的引用;全部移除后删除整个 valueSource - cur.valueSource = cur.valueSource.filter((r: any) => !(r && r.nodeId === startNodeId)); - if (cur.valueSource.length === 0) delete cur.valueSource; + if (startNodeId && cur.valueSource && typeof cur.valueSource === 'object') { + // 移除指向开始节点(表单展示标记)的引用;兼容旧 DSL 单对象(统一数组化),全部移除后删除整个 valueSource + const arr = Array.isArray(cur.valueSource) ? cur.valueSource : [cur.valueSource]; + const keep = arr.filter((r: any) => !(r && r.nodeId === startNodeId)); + if (keep.length === 0) delete cur.valueSource; + else cur.valueSource = keep; } } } @@ -392,8 +400,12 @@ function resolvePath(params: any, path: string): any { if (me) { const idx = Number(me[1]); const templates = arrayTemplatesOf(cur); - if (idx >= templates.length) return undefined; - cur = templates[idx]; + // 路径索引可能为 strip 压缩前记录的原始索引(__originIndex),优先按原始索引匹配; + // 未带 __originIndex 时退化为按数组位置匹配(未压缩场景) + const byOrigin = templates.find((t: any) => t && typeof t.__originIndex === 'number' && t.__originIndex === idx); + if (byOrigin !== undefined) cur = byOrigin; + else if (idx < templates.length) cur = templates[idx]; + else return undefined; continue; } cur = cur[seg]; diff --git a/src/views/settings/workflow/index.vue b/src/views/settings/workflow/index.vue index 239264d..46df319 100644 --- a/src/views/settings/workflow/index.vue +++ b/src/views/settings/workflow/index.vue @@ -1110,7 +1110,7 @@ const buildOutputConfig = (node: Node, 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, label: def.label || def.field }] : undefined; if (def.field === 'responseType') { const expand = entry?.expand || []; return { @@ -1139,7 +1139,7 @@ const buildOutputConfig = (node: Node, 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, label: f.label || f.field || fieldName }] } : {}), ...(f.type === 'uploadMultiple' ? { fieldConstraint: { @@ -1177,14 +1177,20 @@ const buildOutputConfig = (node: Node, 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, label: def.label || def.field }] } : {}), }; }); }; // 子流程节点:编辑器态 subFlowConfig → DSL 保存态 subConfig(runtimeShow → isFormField) -const serializeSubFlowConfig = (config: SubFlowConfig | null | undefined, node?: Node, startNodeId = '') => { +const serializeSubFlowConfig = (config: SubFlowConfig | null | undefined, node?: Node, startNodeId = '', outputIndex?: Map }>) => { if (!config || !config.workflowId) return null; + // 引用来源 label:字段中文名(不带「节点名.」前缀;无索引/未命中时兜底 field) + const resolveRefLabel = (nodeId: string, field: string): string => { + const info = outputIndex?.get(nodeId); + if (!info) return field; + return info.fields.get(field) || field; + }; // 生成次数(节点库 presetOption 的 maxConcurrency):勾选表单展示时聚合进开始节点表单, // 此处同步保存到 subConfig,保证 sub_flow 节点自身也带该配置(后端执行时可读) const formConfig = Array.isArray(node?.data?.formConfig) ? node.data.formConfig : []; @@ -1211,9 +1217,9 @@ const serializeSubFlowConfig = (config: SubFlowConfig | null | undefined, node?: // - 引用上级输出:值来自主工作流上游节点,统一转 field // - 其余:无引用(静态默认值) valueSource: f.runtimeShow - ? [{ nodeId: startNodeId, field: f.field }] + ? [{ nodeId: startNodeId, field: f.field, label: f.label || f.field }] : Array.isArray(f.valueSource) - ? f.valueSource.map((vs: any) => ({ nodeId: vs.nodeId, field: vs.field })) + ? f.valueSource.map((vs: any) => ({ nodeId: vs.nodeId, field: vs.field, label: resolveRefLabel(vs?.nodeId, vs?.field) })) : null, isFormField: Boolean(f.runtimeShow), })), @@ -1455,7 +1461,7 @@ const confirmSaveWorkflow = async () => { ...(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, + subConfig: nodeCode === 'sub_flow' ? serializeSubFlowConfig(n.data?.subFlowConfig, n, startNode?.id || '', outputFieldsIndex) : null, modelConfig: { modelId: n.data?.modelConfig?.modelId || '', // 模型名称/类型随工作流保存,供首页补全弹窗展示模型名与「同类型模型」过滤