首页工作流节点引用多选与保存回显完善: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:
@@ -188,7 +188,7 @@ export interface ExposedField {
|
||||
value?: any;
|
||||
defaultValue?: any; // 默认值:首页初始化/回显用
|
||||
fieldConstraint?: any; // 字段约束:上传格式/大小/数量、数字 min/max 等
|
||||
valueSource?: any; // 引用上游节点输出({ nodeId, field });有则首页只读展示
|
||||
valueSource?: any; // 引用上游节点输出([{ nodeId, field }] 数组,支持多选);有则首页只读展示
|
||||
multiple?: boolean; // 多文件上传标记
|
||||
refNodeId?: string; // 预留:引用其他节点功能(后续启用)
|
||||
}
|
||||
@@ -232,7 +232,7 @@ export function collectExposedFields(params: any, prefix = ''): ExposedField[] {
|
||||
value: def.value,
|
||||
defaultValue: def.defaultValue,
|
||||
fieldConstraint: def.fieldConstraint && typeof def.fieldConstraint === 'object' ? def.fieldConstraint : undefined,
|
||||
valueSource: def.valueSource && typeof def.valueSource === 'object' ? def.valueSource : undefined,
|
||||
valueSource: Array.isArray(def.valueSource) ? def.valueSource : undefined,
|
||||
multiple: def.multiple || def.fieldType === 'uploadMultiple' || undefined,
|
||||
});
|
||||
}
|
||||
@@ -243,12 +243,14 @@ export function collectExposedFields(params: any, prefix = ''): ExposedField[] {
|
||||
// ===== 模型请求参数扁平清单(modelRequestParamsPath)=====
|
||||
// 后端契约:保存时与 modelRequestParams 平级新增 modelRequestParamsPath,
|
||||
// 为嵌套模型参数树的全部叶子字段扁平清单(path 文法与 collectExposedFields 一致),
|
||||
// 后端按 path 重组请求参数。value 保留当前值;valueSource 规则:
|
||||
// - 已有引用配置(用户在设计器配置「引用上游」)→ 原样保留 { nodeId, field }
|
||||
// - 无引用但勾选「表单展示」(runtimeShow=true)→ 补 { nodeId: 开始节点id, field: path }
|
||||
// 后端按 path 重组请求参数。value 保留当前值;valueSource 规则(统一数组 [{ nodeId, field }]):
|
||||
// - 已有引用配置(用户在设计器配置「引用上游」)→ 原样保留数组
|
||||
// - 无引用但勾选「表单展示」(runtimeShow=true)→ 补 [{ nodeId: 开始节点id, field: path }]
|
||||
// - 其余 → 不带 valueSource
|
||||
export interface ModelRequestParamsPathItem {
|
||||
path: string;
|
||||
// 叶子字段中文名(回显/展示用;无 label 时兜底 path 最后一段字段名)
|
||||
label: string;
|
||||
type: string;
|
||||
required: boolean;
|
||||
value: any;
|
||||
@@ -290,17 +292,44 @@ function collectAllLeafFields(params: any, prefix = ''): { path: string; def: an
|
||||
// 生成 modelRequestParamsPath 扁平清单
|
||||
// 所有叶子字段(含对象/数组内部)都支持「引用上游」,valueSource 原样保留;
|
||||
// 与编辑器「引用上级输出与表单展示同级」语义一致
|
||||
export function buildModelRequestParamsPath(params: any, startNodeId = ''): ModelRequestParamsPathItem[] {
|
||||
export function buildModelRequestParamsPath(
|
||||
params: any,
|
||||
startNodeId = '',
|
||||
// 节点输出索引(id → 节点名 + 字段 label),用于补全 valueSource 的引用来源 label
|
||||
outputIndex?: Map<string, { name: string; fields: Map<string, string> }>
|
||||
): ModelRequestParamsPathItem[] {
|
||||
const fields = collectAllLeafFields(params);
|
||||
// 引用来源 label:节点名.字段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 fields.map(({ path, def }) => {
|
||||
const valueSource =
|
||||
def.valueSource && typeof def.valueSource === 'object'
|
||||
? def.valueSource // 引用上游:前端编辑态 { nodeId, field } 原样保留
|
||||
: def.runtimeShow === true && startNodeId
|
||||
? { nodeId: startNodeId, field: path } // 勾选「表单展示」:补后端契约 { nodeId, field }
|
||||
: undefined;
|
||||
let valueSource: any;
|
||||
if (Array.isArray(def.valueSource)) {
|
||||
// 引用上游:前端编辑态 [{ nodeId, field }] 数组,逐项补 label
|
||||
valueSource = def.valueSource.map((r: any) =>
|
||||
r && r.nodeId && r.field
|
||||
? { nodeId: r.nodeId, field: r.field, label: resolveRefLabel(r.nodeId, r.field) }
|
||||
: r
|
||||
);
|
||||
} else if (def.valueSource && typeof def.valueSource === 'object') {
|
||||
// 兼容旧 DSL 单对象 { nodeId, field }
|
||||
const r = def.valueSource as any;
|
||||
valueSource = [
|
||||
r && r.nodeId && r.field
|
||||
? { nodeId: r.nodeId, field: r.field, label: resolveRefLabel(r.nodeId, r.field) }
|
||||
: r,
|
||||
];
|
||||
} else if (def.runtimeShow === true && startNodeId) {
|
||||
// 勾选「表单展示」:补后端契约数组(指向开始节点,label 取开始节点字段名)
|
||||
valueSource = [{ nodeId: startNodeId, field: path, label: resolveRefLabel(startNodeId, path) }];
|
||||
}
|
||||
return {
|
||||
path,
|
||||
label: def.label || path.split('.').pop() || '',
|
||||
type: def.type || 'string',
|
||||
required: Boolean(def.required),
|
||||
value: def.value ?? '',
|
||||
@@ -319,8 +348,10 @@ 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 && cur.valueSource && typeof cur.valueSource === 'object' && cur.valueSource.nodeId === startNodeId) {
|
||||
delete cur.valueSource;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -334,7 +365,7 @@ export function attachFormFieldValueSources(params: any, fields: ExposedField[]
|
||||
if (!f || typeof f.path !== 'string') continue;
|
||||
const cur = resolvePath(params, f.path);
|
||||
if (cur && typeof cur === 'object' && !Array.isArray(cur)) {
|
||||
cur.valueSource = { nodeId: startNodeId, field: f.path };
|
||||
cur.valueSource = [{ nodeId: startNodeId, field: f.path }];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user