首页工作流节点引用多选与保存回显完善: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:
@@ -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 } : {}),
|
||||
// 模型返回参数随工作流保存,保证重开后仍可被下游引用
|
||||
|
||||
Reference in New Issue
Block a user