openyida 2026.9.7 → 2026.9.8

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.
@@ -16,11 +16,33 @@ const {
16
16
  resolveConnectorMode,
17
17
  validateAssignments,
18
18
  } = require('./integration-process-builder');
19
- const { buildAddDataChildList, assertAssignmentFieldsExist } = require('./integration-view-builder');
19
+ const {
20
+ buildAddDataChildList,
21
+ buildSubTableAddDataChildList,
22
+ resolveTableFieldLabel,
23
+ listTableFieldSummaries,
24
+ buildSubTableRelativeItem,
25
+ assertAssignmentFieldsExist,
26
+ } = require('./integration-view-builder');
20
27
  const {
21
28
  lookupConnectorPreset,
22
29
  buildConnectorRulesFromInputs,
23
30
  } = require('./connector-presets');
31
+ const {
32
+ normalizeSourceFormType,
33
+ resolveSourceFormUuid,
34
+ resolveDataSourceFormName,
35
+ resolveDataSourceFormType,
36
+ resolveDataOriginalType,
37
+ resolveDataFormItemType,
38
+ isSubTableOriginalType,
39
+ resolveDataQueryField,
40
+ toRuntimeDataQueryField,
41
+ toDesignerDataQueryField,
42
+ normalizeDataQueryFieldName,
43
+ resolveDesignerDataQueryField,
44
+ resolveDataQueryFieldName,
45
+ } = require('./integration-form-metadata');
24
46
  const { t } = require('../core/i18n');
25
47
 
26
48
  function readIntegrationSpec(filePath) {
@@ -54,7 +76,7 @@ function mapSpecEventTypes(spec, fallbackEvents) {
54
76
  return mapEventTypes(getSpecEventInput(spec, fallbackEvents));
55
77
  }
56
78
 
57
- function validateIntegrationSpec(spec, fallbackEvents = ['insert']) {
79
+ function validateIntegrationSpec(spec, fallbackEvents = ['insert'], options = {}) {
58
80
  if (!spec || typeof spec !== 'object' || Array.isArray(spec)) {
59
81
  throw new Error('--spec must point to a JSON object');
60
82
  }
@@ -81,14 +103,27 @@ function validateIntegrationSpec(spec, fallbackEvents = ['insert']) {
81
103
  }
82
104
  aliases.add(explicitAlias);
83
105
  }
84
- if (type === 'dataRetrieve' && !(node.formUuid || node.sourceId)) {
85
- throw new Error('dataRetrieve node formUuid is required');
106
+ if (type === 'dataRetrieve') {
107
+ if (isSubTableRetrieve(node)) {
108
+ if (!getSourceAlias(node) || !node.subSourceId) {
109
+ throw new Error('dataRetrieve sub_table node source and subSourceId are required');
110
+ }
111
+ } else if (!(node.formUuid || node.sourceId)) {
112
+ throw new Error('dataRetrieve node formUuid is required');
113
+ }
114
+ }
115
+ if (type === 'getSelf' || type === 'dataRetrieve') {
116
+ resolveDeclaredSourceFormType(node);
86
117
  }
87
118
  if (type === 'dataRetrieve' && asArray(node.conditions || node.dataConditions).length === 0) {
88
119
  throw new Error(t('integration.spec_data_retrieve_conditions_required'));
89
120
  }
90
121
  if (type === 'dataCreate') {
91
- if (!(node.formUuid || node.targetFormUuid)) {
122
+ if (isSubTableInsert(node)) {
123
+ if (!getSourceAlias(node) || !node.subFormUuid) {
124
+ throw new Error('dataCreate sub_table node source and subFormUuid are required');
125
+ }
126
+ } else if (!(node.formUuid || node.targetFormUuid)) {
92
127
  throw new Error('dataCreate node formUuid is required');
93
128
  }
94
129
  validateAssignments(asArray(node.assignments), 'dataCreate assignment');
@@ -174,6 +209,7 @@ function validateIntegrationSpec(spec, fallbackEvents = ['insert']) {
174
209
  }
175
210
  }
176
211
  validateNodes(getSpecNodes(spec));
212
+ assertSubTableSources(spec, options.formUuid || options.triggerFormUuid);
177
213
  }
178
214
 
179
215
  function normalizeLogic(logic) {
@@ -188,6 +224,217 @@ function getNodeAlias(node, fallback) {
188
224
  return node.id || node.key || fallback;
189
225
  }
190
226
 
227
+ function isSubTableValue(value) {
228
+ return String(value || '').trim().toLowerCase() === 'sub_table';
229
+ }
230
+
231
+ function isSubTableRetrieve(node = {}) {
232
+ return isSubTableValue(node.originalType);
233
+ }
234
+
235
+ function isSubTableInsert(node = {}) {
236
+ return isSubTableValue(node.insertType);
237
+ }
238
+
239
+ function getSourceAlias(node = {}) {
240
+ return node.source || node.from || '';
241
+ }
242
+
243
+ const SUB_TABLE_SOURCE_TYPES = new Set(['getSelf', 'dataRetrieve']);
244
+
245
+ function getNodeType(node = {}) {
246
+ return normalizeNodeType(node.type || node.componentName);
247
+ }
248
+
249
+ function getDeclaredAlias(node = {}) {
250
+ return node.id || node.key || '';
251
+ }
252
+
253
+ function collectBranchNodes(branch) {
254
+ return branch && (branch.nodes || branch.childNodes || branch.children);
255
+ }
256
+
257
+ function indexSpecNodesByAlias(spec) {
258
+ const byAlias = new Map();
259
+ visitSpecNodes(spec, (node) => {
260
+ const alias = getDeclaredAlias(node);
261
+ if (alias && !byAlias.has(alias)) {
262
+ byAlias.set(alias, node);
263
+ }
264
+ });
265
+ return byAlias;
266
+ }
267
+
268
+ function collectUpstreamAliasMaps(spec) {
269
+ const upstreamByNode = new WeakMap();
270
+ function visit(nodes, inheritedUpstream) {
271
+ const seen = new Map(inheritedUpstream);
272
+ for (const node of asArray(nodes)) {
273
+ if (!node || typeof node !== 'object') {
274
+ continue;
275
+ }
276
+ upstreamByNode.set(node, new Map(seen));
277
+ const type = getNodeType(node);
278
+ if (type === 'route') {
279
+ const branchUpstream = new Map(seen);
280
+ const routeAlias = getDeclaredAlias(node);
281
+ if (routeAlias) {
282
+ branchUpstream.set(routeAlias, node);
283
+ }
284
+ for (const branch of asArray(node.branches || node.conditions)) {
285
+ visit(collectBranchNodes(branch), branchUpstream);
286
+ }
287
+ }
288
+ const alias = getDeclaredAlias(node);
289
+ if (alias) {
290
+ seen.set(alias, node);
291
+ }
292
+ }
293
+ }
294
+ visit(getSpecNodes(spec), new Map());
295
+ return upstreamByNode;
296
+ }
297
+
298
+ function assertSubTableSources(spec, triggerFormUuid) {
299
+ const nodesByAlias = indexSpecNodesByAlias(spec);
300
+ const upstreamByNode = collectUpstreamAliasMaps(spec);
301
+ visitSpecNodes(spec, (node) => {
302
+ if (!isSubTableRetrieve(node) && !isSubTableInsert(node)) {
303
+ return;
304
+ }
305
+ const sourceAlias = getSourceAlias(node);
306
+ if (!sourceAlias) {
307
+ return;
308
+ }
309
+ const sourceNode = nodesByAlias.get(sourceAlias);
310
+ if (!sourceNode) {
311
+ throw new Error(`Unknown integration spec node alias: ${sourceAlias}`);
312
+ }
313
+ const sourceType = getNodeType(sourceNode);
314
+ if (!SUB_TABLE_SOURCE_TYPES.has(sourceType)) {
315
+ throw new Error(
316
+ `sub_table source must be a getSelf or dataRetrieve node: ${sourceAlias}`
317
+ );
318
+ }
319
+ const upstream = upstreamByNode.get(node);
320
+ if (!upstream || !upstream.has(sourceAlias)) {
321
+ throw new Error(`sub_table source must be an upstream node: ${sourceAlias}`);
322
+ }
323
+ resolveSubTableParentFormUuidFromSource(node, sourceNode, triggerFormUuid);
324
+ });
325
+ }
326
+
327
+ function visitSpecNodes(spec, visitor) {
328
+ function visit(nodes) {
329
+ for (const node of asArray(nodes)) {
330
+ if (!node || typeof node !== 'object') {
331
+ continue;
332
+ }
333
+ visitor(node);
334
+ const type = normalizeNodeType(node.type || node.componentName);
335
+ if (type === 'route') {
336
+ for (const branch of asArray(node.branches || node.conditions)) {
337
+ visit(branch && (branch.nodes || branch.childNodes || branch.children));
338
+ }
339
+ }
340
+ }
341
+ }
342
+ visit(getSpecNodes(spec));
343
+ }
344
+
345
+ function findSpecNodeByAlias(spec, alias) {
346
+ if (!alias) {
347
+ return null;
348
+ }
349
+ let found = null;
350
+ visitSpecNodes(spec, (node) => {
351
+ if (!found && (node.id || node.key) === alias) {
352
+ found = node;
353
+ }
354
+ });
355
+ return found;
356
+ }
357
+
358
+ function inferSubTableParentFormUuid(sourceNode, triggerFormUuid) {
359
+ if (!sourceNode) {
360
+ return '';
361
+ }
362
+ if (getNodeType(sourceNode) === 'getSelf') {
363
+ return triggerFormUuid || '';
364
+ }
365
+ return sourceNode.formUuid || sourceNode.sourceId || sourceNode.targetFormUuid || '';
366
+ }
367
+
368
+ function resolveSubTableParentFormUuidFromSource(node, sourceNode, triggerFormUuid) {
369
+ const inferred = inferSubTableParentFormUuid(sourceNode, triggerFormUuid);
370
+ const explicit = node.parentFormUuid ? String(node.parentFormUuid) : '';
371
+ if (explicit && inferred && explicit !== String(inferred)) {
372
+ throw new Error(
373
+ `sub_table parentFormUuid must match the source form: ${explicit} != ${inferred}`
374
+ );
375
+ }
376
+ return explicit || inferred || '';
377
+ }
378
+
379
+ function resolveSubTableParentFormUuid(node, spec, triggerFormUuid) {
380
+ return resolveSubTableParentFormUuidFromSource(
381
+ node,
382
+ findSpecNodeByAlias(spec, getSourceAlias(node)),
383
+ triggerFormUuid
384
+ );
385
+ }
386
+
387
+ function resolveContextSourceNode(node, context) {
388
+ const sourceAlias = getSourceAlias(node);
389
+ if (!sourceAlias || !context.aliasToSpecNode) {
390
+ return null;
391
+ }
392
+ return context.aliasToSpecNode.get(sourceAlias) || null;
393
+ }
394
+
395
+ function resolveSubTableParentFormUuidFromContext(node, context) {
396
+ return resolveSubTableParentFormUuidFromSource(
397
+ node,
398
+ resolveContextSourceNode(node, context),
399
+ context.formUuid
400
+ );
401
+ }
402
+
403
+ function resolveDeclaredSourceFormType(node = {}) {
404
+ const declarations = [
405
+ ['formType', node.formType],
406
+ ['sourceFormType', node.sourceFormType],
407
+ ['originalType', node.originalType],
408
+ ].filter(([field, value]) => value !== undefined
409
+ && value !== null
410
+ && String(value).trim() !== ''
411
+ && !(field === 'originalType' && isSubTableOriginalType(value)))
412
+ .map(([field, value]) => ({
413
+ field,
414
+ rawValue: value,
415
+ normalized: normalizeSourceFormType(value),
416
+ }));
417
+ if (declarations.length === 0) {
418
+ return { rawValue: undefined, normalized: '' };
419
+ }
420
+ const unsupported = declarations.find((declaration) => !declaration.normalized);
421
+ if (unsupported) {
422
+ throw new Error(`Unsupported source form type: ${unsupported.rawValue}`);
423
+ }
424
+ const normalizedTypes = new Set(declarations.map((declaration) => declaration.normalized));
425
+ if (normalizedTypes.size > 1) {
426
+ throw new Error(
427
+ `Conflicting source form types on node: ${declarations.map((declaration) => (
428
+ `${declaration.field}=${declaration.rawValue}`
429
+ )).join(', ')}`
430
+ );
431
+ }
432
+ return {
433
+ rawValue: declarations[0].rawValue,
434
+ normalized: declarations[0].normalized,
435
+ };
436
+ }
437
+
191
438
  function registerNodeId(node, context, prefix) {
192
439
  if (node._nodeId) {
193
440
  return node._nodeId;
@@ -201,6 +448,9 @@ function registerNodeId(node, context, prefix) {
201
448
  node._nodeId = nodeId;
202
449
  context.aliasToNodeId.set(alias, nodeId);
203
450
  context.nodeIdToAlias.set(nodeId, alias);
451
+ if (context.aliasToSpecNode) {
452
+ context.aliasToSpecNode.set(alias, node);
453
+ }
204
454
  return nodeId;
205
455
  }
206
456
 
@@ -273,7 +523,7 @@ function normalizeToUsers(value, fallback = []) {
273
523
  }).filter(Boolean);
274
524
  }
275
525
 
276
- function normalizeDataConditions(conditions, context) {
526
+ function normalizeDataConditions(conditions, context, sourceFormType) {
277
527
  return asArray(conditions).map((condition) => {
278
528
  const bFieldId = condition.bFieldId || condition.fieldId || condition.id;
279
529
  const conditionValue = getFirstNonNullishOwnValue(condition, ['aFieldId', 'value', 'ruleValue']);
@@ -284,9 +534,11 @@ function normalizeDataConditions(conditions, context) {
284
534
  if (!['literal', 'processVar', 'column'].includes(valueType)) {
285
535
  throw new Error(`dataRetrieve condition valueType is unsupported: ${valueType}`);
286
536
  }
537
+ const bFieldName = condition.bFieldName || condition.fieldName || condition.name || condition.label || bFieldId;
538
+ const runtimeFieldId = toRuntimeDataQueryField(resolveNodeRefs(bFieldId, context), sourceFormType);
287
539
  return {
288
- bFieldId: resolveNodeRefs(bFieldId, context),
289
- bFieldName: condition.bFieldName || condition.fieldName || condition.name || condition.label || bFieldId,
540
+ bFieldId: runtimeFieldId,
541
+ bFieldName: normalizeDataQueryFieldName(runtimeFieldId, sourceFormType, bFieldName),
290
542
  aFieldId: resolveNodeRefs(conditionValue.value, context),
291
543
  componentType: condition.componentType || 'TextField',
292
544
  opCode: condition.opCode || condition.op || 'Contain',
@@ -473,19 +725,58 @@ function buildMessageViewNode(node, nodeId, context) {
473
725
  };
474
726
  }
475
727
 
728
+ function resolveRetrieveSourceId(node, context) {
729
+ if (isSubTableRetrieve(node)) {
730
+ return resolveAlias(getSourceAlias(node), context);
731
+ }
732
+ return node.formUuid || node.sourceId || (node.type === 'getSelf' ? context.formUuid : undefined);
733
+ }
734
+
735
+ function resolveRetrieveSourceLabel(node, context, sourceId) {
736
+ if (isSubTableRetrieve(node)) {
737
+ const sourceNode = context.aliasToSpecNode && context.aliasToSpecNode.get(getSourceAlias(node));
738
+ return (sourceNode && (sourceNode.name || sourceNode.formName))
739
+ || node.formName
740
+ || getSourceAlias(node);
741
+ }
742
+ return resolveDataSourceFormName(node, context)
743
+ || node.formName
744
+ || (context.formNamesByUuid && context.formNamesByUuid.get(sourceId))
745
+ || sourceId;
746
+ }
747
+
748
+ function isUsedAsSubTableSource(node, context) {
749
+ const alias = node.id || node.key;
750
+ if (!alias || !context.aliasToSpecNode) {
751
+ return false;
752
+ }
753
+ for (const specNode of context.aliasToSpecNode.values()) {
754
+ if (getSourceAlias(specNode) !== alias) {
755
+ continue;
756
+ }
757
+ if (isSubTableRetrieve(specNode) || isSubTableInsert(specNode)) {
758
+ return true;
759
+ }
760
+ }
761
+ return false;
762
+ }
763
+
476
764
  function buildDataRetrieveProcessNode(node, nodeId, nextNodeId, context) {
477
- const formUuid = node.formUuid || node.sourceId || (node.type === 'getSelf' ? context.formUuid : undefined);
478
- const formName = node.formName || context.formNamesByUuid.get(formUuid) || formUuid;
765
+ const sourceId = resolveRetrieveSourceId(node, context);
766
+ const dataSourceFormType = isSubTableRetrieve(node)
767
+ ? ''
768
+ : resolveDataSourceFormType(node, context);
769
+ const formName = resolveRetrieveSourceLabel(node, context, sourceId);
479
770
  const rawConditions = node.type === 'getSelf' || node.getSelf
480
771
  ? [{
481
- bFieldId: node.queryField || 'pid',
482
- bFieldName: '表单实例ID',
772
+ bFieldId: resolveDataQueryField(node, dataSourceFormType),
773
+ bFieldName: resolveDataQueryFieldName(dataSourceFormType),
483
774
  aFieldId: node.triggerField || '__masterdata_form_inst_id',
484
775
  componentType: 'TextField',
485
776
  opCode: 'Equal',
486
777
  valueType: 'processVar',
487
778
  }]
488
- : normalizeDataConditions(node.conditions || node.dataConditions, context);
779
+ : normalizeDataConditions(node.conditions || node.dataConditions, context, dataSourceFormType);
489
780
  const conditions = rawConditions.length > 0
490
781
  ? buildDataRetrieveCondition(rawConditions, node.logic || node.conditionLogic)
491
782
  : { condition: 'AND', rules: [], ruleId: generateRuleGroupId(), conditionCode: '&&' };
@@ -500,9 +791,11 @@ function buildDataRetrieveProcessNode(node, nodeId, nextNodeId, context) {
500
791
  type: 'single',
501
792
  filterType: 'condition',
502
793
  sort: node.sort || { type: 'none', column: '' },
503
- sourceId: formUuid,
794
+ sourceId,
504
795
  appType: context.appType,
505
- originalType: node.originalType || 'form',
796
+ originalType: isSubTableRetrieve(node)
797
+ ? 'sub_table'
798
+ : resolveDataOriginalType(dataSourceFormType),
506
799
  subSourceId: node.subSourceId || '',
507
800
  condition: conditions,
508
801
  quantity: String(node.quantity || '1'),
@@ -528,7 +821,78 @@ function buildDataRetrieveProcessNode(node, nodeId, nextNodeId, context) {
528
821
  function buildDataRetrieveViewNode(node, nodeId, context) {
529
822
  const processNode = buildDataRetrieveProcessNode(node, nodeId, nodeId, context);
530
823
  const props = processNode.props;
531
- const formName = node.formName || context.formNamesByUuid.get(props.sourceId) || props.sourceId;
824
+ const dataSourceFormType = isSubTableRetrieve(node)
825
+ ? ''
826
+ : resolveDataSourceFormType(node, context);
827
+ const dataSourceFormName = isSubTableRetrieve(node)
828
+ ? ''
829
+ : resolveDataSourceFormName(node, context);
830
+ const formName = resolveRetrieveSourceLabel(node, context, props.sourceId);
831
+ let viewCondition;
832
+ if (node.type === 'getSelf' || node.getSelf) {
833
+ viewCondition = buildDataRetrieveCondition([{
834
+ bFieldId: resolveDesignerDataQueryField(node, dataSourceFormType),
835
+ bFieldName: resolveDataQueryFieldName(dataSourceFormType),
836
+ aFieldId: node.triggerField || '__masterdata_form_inst_id',
837
+ componentType: 'TextField',
838
+ opCode: 'Equal',
839
+ valueType: 'processVar',
840
+ }]);
841
+ } else if (isSubTableRetrieve(node)) {
842
+ viewCondition = props.condition;
843
+ } else {
844
+ viewCondition = {
845
+ ...props.condition,
846
+ rules: (props.condition.rules || []).map((rule) => {
847
+ const designerFieldId = toDesignerDataQueryField(rule.id, dataSourceFormType);
848
+ const designerFieldName = normalizeDataQueryFieldName(
849
+ rule.id,
850
+ dataSourceFormType,
851
+ rule.name,
852
+ );
853
+ return designerFieldId === rule.id && designerFieldName === rule.name
854
+ ? rule
855
+ : {
856
+ ...rule,
857
+ id: designerFieldId,
858
+ name: designerFieldName,
859
+ };
860
+ }),
861
+ };
862
+ }
863
+ let targetItem;
864
+ let relativeItem = {};
865
+ if (isSubTableRetrieve(node)) {
866
+ const parentFormUuid = resolveSubTableParentFormUuidFromContext(node, context);
867
+ const schemaComponents = parentFormUuid
868
+ ? (context.formSchemasByUuid.get(parentFormUuid) || [])
869
+ : [];
870
+ const subTableLabel = node.subSourceLabel
871
+ || resolveTableFieldLabel(schemaComponents, props.subSourceId, props.subSourceId);
872
+ targetItem = {
873
+ deep: 0,
874
+ value: props.sourceId,
875
+ label: formName,
876
+ };
877
+ relativeItem = buildSubTableRelativeItem(props.subSourceId, subTableLabel);
878
+ } else {
879
+ const schemaComponents = context.formSchemasByUuid.get(props.sourceId) || [];
880
+ const tableFields = isUsedAsSubTableSource(node, context)
881
+ ? listTableFieldSummaries(schemaComponents)
882
+ : [];
883
+ targetItem = {
884
+ appType: context.appType,
885
+ appName: '',
886
+ formItem: {
887
+ formType: resolveDataFormItemType(dataSourceFormType),
888
+ advanceProc: 'n',
889
+ formUuid: props.sourceId,
890
+ title: dataSourceFormName || formName,
891
+ fields: tableFields.length > 0 ? tableFields : null,
892
+ hasTableField: tableFields.length > 0 ? true : null,
893
+ },
894
+ };
895
+ }
532
896
  return {
533
897
  componentName: 'GetSingleDataNode',
534
898
  id: nodeId,
@@ -542,22 +906,11 @@ function buildDataRetrieveViewNode(node, nodeId, context) {
542
906
  originalType: props.originalType,
543
907
  appType: context.appType,
544
908
  sourceId: props.sourceId,
545
- targetItem: {
546
- appType: context.appType,
547
- appName: '',
548
- formItem: {
549
- formType: 'receipt',
550
- advanceProc: 'n',
551
- formUuid: props.sourceId,
552
- title: formName,
553
- fields: null,
554
- hasTableField: null,
555
- },
556
- },
909
+ targetItem,
557
910
  subSourceId: props.subSourceId,
558
- relativeItem: {},
911
+ relativeItem,
559
912
  filterType: 'condition',
560
- condition: props.condition,
913
+ condition: viewCondition,
561
914
  sort: props.sort,
562
915
  rulesFilter: [],
563
916
  outputs: [],
@@ -570,24 +923,63 @@ function buildDataRetrieveViewNode(node, nodeId, context) {
570
923
  };
571
924
  }
572
925
 
573
- function buildDataCreateProcessNode(node, nodeId, nextNodeId, context) {
926
+ function resolveCreateTarget(node, context) {
927
+ if (isSubTableInsert(node)) {
928
+ const sourceAlias = getSourceAlias(node);
929
+ const sourceNodeId = resolveAlias(sourceAlias, context);
930
+ const sourceNode = resolveContextSourceNode(node, context) || {};
931
+ const parentFormUuid = resolveSubTableParentFormUuidFromContext(node, context);
932
+ const parentFormName = resolveDataSourceFormName(sourceNode, context)
933
+ || (parentFormUuid && context.formNamesByUuid.get(parentFormUuid))
934
+ || sourceNode.formName
935
+ || parentFormUuid;
936
+ const formName = sourceNode.name
937
+ || sourceNode.formName
938
+ || node.formName
939
+ || parentFormName
940
+ || sourceAlias;
941
+ return {
942
+ formUuid: sourceNodeId,
943
+ insertType: 'sub_table',
944
+ subFormUuid: node.subFormUuid,
945
+ sourceId: '',
946
+ parentFormUuid,
947
+ parentFormName,
948
+ parentFormType: resolveDataSourceFormType(sourceNode, context),
949
+ formName,
950
+ subFormLabel: node.subSourceLabel || node.subFormLabel || node.subFormUuid,
951
+ isSubTable: true,
952
+ };
953
+ }
574
954
  const formUuid = node.formUuid || node.targetFormUuid;
575
- const formName = node.formName || context.formNamesByUuid.get(formUuid) || formUuid;
955
+ return {
956
+ formUuid,
957
+ insertType: node.insertType || 'form',
958
+ subFormUuid: node.subFormUuid || '',
959
+ sourceId: node.sourceId || '',
960
+ parentFormUuid: formUuid,
961
+ formName: node.formName || context.formNamesByUuid.get(formUuid) || formUuid,
962
+ isSubTable: false,
963
+ };
964
+ }
965
+
966
+ function buildDataCreateProcessNode(node, nodeId, nextNodeId, context) {
967
+ const target = resolveCreateTarget(node, context);
576
968
  const assignments = normalizeAssignments(node.assignments, context);
577
969
  return {
578
970
  name: { zh_CN: node.name || '新增数据', en_US: '' },
579
- description: configuredDescription(node, t('integration.spec_desc_create', formName, assignments.length)),
971
+ description: configuredDescription(node, t('integration.spec_desc_create', target.formName, assignments.length)),
580
972
  type: 'dataCreate',
581
973
  nodeId,
582
974
  prevId: '',
583
975
  nextId: [nextNodeId],
584
976
  props: {
585
- formUuid,
977
+ formUuid: target.formUuid,
586
978
  appType: context.appType,
587
- subFormUuid: node.subFormUuid || '',
588
- insertType: node.insertType || 'form',
979
+ subFormUuid: target.subFormUuid || '',
980
+ insertType: target.insertType,
589
981
  type: node.createType || 'single',
590
- sourceId: node.sourceId || '',
982
+ sourceId: target.sourceId,
591
983
  assignments: buildDataCreateAssignments(assignments),
592
984
  },
593
985
  childNodes: [],
@@ -595,18 +987,25 @@ function buildDataCreateProcessNode(node, nodeId, nextNodeId, context) {
595
987
  }
596
988
 
597
989
  function buildDataCreateViewNode(node, nodeId, context) {
598
- const formUuid = node.formUuid || node.targetFormUuid;
599
- const formName = node.formName || context.formNamesByUuid.get(formUuid) || formUuid;
600
- const schemaComponents = context.formSchemasByUuid.get(formUuid) || [];
601
- const childList = buildAddDataChildList(schemaComponents);
990
+ const target = resolveCreateTarget(node, context);
991
+ const schemaComponents = context.formSchemasByUuid.get(target.parentFormUuid) || [];
992
+ const inputChildList = target.isSubTable
993
+ ? buildSubTableAddDataChildList(schemaComponents, target.subFormUuid)
994
+ : buildAddDataChildList(schemaComponents);
995
+ const columnList = inputChildList.filter((item) => item.componentName !== 'TableField');
602
996
  const normalizedAssignments = normalizeAssignments(node.assignments, context);
603
- assertAssignmentFieldsExist(normalizedAssignments, childList);
604
- const componentOptionMap = {};
605
- childList.forEach((item) => {
606
- componentOptionMap[item.fieldId] = '[]';
997
+ assertAssignmentFieldsExist(normalizedAssignments, columnList);
998
+ const inputOptionMap = {};
999
+ inputChildList.forEach((item) => {
1000
+ inputOptionMap[item.fieldId] = item.componentOption || '[]';
1001
+ });
1002
+ const columnOptionMap = {};
1003
+ columnList.forEach((item) => {
1004
+ columnOptionMap[item.fieldId] = item.componentOption || '[]';
607
1005
  });
608
1006
  const assignmentRules = normalizedAssignments.map((assignment) => {
609
- const matchedField = childList.find((item) => item.fieldId === assignment.column);
1007
+ const matchedField = columnList.find((item) => item.fieldId === assignment.column);
1008
+ const label = matchedField ? matchedField.label : assignment.column;
610
1009
  return {
611
1010
  name: assignment.column,
612
1011
  componentName: matchedField ? matchedField.componentName : 'TextField',
@@ -614,8 +1013,9 @@ function buildDataCreateViewNode(node, nodeId, context) {
614
1013
  value: assignment.value,
615
1014
  required: false,
616
1015
  ruleId: generateDataRuleId(),
617
- componentOption: '[]',
618
- label: matchedField ? matchedField.label : assignment.column,
1016
+ componentOption: matchedField && matchedField.componentOption ? matchedField.componentOption : '[]',
1017
+ label,
1018
+ valueLabel: label,
619
1019
  componentProps: {
620
1020
  defaultDataSource: {},
621
1021
  relateAppType: '',
@@ -624,7 +1024,58 @@ function buildDataCreateViewNode(node, nodeId, context) {
624
1024
  },
625
1025
  };
626
1026
  });
627
- const description = node.description || `在[${formName}]中新增 ${assignmentRules.length} 个字段`;
1027
+ const description = node.description
1028
+ || (target.isSubTable
1029
+ ? `在 [${target.formName}] 中新增数据`
1030
+ : `在[${target.formName}]中新增 ${assignmentRules.length} 个字段`);
1031
+ const addDataRules = {
1032
+ description,
1033
+ formUuid: target.formUuid,
1034
+ appType: context.appType,
1035
+ insertType: target.insertType,
1036
+ type: node.createType || 'single',
1037
+ subFormUuid: target.subFormUuid || '',
1038
+ sourceId: target.sourceId,
1039
+ assignments: assignmentRules,
1040
+ inputs: { childList: inputChildList, componentOptionMap: inputOptionMap },
1041
+ rules: {
1042
+ childList: columnList,
1043
+ componentOptionMap: columnOptionMap,
1044
+ ruleId: generateDataRuleId(),
1045
+ rules: assignmentRules,
1046
+ },
1047
+ };
1048
+ if (target.isSubTable) {
1049
+ const tableFields = listTableFieldSummaries(schemaComponents);
1050
+ const parentFormName = target.parentFormName || target.formName;
1051
+ const targetItem = {
1052
+ deep: 0,
1053
+ value: target.formUuid,
1054
+ label: target.formName,
1055
+ appType: context.appType,
1056
+ appName: '',
1057
+ };
1058
+ if (target.parentFormUuid) {
1059
+ targetItem.formItem = {
1060
+ formType: resolveDataFormItemType(target.parentFormType),
1061
+ advanceProc: 'n',
1062
+ formUuid: target.parentFormUuid,
1063
+ title: parentFormName,
1064
+ fields: null,
1065
+ hasTableField: tableFields.length > 0 ? true : null,
1066
+ };
1067
+ addDataRules.formItem = targetItem.formItem;
1068
+ }
1069
+ addDataRules.targetItem = targetItem;
1070
+ addDataRules.crossForm = targetItem;
1071
+ addDataRules.relativeItem = buildSubTableRelativeItem(
1072
+ target.subFormUuid,
1073
+ resolveTableFieldLabel(schemaComponents, target.subFormUuid, target.subFormLabel)
1074
+ );
1075
+ addDataRules.relativeList = [addDataRules.relativeItem];
1076
+ addDataRules.subFormItem = addDataRules.relativeItem;
1077
+ addDataRules.subFormName = addDataRules.relativeItem.label;
1078
+ }
628
1079
  return {
629
1080
  componentName: 'AddDataNode',
630
1081
  id: nodeId,
@@ -632,23 +1083,7 @@ function buildDataCreateViewNode(node, nodeId, context) {
632
1083
  nodeName: 'AddDataNode',
633
1084
  name: node.name || '新增数据',
634
1085
  description,
635
- addDataRules: {
636
- description,
637
- formUuid,
638
- appType: context.appType,
639
- insertType: node.insertType || 'form',
640
- type: node.createType || 'single',
641
- subFormUuid: node.subFormUuid || '',
642
- sourceId: node.sourceId || '',
643
- assignments: assignmentRules,
644
- inputs: { childList, componentOptionMap },
645
- rules: {
646
- childList,
647
- componentOptionMap,
648
- ruleId: generateDataRuleId(),
649
- rules: assignmentRules,
650
- },
651
- },
1086
+ addDataRules,
652
1087
  },
653
1088
  title: node.name || '新增数据',
654
1089
  };
@@ -1172,14 +1607,24 @@ function buildFinishNodes(endNodeId) {
1172
1607
 
1173
1608
  function buildSpecProcessAndViewJson(options) {
1174
1609
  const spec = options.spec || {};
1175
- validateIntegrationSpec(spec, options.formEventTypes || ['insert']);
1610
+ validateIntegrationSpec(spec, options.formEventTypes || ['insert'], {
1611
+ formUuid: options.formUuid,
1612
+ });
1176
1613
  const nodes = getSpecNodes(spec);
1614
+ // Validate all source declarations, including duplicate UUIDs, before any
1615
+ // caller can serialize the generated payload for a remote write.
1616
+ collectDataSourceFormDescriptors(spec, options.formUuid);
1177
1617
 
1178
1618
  const context = {
1179
1619
  appType: options.appType,
1180
1620
  formUuid: options.formUuid,
1181
1621
  flowName: options.flowName || '',
1182
1622
  defaultFormEventTypes: options.formEventTypes || ['insert'],
1623
+ formEventTypes: mapSpecEventTypes(spec, options.formEventTypes || ['insert']),
1624
+ defaultFormType: options.dataFormType || options.formType || '',
1625
+ defaultFormName: options.dataFormName || options.dataSourceFormName || options.formName || '',
1626
+ dataSourceForm: options.dataSourceForm || null,
1627
+ dataSourceFormsByUuid: options.dataSourceFormsByUuid || new Map(),
1183
1628
  defaultNotificationTitle: options.notificationTitle,
1184
1629
  defaultNotificationContent: options.notificationContent,
1185
1630
  defaultToUsers: options.toUsers || [],
@@ -1190,6 +1635,7 @@ function buildSpecProcessAndViewJson(options) {
1190
1635
  processCode: options.processCode,
1191
1636
  aliasToNodeId: new Map(),
1192
1637
  nodeIdToAlias: new Map(),
1638
+ aliasToSpecNode: new Map(),
1193
1639
  generatedIndex: 1,
1194
1640
  };
1195
1641
  const canvasId = options.canvasId || generateNodeId();
@@ -1226,25 +1672,74 @@ function buildSpecProcessAndViewJson(options) {
1226
1672
  };
1227
1673
  }
1228
1674
 
1229
- function collectAddDataFormUuids(spec) {
1230
- const result = new Set();
1675
+ function collectDataSourceFormDescriptors(spec, triggerFormUuid) {
1676
+ const result = new Map();
1677
+
1231
1678
  function visit(nodes) {
1232
1679
  for (const node of asArray(nodes)) {
1680
+ if (!node || typeof node !== 'object') {
1681
+ continue;
1682
+ }
1233
1683
  const type = normalizeNodeType(node.type || node.componentName);
1234
- if (type === 'dataCreate') {
1235
- const formUuid = node.formUuid || node.targetFormUuid;
1684
+ if (type === 'getSelf' || type === 'dataRetrieve') {
1685
+ const declaredType = resolveDeclaredSourceFormType(node);
1686
+ const formUuid = resolveSourceFormUuid({ ...node, type }, { formUuid: triggerFormUuid });
1236
1687
  if (formUuid) {
1237
- result.add(formUuid);
1688
+ const key = String(formUuid);
1689
+ const existing = result.get(key) || { formUuid: key };
1690
+ if (declaredType.normalized) {
1691
+ if (existing.formType
1692
+ && normalizeSourceFormType(existing.formType) !== declaredType.normalized) {
1693
+ throw new Error(
1694
+ `Conflicting source form types for ${key}: ${existing.formType} and ${declaredType.rawValue}`
1695
+ );
1696
+ }
1697
+ existing.formType = declaredType.normalized;
1698
+ }
1699
+ if (node.formName || node.sourceFormName || node.dataFormName) {
1700
+ existing.formName = node.formName || node.sourceFormName || node.dataFormName;
1701
+ }
1702
+ result.set(key, existing);
1238
1703
  }
1239
1704
  }
1240
1705
  if (type === 'route') {
1241
1706
  for (const branch of asArray(node.branches || node.conditions)) {
1242
- visit(branch.nodes || branch.childNodes || branch.children);
1707
+ visit(branch && (branch.nodes || branch.childNodes || branch.children));
1243
1708
  }
1244
1709
  }
1245
1710
  }
1246
1711
  }
1712
+
1247
1713
  visit(getSpecNodes(spec));
1714
+ return Array.from(result.values());
1715
+ }
1716
+
1717
+ function collectAddDataFormUuids(spec, triggerFormUuid, options = {}) {
1718
+ const kind = options.kind || 'all';
1719
+ const result = new Set();
1720
+ visitSpecNodes(spec, (node) => {
1721
+ const type = normalizeNodeType(node.type || node.componentName);
1722
+ if (type !== 'dataCreate') {
1723
+ return;
1724
+ }
1725
+ if (isSubTableInsert(node)) {
1726
+ if (kind === 'direct') {
1727
+ return;
1728
+ }
1729
+ const parentFormUuid = resolveSubTableParentFormUuid(node, spec, triggerFormUuid);
1730
+ if (parentFormUuid) {
1731
+ result.add(parentFormUuid);
1732
+ }
1733
+ return;
1734
+ }
1735
+ if (kind === 'sub_table') {
1736
+ return;
1737
+ }
1738
+ const formUuid = node.formUuid || node.targetFormUuid;
1739
+ if (formUuid) {
1740
+ result.add(formUuid);
1741
+ }
1742
+ });
1248
1743
  return Array.from(result);
1249
1744
  }
1250
1745
 
@@ -1294,6 +1789,7 @@ module.exports = {
1294
1789
  validateIntegrationSpec,
1295
1790
  buildSpecProcessAndViewJson,
1296
1791
  collectAddDataFormUuids,
1792
+ collectDataSourceFormDescriptors,
1297
1793
  collectInitiateApprovalFormUuids,
1298
1794
  hasSpecNodeType,
1299
1795
  _private: {
@@ -1301,6 +1797,7 @@ module.exports = {
1301
1797
  mapSpecEventTypes,
1302
1798
  normalizeNodeType,
1303
1799
  resolveNodeRefs,
1800
+ resolveDesignerSourceRefs,
1304
1801
  buildSpecNodeList,
1305
1802
  buildDataUpdateRules,
1306
1803
  },