openxiangda 2.48.2 → 2.59.1

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 (245) hide show
  1. package/README.md +1 -0
  2. package/dist/browser/AuthoritativeSelector.d.ts +4 -2
  3. package/dist/browser/AuthoritativeSelector.d.ts.map +1 -1
  4. package/dist/browser/AuthoritativeSelector.js +42 -12
  5. package/dist/browser/AuthoritativeSelector.js.map +1 -1
  6. package/dist/browser/FilePreviewPage.d.ts.map +1 -1
  7. package/dist/browser/FilePreviewPage.js +4 -0
  8. package/dist/browser/FilePreviewPage.js.map +1 -1
  9. package/dist/browser/components/administration/RoleMembershipManager.d.ts.map +1 -1
  10. package/dist/browser/components/administration/RoleMembershipManager.js +5 -5
  11. package/dist/browser/components/administration/RoleMembershipManager.js.map +1 -1
  12. package/dist/browser/components/administration/scope-applicability.d.ts +3 -0
  13. package/dist/browser/components/administration/scope-applicability.d.ts.map +1 -0
  14. package/dist/browser/components/administration/scope-applicability.js +7 -0
  15. package/dist/browser/components/administration/scope-applicability.js.map +1 -0
  16. package/dist/browser/components/platform-fields/ConstrainedUserField.d.ts +16 -0
  17. package/dist/browser/components/platform-fields/ConstrainedUserField.d.ts.map +1 -0
  18. package/dist/browser/components/platform-fields/ConstrainedUserField.js +140 -0
  19. package/dist/browser/components/platform-fields/ConstrainedUserField.js.map +1 -0
  20. package/dist/browser/components/platform-fields/DateTimeField.d.ts +1 -1
  21. package/dist/browser/components/platform-fields/DateTimeField.d.ts.map +1 -1
  22. package/dist/browser/components/platform-fields/DateTimeField.js +15 -8
  23. package/dist/browser/components/platform-fields/DateTimeField.js.map +1 -1
  24. package/dist/browser/components/platform-fields/ManagedMediaPlayer.d.ts +19 -0
  25. package/dist/browser/components/platform-fields/ManagedMediaPlayer.d.ts.map +1 -0
  26. package/dist/browser/components/platform-fields/ManagedMediaPlayer.js +42 -0
  27. package/dist/browser/components/platform-fields/ManagedMediaPlayer.js.map +1 -0
  28. package/dist/browser/components/platform-fields/MobileDateTimeField.d.ts +1 -1
  29. package/dist/browser/components/platform-fields/MobileDateTimeField.d.ts.map +1 -1
  30. package/dist/browser/components/platform-fields/MobileDateTimeField.js +5 -3
  31. package/dist/browser/components/platform-fields/MobileDateTimeField.js.map +1 -1
  32. package/dist/browser/components/platform-fields/MobileSelection.d.ts +3 -1
  33. package/dist/browser/components/platform-fields/MobileSelection.d.ts.map +1 -1
  34. package/dist/browser/components/platform-fields/MobileSelection.js +2 -2
  35. package/dist/browser/components/platform-fields/MobileSelection.js.map +1 -1
  36. package/dist/browser/components/platform-fields/MobileZonedDateTimeField.js +2 -2
  37. package/dist/browser/components/platform-fields/MobileZonedDateTimeField.js.map +1 -1
  38. package/dist/browser/components/platform-fields/ResourceReferenceField.d.ts +4 -2
  39. package/dist/browser/components/platform-fields/ResourceReferenceField.d.ts.map +1 -1
  40. package/dist/browser/components/platform-fields/ResourceReferenceField.js +3 -3
  41. package/dist/browser/components/platform-fields/ResourceReferenceField.js.map +1 -1
  42. package/dist/browser/components/platform-fields/SubtableField.d.ts +11 -2
  43. package/dist/browser/components/platform-fields/SubtableField.d.ts.map +1 -1
  44. package/dist/browser/components/platform-fields/SubtableField.js +55 -24
  45. package/dist/browser/components/platform-fields/SubtableField.js.map +1 -1
  46. package/dist/browser/components/platform-fields/ZonedDateTimeField.js +2 -2
  47. package/dist/browser/components/platform-fields/ZonedDateTimeField.js.map +1 -1
  48. package/dist/browser/components/platform-fields/managed-media-read.d.ts +19 -0
  49. package/dist/browser/components/platform-fields/managed-media-read.d.ts.map +1 -0
  50. package/dist/browser/components/platform-fields/managed-media-read.js +32 -0
  51. package/dist/browser/components/platform-fields/managed-media-read.js.map +1 -0
  52. package/dist/browser/components/platform-fields/reference-binding-change.d.ts +6 -0
  53. package/dist/browser/components/platform-fields/reference-binding-change.d.ts.map +1 -0
  54. package/dist/browser/components/platform-fields/reference-binding-change.js +32 -0
  55. package/dist/browser/components/platform-fields/reference-binding-change.js.map +1 -0
  56. package/dist/browser/components/platform-fields/user-candidate-context.d.ts +54 -0
  57. package/dist/browser/components/platform-fields/user-candidate-context.d.ts.map +1 -0
  58. package/dist/browser/components/platform-fields/user-candidate-context.js +70 -0
  59. package/dist/browser/components/platform-fields/user-candidate-context.js.map +1 -0
  60. package/dist/browser/components/platform-fields/zoned-date-time.d.ts +4 -0
  61. package/dist/browser/components/platform-fields/zoned-date-time.d.ts.map +1 -1
  62. package/dist/browser/components/platform-fields/zoned-date-time.js +9 -1
  63. package/dist/browser/components/platform-fields/zoned-date-time.js.map +1 -1
  64. package/dist/browser/components/resource/GeneratedResourceCrud.d.ts.map +1 -1
  65. package/dist/browser/components/resource/GeneratedResourceCrud.js +28 -4
  66. package/dist/browser/components/resource/GeneratedResourceCrud.js.map +1 -1
  67. package/dist/browser/components/resource/GeneratedResourceForm.js +3 -3
  68. package/dist/browser/components/resource/GeneratedResourceForm.js.map +1 -1
  69. package/dist/browser/components/resource/RecordDetailFrame.d.ts +3 -1
  70. package/dist/browser/components/resource/RecordDetailFrame.d.ts.map +1 -1
  71. package/dist/browser/components/resource/RecordDetailFrame.js +2 -2
  72. package/dist/browser/components/resource/RecordDetailFrame.js.map +1 -1
  73. package/dist/browser/components/resource/ResourceFormFrame.d.ts +3 -2
  74. package/dist/browser/components/resource/ResourceFormFrame.d.ts.map +1 -1
  75. package/dist/browser/components/resource/ResourceFormFrame.js +13 -2
  76. package/dist/browser/components/resource/ResourceFormFrame.js.map +1 -1
  77. package/dist/browser/components/resource/ResourceRecordComments.d.ts +13 -0
  78. package/dist/browser/components/resource/ResourceRecordComments.d.ts.map +1 -0
  79. package/dist/browser/components/resource/ResourceRecordComments.js +75 -0
  80. package/dist/browser/components/resource/ResourceRecordComments.js.map +1 -0
  81. package/dist/browser/components/resource/ResourceRecordDeletion.d.ts +7 -0
  82. package/dist/browser/components/resource/ResourceRecordDeletion.d.ts.map +1 -0
  83. package/dist/browser/components/resource/ResourceRecordDeletion.js +53 -0
  84. package/dist/browser/components/resource/ResourceRecordDeletion.js.map +1 -0
  85. package/dist/browser/components/resource/ResourceRecordPrintPreview.d.ts +16 -0
  86. package/dist/browser/components/resource/ResourceRecordPrintPreview.d.ts.map +1 -0
  87. package/dist/browser/components/resource/ResourceRecordPrintPreview.js +92 -0
  88. package/dist/browser/components/resource/ResourceRecordPrintPreview.js.map +1 -0
  89. package/dist/browser/components/resource/SurfaceFields.d.ts +12 -3
  90. package/dist/browser/components/resource/SurfaceFields.d.ts.map +1 -1
  91. package/dist/browser/components/resource/SurfaceFields.js +24 -8
  92. package/dist/browser/components/resource/SurfaceFields.js.map +1 -1
  93. package/dist/browser/components/resource/generated-resource-definition.d.ts +13 -0
  94. package/dist/browser/components/resource/generated-resource-definition.d.ts.map +1 -1
  95. package/dist/browser/components/resource/record-comment-session.d.ts +17 -0
  96. package/dist/browser/components/resource/record-comment-session.d.ts.map +1 -0
  97. package/dist/browser/components/resource/record-comment-session.js +64 -0
  98. package/dist/browser/components/resource/record-comment-session.js.map +1 -0
  99. package/dist/browser/components/resource/record-deletion-session.d.ts +19 -0
  100. package/dist/browser/components/resource/record-deletion-session.d.ts.map +1 -0
  101. package/dist/browser/components/resource/record-deletion-session.js +64 -0
  102. package/dist/browser/components/resource/record-deletion-session.js.map +1 -0
  103. package/dist/browser/components/resource/record-print-session.d.ts +12 -0
  104. package/dist/browser/components/resource/record-print-session.d.ts.map +1 -0
  105. package/dist/browser/components/resource/record-print-session.js +32 -0
  106. package/dist/browser/components/resource/record-print-session.js.map +1 -0
  107. package/dist/browser/components/resource/resource-field-selection.d.ts +2 -0
  108. package/dist/browser/components/resource/resource-field-selection.d.ts.map +1 -1
  109. package/dist/browser/components/resource/useResourceFormDrafts.d.ts.map +1 -1
  110. package/dist/browser/components/workflow/StandardWorkflowPages.d.ts +28 -5
  111. package/dist/browser/components/workflow/StandardWorkflowPages.d.ts.map +1 -1
  112. package/dist/browser/components/workflow/StandardWorkflowPages.js +143 -51
  113. package/dist/browser/components/workflow/StandardWorkflowPages.js.map +1 -1
  114. package/dist/browser/components/workflow/WorkflowDelegationManager.js +13 -8
  115. package/dist/browser/components/workflow/WorkflowDelegationManager.js.map +1 -1
  116. package/dist/browser/components/workflow/WorkflowDiagram.d.ts.map +1 -1
  117. package/dist/browser/components/workflow/WorkflowDiagram.js +5 -2
  118. package/dist/browser/components/workflow/WorkflowDiagram.js.map +1 -1
  119. package/dist/browser/components/workflow/WorkflowFlowCanvas.d.ts.map +1 -1
  120. package/dist/browser/components/workflow/WorkflowFlowCanvas.js +2 -1
  121. package/dist/browser/components/workflow/WorkflowFlowCanvas.js.map +1 -1
  122. package/dist/browser/components/workflow/WorkflowNodeCard.d.ts +1 -1
  123. package/dist/browser/components/workflow/WorkflowNodeCard.d.ts.map +1 -1
  124. package/dist/browser/components/workflow/WorkflowNodeCard.js +7 -5
  125. package/dist/browser/components/workflow/WorkflowNodeCard.js.map +1 -1
  126. package/dist/browser/components/workflow/WorkflowNodeConfigurationEditor.d.ts.map +1 -1
  127. package/dist/browser/components/workflow/WorkflowNodeConfigurationEditor.js +14 -11
  128. package/dist/browser/components/workflow/WorkflowNodeConfigurationEditor.js.map +1 -1
  129. package/dist/browser/components/workflow/WorkflowRecordEditor.js +1 -1
  130. package/dist/browser/components/workflow/WorkflowRecordEditor.js.map +1 -1
  131. package/dist/browser/components/workflow/WorkflowRecordHistoryPanel.d.ts +9 -0
  132. package/dist/browser/components/workflow/WorkflowRecordHistoryPanel.d.ts.map +1 -0
  133. package/dist/browser/components/workflow/WorkflowRecordHistoryPanel.js +50 -0
  134. package/dist/browser/components/workflow/WorkflowRecordHistoryPanel.js.map +1 -0
  135. package/dist/browser/components/workflow/WorkflowTaskForm.d.ts +4 -1
  136. package/dist/browser/components/workflow/WorkflowTaskForm.d.ts.map +1 -1
  137. package/dist/browser/components/workflow/WorkflowTaskForm.js +34 -10
  138. package/dist/browser/components/workflow/WorkflowTaskForm.js.map +1 -1
  139. package/dist/browser/components/workflow/standard-process-values.d.ts +10 -2
  140. package/dist/browser/components/workflow/standard-process-values.d.ts.map +1 -1
  141. package/dist/browser/components/workflow/standard-process-values.js +22 -1
  142. package/dist/browser/components/workflow/standard-process-values.js.map +1 -1
  143. package/dist/browser/components/workflow/workflow-flow-layout.d.ts.map +1 -1
  144. package/dist/browser/components/workflow/workflow-flow-layout.js +33 -3
  145. package/dist/browser/components/workflow/workflow-flow-layout.js.map +1 -1
  146. package/dist/browser/components/workflow/workflow-submission-form.d.ts +22 -0
  147. package/dist/browser/components/workflow/workflow-submission-form.d.ts.map +1 -0
  148. package/dist/browser/components/workflow/workflow-submission-form.js +64 -0
  149. package/dist/browser/components/workflow/workflow-submission-form.js.map +1 -0
  150. package/dist/browser/components/workflow/workflow-task-required-errors.d.ts +10 -0
  151. package/dist/browser/components/workflow/workflow-task-required-errors.d.ts.map +1 -0
  152. package/dist/browser/components/workflow/workflow-task-required-errors.js +28 -0
  153. package/dist/browser/components/workflow/workflow-task-required-errors.js.map +1 -0
  154. package/dist/browser/durable-command.d.ts.map +1 -1
  155. package/dist/browser/durable-command.js +7 -2
  156. package/dist/browser/durable-command.js.map +1 -1
  157. package/dist/browser/managed-read-recovery.d.ts +2 -0
  158. package/dist/browser/managed-read-recovery.d.ts.map +1 -1
  159. package/dist/browser/managed-read-recovery.js +6 -1
  160. package/dist/browser/managed-read-recovery.js.map +1 -1
  161. package/dist/browser/platform-client.d.ts +37 -1
  162. package/dist/browser/platform-client.d.ts.map +1 -1
  163. package/dist/browser/platform-client.js +199 -6
  164. package/dist/browser/platform-client.js.map +1 -1
  165. package/dist/browser/record-comments.css +14 -0
  166. package/dist/browser/record-deletion.css +11 -0
  167. package/dist/browser/record-print.css +31 -0
  168. package/dist/browser/runtime-authorization-recovery.d.ts.map +1 -1
  169. package/dist/browser/runtime-authorization-recovery.js +34 -9
  170. package/dist/browser/runtime-authorization-recovery.js.map +1 -1
  171. package/dist/browser/runtime.d.ts +3 -1
  172. package/dist/browser/runtime.d.ts.map +1 -1
  173. package/dist/browser/runtime.js.map +1 -1
  174. package/dist/browser/styles.css +30 -0
  175. package/dist/browser/workflow-operation-input.d.ts +15 -0
  176. package/dist/browser/workflow-operation-input.d.ts.map +1 -0
  177. package/dist/browser/workflow-operation-input.js +64 -0
  178. package/dist/browser/workflow-operation-input.js.map +1 -0
  179. package/dist/config.d.ts +2 -1
  180. package/dist/config.d.ts.map +1 -1
  181. package/dist/config.js +2 -1
  182. package/dist/config.js.map +1 -1
  183. package/dist/core.d.ts +6 -2
  184. package/dist/core.d.ts.map +1 -1
  185. package/dist/core.js +3 -1
  186. package/dist/core.js.map +1 -1
  187. package/dist/expressions.d.ts +4 -0
  188. package/dist/expressions.d.ts.map +1 -0
  189. package/dist/expressions.js +3 -0
  190. package/dist/expressions.js.map +1 -0
  191. package/dist/field-kit.d.ts +3 -0
  192. package/dist/field-kit.d.ts.map +1 -1
  193. package/dist/field-kit.js +3 -0
  194. package/dist/field-kit.js.map +1 -1
  195. package/dist/nest.d.ts +1 -1
  196. package/dist/nest.d.ts.map +1 -1
  197. package/dist/nest.js.map +1 -1
  198. package/dist/react.d.ts +6 -2
  199. package/dist/react.d.ts.map +1 -1
  200. package/dist/react.js +5 -1
  201. package/dist/react.js.map +1 -1
  202. package/dist/testing.d.ts +1 -1
  203. package/dist/testing.d.ts.map +1 -1
  204. package/dist/testing.js +1 -1
  205. package/dist/testing.js.map +1 -1
  206. package/documentation/administration.md +40 -1
  207. package/documentation/backend.md +189 -3
  208. package/documentation/data-authz.md +72 -2
  209. package/documentation/declarations-cheatsheet.md +11 -1
  210. package/documentation/development.md +7 -0
  211. package/documentation/field-components.md +80 -0
  212. package/documentation/frontend.md +8 -0
  213. package/documentation/getting-started.md +60 -7
  214. package/documentation/managed-concurrency-frontend.md +10 -4
  215. package/documentation/manifest.json +12 -12
  216. package/documentation/testing.md +17 -0
  217. package/documentation/workflow-events.md +403 -9
  218. package/package.json +32 -20
  219. package/releases/2.51.0.json +32 -0
  220. package/releases/2.54.4.json +30 -0
  221. package/releases/2.54.6.json +30 -0
  222. package/releases/2.55.0.json +34 -0
  223. package/releases/2.55.1.json +32 -0
  224. package/releases/2.55.5.json +30 -0
  225. package/releases/2.55.6.json +32 -0
  226. package/releases/2.57.1.json +31 -0
  227. package/releases/2.58.0.json +37 -0
  228. package/releases/2.58.1.json +31 -0
  229. package/releases/2.59.0.json +36 -0
  230. package/releases/2.59.1.json +37 -0
  231. package/skills/manifest.json +1 -1
  232. package/skills/openxiangda-v2/SKILL.md +4 -4
  233. package/skills/openxiangda-v2/references/administration.md +40 -1
  234. package/skills/openxiangda-v2/references/backend.md +189 -3
  235. package/skills/openxiangda-v2/references/data-authz.md +72 -2
  236. package/skills/openxiangda-v2/references/declarations-cheatsheet.md +11 -1
  237. package/skills/openxiangda-v2/references/development.md +7 -0
  238. package/skills/openxiangda-v2/references/field-components.md +80 -0
  239. package/skills/openxiangda-v2/references/frontend.md +8 -0
  240. package/skills/openxiangda-v2/references/getting-started.md +60 -7
  241. package/skills/openxiangda-v2/references/managed-concurrency-frontend.md +10 -4
  242. package/skills/openxiangda-v2/references/testing.md +17 -0
  243. package/skills/openxiangda-v2/references/workflow-events.md +403 -9
  244. package/releases/2.48.1.json +0 -30
  245. package/releases/2.48.2.json +0 -29
@@ -3,6 +3,7 @@ import { applicationCode } from '../../runtime-meta';
3
3
  import { readPendingWorkflowSubmission, writePendingWorkflowSubmission, clearPendingWorkflowSubmission, workflowSubmissionScope, submissionLocatorStorage, standardProcessWasRejected } from '../../workflow-submission-recovery';
4
4
  import { readPendingWorkflowTaskCommand, writePendingWorkflowTaskCommand, clearPendingWorkflowTaskCommand, workflowTaskCommandScope, workflowCommandTokenDigest, workflowTaskCommandWasRejected } from '../../workflow-task-command-recovery';
5
5
  import { WorkflowTaskForm, useWorkflowTaskForm } from './WorkflowTaskForm';
6
+ import { retainedWorkflowOperation, workflowOperationSignature, workflowOperationStringError, workflowOperationInputFailure } from '../../workflow-operation-input';
6
7
  import { WorkflowPendingCommandGuard, WorkflowTaskCommandReceipt } from './WorkflowTaskCommandReceipt';
7
8
  import { PresentationTime, usePresentationTimeZone } from '../../presentation-time';
8
9
  import { ArrowLeftOutlined, ClockCircleOutlined, EllipsisOutlined, ReloadOutlined, CloseOutlined, ExpandOutlined, CompressOutlined, ExportOutlined, } from '@ant-design/icons';
@@ -26,8 +27,9 @@ import { ResourceFormContent } from '../resource/ResourceFormFrame';
26
27
  import { WorkflowRecordEditor } from './WorkflowRecordEditor';
27
28
  import { PlatformAvatar } from '../PlatformAvatar';
28
29
  import { SubtableField } from '../platform-fields/SubtableField';
29
- import { standardProcessFormValues } from './standard-process-values';
30
+ import { assertNamedSubtableReadonlyFields, namedProcessFormValues, standardProcessFormValues } from './standard-process-values';
30
31
  import { fieldWritable } from '../resource/resource-page-helpers';
32
+ import { workflowSubmissionFormProjection, workflowSubmissionPrefill, workflowSubmissionValueLinkage } from './workflow-submission-form';
31
33
  /**
32
34
  * An advanced task command has completed the current task and may have
33
35
  * created a new task. Reading the old task Surface in that case is expected
@@ -53,10 +55,13 @@ export async function completeWorkflowCommand(kind, result, options) {
53
55
  catch (error) {
54
56
  refreshFailure = error;
55
57
  }
56
- 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 });
57
62
  if (nextSurface)
58
63
  await options.onCompleted?.(nextSurface, result);
59
- if (refreshFailure)
64
+ if (refreshFailure && !(taskSurfaceUnavailable && options.onCommandCompleted))
60
65
  throw refreshFailure;
61
66
  return nextSurface;
62
67
  }
@@ -87,6 +92,7 @@ function workflowStatus(status) {
87
92
  running: { label: '审批中', color: 'processing' },
88
93
  returned: { label: '已退回', color: 'orange' },
89
94
  completed: { label: '已完成', color: 'success' },
95
+ skipped: { label: '已跳过', color: 'default' },
90
96
  approved: { label: '已同意', color: 'success' },
91
97
  rejected: { label: '已拒绝', color: 'error' },
92
98
  withdrawn: { label: '已撤回', color: 'default' },
@@ -132,25 +138,6 @@ function workflowSurfaceMatchesInstance(surface, instanceId) {
132
138
  function surfaceBusinessDetail(surface) {
133
139
  return normalizeWorkflowBusinessDetail(surface.presentation.businessDetail);
134
140
  }
135
- function workflowOperationSignature(operation) {
136
- return JSON.stringify({
137
- key: operation.key,
138
- kind: operation.kind,
139
- label: operation.label,
140
- placement: operation.placement,
141
- group: operation.group,
142
- audience: operation.audience,
143
- emphasis: operation.emphasis,
144
- tone: operation.tone,
145
- visible: operation.visible,
146
- enabled: operation.enabled,
147
- disabledReason: operation.disabledReason,
148
- inputSchema: operation.inputSchema,
149
- uiSchema: operation.uiSchema,
150
- execute: operation.execute,
151
- refresh: operation.refresh,
152
- });
153
- }
154
141
  const WORKFLOW_APP_ACTION_DISABLED_REASON = '应用动作由应用页面负责';
155
142
  function standardWorkflowOperations(surface) {
156
143
  return surface.operations
@@ -320,7 +307,7 @@ export function WorkflowTimelineSection({ timeline, }) {
320
307
  const operations = item.operations || [];
321
308
  const visiblePeople = visibleWorkflowTimelinePeople(item, operations);
322
309
  const businessStep = item.nodeKind === 'action' ? item.result.businessStep : undefined;
323
- return (_jsxs("div", { className: timelineTone(item.status), children: [_jsx("span", { className: "oxa-workflow-timeline-dot", children: _jsx(WorkflowStatusIcon, { status: item.status }) }), _jsxs("div", { children: [_jsxs("div", { className: "oxa-workflow-timeline-title", children: [_jsx("strong", { children: item.title }), businessStep?.status === 'waiting' ? _jsx(Tag, { color: "processing", children: "\u7B49\u5F85\u4E1A\u52A1\u7ED3\u679C" }) : businessStep?.status === 'result_ready' ? _jsx(Tag, { color: "warning", children: "\u63A8\u8FDB\u53D7\u963B" }) : _jsx(StatusTag, { status: item.status })] }), businessStep?.status === 'result_ready' && _jsx(Typography.Text, { type: "secondary", children: "\u8BA1\u7B97\u7ED3\u679C\u5DF2\u6536\u5230\uFF0C\u540E\u7EED\u6D41\u8F6C\u7B49\u5F85\u4FEE\u590D\u3002" }), visiblePeople.length > 0 && (_jsx("div", { className: "oxa-workflow-timeline-assignees", children: visiblePeople.map((person, index) => (_jsxs("div", { children: [_jsx(PlatformAvatar, { avatarUrl: person.avatarUrl, size: 20 }), _jsxs("span", { children: [_jsx(Typography.Text, { children: person.displayName }), person.departmentDisplayName && (_jsx("small", { children: person.departmentDisplayName }))] })] }, `${person.userId || person.displayName}:${index}`))) })), operations.length > 0 && (_jsx("div", { className: "oxa-workflow-timeline-operations", children: operations.map((operation) => (_jsxs("div", { children: [_jsx(PlatformAvatar, { avatarUrl: operation.actor.avatarUrl, size: 20 }), _jsxs("span", { className: "oxa-workflow-operation-content", children: [_jsxs("span", { children: [_jsx(Typography.Text, { children: operation.actor.displayName }), _jsx("strong", { children: operation.operationLabel })] }), operation.reason && (_jsxs("span", { className: "oxa-workflow-operation-opinion", children: [_jsx(Typography.Text, { type: "secondary", children: "\u610F\u89C1" }), _jsx(Typography.Text, { children: operation.reason })] }))] })] }, operation.id))) })), item.terminalReason && !operations.some(operation => operation.reason) && (_jsxs(Typography.Text, { type: "secondary", children: ["\u539F\u56E0\uFF1A", item.terminalReason] })), item.primaryDisplayTime && (_jsx("time", { className: "oxa-workflow-primary-time", children: formatTime(item.primaryDisplayTime) }))] })] }, item.key));
310
+ return (_jsxs("div", { className: timelineTone(item.status), children: [_jsx("span", { className: "oxa-workflow-timeline-dot", children: _jsx(WorkflowStatusIcon, { status: item.status }) }), _jsxs("div", { children: [_jsxs("div", { className: "oxa-workflow-timeline-title", children: [_jsx("strong", { children: item.title }), businessStep?.status === 'waiting' ? _jsx(Tag, { color: "processing", children: "\u7B49\u5F85\u4E1A\u52A1\u7ED3\u679C" }) : businessStep?.status === 'result_ready' ? _jsx(Tag, { color: "warning", children: "\u63A8\u8FDB\u53D7\u963B" }) : _jsx(StatusTag, { status: item.status })] }), item.status === 'skipped' && _jsx(Typography.Text, { type: "secondary", children: "\u65E0\u5BA1\u6279\u4EBA\uFF0C\u6309\u6B64\u8282\u70B9\u7684\u89C4\u5219\u81EA\u52A8\u7EE7\u7EED\u3002" }), businessStep?.status === 'result_ready' && _jsx(Typography.Text, { type: "secondary", children: "\u8BA1\u7B97\u7ED3\u679C\u5DF2\u6536\u5230\uFF0C\u540E\u7EED\u6D41\u8F6C\u7B49\u5F85\u4FEE\u590D\u3002" }), visiblePeople.length > 0 && (_jsx("div", { className: "oxa-workflow-timeline-assignees", children: visiblePeople.map((person, index) => (_jsxs("div", { children: [_jsx(PlatformAvatar, { avatarUrl: person.avatarUrl, size: 20 }), _jsxs("span", { children: [_jsx(Typography.Text, { children: person.displayName }), person.departmentDisplayName && (_jsx("small", { children: person.departmentDisplayName }))] })] }, `${person.userId || person.displayName}:${index}`))) })), operations.length > 0 && (_jsx("div", { className: "oxa-workflow-timeline-operations", children: operations.map((operation) => (_jsxs("div", { children: [_jsx(PlatformAvatar, { avatarUrl: operation.actor.avatarUrl, size: 20 }), _jsxs("span", { className: "oxa-workflow-operation-content", children: [_jsxs("span", { children: [_jsx(Typography.Text, { children: operation.actor.displayName }), _jsx("strong", { children: operation.operationLabel })] }), operation.reason && (_jsxs("span", { className: "oxa-workflow-operation-opinion", children: [_jsx(Typography.Text, { type: "secondary", children: "\u610F\u89C1" }), _jsx(Typography.Text, { children: operation.reason })] }))] })] }, operation.id))) })), item.terminalReason && !operations.some(operation => operation.reason) && (_jsxs(Typography.Text, { type: "secondary", children: ["\u539F\u56E0\uFF1A", item.terminalReason] })), item.primaryDisplayTime && (_jsx("time", { className: "oxa-workflow-primary-time", children: formatTime(item.primaryDisplayTime) }))] })] }, item.key));
324
311
  }) })) : (_jsx(Empty, { description: "\u6682\u65E0\u6D41\u7A0B\u8F68\u8FF9", image: Empty.PRESENTED_IMAGE_SIMPLE })) }));
325
312
  }
326
313
  function WorkflowMemberPicker({ id, placeholder, value, onChange, multiple = false, variant }) {
@@ -347,6 +334,11 @@ function OperationFields({ operation, variant, }) {
347
334
  return (_jsx(_Fragment, { children: schemaProperties(operation).map(({ key, property, required, ui }) => {
348
335
  const rules = [
349
336
  ...(required ? [{ required: true, message: `请填写${property.title || key}` }] : []),
337
+ ...(property.type === 'string' ? [{ validator: async (_rule, value) => {
338
+ const error = workflowOperationStringError(property, value, required, property.title || key);
339
+ if (error)
340
+ throw new Error(error);
341
+ } }] : []),
350
342
  ...(property.type === 'array' ? [{ validator: async (_rule, value) => {
351
343
  if (value == null && !required)
352
344
  return;
@@ -370,7 +362,7 @@ function OperationFields({ operation, variant, }) {
370
362
  else if (property.format === 'date-time') {
371
363
  control = _jsx(Input, { type: "datetime-local" });
372
364
  }
373
- return (_jsx(Form.Item, { initialValue: property.default, label: property.title || key, name: key, rules: rules, children: control }, key));
365
+ return (_jsx(Form.Item, { initialValue: property.default, label: property.title || key, name: key, rules: rules, validateFirst: true, children: control }, key));
374
366
  }) }));
375
367
  }
376
368
  function OperationDialog({ error, operation, submitting, variant, onCancel, onSubmit, }) {
@@ -392,6 +384,7 @@ function WorkflowOperations({ surface, variant, onRefresh, onCommandSuccess, pen
392
384
  const [selectedState, setSelectedState] = useState(null);
393
385
  const [moreOpen, setMoreOpen] = useState(false);
394
386
  const [operationError, setOperationError] = useState('');
387
+ const [retainRefusedInput, setRetainRefusedInput] = useState(false);
395
388
  const [submitting, setSubmitting] = useState(false);
396
389
  const [draftLocked, setDraftLocked] = useState(false);
397
390
  const [fileLocked, setFileLocked] = useState(false);
@@ -429,16 +422,14 @@ function WorkflowOperations({ surface, variant, onRefresh, onCommandSuccess, pen
429
422
  timer = setTimeout(tick, Math.min(commandExpiresAt - Date.now(), 2_147_483_647));
430
423
  return () => clearTimeout(timer);
431
424
  }, [surface.commandToken, commandExpiresAt]);
432
- const selected = !commandNeedsRefresh && selectedState?.surface === surface
433
- ? operations.find((operation) => operation.key === selectedState.operation.key &&
434
- workflowOperationSignature(operation) ===
435
- selectedState.operationSignature) || null
436
- : null;
425
+ const selected = commandNeedsRefresh ? null
426
+ : retainedWorkflowOperation(selectedState, surface, operations, retainRefusedInput);
437
427
  useEffect(() => {
438
428
  if (selectedState && !selected && !submitting && !unknown)
439
429
  setSelectedState(null);
440
430
  }, [selected, selectedState, submitting, unknown]);
441
431
  const selectOperation = (operation) => {
432
+ setRetainRefusedInput(false);
442
433
  setOperationError('');
443
434
  setSelectedState({
444
435
  operation,
@@ -519,13 +510,13 @@ function WorkflowOperations({ surface, variant, onRefresh, onCommandSuccess, pen
519
510
  if (rejected) {
520
511
  clearLocator(wire.key);
521
512
  request.current = null;
522
- setSelectedState(null);
513
+ setRetainRefusedInput(true);
523
514
  }
524
515
  else {
525
516
  setUnknown(true);
526
517
  setSelectedState(null);
527
518
  }
528
- const failure = errorMessage(error, '任务操作尚未完成');
519
+ const failure = workflowOperationInputFailure(error) || errorMessage(error, '任务操作尚未完成');
529
520
  setOperationError(failure);
530
521
  if (rejected) {
531
522
  message.error(failure);
@@ -607,12 +598,12 @@ function WorkflowOperations({ surface, variant, onRefresh, onCommandSuccess, pen
607
598
  return (order[left.key] ?? 10) - (order[right.key] ?? 10);
608
599
  });
609
600
  const secondary = operations.filter((operation) => operation.placement !== 'primary');
610
- 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, 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'
611
602
  ? '任务操作已成功,但资料刷新失败' : '本次未提交,最新资料读取失败', description: refreshFailure.message, action: _jsx(Button, { disabled: submitting, onClick: () => void refreshConfirmed(), children: "\u5237\u65B0\u8D44\u6599" }) })] })) : null;
612
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: () => {
613
604
  setMoreOpen(false);
614
605
  selectOperation(operation);
615
- }, type: "text", title: operation.disabledReason, children: operation.label }, operation.key))) }))] }), _jsx(OperationDialog, { error: operationError, onCancel: () => setSelectedState(null), onSubmit: (values) => void submit(values), operation: selected, submitting: submitting, variant: variant })] }));
606
+ }, type: "text", title: operation.disabledReason, children: operation.label }, operation.key))) }))] }), _jsx(OperationDialog, { error: operationError, onCancel: () => { setRetainRefusedInput(false); setSelectedState(null); }, onSubmit: (values) => void submit(values), operation: selected, submitting: submitting, variant: variant })] }));
616
607
  return renderLayout({ content, actions });
617
608
  }
618
609
  /**
@@ -633,6 +624,7 @@ function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: su
633
624
  const [writing, setWriting] = useState(false);
634
625
  const [settlement, setSettlement] = useState(null);
635
626
  const [confirmedReadFailure, setConfirmedReadFailure] = useState(null);
627
+ const [taskCommandSettled, setTaskCommandSettled] = useState(false);
636
628
  const locator = locatorState.scope === locatorScope ? locatorState.value : null;
637
629
  useEffect(() => {
638
630
  setLocatorState({ scope: locatorScope, value: locatorScope ? readPendingWorkflowTaskCommand(submissionLocatorStorage(), locatorScope) : null });
@@ -760,6 +752,7 @@ function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: su
760
752
  ]);
761
753
  useEffect(() => {
762
754
  activeRef.current = true;
755
+ setTaskCommandSettled(false);
763
756
  requestSequence.current += 1;
764
757
  if (!normalizedIdentifier) {
765
758
  setSurface(null);
@@ -842,11 +835,19 @@ function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: su
842
835
  ? nextSurface
843
836
  : null;
844
837
  },
845
- onCommandCompleted: async (commandResult) => {
838
+ onCommandCompleted: async (commandResult, context) => {
846
839
  if (!isCurrentGeneration(identifierAtStart, generationAtStart)) {
847
840
  return;
848
841
  }
849
- 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);
850
851
  },
851
852
  onCompleted: async (nextSurface, commandResult) => {
852
853
  if (!isCurrentGeneration(identifierAtStart, generationAtStart)) {
@@ -919,7 +920,7 @@ function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: su
919
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" })] })) });
920
921
  }
921
922
  if (!renderSurface)
922
- 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 });
923
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}`));
924
925
  }
925
926
  /** Public, embeddable Workflow task operation surface. */
@@ -945,12 +946,14 @@ function StandardWorkflowDetailRenderer({ surface, timeline, warning, operations
945
946
  const timeZone = usePresentationTimeZone();
946
947
  const [tab, setTab] = useState('application');
947
948
  const instance = surfaceInstance(surface);
949
+ const deadline = surfaceTask(surface)?.completionDeadline;
948
950
  const business = surfaceBusinessDetail(surface);
949
951
  const summary = surface.presentation.summary;
950
952
  const titleField = workflowDetailGroups(business).flatMap(group => group.fields).find(field => field.type === 'text.short');
951
953
  const title = titleField && business.record[titleField.key] ? String(business.record[titleField.key]) : summary.title;
952
954
  const activeNode = timeline?.display?.entries.find(entry => entry.status === 'active');
953
- const people = activeNode?.people.map(person => person.displayName).filter(Boolean).join('、');
955
+ const activeFlow = activeNode && timeline?.flow.find(entry => entry.key === activeNode.key && entry.status === 'active');
956
+ const people = activeFlow?.assignees.filter(person => person.status === 'active').map(person => person.displayName).filter(Boolean).join('、');
954
957
  const statusTone = workflowStatus(instance.status).color;
955
958
  const bannerTone = statusTone === 'error' ? 'error' : 'active';
956
959
  const edit = workflowHasEnded(instance) ? onEdit : undefined;
@@ -958,7 +961,7 @@ function StandardWorkflowDetailRenderer({ surface, timeline, warning, operations
958
961
  const loadAudit = useCallback(() => loadWorkflowDataAudit(instance.id), [instance.id]);
959
962
  const back = onClose || (() => navigate(variant === 'mobile' ? '/m/work-center' : '/work-center'));
960
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,
961
- 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: [
962
965
  { key: 'application', label: '申请内容', children: _jsx(WorkflowBusinessDetailSections, { surface: surface, variant: variant }) },
963
966
  { key: 'history', label: '审批历史', children: _jsx(WorkflowTimelineSection, { timeline: timeline }) },
964
967
  { key: 'changes', label: '变更记录', children: tab === 'changes' && business.surface && business.resourceCode && business.recordId
@@ -1044,6 +1047,16 @@ function WorkflowDetailPage({ kind, variant, resourceCode, recordId, onDismiss,
1044
1047
  const id = recordId || String(kind === 'task' ? params.taskId || '' : kind === 'record' ? params.id || '' : params.instanceId || '');
1045
1048
  const navigate = useNavigate();
1046
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]);
1047
1060
  const [editing, setEditing] = useState(false);
1048
1061
  const [editBusy, setEditBusy] = useState(false);
1049
1062
  const { detail, surface, timeline, loading, error, refresh } = useWorkflowDetail({ kind, id, resourceCode });
@@ -1053,12 +1066,17 @@ function WorkflowDetailPage({ kind, variant, resourceCode, recordId, onDismiss,
1053
1066
  const detailPath = surface ? (variant === 'mobile' ? surface.detailNavigation.mobilePath : surface.detailNavigation.desktopPath) : '';
1054
1067
  const newPageHref = useHref(detailPath || '.');
1055
1068
  const close = onDismiss || (() => navigate(returnPath || (detail ? variant === 'mobile' ? detail.navigationContext.mobileReturnPath : detail.navigationContext.desktopReturnPath : variant === 'mobile' ? '/m/work-center' : '/work-center')));
1056
- const onCommandCompleted = async (result) => {
1057
- if (kind === 'task' && result.advanced === true) {
1058
- 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();
1059
1075
  if (!nextId)
1060
- return;
1076
+ throw new Error('WORKFLOW_INSTANCE_ID_REQUIRED_AFTER_COMMAND');
1061
1077
  const next = await loadWorkflowInstanceDetail(nextId);
1078
+ if (commandHostRef.current !== host)
1079
+ return;
1062
1080
  navigate(variant === 'mobile' ? next.surface.detailNavigation.mobilePath : next.surface.detailNavigation.desktopPath, { replace: true });
1063
1081
  }
1064
1082
  else
@@ -1254,7 +1272,7 @@ async function workflowContextPrefillValue(field, rawValue, resources) {
1254
1272
  }
1255
1273
  return workflowContextScalarValue(field, value);
1256
1274
  }
1257
- export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWorkflowCode, variant = 'desktop', onDismiss, onCompleted, } = {}) {
1275
+ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWorkflowCode, variant = 'desktop', onDismiss, onCompleted, formOptions, } = {}) {
1258
1276
  const { workflowCode: routeWorkflowCode = '' } = useParams();
1259
1277
  const workflowCode = declaredWorkflowCode || routeWorkflowCode;
1260
1278
  const definition = useWorkflowDefinition(workflowCode);
@@ -1265,6 +1283,10 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
1265
1283
  const { message } = App.useApp();
1266
1284
  const [subjectForm] = Form.useForm();
1267
1285
  const [requirementForm] = Form.useForm();
1286
+ const watchedFormValues = Form.useWatch([], { form: subjectForm, preserve: true });
1287
+ const [prefillError, setPrefillError] = useState(false);
1288
+ const [linkageError, setLinkageError] = useState(false);
1289
+ const prefill = useRef({ context: '', applied: new Set() });
1268
1290
  const [loading, setLoading] = useState(false);
1269
1291
  const [launchSurface, setLaunchSurface] = useState(null);
1270
1292
  const [launchError, setLaunchError] = useState(null);
@@ -1357,6 +1379,57 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
1357
1379
  namedIntent,
1358
1380
  subjectDefinition,
1359
1381
  ]);
1382
+ const formProjection = useMemo(() => {
1383
+ try {
1384
+ const values = subjectDefinition ? normalizeFormValues(watchedFormValues || {}, subjectDefinition.surface) : {};
1385
+ return { ...workflowSubmissionFormProjection(fields, values, formOptions?.fieldState?.(values)), error: false };
1386
+ }
1387
+ catch {
1388
+ return { fields, values: {}, hiddenValues: {}, error: true };
1389
+ }
1390
+ }, [fields, formOptions?.fieldState, subjectDefinition, watchedFormValues]);
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
+ };
1408
+ useEffect(() => {
1409
+ if (!launchSurface || commandId || pendingSubmission || formOptions?.preparation || mutationMode !== 'create' || !subjectDefinition ||
1410
+ launchSurface.environmentId !== identity.environment.id || (generatedNamedSubmission && !namedIntent))
1411
+ return;
1412
+ if (prefill.current.context !== prefillContext)
1413
+ prefill.current = { context: prefillContext, applied: new Set() };
1414
+ try {
1415
+ const values = workflowSubmissionPrefill(fields, formOptions?.initialValues || {}, prefill.current.applied, key => subjectForm.isFieldTouched(key));
1416
+ const normalized = normalizeRecordForForm(values, subjectDefinition.surface);
1417
+ subjectForm.setFields(Object.entries(normalized).map(([name, value]) => ({ name, value, touched: false })));
1418
+ setPrefillError(false);
1419
+ }
1420
+ catch {
1421
+ setPrefillError(true);
1422
+ }
1423
+ }, [commandId, fields, formOptions?.initialValues, formOptions?.preparation, generatedNamedSubmission, identity.environment.id,
1424
+ launchSurface, mutationMode, namedIntent, pendingSubmission, prefillContext, subjectDefinition, subjectForm]);
1425
+ useEffect(() => {
1426
+ if (loading || commandId || pendingSubmission || !launchSurface || formProjection.error || !subjectDefinition)
1427
+ return;
1428
+ const values = normalizeRecordForForm(formProjection.hiddenValues, subjectDefinition.surface);
1429
+ const changes = Object.entries(values).filter(([key, value]) => JSON.stringify(subjectForm.getFieldValue(key)) !== JSON.stringify(value));
1430
+ if (changes.length)
1431
+ subjectForm.setFields(changes.map(([name, value]) => ({ name, value, errors: [] })));
1432
+ }, [commandId, formProjection, launchSurface, loading, pendingSubmission, subjectDefinition, subjectForm]);
1360
1433
  useEffect(() => {
1361
1434
  let active = true;
1362
1435
  setLaunchSurface(null);
@@ -1691,6 +1764,8 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
1691
1764
  : '当前部署版本未声明 launch 配置;请升级工具链后为 workflows.definitions[].launch 显式声明 mode' }));
1692
1765
  if (!subjectDefinition)
1693
1766
  return frame(_jsx(Result, { status: "500", title: "\u6807\u51C6\u6D41\u7A0B\u5408\u540C\u4E0D\u5B8C\u6574" }));
1767
+ if (!commandId && formOptions?.preparation)
1768
+ return frame(formOptions.preparation);
1694
1769
  if (launchError && !commandId)
1695
1770
  return frame(_jsx(Result, { status: "error", title: "\u53D1\u8D77\u9875\u9762\u5F53\u524D\u4E0D\u53EF\u7528", subTitle: launchError }));
1696
1771
  if (!launchSurface && !commandId)
@@ -1706,11 +1781,12 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
1706
1781
  launchSurface?.submission.kind !== 'standard-process')) {
1707
1782
  return frame(_jsx(Result, { status: "500", title: "\u6807\u51C6\u6D41\u7A0B\u5408\u540C\u4E0D\u5B8C\u6574" }));
1708
1783
  }
1709
- if (!commandId && namedIntent && fields.some(field => field.type === 'subtable')) {
1710
- 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" }));
1711
1787
  }
1712
1788
  const submit = async (values) => {
1713
- if (submitInFlight.current || commandId || completion || !launchSurface)
1789
+ if (submitInFlight.current || commandId || completion || !launchSurface || formOptions?.preparation || prefillError || linkageError || formProjection.error)
1714
1790
  return;
1715
1791
  const unresolved = readPendingWorkflowSubmission(submissionLocatorStorage(), recoveryScope);
1716
1792
  if (pendingSubmission || unresolved) {
@@ -1730,9 +1806,9 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
1730
1806
  setRecoveryObservedAbsent(false);
1731
1807
  };
1732
1808
  try {
1733
- const encoded = namedIntent ? normalizeFormValues(values, subjectDefinition.surface)
1809
+ const encoded = namedIntent ? namedProcessFormValues(values, subjectDefinition.surface, resources, namedIntent.ownedSubject, formOptions?.subtableReadonlyFields)
1734
1810
  : standardProcessFormValues(values, subjectDefinition.surface, resources, (field, mode) => fieldWritable(field, mode === 'create' ? 'create' : 'edit', hasCapability, identity.isAppSuperAdmin));
1735
- const data = Object.fromEntries(fields.filter(field => Object.hasOwn(encoded, field.key)).map(field => [field.key, encoded[field.key]]));
1811
+ const data = workflowSubmissionFormProjection(fields, encoded, formOptions?.fieldState?.(encoded)).values;
1736
1812
  const signature = JSON.stringify({ workflowCode, subjectId, environmentKey: identity.environment.key, submissionMode, data });
1737
1813
  if (submissionAttempt.current?.signature !== signature) {
1738
1814
  submissionAttempt.current = { signature, idempotencyKey: randomId(`process:${workflowCode}`), requestedAt: new Date().toISOString() };
@@ -1920,8 +1996,20 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
1920
1996
  const uploadOperationCode = namedIntent?.operationCode || definition.processOperationCode;
1921
1997
  const fieldRenderers = uploadOperationCode
1922
1998
  ? {
1923
- ...(!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
+ },
1924
2011
  ...(namedIntent ? { referenceLaunch: { workflowCode: definition.code, operationCode: namedIntent.operationCode } } : {}),
2012
+ candidateScopeValues: { ...formOptions?.candidateScopeValues, ...formProjection.values },
1925
2013
  upload: async (field, file) => await uploadOperationManagedFile({
1926
2014
  operationCode: uploadOperationCode,
1927
2015
  resourceCode: subjectDefinition.code,
@@ -1945,7 +2033,7 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
1945
2033
  (namedIntent
1946
2034
  ? '业务操作将在服务端完成校验、幂等提交,并按规则决定是否进入审批'
1947
2035
  : '业务数据和流程发起意图将原子提交'),
1948
- fields,
2036
+ fields: formProjection.fields,
1949
2037
  subjectDefinition,
1950
2038
  subjectForm,
1951
2039
  requirementForm,
@@ -1959,9 +2047,13 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
1959
2047
  completion,
1960
2048
  embedded: Boolean(onDismiss),
1961
2049
  fieldRenderers,
2050
+ formIntro: formOptions?.intro,
2051
+ formError: prefillError || linkageError || formProjection.error ? '表单规则暂不可用,请联系管理员。' : undefined,
1962
2052
  recordId: mutationMode === 'update' ? subjectId : undefined,
2053
+ expectedRevision: mutationMode === 'update' ? loadedSubjectRevision : undefined,
1963
2054
  onBack: onDismiss ? dismissDrawer : () => navigate(-1),
1964
2055
  onSubmit: submit,
2056
+ onValuesChange,
1965
2057
  onAnswer: answer,
1966
2058
  onRetry: retry,
1967
2059
  onRefresh: () => { setProcessError(null); setProcessRefreshAttempt(value => value + 1); },
@@ -1988,7 +2080,7 @@ function StandardProcessSubmissionRenderer(props) {
1988
2080
  }, new Map())].map(([section, fields]) => ({ section, fields }));
1989
2081
  if (props.completion)
1990
2082
  return _jsx(Result, { status: "success", title: "\u63D0\u4EA4\u6210\u529F", extra: _jsx(Button, { onClick: props.onBack, children: "\u8FD4\u56DE" }) });
1991
- 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, form: props.subjectForm, busy: props.loading || props.submitted, pending: props.loading || props.processing, submitDisabled: props.submitted, submitLabel: "\u63D0\u4EA4\u5BA1\u6279", canWriteField: () => true, renderers: props.fieldRenderers, feedback: _jsxs(_Fragment, { children: [props.launchControls, 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 })] });
1992
2084
  }
1993
2085
  function DesktopProcessSubmissionRenderer(props) { return _jsx(StandardProcessSubmissionRenderer, { ...props, variant: "desktop" }); }
1994
2086
  function MobileProcessSubmissionRenderer(props) { return _jsx(StandardProcessSubmissionRenderer, { ...props, variant: "mobile" }); }