openxiangda 2.55.1 → 2.59.2

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 (152) hide show
  1. package/README.md +1 -0
  2. package/dist/browser/AuthoritativeSelector.d.ts +3 -2
  3. package/dist/browser/AuthoritativeSelector.d.ts.map +1 -1
  4. package/dist/browser/AuthoritativeSelector.js +20 -11
  5. package/dist/browser/AuthoritativeSelector.js.map +1 -1
  6. package/dist/browser/components/platform-fields/DateTimeField.d.ts +1 -1
  7. package/dist/browser/components/platform-fields/DateTimeField.d.ts.map +1 -1
  8. package/dist/browser/components/platform-fields/DateTimeField.js +15 -8
  9. package/dist/browser/components/platform-fields/DateTimeField.js.map +1 -1
  10. package/dist/browser/components/platform-fields/MobileDateTimeField.d.ts +1 -1
  11. package/dist/browser/components/platform-fields/MobileDateTimeField.d.ts.map +1 -1
  12. package/dist/browser/components/platform-fields/MobileDateTimeField.js +5 -3
  13. package/dist/browser/components/platform-fields/MobileDateTimeField.js.map +1 -1
  14. package/dist/browser/components/platform-fields/MobileZonedDateTimeField.js +2 -2
  15. package/dist/browser/components/platform-fields/MobileZonedDateTimeField.js.map +1 -1
  16. package/dist/browser/components/platform-fields/ResourceReferenceField.d.ts +3 -2
  17. package/dist/browser/components/platform-fields/ResourceReferenceField.d.ts.map +1 -1
  18. package/dist/browser/components/platform-fields/ResourceReferenceField.js +3 -3
  19. package/dist/browser/components/platform-fields/ResourceReferenceField.js.map +1 -1
  20. package/dist/browser/components/platform-fields/SubtableField.d.ts +11 -2
  21. package/dist/browser/components/platform-fields/SubtableField.d.ts.map +1 -1
  22. package/dist/browser/components/platform-fields/SubtableField.js +55 -24
  23. package/dist/browser/components/platform-fields/SubtableField.js.map +1 -1
  24. package/dist/browser/components/platform-fields/ZonedDateTimeField.js +2 -2
  25. package/dist/browser/components/platform-fields/ZonedDateTimeField.js.map +1 -1
  26. package/dist/browser/components/platform-fields/reference-binding-change.d.ts +6 -0
  27. package/dist/browser/components/platform-fields/reference-binding-change.d.ts.map +1 -0
  28. package/dist/browser/components/platform-fields/reference-binding-change.js +32 -0
  29. package/dist/browser/components/platform-fields/reference-binding-change.js.map +1 -0
  30. package/dist/browser/components/platform-fields/user-candidate-context.d.ts +3 -0
  31. package/dist/browser/components/platform-fields/user-candidate-context.d.ts.map +1 -1
  32. package/dist/browser/components/platform-fields/user-candidate-context.js +12 -4
  33. package/dist/browser/components/platform-fields/user-candidate-context.js.map +1 -1
  34. package/dist/browser/components/platform-fields/zoned-date-time.d.ts +4 -0
  35. package/dist/browser/components/platform-fields/zoned-date-time.d.ts.map +1 -1
  36. package/dist/browser/components/platform-fields/zoned-date-time.js +9 -1
  37. package/dist/browser/components/platform-fields/zoned-date-time.js.map +1 -1
  38. package/dist/browser/components/resource/GeneratedResourceCrud.d.ts.map +1 -1
  39. package/dist/browser/components/resource/GeneratedResourceCrud.js +2 -2
  40. package/dist/browser/components/resource/GeneratedResourceCrud.js.map +1 -1
  41. package/dist/browser/components/resource/GeneratedResourceForm.js +2 -2
  42. package/dist/browser/components/resource/GeneratedResourceForm.js.map +1 -1
  43. package/dist/browser/components/resource/ResourceFormFrame.d.ts +1 -1
  44. package/dist/browser/components/resource/ResourceFormFrame.d.ts.map +1 -1
  45. package/dist/browser/components/resource/ResourceFormFrame.js +12 -1
  46. package/dist/browser/components/resource/ResourceFormFrame.js.map +1 -1
  47. package/dist/browser/components/resource/SurfaceFields.d.ts +5 -1
  48. package/dist/browser/components/resource/SurfaceFields.d.ts.map +1 -1
  49. package/dist/browser/components/resource/SurfaceFields.js +13 -5
  50. package/dist/browser/components/resource/SurfaceFields.js.map +1 -1
  51. package/dist/browser/components/resource/generated-resource-definition.d.ts +1 -0
  52. package/dist/browser/components/resource/generated-resource-definition.d.ts.map +1 -1
  53. package/dist/browser/components/resource/resource-field-selection.d.ts +1 -0
  54. package/dist/browser/components/resource/resource-field-selection.d.ts.map +1 -1
  55. package/dist/browser/components/resource/resource-form-operations.d.ts.map +1 -1
  56. package/dist/browser/components/resource/resource-form-operations.js +3 -2
  57. package/dist/browser/components/resource/resource-form-operations.js.map +1 -1
  58. package/dist/browser/components/resource/useResourceFormDrafts.d.ts.map +1 -1
  59. package/dist/browser/components/workflow/StandardWorkflowPages.d.ts +15 -3
  60. package/dist/browser/components/workflow/StandardWorkflowPages.d.ts.map +1 -1
  61. package/dist/browser/components/workflow/StandardWorkflowPages.js +80 -20
  62. package/dist/browser/components/workflow/StandardWorkflowPages.js.map +1 -1
  63. package/dist/browser/components/workflow/WorkflowDelegationManager.js +13 -8
  64. package/dist/browser/components/workflow/WorkflowDelegationManager.js.map +1 -1
  65. package/dist/browser/components/workflow/WorkflowDiagram.d.ts.map +1 -1
  66. package/dist/browser/components/workflow/WorkflowDiagram.js +5 -2
  67. package/dist/browser/components/workflow/WorkflowDiagram.js.map +1 -1
  68. package/dist/browser/components/workflow/WorkflowFlowCanvas.d.ts.map +1 -1
  69. package/dist/browser/components/workflow/WorkflowFlowCanvas.js +2 -1
  70. package/dist/browser/components/workflow/WorkflowFlowCanvas.js.map +1 -1
  71. package/dist/browser/components/workflow/WorkflowNodeCard.d.ts +1 -1
  72. package/dist/browser/components/workflow/WorkflowNodeCard.d.ts.map +1 -1
  73. package/dist/browser/components/workflow/WorkflowNodeCard.js +7 -5
  74. package/dist/browser/components/workflow/WorkflowNodeCard.js.map +1 -1
  75. package/dist/browser/components/workflow/WorkflowNodeConfigurationEditor.d.ts.map +1 -1
  76. package/dist/browser/components/workflow/WorkflowNodeConfigurationEditor.js +14 -11
  77. package/dist/browser/components/workflow/WorkflowNodeConfigurationEditor.js.map +1 -1
  78. package/dist/browser/components/workflow/WorkflowTaskForm.d.ts +3 -1
  79. package/dist/browser/components/workflow/WorkflowTaskForm.d.ts.map +1 -1
  80. package/dist/browser/components/workflow/WorkflowTaskForm.js +17 -8
  81. package/dist/browser/components/workflow/WorkflowTaskForm.js.map +1 -1
  82. package/dist/browser/components/workflow/standard-process-values.d.ts +10 -2
  83. package/dist/browser/components/workflow/standard-process-values.d.ts.map +1 -1
  84. package/dist/browser/components/workflow/standard-process-values.js +22 -1
  85. package/dist/browser/components/workflow/standard-process-values.js.map +1 -1
  86. package/dist/browser/components/workflow/workflow-flow-layout.d.ts.map +1 -1
  87. package/dist/browser/components/workflow/workflow-flow-layout.js +33 -3
  88. package/dist/browser/components/workflow/workflow-flow-layout.js.map +1 -1
  89. package/dist/browser/components/workflow/workflow-submission-form.d.ts +4 -0
  90. package/dist/browser/components/workflow/workflow-submission-form.d.ts.map +1 -1
  91. package/dist/browser/components/workflow/workflow-submission-form.js +20 -0
  92. package/dist/browser/components/workflow/workflow-submission-form.js.map +1 -1
  93. package/dist/browser/durable-command.js +2 -2
  94. package/dist/browser/durable-command.js.map +1 -1
  95. package/dist/browser/managed-read-recovery.d.ts +2 -0
  96. package/dist/browser/managed-read-recovery.d.ts.map +1 -1
  97. package/dist/browser/managed-read-recovery.js +6 -1
  98. package/dist/browser/managed-read-recovery.js.map +1 -1
  99. package/dist/browser/platform-client.d.ts +8 -1
  100. package/dist/browser/platform-client.d.ts.map +1 -1
  101. package/dist/browser/platform-client.js +39 -2
  102. package/dist/browser/platform-client.js.map +1 -1
  103. package/dist/browser/runtime-authorization-recovery.d.ts.map +1 -1
  104. package/dist/browser/runtime-authorization-recovery.js +36 -9
  105. package/dist/browser/runtime-authorization-recovery.js.map +1 -1
  106. package/dist/browser/runtime.d.ts +3 -1
  107. package/dist/browser/runtime.d.ts.map +1 -1
  108. package/dist/browser/runtime.js.map +1 -1
  109. package/dist/browser/workflow-operation-input.js +1 -1
  110. package/dist/browser/workflow-operation-input.js.map +1 -1
  111. package/dist/config.d.ts +2 -1
  112. package/dist/config.d.ts.map +1 -1
  113. package/dist/config.js +2 -1
  114. package/dist/config.js.map +1 -1
  115. package/dist/core.d.ts +5 -1
  116. package/dist/core.d.ts.map +1 -1
  117. package/dist/core.js +3 -1
  118. package/dist/core.js.map +1 -1
  119. package/dist/expressions.d.ts +4 -0
  120. package/dist/expressions.d.ts.map +1 -0
  121. package/dist/expressions.js +3 -0
  122. package/dist/expressions.js.map +1 -0
  123. package/documentation/administration.md +33 -0
  124. package/documentation/backend.md +117 -2
  125. package/documentation/data-authz.md +30 -0
  126. package/documentation/declarations-cheatsheet.md +2 -1
  127. package/documentation/development.md +7 -0
  128. package/documentation/field-components.md +44 -1
  129. package/documentation/getting-started.md +60 -7
  130. package/documentation/managed-concurrency-frontend.md +10 -4
  131. package/documentation/manifest.json +10 -10
  132. package/documentation/workflow-events.md +303 -9
  133. package/package.json +26 -21
  134. package/releases/2.55.5.json +30 -0
  135. package/releases/2.55.6.json +32 -0
  136. package/releases/2.57.1.json +31 -0
  137. package/releases/2.58.0.json +37 -0
  138. package/releases/2.58.1.json +31 -0
  139. package/releases/2.59.0.json +36 -0
  140. package/releases/2.59.1.json +37 -0
  141. package/releases/2.59.2.json +33 -0
  142. package/skills/manifest.json +1 -1
  143. package/skills/openxiangda-v2/SKILL.md +4 -4
  144. package/skills/openxiangda-v2/references/administration.md +33 -0
  145. package/skills/openxiangda-v2/references/backend.md +117 -2
  146. package/skills/openxiangda-v2/references/data-authz.md +30 -0
  147. package/skills/openxiangda-v2/references/declarations-cheatsheet.md +2 -1
  148. package/skills/openxiangda-v2/references/development.md +7 -0
  149. package/skills/openxiangda-v2/references/field-components.md +44 -1
  150. package/skills/openxiangda-v2/references/getting-started.md +60 -7
  151. package/skills/openxiangda-v2/references/managed-concurrency-frontend.md +10 -4
  152. package/skills/openxiangda-v2/references/workflow-events.md +303 -9
@@ -27,9 +27,9 @@ import { ResourceFormContent } from '../resource/ResourceFormFrame';
27
27
  import { WorkflowRecordEditor } from './WorkflowRecordEditor';
28
28
  import { PlatformAvatar } from '../PlatformAvatar';
29
29
  import { SubtableField } from '../platform-fields/SubtableField';
30
- import { standardProcessFormValues } from './standard-process-values';
30
+ import { assertNamedSubtableReadonlyFields, namedProcessFormValues, standardProcessFormValues } from './standard-process-values';
31
31
  import { fieldWritable } from '../resource/resource-page-helpers';
32
- import { workflowSubmissionFormProjection, workflowSubmissionPrefill } from './workflow-submission-form';
32
+ import { workflowSubmissionFormProjection, workflowSubmissionPrefill, workflowSubmissionValueLinkage } from './workflow-submission-form';
33
33
  /**
34
34
  * An advanced task command has completed the current task and may have
35
35
  * created a new task. Reading the old task Surface in that case is expected
@@ -55,10 +55,13 @@ export async function completeWorkflowCommand(kind, result, options) {
55
55
  catch (error) {
56
56
  refreshFailure = error;
57
57
  }
58
- await options.onCommandCompleted?.(result);
58
+ const failure = refreshFailure;
59
+ const taskSurfaceUnavailable = kind === 'task' && ((failure?.status === 403 && failure.code === 'WORKFLOW_V2_TASK_FORBIDDEN') ||
60
+ (failure?.status === 404 && failure.code === 'WORKFLOW_V2_TASK_NOT_FOUND'));
61
+ await options.onCommandCompleted?.(result, { taskSurfaceUnavailable });
59
62
  if (nextSurface)
60
63
  await options.onCompleted?.(nextSurface, result);
61
- if (refreshFailure)
64
+ if (refreshFailure && !(taskSurfaceUnavailable && options.onCommandCompleted))
62
65
  throw refreshFailure;
63
66
  return nextSurface;
64
67
  }
@@ -595,7 +598,7 @@ function WorkflowOperations({ surface, variant, onRefresh, onCommandSuccess, pen
595
598
  return (order[left.key] ?? 10) - (order[right.key] ?? 10);
596
599
  });
597
600
  const secondary = operations.filter((operation) => operation.placement !== 'primary');
598
- const content = surface.taskForm || commandNeedsRefresh || ((submitting || unknown) && !pendingLocator) || unknown || refreshFailure ? (_jsxs(_Fragment, { children: [commandNeedsRefresh && !unknown && !pendingLocator && !refreshFailure && _jsx(Alert, { type: "info", showIcon: true, title: "\u8BF7\u5237\u65B0\u64CD\u4F5C\u540E\u7EE7\u7EED\u529E\u7406", description: "\u9875\u9762\u505C\u7559\u8F83\u4E45\uFF0C\u9700\u91CD\u65B0\u786E\u8BA4\u5F53\u524D\u529E\u7406\u6743\u9650\u3002\u5237\u65B0\u4F1A\u4FDD\u7559\u5C1A\u672A\u4FDD\u5B58\u7684\u8F93\u5165\u3002", action: _jsx(Button, { disabled: locked, loading: submitting, onClick: () => void refreshConfirmed(), children: "\u5237\u65B0\u64CD\u4F5C" }) }), surface.taskForm && _jsx(WorkflowTaskForm, { controller: taskForm, disabled: locked, variant: variant, taskVersion: surface.task?.version ? Number(surface.task.version) : undefined, taskId: surface.task?.id ? String(surface.task.id) : undefined, draftDisabled: commandLocked, onRefresh: onRefresh, onDraftBusyChange: busy => { setDraftLocked(busy); onBusyChange(busy || fileLocked || busyRef.current); }, onFileBusyChange: busy => { setFileLocked(busy); onBusyChange(busy || draftLocked || busyRef.current); }, resourceCode: surface.presentation.businessDetail.resourceCode || undefined, recordId: surface.presentation.businessDetail.recordId || undefined }), (submitting || unknown) && !pendingLocator && _jsx(WorkflowPendingCommandGuard, {}), unknown && _jsx(Alert, { type: "warning", showIcon: true, title: "\u4FDD\u7559\u4E86\u539F\u63D0\u4EA4\u8BF7\u6C42", description: operationError || '提交结果待确认,请重试原请求或查询原结果。', action: _jsx(Button, { disabled: submitting, loading: submitting, onClick: () => void sendRequest(true), children: "\u91CD\u8BD5\u539F\u8BF7\u6C42" }) }), refreshFailure && _jsx(Alert, { type: "warning", showIcon: true, title: refreshFailure.outcome === 'succeeded'
601
+ const content = surface.taskForm || commandNeedsRefresh || ((submitting || unknown) && !pendingLocator) || unknown || refreshFailure ? (_jsxs(_Fragment, { children: [commandNeedsRefresh && !unknown && !pendingLocator && !refreshFailure && _jsx(Alert, { type: "info", showIcon: true, title: "\u8BF7\u5237\u65B0\u64CD\u4F5C\u540E\u7EE7\u7EED\u529E\u7406", description: "\u9875\u9762\u505C\u7559\u8F83\u4E45\uFF0C\u9700\u91CD\u65B0\u786E\u8BA4\u5F53\u524D\u529E\u7406\u6743\u9650\u3002\u5237\u65B0\u4F1A\u4FDD\u7559\u5C1A\u672A\u4FDD\u5B58\u7684\u8F93\u5165\u3002", action: _jsx(Button, { disabled: locked, loading: submitting, onClick: () => void refreshConfirmed(), children: "\u5237\u65B0\u64CD\u4F5C" }) }), surface.taskForm && _jsx(WorkflowTaskForm, { controller: taskForm, disabled: locked, variant: variant, operationPending: submitting || unknown || Boolean(pendingLocator) || draftLocked || fileLocked, taskVersion: surface.task?.version ? Number(surface.task.version) : undefined, taskId: surface.task?.id ? String(surface.task.id) : undefined, draftDisabled: commandLocked, onRefresh: onRefresh, onDraftBusyChange: busy => { setDraftLocked(busy); onBusyChange(busy || fileLocked || busyRef.current); }, onFileBusyChange: busy => { setFileLocked(busy); onBusyChange(busy || draftLocked || busyRef.current); }, resourceCode: surface.presentation.businessDetail.resourceCode || undefined, recordId: surface.presentation.businessDetail.recordId || undefined }), (submitting || unknown) && !pendingLocator && _jsx(WorkflowPendingCommandGuard, {}), unknown && _jsx(Alert, { type: "warning", showIcon: true, title: "\u4FDD\u7559\u4E86\u539F\u63D0\u4EA4\u8BF7\u6C42", description: operationError || '提交结果待确认,请重试原请求或查询原结果。', action: _jsx(Button, { disabled: submitting, loading: submitting, onClick: () => void sendRequest(true), children: "\u91CD\u8BD5\u539F\u8BF7\u6C42" }) }), refreshFailure && _jsx(Alert, { type: "warning", showIcon: true, title: refreshFailure.outcome === 'succeeded'
599
602
  ? '任务操作已成功,但资料刷新失败' : '本次未提交,最新资料读取失败', description: refreshFailure.message, action: _jsx(Button, { disabled: submitting, onClick: () => void refreshConfirmed(), children: "\u5237\u65B0\u8D44\u6599" }) })] })) : null;
600
603
  const actions = (_jsxs(_Fragment, { children: [_jsxs("div", { className: `oxa-workflow-actions oxa-workflow-actions-${variant}`, children: [primary.map((operation) => (_jsx(Button, { danger: operation.emphasis === 'danger', disabled: locked || commandNeedsRefresh || taskForm.stale || taskForm.draftNeedsSave || !operation.enabled, onClick: () => selectOperation(operation), title: operation.disabledReason, type: operation.emphasis === 'primary' ? 'primary' : 'default', children: operation.label }, operation.key))), secondary.length > 0 && (_jsx(Button, { className: "oxa-workflow-more-trigger", disabled: locked || commandNeedsRefresh || taskForm.stale || taskForm.draftNeedsSave, icon: _jsx(EllipsisOutlined, {}), onClick: () => setMoreOpen((value) => !value), children: "\u66F4\u591A\u64CD\u4F5C" })), secondary.length > 0 && moreOpen && (_jsx("div", { className: "oxa-workflow-more-actions", children: secondary.map((operation) => (_jsx(Button, { disabled: locked || commandNeedsRefresh || taskForm.stale || taskForm.draftNeedsSave || !operation.enabled, onClick: () => {
601
604
  setMoreOpen(false);
@@ -621,6 +624,7 @@ function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: su
621
624
  const [writing, setWriting] = useState(false);
622
625
  const [settlement, setSettlement] = useState(null);
623
626
  const [confirmedReadFailure, setConfirmedReadFailure] = useState(null);
627
+ const [taskCommandSettled, setTaskCommandSettled] = useState(false);
624
628
  const locator = locatorState.scope === locatorScope ? locatorState.value : null;
625
629
  useEffect(() => {
626
630
  setLocatorState({ scope: locatorScope, value: locatorScope ? readPendingWorkflowTaskCommand(submissionLocatorStorage(), locatorScope) : null });
@@ -748,6 +752,7 @@ function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: su
748
752
  ]);
749
753
  useEffect(() => {
750
754
  activeRef.current = true;
755
+ setTaskCommandSettled(false);
751
756
  requestSequence.current += 1;
752
757
  if (!normalizedIdentifier) {
753
758
  setSurface(null);
@@ -830,11 +835,19 @@ function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: su
830
835
  ? nextSurface
831
836
  : null;
832
837
  },
833
- onCommandCompleted: async (commandResult) => {
838
+ onCommandCompleted: async (commandResult, context) => {
834
839
  if (!isCurrentGeneration(identifierAtStart, generationAtStart)) {
835
840
  return;
836
841
  }
837
- await onCommandCompletedRef.current?.(commandResult);
842
+ if (context?.taskSurfaceUnavailable) {
843
+ setSurface(null);
844
+ setSurfaceIdentifier(identifierAtStart);
845
+ setLoading(false);
846
+ setLoadingIdentifier(null);
847
+ setErrorState(null);
848
+ setTaskCommandSettled(true);
849
+ }
850
+ await onCommandCompletedRef.current?.(commandResult, context);
838
851
  },
839
852
  onCompleted: async (nextSurface, commandResult) => {
840
853
  if (!isCurrentGeneration(identifierAtStart, generationAtStart)) {
@@ -907,7 +920,7 @@ function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: su
907
920
  return layout({ actions: null, content: (_jsxs(_Fragment, { children: [receipt, confirmedWarning, _jsx(Alert, { action: _jsx(Button, { onClick: () => void refresh().catch(() => undefined), children: "\u91CD\u8BD5" }), description: renderError, showIcon: true, type: "error" })] })) });
908
921
  }
909
922
  if (!renderSurface)
910
- return layout({ content: receipt || confirmedWarning ? _jsxs(_Fragment, { children: [receipt, confirmedWarning] }) : null, actions: null });
923
+ return layout({ content: receipt || confirmedWarning || taskCommandSettled ? _jsxs(_Fragment, { children: [receipt, confirmedWarning, taskCommandSettled && !confirmedWarning && _jsx(Alert, { type: "success", showIcon: true, title: "\u4EFB\u52A1\u64CD\u4F5C\u5DF2\u6210\u529F", description: "\u4F60\u7684\u672C\u6B21\u529E\u7406\u5DF2\u5B8C\u6210\uFF0C\u6D41\u7A0B\u53EF\u80FD\u4ECD\u5728\u7B49\u5F85\u5176\u4ED6\u5904\u7406\u4EBA\u3002" })] }) : null, actions: null });
911
924
  return (_jsx(WorkflowOperations, { onCommandSuccess: completed, onRefresh: refreshCurrent, surface: renderSurface, variant: variant, pendingLocator: locator, persistLocator: persistLocator, clearLocator: clearLocator, onBusyChange: setWriting, settlement: settlement, confirmedReadPending: Boolean(confirmedReadFailure), renderLayout: ({ content, actions }) => layout({ actions, content: receipt || confirmedWarning || renderError || content ? _jsxs(_Fragment, { children: [receipt, confirmedWarning, renderError && _jsx(Alert, { description: renderError, showIcon: true, type: "warning" }), content] }) : null }) }, `${normalizedIdentifier}:${locatorScope}:${runtime?.identityEpoch || 0}`));
912
925
  }
913
926
  /** Public, embeddable Workflow task operation surface. */
@@ -933,6 +946,7 @@ function StandardWorkflowDetailRenderer({ surface, timeline, warning, operations
933
946
  const timeZone = usePresentationTimeZone();
934
947
  const [tab, setTab] = useState('application');
935
948
  const instance = surfaceInstance(surface);
949
+ const deadline = surfaceTask(surface)?.completionDeadline;
936
950
  const business = surfaceBusinessDetail(surface);
937
951
  const summary = surface.presentation.summary;
938
952
  const titleField = workflowDetailGroups(business).flatMap(group => group.fields).find(field => field.type === 'text.short');
@@ -947,7 +961,7 @@ function StandardWorkflowDetailRenderer({ surface, timeline, warning, operations
947
961
  const loadAudit = useCallback(() => loadWorkflowDataAudit(instance.id), [instance.id]);
948
962
  const back = onClose || (() => navigate(variant === 'mobile' ? '/m/work-center' : '/work-center'));
949
963
  return _jsxs(RecordDetailFrame, { variant: variant, title: "\u7533\u8BF7\u8BE6\u60C5", heading: title, status: _jsx(StatusTag, { status: instance.status }), metadata: [summary.initiator?.displayName || summary.initiatorDisplayName, summary.initiator?.departmentDisplayName || summary.departmentDisplayName,
950
- summary.submittedAt ? `${detailTime(summary.submittedAt, timeZone)} 创建` : ''].filter(Boolean).join(' · '), updatedAt: business.record.updated_at || instance.completedAt || instance.startedAt, onClose: back, onEdit: edit, editing: editing, busy: busy, drawer: drawer, drawerState: drawerState, newPageHref: newPageHref, footer: operations || hasActions || edit ? _jsxs(_Fragment, { children: [operations, edit && _jsx(Button, { onClick: edit, children: "\u7F16\u8F91" })] }) : null, children: [warning && _jsx(Alert, { description: warning, showIcon: true, type: "warning" }), !workflowHasEnded(instance) && activeNode && _jsxs("div", { className: `oxa-workflow-current-node is-${bannerTone}`, role: "status", children: [_jsxs("span", { children: [_jsx(ClockCircleOutlined, {}), "\u5F53\u524D\u8282\u70B9\uFF1A", activeNode.title] }), people && _jsxs("span", { children: ["\u7B49\u5F85", people, "\u5904\u7406"] })] }), _jsx(Tabs, { activeKey: tab, onChange: setTab, items: [
964
+ summary.submittedAt ? `${detailTime(summary.submittedAt, timeZone)} 创建` : ''].filter(Boolean).join(' · '), updatedAt: business.record.updated_at || instance.completedAt || instance.startedAt, onClose: back, onEdit: edit, editing: editing, busy: busy, drawer: drawer, drawerState: drawerState, newPageHref: newPageHref, footer: operations || hasActions || edit ? _jsxs(_Fragment, { children: [operations, edit && _jsx(Button, { onClick: edit, children: "\u7F16\u8F91" })] }) : null, children: [warning && _jsx(Alert, { description: warning, showIcon: true, type: "warning" }), !workflowHasEnded(instance) && activeNode && _jsxs("div", { className: `oxa-workflow-current-node is-${bannerTone}`, role: "status", children: [_jsxs("span", { children: [_jsx(ClockCircleOutlined, {}), "\u5F53\u524D\u8282\u70B9\uFF1A", activeNode.title] }), people && _jsxs("span", { children: ["\u7B49\u5F85", people, "\u5904\u7406"] })] }), deadline && !workflowHasEnded(instance) && ['pending', 'failed'].includes(deadline.status) && _jsx(Alert, { showIcon: true, type: deadline.status === 'failed' ? 'warning' : 'info', title: deadline.status === 'failed' ? '到期自动处理未成功' : `未提前完成时,将于 ${detailTime(deadline.deadlineAt, timeZone)} 自动同意`, description: deadline.status === 'failed' ? '流程仍可人工办理。自动处理已停止,请联系流程管理员查看执行记录。' : '可以在此之前填写可选内容并手动提交;自动处理不会代替任何人记录审批意见。' }), _jsx(Tabs, { activeKey: tab, onChange: setTab, items: [
951
965
  { key: 'application', label: '申请内容', children: _jsx(WorkflowBusinessDetailSections, { surface: surface, variant: variant }) },
952
966
  { key: 'history', label: '审批历史', children: _jsx(WorkflowTimelineSection, { timeline: timeline }) },
953
967
  { key: 'changes', label: '变更记录', children: tab === 'changes' && business.surface && business.resourceCode && business.recordId
@@ -1033,6 +1047,16 @@ function WorkflowDetailPage({ kind, variant, resourceCode, recordId, onDismiss,
1033
1047
  const id = recordId || String(kind === 'task' ? params.taskId || '' : kind === 'record' ? params.id || '' : params.instanceId || '');
1034
1048
  const navigate = useNavigate();
1035
1049
  const { identity } = useRuntime();
1050
+ const commandHostKey = `${identity.identityScope}:${identity.environment.id}:${kind}:${id}`;
1051
+ const commandHostRef = useRef(null);
1052
+ useEffect(() => {
1053
+ const host = { key: commandHostKey };
1054
+ commandHostRef.current = host;
1055
+ return () => {
1056
+ if (commandHostRef.current === host)
1057
+ commandHostRef.current = null;
1058
+ };
1059
+ }, [commandHostKey]);
1036
1060
  const [editing, setEditing] = useState(false);
1037
1061
  const [editBusy, setEditBusy] = useState(false);
1038
1062
  const { detail, surface, timeline, loading, error, refresh } = useWorkflowDetail({ kind, id, resourceCode });
@@ -1042,12 +1066,17 @@ function WorkflowDetailPage({ kind, variant, resourceCode, recordId, onDismiss,
1042
1066
  const detailPath = surface ? (variant === 'mobile' ? surface.detailNavigation.mobilePath : surface.detailNavigation.desktopPath) : '';
1043
1067
  const newPageHref = useHref(detailPath || '.');
1044
1068
  const close = onDismiss || (() => navigate(returnPath || (detail ? variant === 'mobile' ? detail.navigationContext.mobileReturnPath : detail.navigationContext.desktopReturnPath : variant === 'mobile' ? '/m/work-center' : '/work-center')));
1045
- const onCommandCompleted = async (result) => {
1046
- if (kind === 'task' && result.advanced === true) {
1047
- const nextId = String(result.instanceId || '').trim();
1069
+ const onCommandCompleted = async (result, context) => {
1070
+ const host = commandHostRef.current;
1071
+ if (!host || host.key !== commandHostKey)
1072
+ return;
1073
+ if (kind === 'task' && (result.advanced === true || context?.taskSurfaceUnavailable)) {
1074
+ const nextId = String(result.instanceId || surface?.instance.id || '').trim();
1048
1075
  if (!nextId)
1049
- return;
1076
+ throw new Error('WORKFLOW_INSTANCE_ID_REQUIRED_AFTER_COMMAND');
1050
1077
  const next = await loadWorkflowInstanceDetail(nextId);
1078
+ if (commandHostRef.current !== host)
1079
+ return;
1051
1080
  navigate(variant === 'mobile' ? next.surface.detailNavigation.mobilePath : next.surface.detailNavigation.desktopPath, { replace: true });
1052
1081
  }
1053
1082
  else
@@ -1256,6 +1285,7 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
1256
1285
  const [requirementForm] = Form.useForm();
1257
1286
  const watchedFormValues = Form.useWatch([], { form: subjectForm, preserve: true });
1258
1287
  const [prefillError, setPrefillError] = useState(false);
1288
+ const [linkageError, setLinkageError] = useState(false);
1259
1289
  const prefill = useRef({ context: '', applied: new Set() });
1260
1290
  const [loading, setLoading] = useState(false);
1261
1291
  const [launchSurface, setLaunchSurface] = useState(null);
@@ -1359,6 +1389,22 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
1359
1389
  }
1360
1390
  }, [fields, formOptions?.fieldState, subjectDefinition, watchedFormValues]);
1361
1391
  const prefillContext = JSON.stringify([recoveryScope, identity.environment.activeAppVersionId, submissionMode, subjectId]);
1392
+ useEffect(() => { setLinkageError(false); }, [prefillContext]);
1393
+ const onValuesChange = (changed, values) => {
1394
+ if (!formOptions?.valueLinkage || loading || commandId || pendingSubmission || !subjectDefinition)
1395
+ return;
1396
+ try {
1397
+ const canonical = normalizeFormValues(values, subjectDefinition.surface);
1398
+ const visible = workflowSubmissionFormProjection(fields, canonical, formOptions.fieldState?.(canonical)).fields;
1399
+ const patch = workflowSubmissionValueLinkage(visible, normalizeFormValues(changed, subjectDefinition.surface), canonical, formOptions.valueLinkage);
1400
+ const normalized = normalizeRecordForForm(patch, subjectDefinition.surface);
1401
+ subjectForm.setFields(Object.entries(normalized).map(([name, value]) => ({ name, value, touched: true, errors: [] })));
1402
+ setLinkageError(false);
1403
+ }
1404
+ catch {
1405
+ setLinkageError(true);
1406
+ }
1407
+ };
1362
1408
  useEffect(() => {
1363
1409
  if (!launchSurface || commandId || pendingSubmission || formOptions?.preparation || mutationMode !== 'create' || !subjectDefinition ||
1364
1410
  launchSurface.environmentId !== identity.environment.id || (generatedNamedSubmission && !namedIntent))
@@ -1735,11 +1781,12 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
1735
1781
  launchSurface?.submission.kind !== 'standard-process')) {
1736
1782
  return frame(_jsx(Result, { status: "500", title: "\u6807\u51C6\u6D41\u7A0B\u5408\u540C\u4E0D\u5B8C\u6574" }));
1737
1783
  }
1738
- if (!commandId && namedIntent && fields.some(field => field.type === 'subtable')) {
1739
- return frame(_jsx(Result, { status: "error", title: "\u6807\u51C6\u6D41\u7A0B\u4E0D\u652F\u6301\u5305\u542B\u53EF\u5199\u5B50\u8868\u7684\u4E3B\u4F53\u8868\u5355", subTitle: "\u5F53\u524D\u6807\u51C6\u63D0\u4EA4 Surface \u5C1A\u672A\u58F0\u660E\u53EF\u5199\u5B50\u8868\u6E32\u67D3\u5668\u3002" }));
1784
+ if (!commandId && namedIntent && fields.some(field => field.type === 'subtable' &&
1785
+ (submissionMode !== 'create' || namedIntent.ownedSubject?.resourceCode !== subjectDefinition.code || !namedIntent.ownedSubject.subtables.some(table => table.fieldCode === field.key)))) {
1786
+ return frame(_jsx(Result, { status: "error", title: "\u5B50\u8868\u63D0\u4EA4\u914D\u7F6E\u4E0D\u5B8C\u6574", subTitle: "\u8BF7\u8054\u7CFB\u5E94\u7528\u7BA1\u7406\u5458\u68C0\u67E5\u53D1\u8D77\u914D\u7F6E\u3002" }));
1740
1787
  }
1741
1788
  const submit = async (values) => {
1742
- if (submitInFlight.current || commandId || completion || !launchSurface || formOptions?.preparation || prefillError || formProjection.error)
1789
+ if (submitInFlight.current || commandId || completion || !launchSurface || formOptions?.preparation || prefillError || linkageError || formProjection.error)
1743
1790
  return;
1744
1791
  const unresolved = readPendingWorkflowSubmission(submissionLocatorStorage(), recoveryScope);
1745
1792
  if (pendingSubmission || unresolved) {
@@ -1759,7 +1806,7 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
1759
1806
  setRecoveryObservedAbsent(false);
1760
1807
  };
1761
1808
  try {
1762
- const encoded = namedIntent ? normalizeFormValues(values, subjectDefinition.surface)
1809
+ const encoded = namedIntent ? namedProcessFormValues(values, subjectDefinition.surface, resources, namedIntent.ownedSubject, formOptions?.subtableReadonlyFields)
1763
1810
  : standardProcessFormValues(values, subjectDefinition.surface, resources, (field, mode) => fieldWritable(field, mode === 'create' ? 'create' : 'edit', hasCapability, identity.isAppSuperAdmin));
1764
1811
  const data = workflowSubmissionFormProjection(fields, encoded, formOptions?.fieldState?.(encoded)).values;
1765
1812
  const signature = JSON.stringify({ workflowCode, subjectId, environmentKey: identity.environment.key, submissionMode, data });
@@ -1949,8 +1996,20 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
1949
1996
  const uploadOperationCode = namedIntent?.operationCode || definition.processOperationCode;
1950
1997
  const fieldRenderers = uploadOperationCode
1951
1998
  ? {
1952
- ...(!namedIntent ? { renderSubtable: ({ field, disabled, operation, recordId }) => _jsx(SubtableField, { field: field, disabled: disabled, operation: operation, parentRecordId: recordId, mobile: variant === 'mobile' }) } : {}),
1999
+ renderSubtable: ({ field, disabled, operation, recordId }) => {
2000
+ const grant = namedIntent?.ownedSubject?.subtables.find(table => table.fieldCode === field.key);
2001
+ assertNamedSubtableReadonlyFields(subjectDefinition.surface, namedIntent?.ownedSubject, formOptions?.subtableReadonlyFields || {});
2002
+ return _jsx(SubtableField, { field: field, disabled: disabled, operation: operation, parentRecordId: recordId, mobile: variant === 'mobile', ...(namedIntent && grant ? { launch: { fieldCodes: grant.fieldCodes,
2003
+ readonlyFieldCodes: formOptions?.subtableReadonlyFields?.[field.key],
2004
+ reference: { workflowCode: definition.code, operationCode: namedIntent.operationCode, subtableFieldCode: field.key },
2005
+ upload: (childField, file) => uploadOperationManagedFile({
2006
+ operationCode: uploadOperationCode, resourceCode: field.subtable.resourceCode,
2007
+ fieldCode: childField.key, intent: 'create', file,
2008
+ }),
2009
+ } } : {}) });
2010
+ },
1953
2011
  ...(namedIntent ? { referenceLaunch: { workflowCode: definition.code, operationCode: namedIntent.operationCode } } : {}),
2012
+ candidateScopeValues: { ...formOptions?.candidateScopeValues, ...formProjection.values },
1954
2013
  upload: async (field, file) => await uploadOperationManagedFile({
1955
2014
  operationCode: uploadOperationCode,
1956
2015
  resourceCode: subjectDefinition.code,
@@ -1989,11 +2048,12 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
1989
2048
  embedded: Boolean(onDismiss),
1990
2049
  fieldRenderers,
1991
2050
  formIntro: formOptions?.intro,
1992
- formError: prefillError || formProjection.error ? '表单规则暂不可用,请联系管理员。' : undefined,
2051
+ formError: prefillError || linkageError || formProjection.error ? '表单规则暂不可用,请联系管理员。' : undefined,
1993
2052
  recordId: mutationMode === 'update' ? subjectId : undefined,
1994
2053
  expectedRevision: mutationMode === 'update' ? loadedSubjectRevision : undefined,
1995
2054
  onBack: onDismiss ? dismissDrawer : () => navigate(-1),
1996
2055
  onSubmit: submit,
2056
+ onValuesChange,
1997
2057
  onAnswer: answer,
1998
2058
  onRetry: retry,
1999
2059
  onRefresh: () => { setProcessError(null); setProcessRefreshAttempt(value => value + 1); },
@@ -2020,7 +2080,7 @@ function StandardProcessSubmissionRenderer(props) {
2020
2080
  }, new Map())].map(([section, fields]) => ({ section, fields }));
2021
2081
  if (props.completion)
2022
2082
  return _jsx(Result, { status: "success", title: "\u63D0\u4EA4\u6210\u529F", extra: _jsx(Button, { onClick: props.onBack, children: "\u8FD4\u56DE" }) });
2023
- return _jsxs("div", { className: props.variant === 'mobile' ? 'oxa-mobile-page oxa-mobile-entry oxa-workflow-submission-mobile' : 'oxa-workflow-entry', children: [!props.embedded && _jsxs("header", { className: props.variant === 'mobile' ? 'oxa-mobile-header' : 'oxa-workflow-entry-header', children: [_jsx(Button, { type: "text", "aria-label": "\u8FD4\u56DE", icon: _jsx(ArrowLeftOutlined, {}), disabled: props.processing, onClick: props.onBack }), _jsx("h3", { children: props.title }), _jsx("span", {})] }), _jsx(ResourceFormContent, { variant: props.variant, mode: props.mutationMode === 'update' ? 'edit' : 'create', resourceCode: props.subjectDefinition.code, recordId: props.recordId, groups: groups, expectedRevision: props.expectedRevision, initialValues: props.mutationMode === 'update' ? undefined : Object.fromEntries(props.fields.filter(field => field.type === 'user.multiple').map(field => [field.key, []])), form: props.subjectForm, busy: props.loading || props.submitted, pending: props.loading || props.processing, submitDisabled: props.submitted || Boolean(props.formError), submitLabel: "\u63D0\u4EA4\u5BA1\u6279", canWriteField: () => true, renderers: props.fieldRenderers, feedback: _jsxs(_Fragment, { children: [props.formIntro, props.launchControls, props.formError && _jsx(Alert, { type: "error", showIcon: true, title: props.formError }), props.processing && _jsx(Alert, { type: "info", showIcon: true, title: "\u6B63\u5728\u63D0\u4EA4\u7533\u8BF7\uFF0C\u8BF7\u7A0D\u5019\u2026" })] }), onSubmit: values => void props.onSubmit(values) }), _jsx(ProcessCommandPanel, { error: props.processError, form: props.requirementForm, loading: props.loading, onAnswer: props.onAnswer, onRetry: props.onRetry, onRefresh: props.onRefresh, surface: props.processSurface })] });
2083
+ return _jsxs("div", { className: props.variant === 'mobile' ? 'oxa-mobile-page oxa-mobile-entry oxa-workflow-submission-mobile' : 'oxa-workflow-entry', children: [!props.embedded && _jsxs("header", { className: props.variant === 'mobile' ? 'oxa-mobile-header' : 'oxa-workflow-entry-header', children: [_jsx(Button, { type: "text", "aria-label": "\u8FD4\u56DE", icon: _jsx(ArrowLeftOutlined, {}), disabled: props.processing, onClick: props.onBack }), _jsx("h3", { children: props.title }), _jsx("span", {})] }), _jsx(ResourceFormContent, { variant: props.variant, mode: props.mutationMode === 'update' ? 'edit' : 'create', resourceCode: props.subjectDefinition.code, recordId: props.recordId, groups: groups, expectedRevision: props.expectedRevision, initialValues: props.mutationMode === 'update' ? undefined : Object.fromEntries(props.fields.filter(field => field.type === 'user.multiple').map(field => [field.key, []])), form: props.subjectForm, busy: props.loading || props.submitted, pending: props.loading || props.processing, submitDisabled: props.submitted || Boolean(props.formError), submitLabel: "\u63D0\u4EA4\u5BA1\u6279", canWriteField: () => true, renderers: props.fieldRenderers, feedback: _jsxs(_Fragment, { children: [props.formIntro, props.launchControls, props.formError && _jsx(Alert, { type: "error", showIcon: true, title: props.formError }), props.processing && _jsx(Alert, { type: "info", showIcon: true, title: "\u6B63\u5728\u63D0\u4EA4\u7533\u8BF7\uFF0C\u8BF7\u7A0D\u5019\u2026" })] }), onValuesChange: props.onValuesChange, onSubmit: values => void props.onSubmit(values) }), _jsx(ProcessCommandPanel, { error: props.processError, form: props.requirementForm, loading: props.loading, onAnswer: props.onAnswer, onRetry: props.onRetry, onRefresh: props.onRefresh, surface: props.processSurface })] });
2024
2084
  }
2025
2085
  function DesktopProcessSubmissionRenderer(props) { return _jsx(StandardProcessSubmissionRenderer, { ...props, variant: "desktop" }); }
2026
2086
  function MobileProcessSubmissionRenderer(props) { return _jsx(StandardProcessSubmissionRenderer, { ...props, variant: "mobile" }); }