openxiangda 2.35.3 → 2.48.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 (197) hide show
  1. package/dist/browser/FilePreviewPage.d.ts.map +1 -1
  2. package/dist/browser/FilePreviewPage.js +4 -2
  3. package/dist/browser/FilePreviewPage.js.map +1 -1
  4. package/dist/browser/ManagedCommand.d.ts +1 -0
  5. package/dist/browser/ManagedCommand.d.ts.map +1 -1
  6. package/dist/browser/ManagedCommand.js +1 -1
  7. package/dist/browser/ManagedCommand.js.map +1 -1
  8. package/dist/browser/Shell.d.ts.map +1 -1
  9. package/dist/browser/Shell.js +1 -1
  10. package/dist/browser/Shell.js.map +1 -1
  11. package/dist/browser/components/administration/RoleMembershipManager.d.ts +7 -0
  12. package/dist/browser/components/administration/RoleMembershipManager.d.ts.map +1 -0
  13. package/dist/browser/components/administration/RoleMembershipManager.js +724 -0
  14. package/dist/browser/components/administration/RoleMembershipManager.js.map +1 -0
  15. package/dist/browser/components/administration/membership-batch-model.d.ts +17 -0
  16. package/dist/browser/components/administration/membership-batch-model.d.ts.map +1 -0
  17. package/dist/browser/components/administration/membership-batch-model.js +117 -0
  18. package/dist/browser/components/administration/membership-batch-model.js.map +1 -0
  19. package/dist/browser/components/platform-fields/AttachmentFileList.d.ts.map +1 -1
  20. package/dist/browser/components/platform-fields/AttachmentFileList.js +2 -1
  21. package/dist/browser/components/platform-fields/AttachmentFileList.js.map +1 -1
  22. package/dist/browser/components/platform-fields/JsonField.d.ts.map +1 -1
  23. package/dist/browser/components/platform-fields/JsonField.js +6 -1
  24. package/dist/browser/components/platform-fields/JsonField.js.map +1 -1
  25. package/dist/browser/components/platform-fields/MobileManagedFileField.d.ts +3 -1
  26. package/dist/browser/components/platform-fields/MobileManagedFileField.d.ts.map +1 -1
  27. package/dist/browser/components/platform-fields/MobileManagedFileField.js +15 -6
  28. package/dist/browser/components/platform-fields/MobileManagedFileField.js.map +1 -1
  29. package/dist/browser/components/platform-fields/MobileSubtableValidation.d.ts +2 -1
  30. package/dist/browser/components/platform-fields/MobileSubtableValidation.d.ts.map +1 -1
  31. package/dist/browser/components/platform-fields/MobileSubtableValidation.js +3 -1
  32. package/dist/browser/components/platform-fields/MobileSubtableValidation.js.map +1 -1
  33. package/dist/browser/components/platform-fields/RichTextField.d.ts +3 -2
  34. package/dist/browser/components/platform-fields/RichTextField.d.ts.map +1 -1
  35. package/dist/browser/components/platform-fields/RichTextField.js +96 -44
  36. package/dist/browser/components/platform-fields/RichTextField.js.map +1 -1
  37. package/dist/browser/components/platform-fields/SignatureField.d.ts +5 -4
  38. package/dist/browser/components/platform-fields/SignatureField.d.ts.map +1 -1
  39. package/dist/browser/components/platform-fields/SignatureField.js +69 -16
  40. package/dist/browser/components/platform-fields/SignatureField.js.map +1 -1
  41. package/dist/browser/components/platform-fields/SubtableField.d.ts +18 -2
  42. package/dist/browser/components/platform-fields/SubtableField.d.ts.map +1 -1
  43. package/dist/browser/components/platform-fields/SubtableField.js +146 -57
  44. package/dist/browser/components/platform-fields/SubtableField.js.map +1 -1
  45. package/dist/browser/components/platform-fields/subtable-pagination.d.ts +20 -0
  46. package/dist/browser/components/platform-fields/subtable-pagination.d.ts.map +1 -0
  47. package/dist/browser/components/platform-fields/subtable-pagination.js +39 -0
  48. package/dist/browser/components/platform-fields/subtable-pagination.js.map +1 -0
  49. package/dist/browser/components/platform-fields/subtable-value.d.ts.map +1 -1
  50. package/dist/browser/components/platform-fields/subtable-value.js +2 -1
  51. package/dist/browser/components/platform-fields/subtable-value.js.map +1 -1
  52. package/dist/browser/components/platform-fields/surface-field-validation.d.ts +31 -0
  53. package/dist/browser/components/platform-fields/surface-field-validation.d.ts.map +1 -0
  54. package/dist/browser/components/platform-fields/surface-field-validation.js +36 -0
  55. package/dist/browser/components/platform-fields/surface-field-validation.js.map +1 -0
  56. package/dist/browser/components/resource/SurfaceFields.d.ts +4 -3
  57. package/dist/browser/components/resource/SurfaceFields.d.ts.map +1 -1
  58. package/dist/browser/components/resource/SurfaceFields.js +46 -72
  59. package/dist/browser/components/resource/SurfaceFields.js.map +1 -1
  60. package/dist/browser/components/resource/resource-form-operations.d.ts.map +1 -1
  61. package/dist/browser/components/resource/resource-form-operations.js +5 -1
  62. package/dist/browser/components/resource/resource-form-operations.js.map +1 -1
  63. package/dist/browser/components/resource/useResourceFormDrafts.d.ts.map +1 -1
  64. package/dist/browser/components/workflow/StandardWorkflowPages.d.ts +8 -1
  65. package/dist/browser/components/workflow/StandardWorkflowPages.d.ts.map +1 -1
  66. package/dist/browser/components/workflow/StandardWorkflowPages.js +305 -80
  67. package/dist/browser/components/workflow/StandardWorkflowPages.js.map +1 -1
  68. package/dist/browser/components/workflow/WorkflowAssignmentRoutingEditor.d.ts +8 -0
  69. package/dist/browser/components/workflow/WorkflowAssignmentRoutingEditor.d.ts.map +1 -0
  70. package/dist/browser/components/workflow/WorkflowAssignmentRoutingEditor.js +174 -0
  71. package/dist/browser/components/workflow/WorkflowAssignmentRoutingEditor.js.map +1 -0
  72. package/dist/browser/components/workflow/WorkflowAssignmentRoutingManager.d.ts +3 -0
  73. package/dist/browser/components/workflow/WorkflowAssignmentRoutingManager.d.ts.map +1 -0
  74. package/dist/browser/components/workflow/WorkflowAssignmentRoutingManager.js +75 -0
  75. package/dist/browser/components/workflow/WorkflowAssignmentRoutingManager.js.map +1 -0
  76. package/dist/browser/components/workflow/WorkflowBusinessStepRecoveryPanel.d.ts +14 -0
  77. package/dist/browser/components/workflow/WorkflowBusinessStepRecoveryPanel.d.ts.map +1 -0
  78. package/dist/browser/components/workflow/WorkflowBusinessStepRecoveryPanel.js +117 -0
  79. package/dist/browser/components/workflow/WorkflowBusinessStepRecoveryPanel.js.map +1 -0
  80. package/dist/browser/components/workflow/WorkflowDelegationManager.d.ts +14 -0
  81. package/dist/browser/components/workflow/WorkflowDelegationManager.d.ts.map +1 -0
  82. package/dist/browser/components/workflow/WorkflowDelegationManager.js +204 -0
  83. package/dist/browser/components/workflow/WorkflowDelegationManager.js.map +1 -0
  84. package/dist/browser/components/workflow/WorkflowDiagram.d.ts.map +1 -1
  85. package/dist/browser/components/workflow/WorkflowDiagram.js +14 -1
  86. package/dist/browser/components/workflow/WorkflowDiagram.js.map +1 -1
  87. package/dist/browser/components/workflow/WorkflowFlowCanvas.d.ts.map +1 -1
  88. package/dist/browser/components/workflow/WorkflowFlowCanvas.js +27 -8
  89. package/dist/browser/components/workflow/WorkflowFlowCanvas.js.map +1 -1
  90. package/dist/browser/components/workflow/WorkflowNodeCard.d.ts.map +1 -1
  91. package/dist/browser/components/workflow/WorkflowNodeCard.js +5 -2
  92. package/dist/browser/components/workflow/WorkflowNodeCard.js.map +1 -1
  93. package/dist/browser/components/workflow/WorkflowNodeConfigurationEditor.d.ts.map +1 -1
  94. package/dist/browser/components/workflow/WorkflowNodeConfigurationEditor.js +6 -5
  95. package/dist/browser/components/workflow/WorkflowNodeConfigurationEditor.js.map +1 -1
  96. package/dist/browser/components/workflow/WorkflowTaskCommandReceipt.d.ts +10 -0
  97. package/dist/browser/components/workflow/WorkflowTaskCommandReceipt.d.ts.map +1 -0
  98. package/dist/browser/components/workflow/WorkflowTaskCommandReceipt.js +59 -0
  99. package/dist/browser/components/workflow/WorkflowTaskCommandReceipt.js.map +1 -0
  100. package/dist/browser/components/workflow/WorkflowTaskDraftPanel.d.ts +17 -0
  101. package/dist/browser/components/workflow/WorkflowTaskDraftPanel.d.ts.map +1 -0
  102. package/dist/browser/components/workflow/WorkflowTaskDraftPanel.js +136 -0
  103. package/dist/browser/components/workflow/WorkflowTaskDraftPanel.js.map +1 -0
  104. package/dist/browser/components/workflow/WorkflowTaskFiles.d.ts +22 -0
  105. package/dist/browser/components/workflow/WorkflowTaskFiles.d.ts.map +1 -0
  106. package/dist/browser/components/workflow/WorkflowTaskFiles.js +164 -0
  107. package/dist/browser/components/workflow/WorkflowTaskFiles.js.map +1 -0
  108. package/dist/browser/components/workflow/WorkflowTaskForm.d.ts +53 -0
  109. package/dist/browser/components/workflow/WorkflowTaskForm.d.ts.map +1 -0
  110. package/dist/browser/components/workflow/WorkflowTaskForm.js +175 -0
  111. package/dist/browser/components/workflow/WorkflowTaskForm.js.map +1 -0
  112. package/dist/browser/components/workflow/standard-process-values.d.ts +10 -0
  113. package/dist/browser/components/workflow/standard-process-values.d.ts.map +1 -0
  114. package/dist/browser/components/workflow/standard-process-values.js +25 -0
  115. package/dist/browser/components/workflow/standard-process-values.js.map +1 -0
  116. package/dist/browser/components/workflow/workflow-graph-presentation.d.ts +19 -0
  117. package/dist/browser/components/workflow/workflow-graph-presentation.d.ts.map +1 -0
  118. package/dist/browser/components/workflow/workflow-graph-presentation.js +28 -0
  119. package/dist/browser/components/workflow/workflow-graph-presentation.js.map +1 -0
  120. package/dist/browser/components/workflow/workflow-routing-draft.d.ts +4 -0
  121. package/dist/browser/components/workflow/workflow-routing-draft.d.ts.map +1 -0
  122. package/dist/browser/components/workflow/workflow-routing-draft.js +9 -0
  123. package/dist/browser/components/workflow/workflow-routing-draft.js.map +1 -0
  124. package/dist/browser/components/workflow/workflow-task-field-codec.d.ts +4 -0
  125. package/dist/browser/components/workflow/workflow-task-field-codec.d.ts.map +1 -0
  126. package/dist/browser/components/workflow/workflow-task-field-codec.js +9 -0
  127. package/dist/browser/components/workflow/workflow-task-field-codec.js.map +1 -0
  128. package/dist/browser/components/workflow/workflow-task-subtable.d.ts +7 -0
  129. package/dist/browser/components/workflow/workflow-task-subtable.d.ts.map +1 -0
  130. package/dist/browser/components/workflow/workflow-task-subtable.js +58 -0
  131. package/dist/browser/components/workflow/workflow-task-subtable.js.map +1 -0
  132. package/dist/browser/durable-command.d.ts +9 -0
  133. package/dist/browser/durable-command.d.ts.map +1 -1
  134. package/dist/browser/durable-command.js +67 -13
  135. package/dist/browser/durable-command.js.map +1 -1
  136. package/dist/browser/mobile-fields.css +4 -0
  137. package/dist/browser/navigation-guard.d.ts +3 -1
  138. package/dist/browser/navigation-guard.d.ts.map +1 -1
  139. package/dist/browser/navigation-guard.js +7 -3
  140. package/dist/browser/navigation-guard.js.map +1 -1
  141. package/dist/browser/platform-client.d.ts +67 -3
  142. package/dist/browser/platform-client.d.ts.map +1 -1
  143. package/dist/browser/platform-client.js +135 -5
  144. package/dist/browser/platform-client.js.map +1 -1
  145. package/dist/browser/record-detail.css +14 -2
  146. package/dist/browser/runtime.d.ts +2 -0
  147. package/dist/browser/runtime.d.ts.map +1 -1
  148. package/dist/browser/runtime.js +5 -1
  149. package/dist/browser/runtime.js.map +1 -1
  150. package/dist/browser/styles.css +92 -0
  151. package/dist/browser/workflow-task-command-recovery.d.ts +17 -0
  152. package/dist/browser/workflow-task-command-recovery.d.ts.map +1 -0
  153. package/dist/browser/workflow-task-command-recovery.js +50 -0
  154. package/dist/browser/workflow-task-command-recovery.js.map +1 -0
  155. package/dist/browser/workflow-task-file-upload.d.ts +18 -0
  156. package/dist/browser/workflow-task-file-upload.d.ts.map +1 -0
  157. package/dist/browser/workflow-task-file-upload.js +36 -0
  158. package/dist/browser/workflow-task-file-upload.js.map +1 -0
  159. package/dist/config.d.ts +1 -1
  160. package/dist/config.d.ts.map +1 -1
  161. package/dist/core.d.ts +5 -1
  162. package/dist/core.d.ts.map +1 -1
  163. package/dist/core.js +1 -0
  164. package/dist/core.js.map +1 -1
  165. package/dist/nest.d.ts +2 -2
  166. package/dist/nest.d.ts.map +1 -1
  167. package/dist/nest.js.map +1 -1
  168. package/dist/react.d.ts +8 -2
  169. package/dist/react.d.ts.map +1 -1
  170. package/dist/react.js +7 -1
  171. package/dist/react.js.map +1 -1
  172. package/documentation/administration.md +128 -1
  173. package/documentation/field-components.md +1 -1
  174. package/documentation/frontend.md +2 -0
  175. package/documentation/getting-started.md +7 -7
  176. package/documentation/managed-concurrency-frontend.md +9 -4
  177. package/documentation/manifest.json +7 -7
  178. package/documentation/workflow-events.md +303 -3
  179. package/package.json +21 -37
  180. package/releases/2.48.1.json +30 -0
  181. package/releases/2.48.2.json +29 -0
  182. package/skills/manifest.json +1 -1
  183. package/skills/openxiangda-v2/SKILL.md +4 -4
  184. package/skills/openxiangda-v2/references/administration.md +128 -1
  185. package/skills/openxiangda-v2/references/field-components.md +1 -1
  186. package/skills/openxiangda-v2/references/frontend.md +2 -0
  187. package/skills/openxiangda-v2/references/getting-started.md +7 -7
  188. package/skills/openxiangda-v2/references/managed-concurrency-frontend.md +9 -4
  189. package/skills/openxiangda-v2/references/workflow-events.md +303 -3
  190. package/dist/browser/components/platform-fields/MobileRichTextField.d.ts +0 -4
  191. package/dist/browser/components/platform-fields/MobileRichTextField.d.ts.map +0 -1
  192. package/dist/browser/components/platform-fields/MobileRichTextField.js +0 -38
  193. package/dist/browser/components/platform-fields/MobileRichTextField.js.map +0 -1
  194. package/releases/2.35.0.json +0 -42
  195. package/releases/2.35.1.json +0 -43
  196. package/releases/2.35.2.json +0 -45
  197. package/releases/2.35.3.json +0 -46
@@ -1,6 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { applicationCode } from '../../runtime-meta';
3
3
  import { readPendingWorkflowSubmission, writePendingWorkflowSubmission, clearPendingWorkflowSubmission, workflowSubmissionScope, submissionLocatorStorage, standardProcessWasRejected } from '../../workflow-submission-recovery';
4
+ import { readPendingWorkflowTaskCommand, writePendingWorkflowTaskCommand, clearPendingWorkflowTaskCommand, workflowTaskCommandScope, workflowCommandTokenDigest, workflowTaskCommandWasRejected } from '../../workflow-task-command-recovery';
5
+ import { WorkflowTaskForm, useWorkflowTaskForm } from './WorkflowTaskForm';
6
+ import { WorkflowPendingCommandGuard, WorkflowTaskCommandReceipt } from './WorkflowTaskCommandReceipt';
4
7
  import { PresentationTime, usePresentationTimeZone } from '../../presentation-time';
5
8
  import { ArrowLeftOutlined, ClockCircleOutlined, EllipsisOutlined, ReloadOutlined, CloseOutlined, ExpandOutlined, CompressOutlined, ExportOutlined, } from '@ant-design/icons';
6
9
  import { Alert, App, Button, Card, Drawer, Empty, Form, Input, Modal, Result, Segmented, Select, Space, Spin, Table, Tabs, Tag, Tooltip, Typography, } from 'antd';
@@ -13,7 +16,7 @@ import { executeWorkflowOperation, executeWorkflowLaunchNamedOperation, loadWork
13
16
  import { useWorkflowDefinition, } from '../../workflow-definitions';
14
17
  import { useResourceDefinitions } from '../../resource-definitions';
15
18
  import { selectedSurfaceFields } from '../resource/resource-field-selection';
16
- import { useRuntime } from '../../runtime';
19
+ import { useRuntime, useOptionalRuntime } from '../../runtime';
17
20
  import { SurfaceFieldValue, } from '../resource/SurfaceFields';
18
21
  import { workflowContextScalarValue, processStatusReadRetryDelay, buildWorkflowNamedOperationInput, parseWorkflowNamedOperationResult, workflowLaunchContractJsonEqual, } from '../../workflow-launch';
19
22
  import { normalizeFormValues, normalizeRecordForForm, } from '../platform-fields/field-form-codec';
@@ -22,6 +25,9 @@ import { RecordChangeHistory } from '../resource/RecordChangeHistory';
22
25
  import { ResourceFormContent } from '../resource/ResourceFormFrame';
23
26
  import { WorkflowRecordEditor } from './WorkflowRecordEditor';
24
27
  import { PlatformAvatar } from '../PlatformAvatar';
28
+ import { SubtableField } from '../platform-fields/SubtableField';
29
+ import { standardProcessFormValues } from './standard-process-values';
30
+ import { fieldWritable } from '../resource/resource-page-helpers';
25
31
  /**
26
32
  * An advanced task command has completed the current task and may have
27
33
  * created a new task. Reading the old task Surface in that case is expected
@@ -39,13 +45,32 @@ export function shouldRefreshWorkflowSurface(kind, result) {
39
45
  */
40
46
  export async function completeWorkflowCommand(kind, result, options) {
41
47
  const refreshSurface = shouldRefreshWorkflowSurface(kind, result);
42
- const nextSurface = refreshSurface ? await options.refresh() : null;
48
+ let nextSurface = null;
49
+ let refreshFailure;
50
+ try {
51
+ nextSurface = refreshSurface ? await options.refresh() : null;
52
+ }
53
+ catch (error) {
54
+ refreshFailure = error;
55
+ }
43
56
  await options.onCommandCompleted?.(result);
44
57
  if (nextSurface)
45
58
  await options.onCompleted?.(nextSurface, result);
59
+ if (refreshFailure)
60
+ throw refreshFailure;
46
61
  return nextSurface;
47
62
  }
48
63
  function errorMessage(error, fallback) {
64
+ const code = error?.code;
65
+ const taskPageMessages = {
66
+ WORKFLOW_TASK_FORM_REQUIRED: '请完成本页必填资料后再提交。',
67
+ WORKFLOW_TASK_FORM_REVISION_CONFLICT: '业务资料已被其他处理人更新。请刷新并核对,当前输入会保留。',
68
+ WORKFLOW_TASK_FORM_RECOMPUTATION_REQUIRED: '这些资料会影响已完成的计算,请退回到计算前的节点重新办理。',
69
+ WORKFLOW_TASK_FORM_FIELD_NOT_EDITABLE: '页面规则已变化,请刷新资料后重新确认。',
70
+ WORKFLOW_TASK_FORM_COMMAND_FORBIDDEN: '此操作不提交补填资料,请使用保存补填、同意或重新提交。',
71
+ };
72
+ if (code && taskPageMessages[code])
73
+ return taskPageMessages[code];
49
74
  return error instanceof Error ? error.message : fallback;
50
75
  }
51
76
  function randomId(prefix) {
@@ -128,11 +153,6 @@ function workflowOperationSignature(operation) {
128
153
  }
129
154
  const WORKFLOW_APP_ACTION_DISABLED_REASON = '应用动作由应用页面负责';
130
155
  function standardWorkflowOperations(surface) {
131
- if (!surface.commandToken ||
132
- !surface.commandTokenExpiresAt ||
133
- new Date(surface.commandTokenExpiresAt).getTime() <= Date.now()) {
134
- return [];
135
- }
136
156
  return surface.operations
137
157
  .filter((operation) => operation.visible)
138
158
  .map((operation) => {
@@ -299,7 +319,8 @@ export function WorkflowTimelineSection({ timeline, }) {
299
319
  return (_jsx(Card, { className: "oxa-workflow-timeline-card", title: "\u5BA1\u6279\u6D41\u7A0B", children: entries.length ? (_jsx("div", { className: "oxa-workflow-timeline", children: entries.map((item) => {
300
320
  const operations = item.operations || [];
301
321
  const visiblePeople = visibleWorkflowTimelinePeople(item, operations);
302
- 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 }), _jsx(StatusTag, { status: item.status })] }), 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));
322
+ 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));
303
324
  }) })) : (_jsx(Empty, { description: "\u6682\u65E0\u6D41\u7A0B\u8F68\u8FF9", image: Empty.PRESENTED_IMAGE_SIMPLE })) }));
304
325
  }
305
326
  function WorkflowMemberPicker({ id, placeholder, value, onChange, multiple = false, variant }) {
@@ -360,28 +381,63 @@ function OperationDialog({ error, operation, submitting, variant, onCancel, onSu
360
381
  if (!operation)
361
382
  return null;
362
383
  const ui = operation.uiSchema;
363
- const formNode = (_jsxs(Form, { form: form, layout: "vertical", onFinish: onSubmit, children: [error && _jsx(Alert, { role: "alert", title: "\u5BA1\u6279\u5C1A\u672A\u5B8C\u6210", description: error, showIcon: true, type: "error" }), ui.confirmation?.description && (_jsx(Alert, { className: "oxa-workflow-operation-alert", description: ui.confirmation.description, showIcon: true, type: operation.emphasis === 'danger' ? 'warning' : 'info' })), _jsx(OperationFields, { operation: operation, variant: variant })] }));
384
+ const formNode = (_jsxs(Form, { form: form, layout: "vertical", onFinish: onSubmit, disabled: submitting, children: [error && _jsx(Alert, { role: "alert", title: "\u5BA1\u6279\u5C1A\u672A\u5B8C\u6210", description: error, showIcon: true, type: "error" }), ui.confirmation?.description && (_jsx(Alert, { className: "oxa-workflow-operation-alert", description: ui.confirmation.description, showIcon: true, type: operation.emphasis === 'danger' ? 'warning' : 'info' })), _jsx(OperationFields, { operation: operation, variant: variant })] }));
364
385
  if (variant === 'mobile') {
365
386
  return (_jsx(Drawer, { extra: _jsx(Button, { loading: submitting, onClick: () => form.submit(), type: "primary", children: ui.confirmText || `确认${operation.label}` }), rootClassName: "oxa-workflow-operation-drawer", styles: { body: { overflowY: 'auto', minHeight: 0 } }, keyboard: !submitting, mask: { closable: !submitting }, closable: !submitting, onClose: submitting ? undefined : onCancel, open: true, placement: "bottom", size: "min(640px, 92dvh)", title: operation.label, children: formNode }));
366
387
  }
367
- return (_jsx(Modal, { cancelText: "\u53D6\u6D88", confirmLoading: submitting, okText: ui.confirmText || `确认${operation.label}`, onCancel: onCancel, onOk: () => form.submit(), open: true, title: operation.label, children: formNode }));
388
+ return (_jsx(Modal, { cancelText: "\u53D6\u6D88", confirmLoading: submitting, cancelButtonProps: { disabled: submitting }, okText: ui.confirmText || `确认${operation.label}`, onCancel: onCancel, onOk: () => form.submit(), open: true, title: operation.label, keyboard: !submitting, mask: { closable: !submitting }, closable: !submitting, children: formNode }));
368
389
  }
369
- function WorkflowOperations({ surface, variant, onRefresh, onCommandSuccess, }) {
390
+ function WorkflowOperations({ surface, variant, onRefresh, onCommandSuccess, pendingLocator, persistLocator, clearLocator, onBusyChange, settlement, confirmedReadPending, renderLayout, }) {
370
391
  const { message } = App.useApp();
371
392
  const [selectedState, setSelectedState] = useState(null);
372
393
  const [moreOpen, setMoreOpen] = useState(false);
373
394
  const [operationError, setOperationError] = useState('');
374
395
  const [submitting, setSubmitting] = useState(false);
396
+ const [draftLocked, setDraftLocked] = useState(false);
397
+ const [fileLocked, setFileLocked] = useState(false);
398
+ const [refreshFailure, setRefreshFailure] = useState(null);
399
+ const [unknown, setUnknown] = useState(false);
400
+ const request = useRef(null);
401
+ const mounted = useRef(false);
402
+ const busyRef = useRef(false);
403
+ const taskForm = useWorkflowTaskForm(surface.taskForm);
404
+ useEffect(() => { mounted.current = true; return () => { mounted.current = false; }; }, []);
405
+ useEffect(() => {
406
+ if (!settlement || request.current?.key !== settlement.key)
407
+ return;
408
+ if (settlement.result)
409
+ taskForm.committed(request.current.input.form, Number(settlement.result.dataRevision) || undefined);
410
+ request.current = null;
411
+ setUnknown(false);
412
+ setSelectedState(null);
413
+ }, [settlement]);
375
414
  const operations = standardWorkflowOperations(surface);
376
- const selected = selectedState?.surface === surface
415
+ const [, setCommandClock] = useState(0);
416
+ const commandExpiresAt = Date.parse(surface.commandTokenExpiresAt || '');
417
+ const commandNeedsRefresh = operations.some(operation => operation.kind === 'workflow_command') &&
418
+ (!surface.commandToken || !Number.isFinite(commandExpiresAt) || commandExpiresAt <= Date.now());
419
+ useEffect(() => {
420
+ if (!surface.commandToken || !Number.isFinite(commandExpiresAt) || commandExpiresAt <= Date.now())
421
+ return;
422
+ let timer;
423
+ const tick = () => {
424
+ setCommandClock(value => value + 1);
425
+ const remaining = commandExpiresAt - Date.now();
426
+ if (remaining > 0)
427
+ timer = setTimeout(tick, Math.min(remaining, 2_147_483_647));
428
+ };
429
+ timer = setTimeout(tick, Math.min(commandExpiresAt - Date.now(), 2_147_483_647));
430
+ return () => clearTimeout(timer);
431
+ }, [surface.commandToken, commandExpiresAt]);
432
+ const selected = !commandNeedsRefresh && selectedState?.surface === surface
377
433
  ? operations.find((operation) => operation.key === selectedState.operation.key &&
378
434
  workflowOperationSignature(operation) ===
379
435
  selectedState.operationSignature) || null
380
436
  : null;
381
437
  useEffect(() => {
382
- if (selectedState && !selected)
438
+ if (selectedState && !selected && !submitting && !unknown)
383
439
  setSelectedState(null);
384
- }, [selected, selectedState]);
440
+ }, [selected, selectedState, submitting, unknown]);
385
441
  const selectOperation = (operation) => {
386
442
  setOperationError('');
387
443
  setSelectedState({
@@ -391,10 +447,12 @@ function WorkflowOperations({ surface, variant, onRefresh, onCommandSuccess, })
391
447
  });
392
448
  };
393
449
  const submit = async (values) => {
394
- if (!selected || submitting)
450
+ if (!selected || busyRef.current || draftLocked || fileLocked || taskForm.draftNeedsSave || pendingLocator || unknown || refreshFailure || confirmedReadPending)
395
451
  return;
396
452
  setOperationError('');
397
453
  setSubmitting(true);
454
+ busyRef.current = true;
455
+ onBusyChange(true);
398
456
  try {
399
457
  if (selected.kind !== 'workflow_command') {
400
458
  throw new Error('WORKFLOW_APP_ACTION_NOT_EXECUTABLE_IN_STANDARD_PANEL');
@@ -404,19 +462,26 @@ function WorkflowOperations({ surface, variant, onRefresh, onCommandSuccess, })
404
462
  await onRefresh();
405
463
  throw new Error('WORKFLOW_FRESH_COMMAND_TOKEN_REQUIRED');
406
464
  }
407
- if (new Date(surface.commandTokenExpiresAt).getTime() <= Date.now()) {
465
+ if (!Number.isFinite(commandExpiresAt) || commandExpiresAt <= Date.now()) {
408
466
  await onRefresh();
409
- throw new Error('WORKFLOW_COMMAND_SURFACE_EXPIRED');
467
+ throw new Error('操作已刷新,请保留当前输入并重新确认。');
410
468
  }
411
- const result = await executeWorkflowOperation(surface, selected, values, { idempotencyKey: randomId(`workflow:${selected.key}`) });
412
- message.success(`${selected.label}已提交`);
413
- setSelectedState(null);
414
- await onCommandSuccess(result);
469
+ const input = { ...values };
470
+ if (surface.taskForm && ['save_form', 'approve', 'resubmit'].includes(selected.key)) {
471
+ if (taskForm.stale)
472
+ throw new Error('业务资料已更新,请先核对最新资料');
473
+ input.form = await taskForm.build(selected.key !== 'save_form');
474
+ }
475
+ const key = randomId(`workflow:${selected.key}`);
476
+ if (surface.task?.id)
477
+ persistLocator({ taskId: String(surface.task.id), command: selected.key,
478
+ idempotencyKey: key, requestedAt: new Date().toISOString(), tokenDigest: await workflowCommandTokenDigest(commandToken) });
479
+ request.current = { surface, operation: selected, input: JSON.parse(JSON.stringify(input)), key };
415
480
  }
416
481
  catch (error) {
417
482
  const hasFreshSurface = Boolean(error?.data?.freshSurface);
418
483
  if (hasFreshSurface) {
419
- await onRefresh();
484
+ await onRefresh().catch(() => undefined);
420
485
  setSelectedState(null);
421
486
  message.warning('内容已更新,请刷新后重试');
422
487
  }
@@ -427,26 +492,128 @@ function WorkflowOperations({ surface, variant, onRefresh, onCommandSuccess, })
427
492
  setOperationError(failure);
428
493
  message.error(failure);
429
494
  }
495
+ setSubmitting(false);
496
+ busyRef.current = false;
497
+ onBusyChange(false);
498
+ return;
430
499
  }
431
- finally {
500
+ await sendRequest(false);
501
+ };
502
+ const sendRequest = async (recovering) => {
503
+ const wire = request.current;
504
+ if (!wire || (recovering && busyRef.current))
505
+ return;
506
+ setSubmitting(true);
507
+ busyRef.current = true;
508
+ onBusyChange(true);
509
+ setOperationError('');
510
+ let result;
511
+ try {
512
+ // Replay uses the exact token, input and key captured before the first write.
513
+ result = await executeWorkflowOperation(wire.surface, wire.operation, wire.input, { idempotencyKey: wire.key });
514
+ }
515
+ catch (error) {
516
+ if (!mounted.current)
517
+ return;
518
+ const rejected = !recovering && workflowTaskCommandWasRejected(error);
519
+ if (rejected) {
520
+ clearLocator(wire.key);
521
+ request.current = null;
522
+ setSelectedState(null);
523
+ }
524
+ else {
525
+ setUnknown(true);
526
+ setSelectedState(null);
527
+ }
528
+ const failure = errorMessage(error, '任务操作尚未完成');
529
+ setOperationError(failure);
530
+ if (rejected) {
531
+ message.error(failure);
532
+ // A definitive refusal permits a read, never an automatic new write.
533
+ // Keep this controller mounted so dirty inputs survive the fresh CAS.
534
+ try {
535
+ await onRefresh();
536
+ if (mounted.current)
537
+ setRefreshFailure(null);
538
+ }
539
+ catch (reason) {
540
+ if (mounted.current)
541
+ setRefreshFailure({ outcome: 'rejected', message: errorMessage(reason, '最新资料读取失败') });
542
+ }
543
+ }
432
544
  setSubmitting(false);
545
+ busyRef.current = false;
546
+ onBusyChange(false);
547
+ return;
548
+ }
549
+ if (!mounted.current)
550
+ return;
551
+ clearLocator(wire.key);
552
+ request.current = null;
553
+ setUnknown(false);
554
+ setSelectedState(null);
555
+ taskForm.committed(wire.input.form, Number(result.dataRevision) || undefined);
556
+ onBusyChange(false);
557
+ message.success(`${wire.operation.label}已提交`);
558
+ // The write has succeeded. A callback or read failure can only affect refresh.
559
+ try {
560
+ await onCommandSuccess(result);
561
+ }
562
+ catch (error) {
563
+ if (mounted.current)
564
+ setRefreshFailure({ outcome: 'succeeded', message: errorMessage(error, '页面刷新失败') });
565
+ }
566
+ finally {
567
+ if (mounted.current) {
568
+ setSubmitting(false);
569
+ busyRef.current = false;
570
+ onBusyChange(false);
571
+ }
433
572
  }
434
573
  };
435
- if (!operations.length)
436
- return null;
574
+ const refreshConfirmed = async () => {
575
+ if (busyRef.current)
576
+ return;
577
+ setSubmitting(true);
578
+ busyRef.current = true;
579
+ onBusyChange(true);
580
+ try {
581
+ await onRefresh();
582
+ if (mounted.current)
583
+ setRefreshFailure(null);
584
+ }
585
+ catch (error) {
586
+ if (mounted.current)
587
+ setRefreshFailure(current => ({ outcome: current?.outcome || 'rejected', message: errorMessage(error, '页面刷新失败') }));
588
+ }
589
+ finally {
590
+ if (mounted.current) {
591
+ setSubmitting(false);
592
+ busyRef.current = false;
593
+ onBusyChange(false);
594
+ }
595
+ }
596
+ };
597
+ const commandLocked = submitting || unknown || Boolean(pendingLocator) || Boolean(refreshFailure) || confirmedReadPending;
598
+ const locked = commandLocked || draftLocked || fileLocked;
599
+ if (!operations.length && !surface.taskForm && !unknown && !refreshFailure)
600
+ return renderLayout({ content: null, actions: null });
437
601
  const primary = operations
438
602
  .filter((operation) => operation.placement === 'primary')
439
603
  .sort((left, right) => {
440
604
  if (variant !== 'mobile')
441
605
  return 0;
442
- const order = { reject: 0, approve: 1 };
606
+ const order = { reject: 0, approve: 1, resubmit: 1 };
443
607
  return (order[left.key] ?? 10) - (order[right.key] ?? 10);
444
608
  });
445
609
  const secondary = operations.filter((operation) => operation.placement !== 'primary');
446
- return (_jsxs(_Fragment, { children: [_jsxs("div", { className: `oxa-workflow-actions oxa-workflow-actions-${variant}`, children: [primary.map((operation) => (_jsx(Button, { danger: operation.emphasis === 'danger', disabled: submitting || !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: submitting, 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: submitting || !operation.enabled, onClick: () => {
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'
611
+ ? '任务操作已成功,但资料刷新失败' : '本次未提交,最新资料读取失败', description: refreshFailure.message, action: _jsx(Button, { disabled: submitting, onClick: () => void refreshConfirmed(), children: "\u5237\u65B0\u8D44\u6599" }) })] })) : null;
612
+ 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: () => {
447
613
  setMoreOpen(false);
448
614
  selectOperation(operation);
449
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 })] }));
616
+ return renderLayout({ content, actions });
450
617
  }
451
618
  /**
452
619
  * Shared lifecycle for the embeddable task and instance operation panels.
@@ -456,13 +623,40 @@ function WorkflowOperations({ surface, variant, onRefresh, onCommandSuccess, })
456
623
  * idempotency, and state transitions; all operation controls continue to be
457
624
  * rendered by WorkflowOperations above.
458
625
  */
459
- function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: suppliedSurface, onSurfaceChange, onCompleted, onCommandCompleted, loadSurface, matchesSurface, missingIdentifierMessage, surfaceMismatchMessage, loadingLabel, loadErrorFallback, }) {
626
+ function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: suppliedSurface, onSurfaceChange, onCompleted, onCommandCompleted, loadSurface, matchesSurface, missingIdentifierMessage, surfaceMismatchMessage, loadingLabel, loadErrorFallback, renderLayout, }) {
460
627
  const normalizedIdentifier = identifier.trim();
628
+ const runtime = useOptionalRuntime();
629
+ const { message } = App.useApp();
630
+ const locatorScope = surfaceKind === 'task' && runtime
631
+ ? workflowTaskCommandScope(applicationCode(), runtime.identity.environment.id, runtime.identity.userId, normalizedIdentifier) : '';
632
+ const [locatorState, setLocatorState] = useState(() => ({ scope: locatorScope, value: locatorScope ? readPendingWorkflowTaskCommand(submissionLocatorStorage(), locatorScope) : null }));
633
+ const [writing, setWriting] = useState(false);
634
+ const [settlement, setSettlement] = useState(null);
635
+ const [confirmedReadFailure, setConfirmedReadFailure] = useState(null);
636
+ const locator = locatorState.scope === locatorScope ? locatorState.value : null;
637
+ useEffect(() => {
638
+ setLocatorState({ scope: locatorScope, value: locatorScope ? readPendingWorkflowTaskCommand(submissionLocatorStorage(), locatorScope) : null });
639
+ }, [locatorScope]);
640
+ const persistLocator = (value) => {
641
+ if (!locatorScope)
642
+ return;
643
+ if (!writePendingWorkflowTaskCommand(submissionLocatorStorage(), locatorScope, value))
644
+ message.warning('原请求已保留在当前页面,请在此确认提交结果。');
645
+ setLocatorState({ scope: locatorScope, value });
646
+ };
647
+ const clearLocator = (key) => {
648
+ if (locatorScope)
649
+ clearPendingWorkflowTaskCommand(submissionLocatorStorage(), locatorScope, key);
650
+ setLocatorState(current => current.scope === locatorScope && current.value?.idempotencyKey === key
651
+ ? { scope: locatorScope, value: null } : current);
652
+ };
461
653
  const inputSurface = suppliedSurface || null;
462
654
  const suppliedSurfaceMatches = matchesSurface(inputSurface, normalizedIdentifier);
463
655
  const initialSurface = suppliedSurfaceMatches ? inputSurface : null;
464
656
  const [surface, setSurface] = useState(initialSurface);
465
657
  const [surfaceIdentifier, setSurfaceIdentifier] = useState(initialSurface ? normalizedIdentifier : null);
658
+ const displayedSurfaceRef = useRef(surface);
659
+ displayedSurfaceRef.current = surface;
466
660
  const [acceptedInputSurface, setAcceptedInputSurface] = useState(initialSurface ? inputSurface : null);
467
661
  const [loading, setLoading] = useState(!initialSurface);
468
662
  const [loadingIdentifier, setLoadingIdentifier] = useState(initialSurface ? null : normalizedIdentifier);
@@ -514,8 +708,10 @@ function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: su
514
708
  }
515
709
  const requestInputSurface = currentInputSurfaceRef.current;
516
710
  setAcceptedInputSurface(requestInputSurface);
517
- setSurface(null);
518
- setSurfaceIdentifier(null);
711
+ if (!matchesSurface(displayedSurfaceRef.current, requestIdentifier)) {
712
+ setSurface(null);
713
+ setSurfaceIdentifier(null);
714
+ }
519
715
  setLoading(true);
520
716
  setLoadingIdentifier(requestIdentifier);
521
717
  setErrorState(null);
@@ -530,7 +726,7 @@ function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: su
530
726
  identifier: requestIdentifier,
531
727
  message: surfaceMismatchMessage,
532
728
  });
533
- return null;
729
+ throw new Error(surfaceMismatchMessage);
534
730
  }
535
731
  setSurface(nextSurface);
536
732
  setSurfaceIdentifier(requestIdentifier);
@@ -546,7 +742,7 @@ function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: su
546
742
  message: errorMessage(reason, loadErrorFallback),
547
743
  });
548
744
  }
549
- return null;
745
+ throw reason;
550
746
  }
551
747
  finally {
552
748
  if (isCurrentRequest(sequence)) {
@@ -592,9 +788,11 @@ function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: su
592
788
  requestSequence.current += 1;
593
789
  };
594
790
  }
595
- setSurface(null);
596
- setSurfaceIdentifier(null);
597
- void refresh();
791
+ if (!matchesSurface(displayedSurfaceRef.current, normalizedIdentifier)) {
792
+ setSurface(null);
793
+ setSurfaceIdentifier(null);
794
+ }
795
+ void refresh().catch(() => undefined);
598
796
  return () => {
599
797
  activeRef.current = false;
600
798
  requestSequence.current += 1;
@@ -633,32 +831,40 @@ function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: su
633
831
  setLoading(false);
634
832
  setLoadingIdentifier(null);
635
833
  }
636
- await completeWorkflowCommand(surfaceKind, result, {
637
- refresh: async () => {
638
- if (!isCurrentGeneration(identifierAtStart, generationAtStart)) {
639
- return null;
640
- }
641
- const nextSurface = await refresh();
642
- return isCurrentGeneration(identifierAtStart, generationAtStart)
643
- ? nextSurface
644
- : null;
645
- },
646
- onCommandCompleted: async (commandResult) => {
647
- if (!isCurrentGeneration(identifierAtStart, generationAtStart)) {
648
- return;
649
- }
650
- await onCommandCompletedRef.current?.(commandResult);
651
- },
652
- onCompleted: async (nextSurface, commandResult) => {
653
- if (!isCurrentGeneration(identifierAtStart, generationAtStart)) {
654
- return;
655
- }
656
- const callback = onCompletedRef.current;
657
- if (!callback)
658
- return;
659
- await callback(nextSurface, commandResult);
660
- },
661
- });
834
+ try {
835
+ await completeWorkflowCommand(surfaceKind, result, {
836
+ refresh: async () => {
837
+ if (!isCurrentGeneration(identifierAtStart, generationAtStart)) {
838
+ return null;
839
+ }
840
+ const nextSurface = await refresh();
841
+ return isCurrentGeneration(identifierAtStart, generationAtStart)
842
+ ? nextSurface
843
+ : null;
844
+ },
845
+ onCommandCompleted: async (commandResult) => {
846
+ if (!isCurrentGeneration(identifierAtStart, generationAtStart)) {
847
+ return;
848
+ }
849
+ await onCommandCompletedRef.current?.(commandResult);
850
+ },
851
+ onCompleted: async (nextSurface, commandResult) => {
852
+ if (!isCurrentGeneration(identifierAtStart, generationAtStart)) {
853
+ return;
854
+ }
855
+ const callback = onCompletedRef.current;
856
+ if (!callback)
857
+ return;
858
+ await callback(nextSurface, commandResult);
859
+ },
860
+ });
861
+ if (isCurrentGeneration(identifierAtStart, generationAtStart))
862
+ setConfirmedReadFailure(null);
863
+ }
864
+ catch (error) {
865
+ if (isCurrentGeneration(identifierAtStart, generationAtStart))
866
+ setConfirmedReadFailure({ result, message: errorMessage(error, '页面刷新失败') });
867
+ }
662
868
  }, [
663
869
  isCurrentGeneration,
664
870
  normalizedIdentifier,
@@ -689,19 +895,36 @@ function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: su
689
895
  loadingIdentifier === normalizedIdentifier ||
690
896
  surfaceIdentifier !== normalizedIdentifier ||
691
897
  !acceptedInput);
898
+ const receipt = locator && _jsx(WorkflowTaskCommandReceipt, { locator: locator, busy: writing, onSettled: async (result, text) => {
899
+ clearLocator(locator.idempotencyKey);
900
+ setSettlement({ key: locator.idempotencyKey, result });
901
+ message.info(text);
902
+ try {
903
+ if (result)
904
+ await completed(result);
905
+ else
906
+ await refreshCurrent();
907
+ }
908
+ catch (error) {
909
+ message.warning(result ? '原操作已成功,请刷新查看最新资料。' : errorMessage(error, '资料刷新失败'));
910
+ }
911
+ } }, locatorScope);
912
+ const confirmedWarning = confirmedReadFailure && _jsx(Alert, { showIcon: true, type: "warning", title: "\u4EFB\u52A1\u64CD\u4F5C\u5DF2\u6210\u529F\uFF0C\u4F46\u9875\u9762\u5237\u65B0\u5931\u8D25", description: confirmedReadFailure.message, action: _jsx(Button, { onClick: () => void completed(confirmedReadFailure.result), children: "\u5237\u65B0\u8D44\u6599" }) });
913
+ const layout = (parts) => renderLayout
914
+ ? renderLayout({ ...parts, surface: renderSurface || undefined }) : _jsxs(_Fragment, { children: [parts.content, parts.actions] });
692
915
  if (renderLoading) {
693
- return (_jsx("div", { "aria-busy": "true", "aria-label": loadingLabel, className: `oxa-workflow-actions-loading oxa-workflow-actions-loading-${variant}`, children: _jsx(Spin, {}) }));
916
+ return layout({ actions: null, content: (_jsxs(_Fragment, { children: [receipt, confirmedWarning, _jsx("div", { "aria-busy": "true", "aria-label": loadingLabel, className: `oxa-workflow-actions-loading oxa-workflow-actions-loading-${variant}`, children: _jsx(Spin, {}) })] })) });
694
917
  }
695
918
  if (renderError && !renderSurface) {
696
- return (_jsx(Alert, { action: _jsx(Button, { onClick: () => void refresh(), children: "\u91CD\u8BD5" }), description: renderError, showIcon: true, type: "error" }));
919
+ 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" })] })) });
697
920
  }
698
921
  if (!renderSurface)
699
- return null;
700
- return (_jsxs(_Fragment, { children: [renderError && (_jsx(Alert, { description: renderError, showIcon: true, type: "warning" })), _jsx(WorkflowOperations, { onCommandSuccess: completed, onRefresh: refreshCurrent, surface: renderSurface, variant: variant })] }));
922
+ return layout({ content: receipt || confirmedWarning ? _jsxs(_Fragment, { children: [receipt, confirmedWarning] }) : null, actions: null });
923
+ 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}`));
701
924
  }
702
925
  /** Public, embeddable Workflow task operation surface. */
703
- export function WorkflowTaskOperationsPanel({ taskId, variant = 'desktop', surface, onSurfaceChange, onCompleted, onCommandCompleted, }) {
704
- return (_jsx(WorkflowOperationsPanel, { identifier: taskId, onCommandCompleted: onCommandCompleted, loadErrorFallback: "\u5BA1\u6279\u4EFB\u52A1\u52A0\u8F7D\u5931\u8D25", loadSurface: loadWorkflowTaskSurface, matchesSurface: workflowSurfaceMatchesTask, missingIdentifierMessage: "WORKFLOW_TASK_ID_REQUIRED", onCompleted: onCompleted, onSurfaceChange: onSurfaceChange, surface: surface, surfaceMismatchMessage: "WORKFLOW_TASK_SURFACE_TASK_MISMATCH", surfaceKind: "task", variant: variant, loadingLabel: "\u6B63\u5728\u52A0\u8F7D\u5BA1\u6279\u64CD\u4F5C" }));
926
+ export function WorkflowTaskOperationsPanel({ taskId, variant = 'desktop', surface, onSurfaceChange, onCompleted, onCommandCompleted, renderLayout, }) {
927
+ return (_jsx(WorkflowOperationsPanel, { identifier: taskId, onCommandCompleted: onCommandCompleted, renderLayout: renderLayout, loadErrorFallback: "\u5BA1\u6279\u4EFB\u52A1\u52A0\u8F7D\u5931\u8D25", loadSurface: loadWorkflowTaskSurface, matchesSurface: workflowSurfaceMatchesTask, missingIdentifierMessage: "WORKFLOW_TASK_ID_REQUIRED", onCompleted: onCompleted, onSurfaceChange: onSurfaceChange, surface: surface, surfaceMismatchMessage: "WORKFLOW_TASK_SURFACE_TASK_MISMATCH", surfaceKind: "task", variant: variant, loadingLabel: "\u6B63\u5728\u52A0\u8F7D\u5BA1\u6279\u64CD\u4F5C" }));
705
928
  }
706
929
  /** Public, embeddable Workflow instance operation surface. */
707
930
  export function WorkflowInstanceOperationsPanel({ instanceId, variant = 'desktop', surface, onSurfaceChange, onCompleted, onCommandCompleted, }) {
@@ -717,7 +940,7 @@ export function WorkflowSummarySection({ surface }) {
717
940
  };
718
941
  return (_jsxs("div", { className: "oxa-workflow-summary-metadata", children: [_jsxs("span", { children: [_jsx(Typography.Text, { type: "secondary", children: "\u7533\u8BF7\u4EBA" }), _jsxs("span", { className: "oxa-workflow-applicant", children: [_jsx(PlatformAvatar, { avatarUrl: initiator.avatarUrl, size: 32 }), _jsxs("span", { children: [_jsx("strong", { children: initiator.displayName }), initiator.departmentDisplayName && (_jsx("small", { children: initiator.departmentDisplayName }))] })] })] }), _jsxs("span", { children: [_jsx(Typography.Text, { type: "secondary", children: "\u63D0\u4EA4\u65F6\u95F4" }), formatTime(summary.submittedAt)] }), summary.businessNumber && (_jsxs("span", { children: [_jsx(Typography.Text, { type: "secondary", children: "\u4E1A\u52A1\u7F16\u53F7" }), summary.businessNumber] }))] }));
719
942
  }
720
- function StandardWorkflowDetailRenderer({ surface, timeline, warning, operations, variant, onClose, onEdit, drawer, drawerState, newPageHref, editing, busy, }) {
943
+ function StandardWorkflowDetailRenderer({ surface, timeline, warning, operations, taskContent, variant, onClose, onEdit, drawer, drawerState, newPageHref, editing, busy, }) {
721
944
  const navigate = useNavigate();
722
945
  const timeZone = usePresentationTimeZone();
723
946
  const [tab, setTab] = useState('application');
@@ -735,13 +958,13 @@ function StandardWorkflowDetailRenderer({ surface, timeline, warning, operations
735
958
  const loadAudit = useCallback(() => loadWorkflowDataAudit(instance.id), [instance.id]);
736
959
  const back = onClose || (() => navigate(variant === 'mobile' ? '/m/work-center' : '/work-center'));
737
960
  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,
738
- 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: 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: [
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: [
739
962
  { key: 'application', label: '申请内容', children: _jsx(WorkflowBusinessDetailSections, { surface: surface, variant: variant }) },
740
963
  { key: 'history', label: '审批历史', children: _jsx(WorkflowTimelineSection, { timeline: timeline }) },
741
964
  { key: 'changes', label: '变更记录', children: tab === 'changes' && business.surface && business.resourceCode && business.recordId
742
965
  ? _jsx(RecordChangeHistory, { resourceCode: business.resourceCode, recordId: business.recordId, surface: business.surface, readable: () => true, loadPage: loadAudit })
743
966
  : _jsx(Empty, { description: "\u6682\u65E0\u53D8\u66F4\u8BB0\u5F55" }) },
744
- ] })] });
967
+ ] }), taskContent && _jsx("div", { className: "oxa-workflow-task-content", children: taskContent })] });
745
968
  }
746
969
  export function DesktopWorkflowDetailRenderer(props) {
747
970
  return _jsx(StandardWorkflowDetailRenderer, { ...props, variant: "desktop" });
@@ -830,10 +1053,6 @@ function WorkflowDetailPage({ kind, variant, resourceCode, recordId, onDismiss,
830
1053
  const detailPath = surface ? (variant === 'mobile' ? surface.detailNavigation.mobilePath : surface.detailNavigation.desktopPath) : '';
831
1054
  const newPageHref = useHref(detailPath || '.');
832
1055
  const close = onDismiss || (() => navigate(returnPath || (detail ? variant === 'mobile' ? detail.navigationContext.mobileReturnPath : detail.navigationContext.desktopReturnPath : variant === 'mobile' ? '/m/work-center' : '/work-center')));
833
- if (!surface || !detail || !instance)
834
- return _jsx(RecordDetailFrame, { variant: variant, title: "\u7533\u8BF7\u8BE6\u60C5", drawer: Boolean(onDismiss), drawerState: drawerState, onClose: close, children: loading ? _jsx(Spin, {}) : _jsx(Result, { status: "error", title: "\u6D41\u7A0B\u8BE6\u60C5\u52A0\u8F7D\u5931\u8D25", subTitle: error || '流程不存在', extra: _jsx(Button, { onClick: () => void refresh(), children: "\u91CD\u8BD5" }) }) });
835
- if (surface.detailNavigation.custom)
836
- return _jsx(Navigate, { replace: true, to: detailPath });
837
1056
  const onCommandCompleted = async (result) => {
838
1057
  if (kind === 'task' && result.advanced === true) {
839
1058
  const nextId = String(result.instanceId || '').trim();
@@ -845,11 +1064,15 @@ function WorkflowDetailPage({ kind, variant, resourceCode, recordId, onDismiss,
845
1064
  else
846
1065
  await refresh();
847
1066
  };
848
- const operations = task
849
- ? _jsx(WorkflowTaskOperationsPanel, { onCommandCompleted: onCommandCompleted, surface: surface, taskId: task.id, variant: variant }, task.id)
850
- : _jsx(WorkflowInstanceOperationsPanel, { instanceId: instance.id, onCommandCompleted: onCommandCompleted, surface: surface, variant: variant });
851
- return _jsx(StandardWorkflowDetailRenderer, { surface: surface, timeline: timeline, warning: error, operations: operations, variant: variant, drawer: Boolean(onDismiss), drawerState: drawerState, newPageHref: newPageHref, onClose: editing ? () => { if (!editBusy)
1067
+ if (!surface || !detail || !instance)
1068
+ return _jsxs(RecordDetailFrame, { variant: variant, title: "\u7533\u8BF7\u8BE6\u60C5", drawer: Boolean(onDismiss), drawerState: drawerState, onClose: close, children: [kind === 'task' && readPendingWorkflowTaskCommand(submissionLocatorStorage(), workflowTaskCommandScope(applicationCode(), identity.environment.id, identity.userId, id)) &&
1069
+ _jsx(WorkflowTaskOperationsPanel, { taskId: id, variant: variant, onCommandCompleted: onCommandCompleted }), loading ? _jsx(Spin, {}) : _jsx(Result, { status: "error", title: "\u6D41\u7A0B\u8BE6\u60C5\u52A0\u8F7D\u5931\u8D25", subTitle: error || '流程不存在', extra: _jsx(Button, { onClick: () => void refresh(), children: "\u91CD\u8BD5" }) })] });
1070
+ if (surface.detailNavigation.custom)
1071
+ return _jsx(Navigate, { replace: true, to: detailPath });
1072
+ const renderDetail = (operations, taskContent, currentSurface = surface) => _jsx(StandardWorkflowDetailRenderer, { surface: currentSurface, timeline: timeline, warning: error, operations: operations, taskContent: taskContent, variant: variant, drawer: Boolean(onDismiss), drawerState: drawerState, newPageHref: newPageHref, onClose: editing ? () => { if (!editBusy)
852
1073
  setEditing(false); } : close, busy: editBusy, editing: editing && business?.resourceCode && business.recordId ? _jsx(WorkflowRecordEditor, { resourceCode: business.resourceCode, recordId: business.recordId, variant: variant, presentation: "embedded", onBusyChange: setEditBusy, onDismiss: () => setEditing(false), onSaved: () => { setEditing(false); void refresh(); } }) : undefined, onEdit: workflowHasEnded(instance) && identity.isAppSuperAdmin && (business?.status === 'ready' || business?.status === 'stale') ? () => setEditing(true) : undefined });
1074
+ return task ? _jsx(WorkflowTaskOperationsPanel, { onCommandCompleted: onCommandCompleted, surface: surface, taskId: task.id, variant: variant, renderLayout: ({ content, actions, surface: currentSurface }) => renderDetail(actions, content, currentSurface) }, task.id)
1075
+ : renderDetail(_jsx(WorkflowInstanceOperationsPanel, { instanceId: instance.id, onCommandCompleted: onCommandCompleted, surface: surface, variant: variant }));
853
1076
  }
854
1077
  export function WorkflowRecordDetailPage({ variant = 'desktop', ...props }) { return _jsx(WorkflowDetailPage, { ...props, variant: variant, kind: "record" }); }
855
1078
  export function WorkflowTaskPage({ variant = 'desktop', returnPath, }) {
@@ -1483,7 +1706,7 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
1483
1706
  launchSurface?.submission.kind !== 'standard-process')) {
1484
1707
  return frame(_jsx(Result, { status: "500", title: "\u6807\u51C6\u6D41\u7A0B\u5408\u540C\u4E0D\u5B8C\u6574" }));
1485
1708
  }
1486
- if (!commandId && fields.some(field => field.type === 'subtable')) {
1709
+ if (!commandId && namedIntent && fields.some(field => field.type === 'subtable')) {
1487
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" }));
1488
1711
  }
1489
1712
  const submit = async (values) => {
@@ -1507,7 +1730,8 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
1507
1730
  setRecoveryObservedAbsent(false);
1508
1731
  };
1509
1732
  try {
1510
- const encoded = normalizeFormValues(values, subjectDefinition.surface);
1733
+ const encoded = namedIntent ? normalizeFormValues(values, subjectDefinition.surface)
1734
+ : standardProcessFormValues(values, subjectDefinition.surface, resources, (field, mode) => fieldWritable(field, mode === 'create' ? 'create' : 'edit', hasCapability, identity.isAppSuperAdmin));
1511
1735
  const data = Object.fromEntries(fields.filter(field => Object.hasOwn(encoded, field.key)).map(field => [field.key, encoded[field.key]]));
1512
1736
  const signature = JSON.stringify({ workflowCode, subjectId, environmentKey: identity.environment.key, submissionMode, data });
1513
1737
  if (submissionAttempt.current?.signature !== signature) {
@@ -1696,6 +1920,7 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
1696
1920
  const uploadOperationCode = namedIntent?.operationCode || definition.processOperationCode;
1697
1921
  const fieldRenderers = uploadOperationCode
1698
1922
  ? {
1923
+ ...(!namedIntent ? { renderSubtable: ({ field, disabled, operation, recordId }) => _jsx(SubtableField, { field: field, disabled: disabled, operation: operation, parentRecordId: recordId, mobile: variant === 'mobile' }) } : {}),
1699
1924
  ...(namedIntent ? { referenceLaunch: { workflowCode: definition.code, operationCode: namedIntent.operationCode } } : {}),
1700
1925
  upload: async (field, file) => await uploadOperationManagedFile({
1701
1926
  operationCode: uploadOperationCode,