sub_flow 节点引入工作流修复与首页执行工作流重构
- 引入工作流列表字段兼容 flowName,选择弹窗卡片正常展示数据
- 生成次数 maxConcurrency 同步保存到 sub_flow 节点,回显兜底恢复
- valueSource 按后端契约统一 {nodeId, fieldName},表单展示/引用正确保存与回显
- 首页执行工作流 DSL 字段路径改为 .enumValues[i],支持引用上游输出与上传文件名
This commit is contained in:
@@ -128,9 +128,47 @@ export function stripReadonlyFields(params: any): any {
|
||||
return params;
|
||||
}
|
||||
|
||||
// 剔除 array 类型字段的实例值(value):后端模型定义中 array 仅保存结构/模板
|
||||
// (enumValues / attrs),前端渲染时从模板复制并填充的 value 实例是冗余数据,
|
||||
// 保存工作流时不提交。递归处理嵌套结构(array 字段可嵌套于 enumValues 模板中,
|
||||
// 如 messages.enumValues[0].attrs.content 仍是 array,同样需删除 value)。
|
||||
export function removeArrayValueInstances(params: any): any {
|
||||
if (!params || typeof params !== 'object' || Array.isArray(params)) return params;
|
||||
// 单个字段定义(带 type):array 删实例值并递归模板;object 递归 attrs
|
||||
if (typeof params.type === 'string') {
|
||||
if (params.type === 'array') {
|
||||
delete params.value;
|
||||
if (params.attrs && typeof params.attrs === 'object') {
|
||||
if (Array.isArray(params.attrs)) params.attrs.forEach((it: any) => removeArrayValueInstances(it));
|
||||
else removeArrayValueInstances(params.attrs);
|
||||
}
|
||||
if (Array.isArray(params.enumValues)) params.enumValues.forEach((tpl: any) => removeArrayValueInstances(tpl));
|
||||
} else if (params.type === 'object' && params.attrs && typeof params.attrs === 'object' && !Array.isArray(params.attrs)) {
|
||||
removeArrayValueInstances(params.attrs);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
// 字段定义容器 { key: fieldDef }:逐个字段递归
|
||||
for (const key of Object.keys(params)) {
|
||||
removeArrayValueInstances(params[key]);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
// ===== 暴露清单:在工作流表单中展示的勾选字段 =====
|
||||
|
||||
// 单个暴露叶子字段(路径带实例索引,如 "messages.value[0].attrs.content")
|
||||
// array 的模板元素列表(与 ModelField.arrayTemplates 优先级一致):
|
||||
// enumValues 数组 → attrs 数组 → 单个 attrs 对象。勾选/值都跟随模板,故 path 用
|
||||
// enumValues[i] 定位模板,collect 与 resolve 共用本函数保持文法一致。
|
||||
function arrayTemplatesOf(def: any): any[] {
|
||||
if (!def || typeof def !== 'object') return [];
|
||||
if (Array.isArray(def.enumValues) && def.enumValues.length) return def.enumValues;
|
||||
if (Array.isArray(def.attrs) && def.attrs.length) return def.attrs;
|
||||
if (def.attrs && typeof def.attrs === 'object' && !Array.isArray(def.attrs)) return [{ type: 'object', attrs: def.attrs }];
|
||||
return [];
|
||||
}
|
||||
|
||||
// 单个暴露叶子字段(路径带模板索引,如 "messages.enumValues[0].attrs.content")
|
||||
export interface ExposedField {
|
||||
path: string;
|
||||
label: string;
|
||||
@@ -139,13 +177,17 @@ export interface ExposedField {
|
||||
required: boolean;
|
||||
options?: any[];
|
||||
value?: any;
|
||||
defaultValue?: any; // 默认值:首页初始化/回显用
|
||||
fieldConstraint?: any; // 字段约束:上传格式/大小/数量、数字 min/max 等
|
||||
valueSource?: any; // 引用上游节点输出({ nodeId, field });有则首页只读展示
|
||||
multiple?: boolean; // 多文件上传标记
|
||||
refNodeId?: string; // 预留:引用其他节点功能(后续启用)
|
||||
}
|
||||
|
||||
// 收集 runtimeShow === true 的叶子字段,生成暴露清单
|
||||
// 路径文法(与 restoreRuntimeShow 共用):
|
||||
// object 子字段 → 父路径 + ".attrs." + 子key
|
||||
// array 实例元素 → 父路径 + ".value[i]"
|
||||
// array 模板元素 → 父路径 + ".enumValues[i]"
|
||||
// 原始值数组元素(非 object 包装)不可勾选,跳过
|
||||
export function collectExposedFields(params: any, prefix = ''): ExposedField[] {
|
||||
if (!params || typeof params !== 'object') return [];
|
||||
@@ -161,14 +203,14 @@ export function collectExposedFields(params: any, prefix = ''): ExposedField[] {
|
||||
result.push(...collectExposedFields(attrs, `${path}.attrs`));
|
||||
}
|
||||
} else if (t === 'array') {
|
||||
// 仅遍历实例数组;元素为 { type:'object', attrs } 包装时才递归
|
||||
if (Array.isArray(def.value)) {
|
||||
def.value.forEach((item: any, i: number) => {
|
||||
if (item && typeof item === 'object' && item.attrs && typeof item.attrs === 'object' && !Array.isArray(item.attrs)) {
|
||||
result.push(...collectExposedFields(item.attrs, `${path}.value[${i}].attrs`));
|
||||
}
|
||||
});
|
||||
}
|
||||
// 勾选跟随模板:渲染时 value 元素与模板共享引用,runtimeShow 落在模板上,
|
||||
// 故遍历模板(enumValues 优先)收集,path 用 enumValues[i] 定位模板
|
||||
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`));
|
||||
}
|
||||
});
|
||||
} else if (def.runtimeShow === true) {
|
||||
// 叶子且已勾选
|
||||
result.push({
|
||||
@@ -179,6 +221,10 @@ export function collectExposedFields(params: any, prefix = ''): ExposedField[] {
|
||||
required: !!def.required,
|
||||
options: Array.isArray(def.options) ? def.options : undefined,
|
||||
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,
|
||||
multiple: def.multiple || def.fieldType === 'uploadMultiple' || undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -208,14 +254,22 @@ function resolvePath(params: any, path: string): any {
|
||||
cur = cur.attrs;
|
||||
if (!cur || typeof cur !== 'object') return undefined;
|
||||
} else {
|
||||
const m = seg.match(/^value\[(\d+)\]$/);
|
||||
if (m) {
|
||||
const idx = Number(m[1]);
|
||||
const mv = seg.match(/^value\[(\d+)\]$/);
|
||||
if (mv) {
|
||||
const idx = Number(mv[1]);
|
||||
if (!Array.isArray(cur.value) || idx >= cur.value.length) return undefined;
|
||||
cur = cur.value[idx];
|
||||
} else {
|
||||
cur = cur[seg];
|
||||
continue;
|
||||
}
|
||||
const me = seg.match(/^enumValues\[(\d+)\]$/);
|
||||
if (me) {
|
||||
const idx = Number(me[1]);
|
||||
const templates = arrayTemplatesOf(cur);
|
||||
if (idx >= templates.length) return undefined;
|
||||
cur = templates[idx];
|
||||
continue;
|
||||
}
|
||||
cur = cur[seg];
|
||||
}
|
||||
}
|
||||
return cur;
|
||||
|
||||
Reference in New Issue
Block a user