openxiangda 2.34.3 → 2.44.0

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 (242) 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/application.d.ts.map +1 -1
  12. package/dist/browser/application.js +5 -4
  13. package/dist/browser/application.js.map +1 -1
  14. package/dist/browser/components/administration/RoleMembershipManager.d.ts +7 -0
  15. package/dist/browser/components/administration/RoleMembershipManager.d.ts.map +1 -0
  16. package/dist/browser/components/administration/RoleMembershipManager.js +724 -0
  17. package/dist/browser/components/administration/RoleMembershipManager.js.map +1 -0
  18. package/dist/browser/components/administration/membership-batch-model.d.ts +17 -0
  19. package/dist/browser/components/administration/membership-batch-model.d.ts.map +1 -0
  20. package/dist/browser/components/administration/membership-batch-model.js +117 -0
  21. package/dist/browser/components/administration/membership-batch-model.js.map +1 -0
  22. package/dist/browser/components/platform-fields/AttachmentFileList.d.ts.map +1 -1
  23. package/dist/browser/components/platform-fields/AttachmentFileList.js +2 -1
  24. package/dist/browser/components/platform-fields/AttachmentFileList.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 +9 -5
  28. package/dist/browser/components/platform-fields/MobileManagedFileField.js.map +1 -1
  29. package/dist/browser/components/resource/SurfaceFields.d.ts +3 -2
  30. package/dist/browser/components/resource/SurfaceFields.d.ts.map +1 -1
  31. package/dist/browser/components/resource/SurfaceFields.js +9 -9
  32. package/dist/browser/components/resource/SurfaceFields.js.map +1 -1
  33. package/dist/browser/components/resource/useResourceFormDrafts.d.ts.map +1 -1
  34. package/dist/browser/components/workflow/StandardWorkflowPages.d.ts +14 -3
  35. package/dist/browser/components/workflow/StandardWorkflowPages.d.ts.map +1 -1
  36. package/dist/browser/components/workflow/StandardWorkflowPages.js +283 -75
  37. package/dist/browser/components/workflow/StandardWorkflowPages.js.map +1 -1
  38. package/dist/browser/components/workflow/WorkflowAssignmentRoutingEditor.d.ts +8 -0
  39. package/dist/browser/components/workflow/WorkflowAssignmentRoutingEditor.d.ts.map +1 -0
  40. package/dist/browser/components/workflow/WorkflowAssignmentRoutingEditor.js +174 -0
  41. package/dist/browser/components/workflow/WorkflowAssignmentRoutingEditor.js.map +1 -0
  42. package/dist/browser/components/workflow/WorkflowAssignmentRoutingManager.d.ts +3 -0
  43. package/dist/browser/components/workflow/WorkflowAssignmentRoutingManager.d.ts.map +1 -0
  44. package/dist/browser/components/workflow/WorkflowAssignmentRoutingManager.js +75 -0
  45. package/dist/browser/components/workflow/WorkflowAssignmentRoutingManager.js.map +1 -0
  46. package/dist/browser/components/workflow/WorkflowBusinessStepRecoveryPanel.d.ts +14 -0
  47. package/dist/browser/components/workflow/WorkflowBusinessStepRecoveryPanel.d.ts.map +1 -0
  48. package/dist/browser/components/workflow/WorkflowBusinessStepRecoveryPanel.js +117 -0
  49. package/dist/browser/components/workflow/WorkflowBusinessStepRecoveryPanel.js.map +1 -0
  50. package/dist/browser/components/workflow/WorkflowDelegationManager.d.ts +14 -0
  51. package/dist/browser/components/workflow/WorkflowDelegationManager.d.ts.map +1 -0
  52. package/dist/browser/components/workflow/WorkflowDelegationManager.js +204 -0
  53. package/dist/browser/components/workflow/WorkflowDelegationManager.js.map +1 -0
  54. package/dist/browser/components/workflow/WorkflowDiagram.d.ts +16 -0
  55. package/dist/browser/components/workflow/WorkflowDiagram.d.ts.map +1 -0
  56. package/dist/browser/components/workflow/WorkflowDiagram.js +110 -0
  57. package/dist/browser/components/workflow/WorkflowDiagram.js.map +1 -0
  58. package/dist/browser/components/workflow/WorkflowFlowCanvas.d.ts +23 -0
  59. package/dist/browser/components/workflow/WorkflowFlowCanvas.d.ts.map +1 -0
  60. package/dist/browser/components/workflow/WorkflowFlowCanvas.js +78 -0
  61. package/dist/browser/components/workflow/WorkflowFlowCanvas.js.map +1 -0
  62. package/dist/browser/components/workflow/WorkflowNodeCard.d.ts +15 -0
  63. package/dist/browser/components/workflow/WorkflowNodeCard.d.ts.map +1 -0
  64. package/dist/browser/components/workflow/WorkflowNodeCard.js +16 -0
  65. package/dist/browser/components/workflow/WorkflowNodeCard.js.map +1 -0
  66. package/dist/browser/components/workflow/WorkflowNodeConfigurationEditor.d.ts +13 -0
  67. package/dist/browser/components/workflow/WorkflowNodeConfigurationEditor.d.ts.map +1 -0
  68. package/dist/browser/components/workflow/WorkflowNodeConfigurationEditor.js +151 -0
  69. package/dist/browser/components/workflow/WorkflowNodeConfigurationEditor.js.map +1 -0
  70. package/dist/browser/components/workflow/WorkflowTaskCommandReceipt.d.ts +10 -0
  71. package/dist/browser/components/workflow/WorkflowTaskCommandReceipt.d.ts.map +1 -0
  72. package/dist/browser/components/workflow/WorkflowTaskCommandReceipt.js +59 -0
  73. package/dist/browser/components/workflow/WorkflowTaskCommandReceipt.js.map +1 -0
  74. package/dist/browser/components/workflow/WorkflowTaskDraftPanel.d.ts +17 -0
  75. package/dist/browser/components/workflow/WorkflowTaskDraftPanel.d.ts.map +1 -0
  76. package/dist/browser/components/workflow/WorkflowTaskDraftPanel.js +136 -0
  77. package/dist/browser/components/workflow/WorkflowTaskDraftPanel.js.map +1 -0
  78. package/dist/browser/components/workflow/WorkflowTaskFiles.d.ts +20 -0
  79. package/dist/browser/components/workflow/WorkflowTaskFiles.d.ts.map +1 -0
  80. package/dist/browser/components/workflow/WorkflowTaskFiles.js +124 -0
  81. package/dist/browser/components/workflow/WorkflowTaskFiles.js.map +1 -0
  82. package/dist/browser/components/workflow/WorkflowTaskForm.d.ts +49 -0
  83. package/dist/browser/components/workflow/WorkflowTaskForm.d.ts.map +1 -0
  84. package/dist/browser/components/workflow/WorkflowTaskForm.js +141 -0
  85. package/dist/browser/components/workflow/WorkflowTaskForm.js.map +1 -0
  86. package/dist/browser/components/workflow/workflow-flow-layout.d.ts +28 -0
  87. package/dist/browser/components/workflow/workflow-flow-layout.d.ts.map +1 -0
  88. package/dist/browser/components/workflow/workflow-flow-layout.js +54 -0
  89. package/dist/browser/components/workflow/workflow-flow-layout.js.map +1 -0
  90. package/dist/browser/components/workflow/workflow-routing-draft.d.ts +4 -0
  91. package/dist/browser/components/workflow/workflow-routing-draft.d.ts.map +1 -0
  92. package/dist/browser/components/workflow/workflow-routing-draft.js +9 -0
  93. package/dist/browser/components/workflow/workflow-routing-draft.js.map +1 -0
  94. package/dist/browser/durable-command.d.ts +9 -0
  95. package/dist/browser/durable-command.d.ts.map +1 -1
  96. package/dist/browser/durable-command.js +67 -13
  97. package/dist/browser/durable-command.js.map +1 -1
  98. package/dist/browser/navigation-guard.d.ts +3 -1
  99. package/dist/browser/navigation-guard.d.ts.map +1 -1
  100. package/dist/browser/navigation-guard.js +7 -3
  101. package/dist/browser/navigation-guard.js.map +1 -1
  102. package/dist/browser/platform-client.d.ts +76 -3
  103. package/dist/browser/platform-client.d.ts.map +1 -1
  104. package/dist/browser/platform-client.js +292 -47
  105. package/dist/browser/platform-client.js.map +1 -1
  106. package/dist/browser/record-detail.css +10 -0
  107. package/dist/browser/runtime-authorization-recovery.d.ts +7 -0
  108. package/dist/browser/runtime-authorization-recovery.d.ts.map +1 -0
  109. package/dist/browser/runtime-authorization-recovery.js +119 -0
  110. package/dist/browser/runtime-authorization-recovery.js.map +1 -0
  111. package/dist/browser/runtime.d.ts +2 -0
  112. package/dist/browser/runtime.d.ts.map +1 -1
  113. package/dist/browser/runtime.js +39 -13
  114. package/dist/browser/runtime.js.map +1 -1
  115. package/dist/browser/styles.css +155 -0
  116. package/dist/browser/workflow-task-command-recovery.d.ts +17 -0
  117. package/dist/browser/workflow-task-command-recovery.d.ts.map +1 -0
  118. package/dist/browser/workflow-task-command-recovery.js +50 -0
  119. package/dist/browser/workflow-task-command-recovery.js.map +1 -0
  120. package/dist/browser/workflow-task-file-upload.d.ts +18 -0
  121. package/dist/browser/workflow-task-file-upload.d.ts.map +1 -0
  122. package/dist/browser/workflow-task-file-upload.js +35 -0
  123. package/dist/browser/workflow-task-file-upload.js.map +1 -0
  124. package/dist/config.d.ts +1 -1
  125. package/dist/config.d.ts.map +1 -1
  126. package/dist/core.d.ts +9 -1
  127. package/dist/core.d.ts.map +1 -1
  128. package/dist/core.js +3 -0
  129. package/dist/core.js.map +1 -1
  130. package/dist/nest.d.ts +2 -2
  131. package/dist/nest.d.ts.map +1 -1
  132. package/dist/nest.js.map +1 -1
  133. package/dist/react.d.ts +10 -2
  134. package/dist/react.d.ts.map +1 -1
  135. package/dist/react.js +9 -1
  136. package/dist/react.js.map +1 -1
  137. package/documentation/administration.md +190 -0
  138. package/documentation/frontend.md +2 -0
  139. package/documentation/getting-started.md +7 -7
  140. package/documentation/managed-concurrency-frontend.md +9 -4
  141. package/documentation/managed-concurrency.md +10 -0
  142. package/documentation/manifest.json +7 -7
  143. package/documentation/workflow-events.md +237 -3
  144. package/package.json +26 -23
  145. package/releases/2.43.0.json +38 -0
  146. package/releases/2.44.0.json +36 -0
  147. package/skills/manifest.json +1 -1
  148. package/skills/openxiangda-v2/SKILL.md +4 -4
  149. package/skills/openxiangda-v2/references/administration.md +190 -0
  150. package/skills/openxiangda-v2/references/frontend.md +2 -0
  151. package/skills/openxiangda-v2/references/getting-started.md +7 -7
  152. package/skills/openxiangda-v2/references/managed-concurrency-frontend.md +9 -4
  153. package/skills/openxiangda-v2/references/managed-concurrency.md +10 -0
  154. package/skills/openxiangda-v2/references/workflow-events.md +237 -3
  155. package/releases/2.0.0.json +0 -50
  156. package/releases/2.0.1.json +0 -39
  157. package/releases/2.1.0.json +0 -44
  158. package/releases/2.1.1.json +0 -48
  159. package/releases/2.10.0.json +0 -42
  160. package/releases/2.11.0.json +0 -41
  161. package/releases/2.12.0.json +0 -38
  162. package/releases/2.13.0.json +0 -41
  163. package/releases/2.13.1.json +0 -33
  164. package/releases/2.13.2.json +0 -31
  165. package/releases/2.14.0.json +0 -41
  166. package/releases/2.15.0.json +0 -40
  167. package/releases/2.16.0.json +0 -41
  168. package/releases/2.17.0.json +0 -39
  169. package/releases/2.17.1.json +0 -34
  170. package/releases/2.18.0.json +0 -37
  171. package/releases/2.18.1.json +0 -31
  172. package/releases/2.18.10.json +0 -29
  173. package/releases/2.18.2.json +0 -30
  174. package/releases/2.18.3.json +0 -30
  175. package/releases/2.18.9.json +0 -37
  176. package/releases/2.19.0.json +0 -38
  177. package/releases/2.2.0.json +0 -48
  178. package/releases/2.2.1.json +0 -35
  179. package/releases/2.2.2.json +0 -34
  180. package/releases/2.20.0.json +0 -36
  181. package/releases/2.20.1.json +0 -30
  182. package/releases/2.20.4.json +0 -30
  183. package/releases/2.21.0.json +0 -35
  184. package/releases/2.21.1.json +0 -36
  185. package/releases/2.21.2.json +0 -34
  186. package/releases/2.21.3.json +0 -31
  187. package/releases/2.21.4.json +0 -31
  188. package/releases/2.22.0.json +0 -35
  189. package/releases/2.22.1.json +0 -32
  190. package/releases/2.22.2.json +0 -35
  191. package/releases/2.22.3.json +0 -32
  192. package/releases/2.22.4.json +0 -33
  193. package/releases/2.22.5.json +0 -31
  194. package/releases/2.22.6.json +0 -29
  195. package/releases/2.22.7.json +0 -29
  196. package/releases/2.23.0.json +0 -34
  197. package/releases/2.24.0.json +0 -33
  198. package/releases/2.24.1.json +0 -33
  199. package/releases/2.24.2.json +0 -31
  200. package/releases/2.24.3.json +0 -33
  201. package/releases/2.25.0.json +0 -31
  202. package/releases/2.25.1.json +0 -33
  203. package/releases/2.25.2.json +0 -32
  204. package/releases/2.25.3.json +0 -32
  205. package/releases/2.26.0.json +0 -41
  206. package/releases/2.27.0.json +0 -38
  207. package/releases/2.28.0.json +0 -37
  208. package/releases/2.28.1.json +0 -34
  209. package/releases/2.29.0.json +0 -37
  210. package/releases/2.29.1.json +0 -34
  211. package/releases/2.29.2.json +0 -32
  212. package/releases/2.3.0.json +0 -37
  213. package/releases/2.30.0.json +0 -34
  214. package/releases/2.31.0.json +0 -39
  215. package/releases/2.31.1.json +0 -38
  216. package/releases/2.31.2.json +0 -34
  217. package/releases/2.31.3.json +0 -32
  218. package/releases/2.31.4.json +0 -33
  219. package/releases/2.31.5.json +0 -33
  220. package/releases/2.31.6.json +0 -29
  221. package/releases/2.31.7.json +0 -29
  222. package/releases/2.31.8.json +0 -33
  223. package/releases/2.31.9.json +0 -33
  224. package/releases/2.32.0.json +0 -38
  225. package/releases/2.33.0.json +0 -33
  226. package/releases/2.34.0.json +0 -38
  227. package/releases/2.34.1.json +0 -34
  228. package/releases/2.34.2.json +0 -36
  229. package/releases/2.34.3.json +0 -35
  230. package/releases/2.4.0.json +0 -37
  231. package/releases/2.4.1.json +0 -35
  232. package/releases/2.5.0.json +0 -37
  233. package/releases/2.6.0.json +0 -37
  234. package/releases/2.7.0.json +0 -37
  235. package/releases/2.7.1.json +0 -31
  236. package/releases/2.8.0.json +0 -32
  237. package/releases/2.8.1.json +0 -30
  238. package/releases/2.9.0.json +0 -33
  239. package/releases/2.9.1.json +0 -31
  240. package/releases/2.9.2.json +0 -33
  241. package/releases/2.9.3.json +0 -31
  242. package/releases/2.9.4.json +0 -37
@@ -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';
@@ -39,13 +42,32 @@ export function shouldRefreshWorkflowSurface(kind, result) {
39
42
  */
40
43
  export async function completeWorkflowCommand(kind, result, options) {
41
44
  const refreshSurface = shouldRefreshWorkflowSurface(kind, result);
42
- const nextSurface = refreshSurface ? await options.refresh() : null;
45
+ let nextSurface = null;
46
+ let refreshFailure;
47
+ try {
48
+ nextSurface = refreshSurface ? await options.refresh() : null;
49
+ }
50
+ catch (error) {
51
+ refreshFailure = error;
52
+ }
43
53
  await options.onCommandCompleted?.(result);
44
54
  if (nextSurface)
45
55
  await options.onCompleted?.(nextSurface, result);
56
+ if (refreshFailure)
57
+ throw refreshFailure;
46
58
  return nextSurface;
47
59
  }
48
60
  function errorMessage(error, fallback) {
61
+ const code = error?.code;
62
+ const taskPageMessages = {
63
+ WORKFLOW_TASK_FORM_REQUIRED: '请完成本页必填资料后再提交。',
64
+ WORKFLOW_TASK_FORM_REVISION_CONFLICT: '业务资料已被其他处理人更新。请刷新并核对,当前输入会保留。',
65
+ WORKFLOW_TASK_FORM_RECOMPUTATION_REQUIRED: '这些资料会影响已完成的计算,请退回到计算前的节点重新办理。',
66
+ WORKFLOW_TASK_FORM_FIELD_NOT_EDITABLE: '页面规则已变化,请刷新资料后重新确认。',
67
+ WORKFLOW_TASK_FORM_COMMAND_FORBIDDEN: '此操作不提交补填资料,请使用保存补填、同意或重新提交。',
68
+ };
69
+ if (code && taskPageMessages[code])
70
+ return taskPageMessages[code];
49
71
  return error instanceof Error ? error.message : fallback;
50
72
  }
51
73
  function randomId(prefix) {
@@ -299,7 +321,8 @@ export function WorkflowTimelineSection({ timeline, }) {
299
321
  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
322
  const operations = item.operations || [];
301
323
  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));
324
+ const businessStep = item.nodeKind === 'action' ? item.result.businessStep : undefined;
325
+ 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
326
  }) })) : (_jsx(Empty, { description: "\u6682\u65E0\u6D41\u7A0B\u8F68\u8FF9", image: Empty.PRESENTED_IMAGE_SIMPLE })) }));
304
327
  }
305
328
  function WorkflowMemberPicker({ id, placeholder, value, onChange, multiple = false, variant }) {
@@ -360,18 +383,36 @@ function OperationDialog({ error, operation, submitting, variant, onCancel, onSu
360
383
  if (!operation)
361
384
  return null;
362
385
  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 })] }));
386
+ 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
387
  if (variant === 'mobile') {
365
388
  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
389
  }
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 }));
390
+ 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
391
  }
369
- function WorkflowOperations({ surface, variant, onRefresh, onCommandSuccess, }) {
392
+ function WorkflowOperations({ surface, variant, onRefresh, onCommandSuccess, pendingLocator, persistLocator, clearLocator, onBusyChange, settlement, confirmedReadPending, renderLayout, }) {
370
393
  const { message } = App.useApp();
371
394
  const [selectedState, setSelectedState] = useState(null);
372
395
  const [moreOpen, setMoreOpen] = useState(false);
373
396
  const [operationError, setOperationError] = useState('');
374
397
  const [submitting, setSubmitting] = useState(false);
398
+ const [draftLocked, setDraftLocked] = useState(false);
399
+ const [fileLocked, setFileLocked] = useState(false);
400
+ const [refreshFailure, setRefreshFailure] = useState(null);
401
+ const [unknown, setUnknown] = useState(false);
402
+ const request = useRef(null);
403
+ const mounted = useRef(false);
404
+ const busyRef = useRef(false);
405
+ const taskForm = useWorkflowTaskForm(surface.taskForm);
406
+ useEffect(() => { mounted.current = true; return () => { mounted.current = false; }; }, []);
407
+ useEffect(() => {
408
+ if (!settlement || request.current?.key !== settlement.key)
409
+ return;
410
+ if (settlement.result)
411
+ taskForm.committed(request.current.input.form, Number(settlement.result.dataRevision) || undefined);
412
+ request.current = null;
413
+ setUnknown(false);
414
+ setSelectedState(null);
415
+ }, [settlement]);
375
416
  const operations = standardWorkflowOperations(surface);
376
417
  const selected = selectedState?.surface === surface
377
418
  ? operations.find((operation) => operation.key === selectedState.operation.key &&
@@ -379,9 +420,9 @@ function WorkflowOperations({ surface, variant, onRefresh, onCommandSuccess, })
379
420
  selectedState.operationSignature) || null
380
421
  : null;
381
422
  useEffect(() => {
382
- if (selectedState && !selected)
423
+ if (selectedState && !selected && !submitting && !unknown)
383
424
  setSelectedState(null);
384
- }, [selected, selectedState]);
425
+ }, [selected, selectedState, submitting, unknown]);
385
426
  const selectOperation = (operation) => {
386
427
  setOperationError('');
387
428
  setSelectedState({
@@ -391,10 +432,12 @@ function WorkflowOperations({ surface, variant, onRefresh, onCommandSuccess, })
391
432
  });
392
433
  };
393
434
  const submit = async (values) => {
394
- if (!selected || submitting)
435
+ if (!selected || busyRef.current || draftLocked || fileLocked || taskForm.draftNeedsSave || pendingLocator || unknown || refreshFailure || confirmedReadPending)
395
436
  return;
396
437
  setOperationError('');
397
438
  setSubmitting(true);
439
+ busyRef.current = true;
440
+ onBusyChange(true);
398
441
  try {
399
442
  if (selected.kind !== 'workflow_command') {
400
443
  throw new Error('WORKFLOW_APP_ACTION_NOT_EXECUTABLE_IN_STANDARD_PANEL');
@@ -408,15 +451,22 @@ function WorkflowOperations({ surface, variant, onRefresh, onCommandSuccess, })
408
451
  await onRefresh();
409
452
  throw new Error('WORKFLOW_COMMAND_SURFACE_EXPIRED');
410
453
  }
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);
454
+ const input = { ...values };
455
+ if (surface.taskForm && ['save_form', 'approve', 'resubmit'].includes(selected.key)) {
456
+ if (taskForm.stale)
457
+ throw new Error('业务资料已更新,请先核对最新资料');
458
+ input.form = await taskForm.build(selected.key !== 'save_form');
459
+ }
460
+ const key = randomId(`workflow:${selected.key}`);
461
+ if (surface.task?.id)
462
+ persistLocator({ taskId: String(surface.task.id), command: selected.key,
463
+ idempotencyKey: key, requestedAt: new Date().toISOString(), tokenDigest: await workflowCommandTokenDigest(commandToken) });
464
+ request.current = { surface, operation: selected, input: JSON.parse(JSON.stringify(input)), key };
415
465
  }
416
466
  catch (error) {
417
467
  const hasFreshSurface = Boolean(error?.data?.freshSurface);
418
468
  if (hasFreshSurface) {
419
- await onRefresh();
469
+ await onRefresh().catch(() => undefined);
420
470
  setSelectedState(null);
421
471
  message.warning('内容已更新,请刷新后重试');
422
472
  }
@@ -427,13 +477,112 @@ function WorkflowOperations({ surface, variant, onRefresh, onCommandSuccess, })
427
477
  setOperationError(failure);
428
478
  message.error(failure);
429
479
  }
480
+ setSubmitting(false);
481
+ busyRef.current = false;
482
+ onBusyChange(false);
483
+ return;
430
484
  }
431
- finally {
485
+ await sendRequest(false);
486
+ };
487
+ const sendRequest = async (recovering) => {
488
+ const wire = request.current;
489
+ if (!wire || (recovering && busyRef.current))
490
+ return;
491
+ setSubmitting(true);
492
+ busyRef.current = true;
493
+ onBusyChange(true);
494
+ setOperationError('');
495
+ let result;
496
+ try {
497
+ // Replay uses the exact token, input and key captured before the first write.
498
+ result = await executeWorkflowOperation(wire.surface, wire.operation, wire.input, { idempotencyKey: wire.key });
499
+ }
500
+ catch (error) {
501
+ if (!mounted.current)
502
+ return;
503
+ const rejected = !recovering && workflowTaskCommandWasRejected(error);
504
+ if (rejected) {
505
+ clearLocator(wire.key);
506
+ request.current = null;
507
+ setSelectedState(null);
508
+ }
509
+ else {
510
+ setUnknown(true);
511
+ setSelectedState(null);
512
+ }
513
+ const failure = errorMessage(error, '任务操作尚未完成');
514
+ setOperationError(failure);
515
+ if (rejected) {
516
+ message.error(failure);
517
+ // A definitive refusal permits a read, never an automatic new write.
518
+ // Keep this controller mounted so dirty inputs survive the fresh CAS.
519
+ try {
520
+ await onRefresh();
521
+ if (mounted.current)
522
+ setRefreshFailure(null);
523
+ }
524
+ catch (reason) {
525
+ if (mounted.current)
526
+ setRefreshFailure({ outcome: 'rejected', message: errorMessage(reason, '最新资料读取失败') });
527
+ }
528
+ }
432
529
  setSubmitting(false);
530
+ busyRef.current = false;
531
+ onBusyChange(false);
532
+ return;
533
+ }
534
+ if (!mounted.current)
535
+ return;
536
+ clearLocator(wire.key);
537
+ request.current = null;
538
+ setUnknown(false);
539
+ setSelectedState(null);
540
+ taskForm.committed(wire.input.form, Number(result.dataRevision) || undefined);
541
+ onBusyChange(false);
542
+ message.success(`${wire.operation.label}已提交`);
543
+ // The write has succeeded. A callback or read failure can only affect refresh.
544
+ try {
545
+ await onCommandSuccess(result);
546
+ }
547
+ catch (error) {
548
+ if (mounted.current)
549
+ setRefreshFailure({ outcome: 'succeeded', message: errorMessage(error, '页面刷新失败') });
550
+ }
551
+ finally {
552
+ if (mounted.current) {
553
+ setSubmitting(false);
554
+ busyRef.current = false;
555
+ onBusyChange(false);
556
+ }
433
557
  }
434
558
  };
435
- if (!operations.length)
436
- return null;
559
+ const refreshConfirmed = async () => {
560
+ if (busyRef.current)
561
+ return;
562
+ setSubmitting(true);
563
+ busyRef.current = true;
564
+ onBusyChange(true);
565
+ try {
566
+ await onRefresh();
567
+ if (mounted.current)
568
+ setRefreshFailure(null);
569
+ }
570
+ catch (error) {
571
+ if (mounted.current)
572
+ setRefreshFailure(current => ({ outcome: current?.outcome || 'rejected', message: errorMessage(error, '页面刷新失败') }));
573
+ }
574
+ finally {
575
+ if (mounted.current) {
576
+ setSubmitting(false);
577
+ busyRef.current = false;
578
+ onBusyChange(false);
579
+ }
580
+ }
581
+ };
582
+ const commandLocked = submitting || unknown || Boolean(pendingLocator) || Boolean(refreshFailure) || confirmedReadPending;
583
+ const locked = commandLocked || draftLocked || fileLocked;
584
+ if (!operations.length && !surface.taskForm && !unknown && !refreshFailure)
585
+ return renderLayout({ content: null, actions: null });
437
586
  const primary = operations
438
587
  .filter((operation) => operation.placement === 'primary')
439
588
  .sort((left, right) => {
@@ -443,10 +592,13 @@ function WorkflowOperations({ surface, variant, onRefresh, onCommandSuccess, })
443
592
  return (order[left.key] ?? 10) - (order[right.key] ?? 10);
444
593
  });
445
594
  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: () => {
595
+ const content = surface.taskForm || ((submitting || unknown) && !pendingLocator) || unknown || refreshFailure ? (_jsxs(_Fragment, { children: [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'
596
+ ? '任务操作已成功,但资料刷新失败' : '本次未提交,最新资料读取失败', description: refreshFailure.message, action: _jsx(Button, { disabled: submitting, onClick: () => void refreshConfirmed(), children: "\u5237\u65B0\u8D44\u6599" }) })] })) : null;
597
+ 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 || 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 || 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 || taskForm.stale || taskForm.draftNeedsSave || !operation.enabled, onClick: () => {
447
598
  setMoreOpen(false);
448
599
  selectOperation(operation);
449
600
  }, 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 })] }));
601
+ return renderLayout({ content, actions });
450
602
  }
451
603
  /**
452
604
  * Shared lifecycle for the embeddable task and instance operation panels.
@@ -456,13 +608,40 @@ function WorkflowOperations({ surface, variant, onRefresh, onCommandSuccess, })
456
608
  * idempotency, and state transitions; all operation controls continue to be
457
609
  * rendered by WorkflowOperations above.
458
610
  */
459
- function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: suppliedSurface, onSurfaceChange, onCompleted, onCommandCompleted, loadSurface, matchesSurface, missingIdentifierMessage, surfaceMismatchMessage, loadingLabel, loadErrorFallback, }) {
611
+ function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: suppliedSurface, onSurfaceChange, onCompleted, onCommandCompleted, loadSurface, matchesSurface, missingIdentifierMessage, surfaceMismatchMessage, loadingLabel, loadErrorFallback, renderLayout, }) {
460
612
  const normalizedIdentifier = identifier.trim();
613
+ const runtime = useOptionalRuntime();
614
+ const { message } = App.useApp();
615
+ const locatorScope = surfaceKind === 'task' && runtime
616
+ ? workflowTaskCommandScope(applicationCode(), runtime.identity.environment.id, runtime.identity.userId, normalizedIdentifier) : '';
617
+ const [locatorState, setLocatorState] = useState(() => ({ scope: locatorScope, value: locatorScope ? readPendingWorkflowTaskCommand(submissionLocatorStorage(), locatorScope) : null }));
618
+ const [writing, setWriting] = useState(false);
619
+ const [settlement, setSettlement] = useState(null);
620
+ const [confirmedReadFailure, setConfirmedReadFailure] = useState(null);
621
+ const locator = locatorState.scope === locatorScope ? locatorState.value : null;
622
+ useEffect(() => {
623
+ setLocatorState({ scope: locatorScope, value: locatorScope ? readPendingWorkflowTaskCommand(submissionLocatorStorage(), locatorScope) : null });
624
+ }, [locatorScope]);
625
+ const persistLocator = (value) => {
626
+ if (!locatorScope)
627
+ return;
628
+ if (!writePendingWorkflowTaskCommand(submissionLocatorStorage(), locatorScope, value))
629
+ message.warning('原请求已保留在当前页面,请在此确认提交结果。');
630
+ setLocatorState({ scope: locatorScope, value });
631
+ };
632
+ const clearLocator = (key) => {
633
+ if (locatorScope)
634
+ clearPendingWorkflowTaskCommand(submissionLocatorStorage(), locatorScope, key);
635
+ setLocatorState(current => current.scope === locatorScope && current.value?.idempotencyKey === key
636
+ ? { scope: locatorScope, value: null } : current);
637
+ };
461
638
  const inputSurface = suppliedSurface || null;
462
639
  const suppliedSurfaceMatches = matchesSurface(inputSurface, normalizedIdentifier);
463
640
  const initialSurface = suppliedSurfaceMatches ? inputSurface : null;
464
641
  const [surface, setSurface] = useState(initialSurface);
465
642
  const [surfaceIdentifier, setSurfaceIdentifier] = useState(initialSurface ? normalizedIdentifier : null);
643
+ const displayedSurfaceRef = useRef(surface);
644
+ displayedSurfaceRef.current = surface;
466
645
  const [acceptedInputSurface, setAcceptedInputSurface] = useState(initialSurface ? inputSurface : null);
467
646
  const [loading, setLoading] = useState(!initialSurface);
468
647
  const [loadingIdentifier, setLoadingIdentifier] = useState(initialSurface ? null : normalizedIdentifier);
@@ -514,8 +693,10 @@ function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: su
514
693
  }
515
694
  const requestInputSurface = currentInputSurfaceRef.current;
516
695
  setAcceptedInputSurface(requestInputSurface);
517
- setSurface(null);
518
- setSurfaceIdentifier(null);
696
+ if (!matchesSurface(displayedSurfaceRef.current, requestIdentifier)) {
697
+ setSurface(null);
698
+ setSurfaceIdentifier(null);
699
+ }
519
700
  setLoading(true);
520
701
  setLoadingIdentifier(requestIdentifier);
521
702
  setErrorState(null);
@@ -530,7 +711,7 @@ function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: su
530
711
  identifier: requestIdentifier,
531
712
  message: surfaceMismatchMessage,
532
713
  });
533
- return null;
714
+ throw new Error(surfaceMismatchMessage);
534
715
  }
535
716
  setSurface(nextSurface);
536
717
  setSurfaceIdentifier(requestIdentifier);
@@ -546,7 +727,7 @@ function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: su
546
727
  message: errorMessage(reason, loadErrorFallback),
547
728
  });
548
729
  }
549
- return null;
730
+ throw reason;
550
731
  }
551
732
  finally {
552
733
  if (isCurrentRequest(sequence)) {
@@ -592,9 +773,11 @@ function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: su
592
773
  requestSequence.current += 1;
593
774
  };
594
775
  }
595
- setSurface(null);
596
- setSurfaceIdentifier(null);
597
- void refresh();
776
+ if (!matchesSurface(displayedSurfaceRef.current, normalizedIdentifier)) {
777
+ setSurface(null);
778
+ setSurfaceIdentifier(null);
779
+ }
780
+ void refresh().catch(() => undefined);
598
781
  return () => {
599
782
  activeRef.current = false;
600
783
  requestSequence.current += 1;
@@ -633,32 +816,40 @@ function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: su
633
816
  setLoading(false);
634
817
  setLoadingIdentifier(null);
635
818
  }
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
- });
819
+ try {
820
+ await completeWorkflowCommand(surfaceKind, result, {
821
+ refresh: async () => {
822
+ if (!isCurrentGeneration(identifierAtStart, generationAtStart)) {
823
+ return null;
824
+ }
825
+ const nextSurface = await refresh();
826
+ return isCurrentGeneration(identifierAtStart, generationAtStart)
827
+ ? nextSurface
828
+ : null;
829
+ },
830
+ onCommandCompleted: async (commandResult) => {
831
+ if (!isCurrentGeneration(identifierAtStart, generationAtStart)) {
832
+ return;
833
+ }
834
+ await onCommandCompletedRef.current?.(commandResult);
835
+ },
836
+ onCompleted: async (nextSurface, commandResult) => {
837
+ if (!isCurrentGeneration(identifierAtStart, generationAtStart)) {
838
+ return;
839
+ }
840
+ const callback = onCompletedRef.current;
841
+ if (!callback)
842
+ return;
843
+ await callback(nextSurface, commandResult);
844
+ },
845
+ });
846
+ if (isCurrentGeneration(identifierAtStart, generationAtStart))
847
+ setConfirmedReadFailure(null);
848
+ }
849
+ catch (error) {
850
+ if (isCurrentGeneration(identifierAtStart, generationAtStart))
851
+ setConfirmedReadFailure({ result, message: errorMessage(error, '页面刷新失败') });
852
+ }
662
853
  }, [
663
854
  isCurrentGeneration,
664
855
  normalizedIdentifier,
@@ -689,19 +880,36 @@ function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: su
689
880
  loadingIdentifier === normalizedIdentifier ||
690
881
  surfaceIdentifier !== normalizedIdentifier ||
691
882
  !acceptedInput);
883
+ const receipt = locator && _jsx(WorkflowTaskCommandReceipt, { locator: locator, busy: writing, onSettled: async (result, text) => {
884
+ clearLocator(locator.idempotencyKey);
885
+ setSettlement({ key: locator.idempotencyKey, result });
886
+ message.info(text);
887
+ try {
888
+ if (result)
889
+ await completed(result);
890
+ else
891
+ await refreshCurrent();
892
+ }
893
+ catch (error) {
894
+ message.warning(result ? '原操作已成功,请刷新查看最新资料。' : errorMessage(error, '资料刷新失败'));
895
+ }
896
+ } }, locatorScope);
897
+ 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" }) });
898
+ const layout = (parts) => renderLayout
899
+ ? renderLayout({ ...parts, surface: renderSurface || undefined }) : _jsxs(_Fragment, { children: [parts.content, parts.actions] });
692
900
  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, {}) }));
901
+ 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
902
  }
695
903
  if (renderError && !renderSurface) {
696
- return (_jsx(Alert, { action: _jsx(Button, { onClick: () => void refresh(), children: "\u91CD\u8BD5" }), description: renderError, showIcon: true, type: "error" }));
904
+ 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
905
  }
698
906
  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 })] }));
907
+ return layout({ content: receipt || confirmedWarning ? _jsxs(_Fragment, { children: [receipt, confirmedWarning] }) : null, actions: null });
908
+ 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
909
  }
702
910
  /** 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" }));
911
+ export function WorkflowTaskOperationsPanel({ taskId, variant = 'desktop', surface, onSurfaceChange, onCompleted, onCommandCompleted, renderLayout, }) {
912
+ 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
913
  }
706
914
  /** Public, embeddable Workflow instance operation surface. */
707
915
  export function WorkflowInstanceOperationsPanel({ instanceId, variant = 'desktop', surface, onSurfaceChange, onCompleted, onCommandCompleted, }) {
@@ -717,7 +925,7 @@ export function WorkflowSummarySection({ surface }) {
717
925
  };
718
926
  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
927
  }
720
- function StandardWorkflowDetailRenderer({ surface, timeline, warning, operations, variant, onClose, onEdit, drawer, drawerState, newPageHref, editing, busy, }) {
928
+ function StandardWorkflowDetailRenderer({ surface, timeline, warning, operations, taskContent, variant, onClose, onEdit, drawer, drawerState, newPageHref, editing, busy, }) {
721
929
  const navigate = useNavigate();
722
930
  const timeZone = usePresentationTimeZone();
723
931
  const [tab, setTab] = useState('application');
@@ -735,13 +943,13 @@ function StandardWorkflowDetailRenderer({ surface, timeline, warning, operations
735
943
  const loadAudit = useCallback(() => loadWorkflowDataAudit(instance.id), [instance.id]);
736
944
  const back = onClose || (() => navigate(variant === 'mobile' ? '/m/work-center' : '/work-center'));
737
945
  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: [
946
+ 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
947
  { key: 'application', label: '申请内容', children: _jsx(WorkflowBusinessDetailSections, { surface: surface, variant: variant }) },
740
948
  { key: 'history', label: '审批历史', children: _jsx(WorkflowTimelineSection, { timeline: timeline }) },
741
949
  { key: 'changes', label: '变更记录', children: tab === 'changes' && business.surface && business.resourceCode && business.recordId
742
950
  ? _jsx(RecordChangeHistory, { resourceCode: business.resourceCode, recordId: business.recordId, surface: business.surface, readable: () => true, loadPage: loadAudit })
743
951
  : _jsx(Empty, { description: "\u6682\u65E0\u53D8\u66F4\u8BB0\u5F55" }) },
744
- ] })] });
952
+ ] }), taskContent && _jsx("div", { className: "oxa-workflow-task-content", children: taskContent })] });
745
953
  }
746
954
  export function DesktopWorkflowDetailRenderer(props) {
747
955
  return _jsx(StandardWorkflowDetailRenderer, { ...props, variant: "desktop" });
@@ -816,7 +1024,7 @@ export function useWorkflowDetail({ kind, id, resourceCode }) {
816
1024
  refresh,
817
1025
  };
818
1026
  }
819
- function WorkflowDetailPage({ kind, variant, resourceCode, recordId, onDismiss, drawerState }) {
1027
+ function WorkflowDetailPage({ kind, variant, resourceCode, recordId, onDismiss, drawerState, returnPath }) {
820
1028
  const params = useParams();
821
1029
  const id = recordId || String(kind === 'task' ? params.taskId || '' : kind === 'record' ? params.id || '' : params.instanceId || '');
822
1030
  const navigate = useNavigate();
@@ -829,11 +1037,7 @@ function WorkflowDetailPage({ kind, variant, resourceCode, recordId, onDismiss,
829
1037
  const business = surface ? surfaceBusinessDetail(surface) : null;
830
1038
  const detailPath = surface ? (variant === 'mobile' ? surface.detailNavigation.mobilePath : surface.detailNavigation.desktopPath) : '';
831
1039
  const newPageHref = useHref(detailPath || '.');
832
- const close = onDismiss || (() => navigate(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 });
1040
+ const close = onDismiss || (() => navigate(returnPath || (detail ? variant === 'mobile' ? detail.navigationContext.mobileReturnPath : detail.navigationContext.desktopReturnPath : variant === 'mobile' ? '/m/work-center' : '/work-center')));
837
1041
  const onCommandCompleted = async (result) => {
838
1042
  if (kind === 'task' && result.advanced === true) {
839
1043
  const nextId = String(result.instanceId || '').trim();
@@ -845,18 +1049,22 @@ function WorkflowDetailPage({ kind, variant, resourceCode, recordId, onDismiss,
845
1049
  else
846
1050
  await refresh();
847
1051
  };
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)
1052
+ if (!surface || !detail || !instance)
1053
+ 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)) &&
1054
+ _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" }) })] });
1055
+ if (surface.detailNavigation.custom)
1056
+ return _jsx(Navigate, { replace: true, to: detailPath });
1057
+ 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
1058
  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 });
1059
+ return task ? _jsx(WorkflowTaskOperationsPanel, { onCommandCompleted: onCommandCompleted, surface: surface, taskId: task.id, variant: variant, renderLayout: ({ content, actions, surface: currentSurface }) => renderDetail(actions, content, currentSurface) }, task.id)
1060
+ : renderDetail(_jsx(WorkflowInstanceOperationsPanel, { instanceId: instance.id, onCommandCompleted: onCommandCompleted, surface: surface, variant: variant }));
853
1061
  }
854
1062
  export function WorkflowRecordDetailPage({ variant = 'desktop', ...props }) { return _jsx(WorkflowDetailPage, { ...props, variant: variant, kind: "record" }); }
855
- export function WorkflowTaskPage({ variant = 'desktop', }) {
856
- return _jsx(WorkflowDetailPage, { kind: "task", variant: variant });
1063
+ export function WorkflowTaskPage({ variant = 'desktop', returnPath, }) {
1064
+ return _jsx(WorkflowDetailPage, { kind: "task", variant: variant, returnPath: returnPath });
857
1065
  }
858
- export function WorkflowInstancePage({ variant = 'desktop', }) {
859
- return _jsx(WorkflowDetailPage, { kind: "instance", variant: variant });
1066
+ export function WorkflowInstancePage({ variant = 'desktop', returnPath, }) {
1067
+ return _jsx(WorkflowDetailPage, { kind: "instance", variant: variant, returnPath: returnPath });
860
1068
  }
861
1069
  const WORK_VIEWS = [
862
1070
  { value: 'created', label: '我创建的' }, { value: 'pending', label: '待我审批' },