weaver-e10-builder 1.0.9-beta → 1.0.11-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/dist/commands/actionflow.d.ts.map +1 -1
  2. package/dist/commands/actionflow.js +10 -6
  3. package/dist/commands/actionflow.js.map +1 -1
  4. package/dist/commands/form-layout-init.d.ts.map +1 -1
  5. package/dist/commands/form-layout-init.js +7 -1
  6. package/dist/commands/form-layout-init.js.map +1 -1
  7. package/dist/commands/form-native.d.ts.map +1 -1
  8. package/dist/commands/form-native.js +30 -8
  9. package/dist/commands/form-native.js.map +1 -1
  10. package/dist/commands/form.d.ts.map +1 -1
  11. package/dist/commands/form.js +209 -1
  12. package/dist/commands/form.js.map +1 -1
  13. package/dist/commands/wf-form.d.ts +18 -0
  14. package/dist/commands/wf-form.d.ts.map +1 -1
  15. package/dist/commands/wf-form.js +185 -188
  16. package/dist/commands/wf-form.js.map +1 -1
  17. package/dist/commands/workflow-create-workflow.d.ts.map +1 -1
  18. package/dist/commands/workflow-create-workflow.js +14 -0
  19. package/dist/commands/workflow-create-workflow.js.map +1 -1
  20. package/dist/commands/workflow-node-validate.d.ts.map +1 -1
  21. package/dist/commands/workflow-node-validate.js +16 -2
  22. package/dist/commands/workflow-node-validate.js.map +1 -1
  23. package/dist/commands/workflow-numbering.d.ts.map +1 -1
  24. package/dist/commands/workflow-numbering.js +18 -2
  25. package/dist/commands/workflow-numbering.js.map +1 -1
  26. package/dist/commands/workflow.d.ts.map +1 -1
  27. package/dist/commands/workflow.js +47 -35
  28. package/dist/commands/workflow.js.map +1 -1
  29. package/dist/services/actionflow/build_workflow_draft.d.ts.map +1 -1
  30. package/dist/services/actionflow/build_workflow_draft.js +23 -2
  31. package/dist/services/actionflow/build_workflow_draft.js.map +1 -1
  32. package/dist/services/actionflow/command_output.d.ts +6 -0
  33. package/dist/services/actionflow/command_output.d.ts.map +1 -0
  34. package/dist/services/actionflow/command_output.js +91 -0
  35. package/dist/services/actionflow/command_output.js.map +1 -0
  36. package/dist/services/actionflow/components/action_eb_add.d.ts.map +1 -1
  37. package/dist/services/actionflow/components/action_eb_add.js +5 -4
  38. package/dist/services/actionflow/components/action_eb_add.js.map +1 -1
  39. package/dist/services/actionflow/components/action_eb_delete.js +1 -1
  40. package/dist/services/actionflow/components/action_eb_delete.js.map +1 -1
  41. package/dist/services/actionflow/components/action_eb_update.js +4 -4
  42. package/dist/services/actionflow/components/action_eb_update.js.map +1 -1
  43. package/dist/services/actionflow/components/action_eb_update_compiler.d.ts.map +1 -1
  44. package/dist/services/actionflow/components/action_eb_update_compiler.js +4 -1
  45. package/dist/services/actionflow/components/action_eb_update_compiler.js.map +1 -1
  46. package/dist/services/actionflow/components/approve_create.js +1 -1
  47. package/dist/services/actionflow/components/approve_create.js.map +1 -1
  48. package/dist/services/actionflow/components/approve_trigger.d.ts.map +1 -1
  49. package/dist/services/actionflow/components/approve_trigger.js +48 -16
  50. package/dist/services/actionflow/components/approve_trigger.js.map +1 -1
  51. package/dist/services/actionflow/components/date_trigger.js +3 -3
  52. package/dist/services/actionflow/components/date_trigger.js.map +1 -1
  53. package/dist/services/actionflow/components/enquiry_form.d.ts.map +1 -1
  54. package/dist/services/actionflow/components/enquiry_form.js +15 -8
  55. package/dist/services/actionflow/components/enquiry_form.js.map +1 -1
  56. package/dist/services/actionflow/enrich_approve_trigger.d.ts.map +1 -1
  57. package/dist/services/actionflow/enrich_approve_trigger.js +47 -17
  58. package/dist/services/actionflow/enrich_approve_trigger.js.map +1 -1
  59. package/dist/services/actionflow/formula_service.d.ts.map +1 -1
  60. package/dist/services/actionflow/formula_service.js +71 -3
  61. package/dist/services/actionflow/formula_service.js.map +1 -1
  62. package/dist/services/actionflow/functions_catalog.d.ts +26 -0
  63. package/dist/services/actionflow/functions_catalog.d.ts.map +1 -0
  64. package/dist/services/actionflow/functions_catalog.js +8986 -0
  65. package/dist/services/actionflow/functions_catalog.js.map +1 -0
  66. package/dist/services/actionflow/functions_source.d.ts +7 -0
  67. package/dist/services/actionflow/functions_source.d.ts.map +1 -0
  68. package/dist/services/actionflow/functions_source.js +50 -0
  69. package/dist/services/actionflow/functions_source.js.map +1 -0
  70. package/dist/services/actionflow/generation_service.d.ts +8 -0
  71. package/dist/services/actionflow/generation_service.d.ts.map +1 -1
  72. package/dist/services/actionflow/generation_service.js +60 -2
  73. package/dist/services/actionflow/generation_service.js.map +1 -1
  74. package/dist/services/actionflow/preflight_service.d.ts +6 -0
  75. package/dist/services/actionflow/preflight_service.d.ts.map +1 -1
  76. package/dist/services/actionflow/preflight_service.js +69 -0
  77. package/dist/services/actionflow/preflight_service.js.map +1 -1
  78. package/dist/services/actionflow/workflow_compiler.d.ts.map +1 -1
  79. package/dist/services/actionflow/workflow_compiler.js +102 -85
  80. package/dist/services/actionflow/workflow_compiler.js.map +1 -1
  81. package/dist/services/actionflow/workflow_source.d.ts +2 -0
  82. package/dist/services/actionflow/workflow_source.d.ts.map +1 -1
  83. package/dist/services/actionflow/workflow_source.js +8 -0
  84. package/dist/services/actionflow/workflow_source.js.map +1 -1
  85. package/dist/services/workflow-operator.d.ts +3 -1
  86. package/dist/services/workflow-operator.d.ts.map +1 -1
  87. package/dist/services/workflow-operator.js +5 -4
  88. package/dist/services/workflow-operator.js.map +1 -1
  89. package/package.json +1 -1
@@ -103,6 +103,120 @@ function mergeRuntimeSingleRow(configs, srSet) {
103
103
  };
104
104
  walk(configs);
105
105
  }
106
+ // 落单/孤字段占满行(对齐 eb 表 form-create.ts:两列排布时「奇数个的最后一个」+「被 singleRow
107
+ // 挤落单的前一字段」自动 singleRow 独占整行,避免右列留白)。明细表恒 singleRow。
108
+ // 排布对象仅业务主字段;系统字段与 Fold/Collapse 分组容器原样保留(分组内字段由平台容器排布)。
109
+ function applyLoneSingleRows(configs) {
110
+ const isGroup = (c) => c.componentKey === 'Fold' || c.type === 'Collapse';
111
+ const isDetail = (c) => c.type === 'DetailTable' || c.componentKey === 'DataTable';
112
+ const isBusiness = (c) => !c.system && !isGroup(c) && !isDetail(c);
113
+ const mainFields = configs.filter(isBusiness);
114
+ const details = configs.filter(isDetail);
115
+ const others = configs.filter((c) => !isBusiness(c) && !isDetail(c));
116
+ const mainIds = mainFields.map((c) => String(c.id));
117
+ const detailIds = new Set(details.map((c) => String(c.id)));
118
+ // 奇数个最后一个落单 → 独占一行
119
+ const loneRowId = mainIds.length % 2 === 1 ? mainIds[mainIds.length - 1] : null;
120
+ // 落单兜底:模拟两列流,singleRow 字段位于 col=1 时其前一字段(col0 未配对)也标 singleRow
121
+ const orphanIds = new Set();
122
+ let col = 0;
123
+ let prevId = '';
124
+ for (const id of [...mainIds, ...details.map((c) => String(c.id))]) {
125
+ const c = mainFields.find((x) => String(x.id) === id) || details.find((x) => String(x.id) === id);
126
+ const isSingle = c?.singleRow === true || id === loneRowId || detailIds.has(id);
127
+ if (isSingle) {
128
+ if (col === 1 && prevId)
129
+ orphanIds.add(prevId);
130
+ col = 0;
131
+ }
132
+ else {
133
+ col = col === 0 ? 1 : 0;
134
+ }
135
+ prevId = id;
136
+ }
137
+ const biz = mainFields.map((c) => ({
138
+ ...c,
139
+ singleRow: String(c.id) === loneRowId || c.singleRow === true || orphanIds.has(String(c.id)),
140
+ }));
141
+ const det = details.map((d) => ({ ...d, singleRow: true }));
142
+ return [...others, ...biz, ...det];
143
+ }
144
+ // 读表单现有字段分组 → refFieldIds(initLayout/getConfig 的 groupFieldIds)。
145
+ // getFieldGroupPage 返回全部主表字段,字段 formField.group 带 groupId+groupName;
146
+ // refFieldId = groupId - 1(平台先分配 layout config 再分配 formFieldGroup,顺序相连)。
147
+ // 用于 field-prop / layout init 重跑 initLayout 时保留 Collapse 折叠分组。
148
+ async function fetchFormGroups(client, baseUrl, formId) {
149
+ try {
150
+ const r = await client.post(`${baseUrl}${ROUTE}/field/manage/getFieldGroupPage`, { pageNo: 1, pageSize: 100, module: MODULE, formFieldSearchEntity: { formId }, customParam: {} }, { ebbusinessid: formId });
151
+ const list = r?.data?.pageDatas?.result || [];
152
+ const seen = new Set();
153
+ for (const f of list) {
154
+ const g = f?.formField?.group;
155
+ if (g?.id)
156
+ seen.add(String(g.id));
157
+ }
158
+ return [...seen].map((id) => String(BigInt(id) - 1n));
159
+ }
160
+ catch {
161
+ return [];
162
+ }
163
+ }
164
+ /** 初始化/重建流程表单布局(initLayout):getConfig 读 → 强制业务字段 readOnly=false(防 FLOW 模板默认锁死)
165
+ * → 合并运行时 singleRow → applyLoneSingleRows(落单/孤字段独占整行)→ initLayout 落库运行时布局。
166
+ * 供 `wf-form layout init` 与 `workflow create-workflow` 建后自动解锁、`wf-form add-field/add-fields/add-detail-table` 末尾重排复用。
167
+ * hooks.preLayout: 在合并运行时 singleRow 之后、applyLoneSingleRows 之前介入(add-field --single-row 用此给新字段单标 singleRow)。 */
168
+ export async function initWorkflowFormLayout(client, baseUrl, formId, o = {}, hooks = {}) {
169
+ const ebb = { ebbusinessid: formId };
170
+ const meta = await getLayoutMeta(client, baseUrl, formId);
171
+ if (!meta.layoutId)
172
+ throw new Error('无法获取 layoutId');
173
+ // 保留字段分组:getConfig/initLayout 带 groupFieldIds,否则 Collapse 折叠被冲掉
174
+ const groupIds = await fetchFormGroups(client, baseUrl, formId);
175
+ const cfgRes = await client.post(`${baseUrl}${ROUTE}/view/relate/getConfig`, {
176
+ formId, module: MODULE, apiModule: 'workflow/form', layoutId: meta.layoutId,
177
+ customParam: { terminal: '', objId: formId }, initMode: 5, groupFieldIds: groupIds,
178
+ designerParam: { sourceModule: MODULE },
179
+ }, ebb);
180
+ // 流程表单 FLOW 布局 getConfig 默认把业务字段 readOnly=true(锁死),init 时强制可编辑:
181
+ // 业务字段 readOnly=false / disableEdit=false(除非 --read-only 显式锁);明细/系统字段/Collapse 分组容器保留。
182
+ const configs = normalizeDetailTableChildren((cfgRes?.data?.configs || []).map((c) => {
183
+ const isBusiness = !c.system && c.componentKey !== 'DataTable' && c.type !== 'DetailTable' && c.type !== 'Collapse' && c.componentKey !== 'Fold';
184
+ const out = { ...c, display: c.system ? c.display : true, show: c.system ? c.show : true };
185
+ if (isBusiness) {
186
+ out.readOnly = o.readOnly === true;
187
+ out.disableEdit = false;
188
+ if (o.required !== undefined)
189
+ out.required = o.required;
190
+ if (o.singleRow !== undefined)
191
+ out.singleRow = o.singleRow;
192
+ }
193
+ return out;
194
+ }));
195
+ // 合并运行时已保存的 singleRow(getConfig 不带,防 initLayout 冲掉独占一行)
196
+ const initSrSet = await fetchRuntimeSingleRows(client, baseUrl, formId);
197
+ mergeRuntimeSingleRow(configs, initSrSet);
198
+ // 介入点:add-field --single-row 在此给新加字段单标 singleRow(早于 applyLoneSingleRows,让其 orphan 逻辑接得住)
199
+ if (hooks.preLayout)
200
+ hooks.preLayout(configs);
201
+ // 落单/孤字段占满行(对齐 eb 表):奇数个最后一个 + 被 singleRow 挤落单的前一字段自动 singleRow
202
+ const finalConfigs = applyLoneSingleRows(configs);
203
+ const styleTemplateId = o.styleTemplate
204
+ || (meta.layoutType === 'FLOW' ? '820000000000000001' : '700000000000000008');
205
+ const initRes = await client.post(`${baseUrl}${ROUTE}/view/relate/initLayout`, {
206
+ formId, module: MODULE, apiModule: 'workflow/form', layoutId: meta.layoutId,
207
+ generateCollapse: o.generateCollapse !== false,
208
+ styleTemplateId: /^\d+$/.test(styleTemplateId) ? styleTemplateId : styleTemplateId, styleTemplateType: 1,
209
+ configs: finalConfigs, addLayout: false, ecodeType: ['js', 'css'], initMode: '1',
210
+ groupFieldIds: groupIds,
211
+ customParam: { terminal: '', objId: formId },
212
+ formLayoutMultiple: { isSys: true, id: meta.layoutMultiId, name: meta.layoutName, terminalType: 'PC', layoutType: meta.layoutType },
213
+ designerParam: { onlyLayout: false },
214
+ }, ebb);
215
+ if (initRes?.code !== 200 || !initRes?.status)
216
+ throw new Error(`initLayout 失败: ${initRes?.msg}`);
217
+ const comps = initRes?.data?.pageLayouts?.[0]?.comps || initRes?.data?.formLayout?.pageLayouts?.[0]?.comps || [];
218
+ return { layoutType: meta.layoutType, styleTemplateId, comps: comps.length };
219
+ }
106
220
  async function genIds(client, baseUrl, n) {
107
221
  const r = await client.post(`${baseUrl}/api/form/local/generatorIds?size=${n}`, {});
108
222
  return (r?.data?.generatorIds || []).map(String);
@@ -329,39 +443,26 @@ export function registerWorkflowFormCommands(program) {
329
443
  ? JSON.stringify({ success: true, formId, fieldId, fieldName, componentKey: jsonObj.componentKey, subFormId: o.subFormId || undefined }, null, 2)
330
444
  : `字段已加: ${fieldName} (${jsonObj.componentKey}) fieldId=${fieldId}${o.subFormId ? ` (明细 ${o.subFormId})` : ''}${ck === 'Select' ? ' 注:选项需单独配' : ''}`);
331
445
  // 末尾自动 layout init(解决 add-field 不自动重新布局的缺口,对齐 add-fields 批量)。
332
- // 含 fetchRuntimeSingleRows 合并,防 init 冲掉已设的独占一行(singleRow)。
446
+ // 走 initWorkflowFormLayout:强制业务字段可编辑(防 FLOW 锁死)+ 合并运行时 singleRow + applyLoneSingleRows(落单独占整行)。
333
447
  if (o.init !== false) {
334
448
  try {
335
- const initMeta = await getLayoutMeta(client, baseUrl, formId);
336
- const initCfg = await client.post(`${baseUrl}${ROUTE}/view/relate/getConfig`, {
337
- formId, module: MODULE, apiModule: 'workflow/form', layoutId: initMeta.layoutId,
338
- customParam: { terminal: '', objId: formId }, initMode: 5, groupFieldIds: [],
339
- designerParam: { sourceModule: MODULE },
340
- }, ebb);
341
- const initConfigs = normalizeDetailTableChildren((initCfg?.data?.configs || []).map((c) => ({ ...c, display: c.system ? c.display : true, show: c.system ? c.show : true })));
342
- const srSet = await fetchRuntimeSingleRows(client, baseUrl, formId);
343
- mergeRuntimeSingleRow(initConfigs, srSet);
344
- // --single-row:field jsonObj 的 singleRow 平台不落到 layout comp(getConfig 不带),
345
- // 显式给新加字段 config.singleRow=true(递归找 fieldId,含明细 children)。
346
- if (o.singleRow) {
347
- const walkSet = (arr) => { for (const c of arr) {
348
- if (String(c.id) === String(fieldId)) {
349
- c.singleRow = true;
350
- c.columnRatio = '12';
351
- }
352
- if (c.children?.length)
353
- walkSet(c.children);
354
- } };
355
- walkSet(initConfigs);
356
- }
357
- const styleTemplateId = initMeta.layoutType === 'FLOW' ? '820000000000000001' : '700000000000000008';
358
- await client.post(`${baseUrl}${ROUTE}/view/relate/initLayout`, {
359
- formId, module: MODULE, apiModule: 'workflow/form', layoutId: initMeta.layoutId, generateCollapse: true,
360
- styleTemplateId, styleTemplateType: 1, configs: initConfigs, addLayout: false, ecodeType: ['js', 'css'],
361
- initMode: '1', customParam: { terminal: '', objId: formId },
362
- formLayoutMultiple: { isSys: true, id: initMeta.layoutMultiId, name: initMeta.layoutName, terminalType: 'PC', layoutType: initMeta.layoutType },
363
- designerParam: { onlyLayout: false },
364
- }, ebb);
449
+ await initWorkflowFormLayout(client, baseUrl, formId, {}, o.singleRow ? {
450
+ preLayout: (cfgs) => {
451
+ // --single-row:field jsonObj 的 singleRow 平台不落到 layout comp(getConfig 不带),
452
+ // 显式给新加字段 config.singleRow=true(递归找 fieldId,含明细 children)。
453
+ const walkSet = (arr) => {
454
+ for (const c of arr) {
455
+ if (String(c.id) === String(fieldId)) {
456
+ c.singleRow = true;
457
+ c.columnRatio = '12';
458
+ }
459
+ if (c.children?.length)
460
+ walkSet(c.children);
461
+ }
462
+ };
463
+ walkSet(cfgs);
464
+ },
465
+ } : undefined);
365
466
  console.error(` 布局已 init`);
366
467
  }
367
468
  catch (e) {
@@ -956,25 +1057,17 @@ export function registerWorkflowFormCommands(program) {
956
1057
  }
957
1058
  console.error(`已加 ${added}/${defs.length} 个字段`);
958
1059
  // 末尾自动 layout init(解决 add-field 不自动重新布局的缺口)
1060
+ // 走 initWorkflowFormLayout:强制业务字段可编辑(防 FLOW 锁死)+ 合并运行时 singleRow + applyLoneSingleRows(落单独占整行)。
959
1061
  let compCount = 0;
960
1062
  if (cmdOpts.init !== false && added > 0) {
961
- const meta = await getLayoutMeta(client, baseUrl, formId);
962
- const cfg = await client.post(`${baseUrl}${ROUTE}/view/relate/getConfig`, {
963
- formId, module: MODULE, apiModule: 'workflow/form', layoutId: meta.layoutId,
964
- customParam: { terminal: '', objId: formId }, initMode: 5, groupFieldIds: [],
965
- designerParam: { sourceModule: MODULE },
966
- }, ebb);
967
- const configs = normalizeDetailTableChildren((cfg?.data?.configs || []).map((c) => ({ ...c, display: c.system ? c.display : true, show: c.system ? c.show : true })));
968
- const styleTemplateId = meta.layoutType === 'FLOW' ? '820000000000000001' : '700000000000000008';
969
- const initRes = await client.post(`${baseUrl}${ROUTE}/view/relate/initLayout`, {
970
- formId, module: MODULE, apiModule: 'workflow/form', layoutId: meta.layoutId, generateCollapse: true,
971
- styleTemplateId, styleTemplateType: 1, configs, addLayout: false, ecodeType: ['js', 'css'],
972
- initMode: '1', customParam: { terminal: '', objId: formId },
973
- formLayoutMultiple: { isSys: true, id: meta.layoutMultiId, name: meta.layoutName, terminalType: 'PC', layoutType: meta.layoutType },
974
- designerParam: { onlyLayout: false },
975
- }, ebb);
976
- compCount = initRes?.data?.pageLayouts?.[0]?.comps?.length || initRes?.data?.formLayout?.pageLayouts?.[0]?.comps?.length || 0;
977
- console.error(` 布局已 init: 组件 ${compCount} 个`);
1063
+ try {
1064
+ const r = await initWorkflowFormLayout(client, baseUrl, formId);
1065
+ compCount = r.comps;
1066
+ console.error(` 布局已 init: 组件 ${compCount} 个`);
1067
+ }
1068
+ catch (e) {
1069
+ console.error(` 布局 init 失败: ${e?.message || e}(字段已加,手动跑 wf-form layout init)`);
1070
+ }
978
1071
  }
979
1072
  console.log(opts.json
980
1073
  ? JSON.stringify({ success: true, formId, added, total: defs.length, initComps: compCount || undefined }, null, 2)
@@ -1341,33 +1434,18 @@ export function registerWorkflowFormCommands(program) {
1341
1434
  if (o.init) {
1342
1435
  if (!firstFieldName)
1343
1436
  throw new Error('--init 需配合首字段名(空子表放不了 DetailTable)');
1344
- const meta = await getLayoutMeta(client, baseUrl, formId);
1345
- const cfg = await client.post(`${baseUrl}${ROUTE}/view/relate/getConfig`, {
1346
- formId, module: MODULE, apiModule: 'workflow/form', layoutId: meta.layoutId,
1347
- customParam: { terminal: '', objId: formId }, initMode: 5, groupFieldIds: [],
1348
- designerParam: { sourceModule: MODULE },
1349
- }, ebb);
1350
- const configs = normalizeDetailTableChildren((cfg?.data?.configs || []).map((c) => {
1351
- const isBusiness = !c.system && c.componentKey !== 'DataTable' && c.type !== 'DetailTable';
1352
- const out = { ...c, display: c.system ? c.display : true, show: c.system ? c.show : true };
1353
- if (isBusiness) {
1354
- out.readOnly = false;
1355
- out.disableEdit = false;
1356
- } // FLOW 默认锁,强制可编辑
1357
- return out;
1358
- }));
1359
- const styleTemplateId = meta.layoutType === 'FLOW' ? '820000000000000001' : '700000000000000008';
1360
- const initRes = await client.post(`${baseUrl}${ROUTE}/view/relate/initLayout`, {
1361
- formId, module: MODULE, apiModule: 'workflow/form', layoutId: meta.layoutId, generateCollapse: true,
1362
- styleTemplateId, styleTemplateType: 1, configs, addLayout: false, ecodeType: ['js', 'css'],
1363
- initMode: '1', customParam: { terminal: '', objId: formId },
1364
- formLayoutMultiple: { isSys: true, id: meta.layoutMultiId, name: meta.layoutName, terminalType: 'PC', layoutType: meta.layoutType },
1365
- designerParam: { onlyLayout: false },
1366
- }, ebb);
1367
- if (initRes?.code !== 200 || !initRes?.status)
1368
- throw new Error(`initLayout 失败: ${initRes?.msg}`);
1369
- compCount = initRes?.data?.pageLayouts?.[0]?.comps?.length || initRes?.data?.formLayout?.pageLayouts?.[0]?.comps?.length || 0;
1370
- console.error(` 布局已 init: 组件 ${compCount} 个(含 DetailTable)`);
1437
+ // 走 initWorkflowFormLayout:强制业务字段可编辑 + applyLoneSingleRows(落单独占整行)+ 保留分组;
1438
+ // DetailTable 由 normalizeDetailTableChildren 保留在 configs,applyLoneSingleRows 对明细恒 singleRow。
1439
+ try {
1440
+ const r = await initWorkflowFormLayout(client, baseUrl, formId);
1441
+ compCount = r.comps;
1442
+ console.error(` 布局已 init: 组件 ${compCount} 个(含 DetailTable)`);
1443
+ }
1444
+ catch (e) {
1445
+ if (e?.message?.includes('initLayout'))
1446
+ throw e;
1447
+ throw new Error(`initLayout 失败: ${e?.message || e}`);
1448
+ }
1371
1449
  }
1372
1450
  console.log(opts.json
1373
1451
  ? JSON.stringify({ success: true, formId, subFormId, name, dataKey, firstFieldId, initComps: compCount || undefined }, null, 2)
@@ -1381,64 +1459,6 @@ export function registerWorkflowFormCommands(program) {
1381
1459
  process.exitCode = 1;
1382
1460
  }
1383
1461
  });
1384
- // 落单/孤字段占满行(对齐 eb 表 form-create.ts:两列排布时「奇数个的最后一个」+「被 singleRow
1385
- // 挤落单的前一字段」自动 singleRow 独占整行,避免右列留白)。明细表恒 singleRow。
1386
- // 排布对象仅业务主字段;系统字段与 Fold/Collapse 分组容器原样保留(分组内字段由平台容器排布)。
1387
- function applyLoneSingleRows(configs) {
1388
- const isGroup = (c) => c.componentKey === 'Fold' || c.type === 'Collapse';
1389
- const isDetail = (c) => c.type === 'DetailTable' || c.componentKey === 'DataTable';
1390
- const isBusiness = (c) => !c.system && !isGroup(c) && !isDetail(c);
1391
- const mainFields = configs.filter(isBusiness);
1392
- const details = configs.filter(isDetail);
1393
- const others = configs.filter((c) => !isBusiness(c) && !isDetail(c));
1394
- const mainIds = mainFields.map((c) => String(c.id));
1395
- const detailIds = new Set(details.map((c) => String(c.id)));
1396
- // 奇数个最后一个落单 → 独占一行
1397
- const loneRowId = mainIds.length % 2 === 1 ? mainIds[mainIds.length - 1] : null;
1398
- // 落单兜底:模拟两列流,singleRow 字段位于 col=1 时其前一字段(col0 未配对)也标 singleRow
1399
- const orphanIds = new Set();
1400
- let col = 0;
1401
- let prevId = '';
1402
- for (const id of [...mainIds, ...details.map((c) => String(c.id))]) {
1403
- const c = mainFields.find((x) => String(x.id) === id) || details.find((x) => String(x.id) === id);
1404
- const isSingle = c?.singleRow === true || id === loneRowId || detailIds.has(id);
1405
- if (isSingle) {
1406
- if (col === 1 && prevId)
1407
- orphanIds.add(prevId);
1408
- col = 0;
1409
- }
1410
- else {
1411
- col = col === 0 ? 1 : 0;
1412
- }
1413
- prevId = id;
1414
- }
1415
- const biz = mainFields.map((c) => ({
1416
- ...c,
1417
- singleRow: String(c.id) === loneRowId || c.singleRow === true || orphanIds.has(String(c.id)),
1418
- }));
1419
- const det = details.map((d) => ({ ...d, singleRow: true }));
1420
- return [...others, ...biz, ...det];
1421
- }
1422
- // 读表单现有字段分组 → refFieldIds(initLayout/getConfig 的 groupFieldIds)。
1423
- // getFieldGroupPage 返回全部主表字段,字段 formField.group 带 groupId+groupName;
1424
- // refFieldId = groupId - 1(平台先分配 layout config 再分配 formFieldGroup,顺序相连)。
1425
- // 用于 field-prop / layout init 重跑 initLayout 时保留 Collapse 折叠分组。
1426
- async function fetchFormGroups(client, baseUrl, formId) {
1427
- try {
1428
- const r = await client.post(`${baseUrl}${ROUTE}/field/manage/getFieldGroupPage`, { pageNo: 1, pageSize: 100, module: MODULE, formFieldSearchEntity: { formId }, customParam: {} }, { ebbusinessid: formId });
1429
- const list = r?.data?.pageDatas?.result || [];
1430
- const seen = new Set();
1431
- for (const f of list) {
1432
- const g = f?.formField?.group;
1433
- if (g?.id)
1434
- seen.add(String(g.id));
1435
- }
1436
- return [...seen].map((id) => String(BigInt(id) - 1n));
1437
- }
1438
- catch {
1439
- return [];
1440
- }
1441
- }
1442
1462
  // ---------- wf-form group(字段分组,对齐 eb 表)----------
1443
1463
  // 流程表单字段分组(基本信息/系统设置等折叠容器)。三步接口(module=workflow,实测可用):
1444
1464
  // ① saveFieldGroup 建分组 → data.group.id(formFieldGroup记录id) + refFieldId(layout Collapse id)
@@ -1491,13 +1511,36 @@ export function registerWorkflowFormCommands(program) {
1491
1511
  } };
1492
1512
  walk(configs);
1493
1513
  const resolveField = (ref) => fieldByName.get(String(ref)) || fieldByName.get(String(ref)) || null;
1494
- // ① 建全部分组(saveFieldGroup)
1514
+ // ① 建全部分组(saveFieldGroup,去重:同名复用避免重跑建重复分组——saveFieldGroup 不幂等)
1515
+ const existingByName = new Map();
1516
+ try {
1517
+ const gr = await client.post(`${baseUrl}${ROUTE}/field/manage/getFieldGroupPage`, { pageNo: 1, pageSize: 100, module: MODULE, formFieldSearchEntity: { formId }, customParam: {} }, ebb);
1518
+ for (const f of (gr?.data?.pageDatas?.result || [])) {
1519
+ const g = f?.formField?.group;
1520
+ if (g?.id && g?.groupName && !existingByName.has(g.groupName)) {
1521
+ existingByName.set(g.groupName, { groupId: String(g.id), refFieldId: String(BigInt(g.id) - 1n) });
1522
+ }
1523
+ }
1524
+ }
1525
+ catch { /* 忽略,按无现有处理 */ }
1495
1526
  const created = [];
1496
1527
  for (const [gname, fieldRefs] of entries) {
1497
- const sr = await client.post(`${baseUrl}${ROUTE}/field/manage/saveFieldGroup`, { module: MODULE, formFieldGroup: { groupName: gname, formId }, customParam: {} }, ebb);
1498
- if (sr?.code !== 200 || !sr?.status)
1499
- throw new Error(`建分组「${gname}」失败: ${sr?.msg || '未知错误'}`);
1500
- const g = sr?.data?.group || {};
1528
+ let groupId = '', refFieldId = '';
1529
+ const ex = existingByName.get(gname);
1530
+ if (ex) {
1531
+ groupId = ex.groupId;
1532
+ refFieldId = ex.refFieldId;
1533
+ console.error(` 分组复用: ${gname} groupId=${groupId} refFieldId=${refFieldId}`);
1534
+ }
1535
+ else {
1536
+ const sr = await client.post(`${baseUrl}${ROUTE}/field/manage/saveFieldGroup`, { module: MODULE, formFieldGroup: { groupName: gname, formId }, customParam: {} }, ebb);
1537
+ if (sr?.code !== 200 || !sr?.status)
1538
+ throw new Error(`建分组「${gname}」失败: ${sr?.msg || '未知错误'}`);
1539
+ const g = sr?.data?.group || {};
1540
+ groupId = String(g.id || '');
1541
+ refFieldId = String(g.refFieldId || '');
1542
+ console.error(` 分组已建: ${gname} groupId=${groupId} refFieldId=${refFieldId}`);
1543
+ }
1501
1544
  const fieldIds = [];
1502
1545
  const missing = [];
1503
1546
  for (const ref of (fieldRefs || [])) {
@@ -1509,8 +1552,7 @@ export function registerWorkflowFormCommands(program) {
1509
1552
  }
1510
1553
  if (missing.length)
1511
1554
  console.error(` ⚠ 分组「${gname}」未找到字段: ${missing.join(', ')}`);
1512
- created.push({ name: gname, groupId: String(g.id || ''), refFieldId: String(g.refFieldId || ''), fieldIds });
1513
- console.error(` 分组已建: ${gname} groupId=${g.id} refFieldId=${g.refFieldId}${fieldIds.length ? ` 字段×${fieldIds.length}` : '(空分组)'}`);
1555
+ created.push({ name: gname, groupId, refFieldId, fieldIds });
1514
1556
  }
1515
1557
  // ② 分配字段(updateFormFieldGroup)
1516
1558
  for (const grp of created) {
@@ -1675,55 +1717,10 @@ export function registerWorkflowFormCommands(program) {
1675
1717
  throw new Error('无法加载认证');
1676
1718
  const client = new ApiClient(auth);
1677
1719
  const baseUrl = auth.getBaseUrl();
1678
- const ebb = { ebbusinessid: formId };
1679
- const meta = await getLayoutMeta(client, baseUrl, formId);
1680
- if (!meta.layoutId)
1681
- throw new Error('无法获取 layoutId');
1682
- // 保留字段分组:getConfig/initLayout 带 groupFieldIds,否则 Collapse 折叠被冲掉
1683
- const groupIds = await fetchFormGroups(client, baseUrl, formId);
1684
- const cfgRes = await client.post(`${baseUrl}${ROUTE}/view/relate/getConfig`, {
1685
- formId, module: MODULE, apiModule: 'workflow/form', layoutId: meta.layoutId,
1686
- customParam: { terminal: '', objId: formId }, initMode: 5, groupFieldIds: groupIds,
1687
- designerParam: { sourceModule: MODULE },
1688
- }, ebb);
1689
- // 流程表单 FLOW 布局 getConfig 默认把业务字段 readOnly=true(锁死),init 时强制可编辑:
1690
- // 业务字段 readOnly=false / disableEdit=false(除非 --read-only 显式锁);明细/系统字段/Collapse 分组容器保留。
1691
- const configs = normalizeDetailTableChildren((cfgRes?.data?.configs || []).map((c) => {
1692
- const isBusiness = !c.system && c.componentKey !== 'DataTable' && c.type !== 'DetailTable' && c.type !== 'Collapse' && c.componentKey !== 'Fold';
1693
- const out = { ...c, display: c.system ? c.display : true, show: c.system ? c.show : true };
1694
- if (isBusiness) {
1695
- out.readOnly = o.readOnly === true;
1696
- out.disableEdit = false;
1697
- if (o.required !== undefined)
1698
- out.required = o.required;
1699
- if (o.singleRow !== undefined)
1700
- out.singleRow = o.singleRow;
1701
- }
1702
- return out;
1703
- }));
1704
- // 合并运行时已保存的 singleRow(getConfig 不带,防 initLayout 冲掉独占一行)
1705
- const initSrSet = await fetchRuntimeSingleRows(client, baseUrl, formId);
1706
- mergeRuntimeSingleRow(configs, initSrSet);
1707
- // 落单/孤字段占满行(对齐 eb 表):奇数个最后一个 + 被 singleRow 挤落单的前一字段自动 singleRow
1708
- const finalConfigs = applyLoneSingleRows(configs);
1709
- const styleTemplateId = o.styleTemplate
1710
- || (meta.layoutType === 'FLOW' ? '820000000000000001' : '700000000000000008');
1711
- const initRes = await client.post(`${baseUrl}${ROUTE}/view/relate/initLayout`, {
1712
- formId, module: MODULE, apiModule: 'workflow/form', layoutId: meta.layoutId,
1713
- generateCollapse: o.generateCollapse !== false,
1714
- styleTemplateId: /^\d+$/.test(styleTemplateId) ? styleTemplateId : styleTemplateId, styleTemplateType: 1,
1715
- configs: finalConfigs, addLayout: false, ecodeType: ['js', 'css'], initMode: '1',
1716
- groupFieldIds: groupIds,
1717
- customParam: { terminal: '', objId: formId },
1718
- formLayoutMultiple: { isSys: true, id: meta.layoutMultiId, name: meta.layoutName, terminalType: 'PC', layoutType: meta.layoutType },
1719
- designerParam: { onlyLayout: false },
1720
- }, ebb);
1721
- if (initRes?.code !== 200 || !initRes?.status)
1722
- throw new Error(`initLayout 失败: ${initRes?.msg}`);
1723
- const comps = initRes?.data?.pageLayouts?.[0]?.comps || initRes?.data?.formLayout?.pageLayouts?.[0]?.comps || [];
1720
+ const r = await initWorkflowFormLayout(client, baseUrl, formId, o);
1724
1721
  console.log(opts.json
1725
- ? JSON.stringify({ success: true, formId, layoutType: meta.layoutType, styleTemplateId, comps: comps.length }, null, 2)
1726
- : `布局已初始化: 类型=${meta.layoutType} 模板=${styleTemplateId} 组件=${comps.length}`);
1722
+ ? JSON.stringify({ success: true, formId, layoutType: r.layoutType, styleTemplateId: r.styleTemplateId, comps: r.comps }, null, 2)
1723
+ : `布局已初始化: 类型=${r.layoutType} 模板=${r.styleTemplateId} 组件=${r.comps}`);
1727
1724
  }
1728
1725
  catch (e) {
1729
1726
  console.error(`初始化布局失败: ${e.message}`);