openxiangda 2.48.2 → 2.59.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/browser/AuthoritativeSelector.d.ts +4 -2
- package/dist/browser/AuthoritativeSelector.d.ts.map +1 -1
- package/dist/browser/AuthoritativeSelector.js +42 -12
- package/dist/browser/AuthoritativeSelector.js.map +1 -1
- package/dist/browser/FilePreviewPage.d.ts.map +1 -1
- package/dist/browser/FilePreviewPage.js +4 -0
- package/dist/browser/FilePreviewPage.js.map +1 -1
- package/dist/browser/components/administration/RoleMembershipManager.d.ts.map +1 -1
- package/dist/browser/components/administration/RoleMembershipManager.js +5 -5
- package/dist/browser/components/administration/RoleMembershipManager.js.map +1 -1
- package/dist/browser/components/administration/scope-applicability.d.ts +3 -0
- package/dist/browser/components/administration/scope-applicability.d.ts.map +1 -0
- package/dist/browser/components/administration/scope-applicability.js +7 -0
- package/dist/browser/components/administration/scope-applicability.js.map +1 -0
- package/dist/browser/components/platform-fields/ConstrainedUserField.d.ts +16 -0
- package/dist/browser/components/platform-fields/ConstrainedUserField.d.ts.map +1 -0
- package/dist/browser/components/platform-fields/ConstrainedUserField.js +140 -0
- package/dist/browser/components/platform-fields/ConstrainedUserField.js.map +1 -0
- package/dist/browser/components/platform-fields/DateTimeField.d.ts +1 -1
- package/dist/browser/components/platform-fields/DateTimeField.d.ts.map +1 -1
- package/dist/browser/components/platform-fields/DateTimeField.js +15 -8
- package/dist/browser/components/platform-fields/DateTimeField.js.map +1 -1
- package/dist/browser/components/platform-fields/ManagedMediaPlayer.d.ts +19 -0
- package/dist/browser/components/platform-fields/ManagedMediaPlayer.d.ts.map +1 -0
- package/dist/browser/components/platform-fields/ManagedMediaPlayer.js +42 -0
- package/dist/browser/components/platform-fields/ManagedMediaPlayer.js.map +1 -0
- package/dist/browser/components/platform-fields/MobileDateTimeField.d.ts +1 -1
- package/dist/browser/components/platform-fields/MobileDateTimeField.d.ts.map +1 -1
- package/dist/browser/components/platform-fields/MobileDateTimeField.js +5 -3
- package/dist/browser/components/platform-fields/MobileDateTimeField.js.map +1 -1
- package/dist/browser/components/platform-fields/MobileSelection.d.ts +3 -1
- package/dist/browser/components/platform-fields/MobileSelection.d.ts.map +1 -1
- package/dist/browser/components/platform-fields/MobileSelection.js +2 -2
- package/dist/browser/components/platform-fields/MobileSelection.js.map +1 -1
- package/dist/browser/components/platform-fields/MobileZonedDateTimeField.js +2 -2
- package/dist/browser/components/platform-fields/MobileZonedDateTimeField.js.map +1 -1
- package/dist/browser/components/platform-fields/ResourceReferenceField.d.ts +4 -2
- package/dist/browser/components/platform-fields/ResourceReferenceField.d.ts.map +1 -1
- package/dist/browser/components/platform-fields/ResourceReferenceField.js +3 -3
- package/dist/browser/components/platform-fields/ResourceReferenceField.js.map +1 -1
- package/dist/browser/components/platform-fields/SubtableField.d.ts +11 -2
- package/dist/browser/components/platform-fields/SubtableField.d.ts.map +1 -1
- package/dist/browser/components/platform-fields/SubtableField.js +55 -24
- package/dist/browser/components/platform-fields/SubtableField.js.map +1 -1
- package/dist/browser/components/platform-fields/ZonedDateTimeField.js +2 -2
- package/dist/browser/components/platform-fields/ZonedDateTimeField.js.map +1 -1
- package/dist/browser/components/platform-fields/managed-media-read.d.ts +19 -0
- package/dist/browser/components/platform-fields/managed-media-read.d.ts.map +1 -0
- package/dist/browser/components/platform-fields/managed-media-read.js +32 -0
- package/dist/browser/components/platform-fields/managed-media-read.js.map +1 -0
- package/dist/browser/components/platform-fields/reference-binding-change.d.ts +6 -0
- package/dist/browser/components/platform-fields/reference-binding-change.d.ts.map +1 -0
- package/dist/browser/components/platform-fields/reference-binding-change.js +32 -0
- package/dist/browser/components/platform-fields/reference-binding-change.js.map +1 -0
- package/dist/browser/components/platform-fields/user-candidate-context.d.ts +54 -0
- package/dist/browser/components/platform-fields/user-candidate-context.d.ts.map +1 -0
- package/dist/browser/components/platform-fields/user-candidate-context.js +70 -0
- package/dist/browser/components/platform-fields/user-candidate-context.js.map +1 -0
- package/dist/browser/components/platform-fields/zoned-date-time.d.ts +4 -0
- package/dist/browser/components/platform-fields/zoned-date-time.d.ts.map +1 -1
- package/dist/browser/components/platform-fields/zoned-date-time.js +9 -1
- package/dist/browser/components/platform-fields/zoned-date-time.js.map +1 -1
- package/dist/browser/components/resource/GeneratedResourceCrud.d.ts.map +1 -1
- package/dist/browser/components/resource/GeneratedResourceCrud.js +28 -4
- package/dist/browser/components/resource/GeneratedResourceCrud.js.map +1 -1
- package/dist/browser/components/resource/GeneratedResourceForm.js +3 -3
- package/dist/browser/components/resource/GeneratedResourceForm.js.map +1 -1
- package/dist/browser/components/resource/RecordDetailFrame.d.ts +3 -1
- package/dist/browser/components/resource/RecordDetailFrame.d.ts.map +1 -1
- package/dist/browser/components/resource/RecordDetailFrame.js +2 -2
- package/dist/browser/components/resource/RecordDetailFrame.js.map +1 -1
- package/dist/browser/components/resource/ResourceFormFrame.d.ts +3 -2
- package/dist/browser/components/resource/ResourceFormFrame.d.ts.map +1 -1
- package/dist/browser/components/resource/ResourceFormFrame.js +13 -2
- package/dist/browser/components/resource/ResourceFormFrame.js.map +1 -1
- package/dist/browser/components/resource/ResourceRecordComments.d.ts +13 -0
- package/dist/browser/components/resource/ResourceRecordComments.d.ts.map +1 -0
- package/dist/browser/components/resource/ResourceRecordComments.js +75 -0
- package/dist/browser/components/resource/ResourceRecordComments.js.map +1 -0
- package/dist/browser/components/resource/ResourceRecordDeletion.d.ts +7 -0
- package/dist/browser/components/resource/ResourceRecordDeletion.d.ts.map +1 -0
- package/dist/browser/components/resource/ResourceRecordDeletion.js +53 -0
- package/dist/browser/components/resource/ResourceRecordDeletion.js.map +1 -0
- package/dist/browser/components/resource/ResourceRecordPrintPreview.d.ts +16 -0
- package/dist/browser/components/resource/ResourceRecordPrintPreview.d.ts.map +1 -0
- package/dist/browser/components/resource/ResourceRecordPrintPreview.js +92 -0
- package/dist/browser/components/resource/ResourceRecordPrintPreview.js.map +1 -0
- package/dist/browser/components/resource/SurfaceFields.d.ts +12 -3
- package/dist/browser/components/resource/SurfaceFields.d.ts.map +1 -1
- package/dist/browser/components/resource/SurfaceFields.js +24 -8
- package/dist/browser/components/resource/SurfaceFields.js.map +1 -1
- package/dist/browser/components/resource/generated-resource-definition.d.ts +13 -0
- package/dist/browser/components/resource/generated-resource-definition.d.ts.map +1 -1
- package/dist/browser/components/resource/record-comment-session.d.ts +17 -0
- package/dist/browser/components/resource/record-comment-session.d.ts.map +1 -0
- package/dist/browser/components/resource/record-comment-session.js +64 -0
- package/dist/browser/components/resource/record-comment-session.js.map +1 -0
- package/dist/browser/components/resource/record-deletion-session.d.ts +19 -0
- package/dist/browser/components/resource/record-deletion-session.d.ts.map +1 -0
- package/dist/browser/components/resource/record-deletion-session.js +64 -0
- package/dist/browser/components/resource/record-deletion-session.js.map +1 -0
- package/dist/browser/components/resource/record-print-session.d.ts +12 -0
- package/dist/browser/components/resource/record-print-session.d.ts.map +1 -0
- package/dist/browser/components/resource/record-print-session.js +32 -0
- package/dist/browser/components/resource/record-print-session.js.map +1 -0
- package/dist/browser/components/resource/resource-field-selection.d.ts +2 -0
- package/dist/browser/components/resource/resource-field-selection.d.ts.map +1 -1
- package/dist/browser/components/resource/useResourceFormDrafts.d.ts.map +1 -1
- package/dist/browser/components/workflow/StandardWorkflowPages.d.ts +28 -5
- package/dist/browser/components/workflow/StandardWorkflowPages.d.ts.map +1 -1
- package/dist/browser/components/workflow/StandardWorkflowPages.js +143 -51
- package/dist/browser/components/workflow/StandardWorkflowPages.js.map +1 -1
- package/dist/browser/components/workflow/WorkflowDelegationManager.js +13 -8
- package/dist/browser/components/workflow/WorkflowDelegationManager.js.map +1 -1
- package/dist/browser/components/workflow/WorkflowDiagram.d.ts.map +1 -1
- package/dist/browser/components/workflow/WorkflowDiagram.js +5 -2
- package/dist/browser/components/workflow/WorkflowDiagram.js.map +1 -1
- package/dist/browser/components/workflow/WorkflowFlowCanvas.d.ts.map +1 -1
- package/dist/browser/components/workflow/WorkflowFlowCanvas.js +2 -1
- package/dist/browser/components/workflow/WorkflowFlowCanvas.js.map +1 -1
- package/dist/browser/components/workflow/WorkflowNodeCard.d.ts +1 -1
- package/dist/browser/components/workflow/WorkflowNodeCard.d.ts.map +1 -1
- package/dist/browser/components/workflow/WorkflowNodeCard.js +7 -5
- package/dist/browser/components/workflow/WorkflowNodeCard.js.map +1 -1
- package/dist/browser/components/workflow/WorkflowNodeConfigurationEditor.d.ts.map +1 -1
- package/dist/browser/components/workflow/WorkflowNodeConfigurationEditor.js +14 -11
- package/dist/browser/components/workflow/WorkflowNodeConfigurationEditor.js.map +1 -1
- package/dist/browser/components/workflow/WorkflowRecordEditor.js +1 -1
- package/dist/browser/components/workflow/WorkflowRecordEditor.js.map +1 -1
- package/dist/browser/components/workflow/WorkflowRecordHistoryPanel.d.ts +9 -0
- package/dist/browser/components/workflow/WorkflowRecordHistoryPanel.d.ts.map +1 -0
- package/dist/browser/components/workflow/WorkflowRecordHistoryPanel.js +50 -0
- package/dist/browser/components/workflow/WorkflowRecordHistoryPanel.js.map +1 -0
- package/dist/browser/components/workflow/WorkflowTaskForm.d.ts +4 -1
- package/dist/browser/components/workflow/WorkflowTaskForm.d.ts.map +1 -1
- package/dist/browser/components/workflow/WorkflowTaskForm.js +34 -10
- package/dist/browser/components/workflow/WorkflowTaskForm.js.map +1 -1
- package/dist/browser/components/workflow/standard-process-values.d.ts +10 -2
- package/dist/browser/components/workflow/standard-process-values.d.ts.map +1 -1
- package/dist/browser/components/workflow/standard-process-values.js +22 -1
- package/dist/browser/components/workflow/standard-process-values.js.map +1 -1
- package/dist/browser/components/workflow/workflow-flow-layout.d.ts.map +1 -1
- package/dist/browser/components/workflow/workflow-flow-layout.js +33 -3
- package/dist/browser/components/workflow/workflow-flow-layout.js.map +1 -1
- package/dist/browser/components/workflow/workflow-submission-form.d.ts +22 -0
- package/dist/browser/components/workflow/workflow-submission-form.d.ts.map +1 -0
- package/dist/browser/components/workflow/workflow-submission-form.js +64 -0
- package/dist/browser/components/workflow/workflow-submission-form.js.map +1 -0
- package/dist/browser/components/workflow/workflow-task-required-errors.d.ts +10 -0
- package/dist/browser/components/workflow/workflow-task-required-errors.d.ts.map +1 -0
- package/dist/browser/components/workflow/workflow-task-required-errors.js +28 -0
- package/dist/browser/components/workflow/workflow-task-required-errors.js.map +1 -0
- package/dist/browser/durable-command.d.ts.map +1 -1
- package/dist/browser/durable-command.js +7 -2
- package/dist/browser/durable-command.js.map +1 -1
- package/dist/browser/managed-read-recovery.d.ts +2 -0
- package/dist/browser/managed-read-recovery.d.ts.map +1 -1
- package/dist/browser/managed-read-recovery.js +6 -1
- package/dist/browser/managed-read-recovery.js.map +1 -1
- package/dist/browser/platform-client.d.ts +37 -1
- package/dist/browser/platform-client.d.ts.map +1 -1
- package/dist/browser/platform-client.js +199 -6
- package/dist/browser/platform-client.js.map +1 -1
- package/dist/browser/record-comments.css +14 -0
- package/dist/browser/record-deletion.css +11 -0
- package/dist/browser/record-print.css +31 -0
- package/dist/browser/runtime-authorization-recovery.d.ts.map +1 -1
- package/dist/browser/runtime-authorization-recovery.js +34 -9
- package/dist/browser/runtime-authorization-recovery.js.map +1 -1
- package/dist/browser/runtime.d.ts +3 -1
- package/dist/browser/runtime.d.ts.map +1 -1
- package/dist/browser/runtime.js.map +1 -1
- package/dist/browser/styles.css +30 -0
- package/dist/browser/workflow-operation-input.d.ts +15 -0
- package/dist/browser/workflow-operation-input.d.ts.map +1 -0
- package/dist/browser/workflow-operation-input.js +64 -0
- package/dist/browser/workflow-operation-input.js.map +1 -0
- package/dist/config.d.ts +2 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -1
- package/dist/config.js.map +1 -1
- package/dist/core.d.ts +6 -2
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +3 -1
- package/dist/core.js.map +1 -1
- package/dist/expressions.d.ts +4 -0
- package/dist/expressions.d.ts.map +1 -0
- package/dist/expressions.js +3 -0
- package/dist/expressions.js.map +1 -0
- package/dist/field-kit.d.ts +3 -0
- package/dist/field-kit.d.ts.map +1 -1
- package/dist/field-kit.js +3 -0
- package/dist/field-kit.js.map +1 -1
- package/dist/nest.d.ts +1 -1
- package/dist/nest.d.ts.map +1 -1
- package/dist/nest.js.map +1 -1
- package/dist/react.d.ts +6 -2
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +5 -1
- package/dist/react.js.map +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +1 -1
- package/dist/testing.js.map +1 -1
- package/documentation/administration.md +40 -1
- package/documentation/backend.md +189 -3
- package/documentation/data-authz.md +72 -2
- package/documentation/declarations-cheatsheet.md +11 -1
- package/documentation/development.md +7 -0
- package/documentation/field-components.md +80 -0
- package/documentation/frontend.md +8 -0
- package/documentation/getting-started.md +60 -7
- package/documentation/managed-concurrency-frontend.md +10 -4
- package/documentation/manifest.json +12 -12
- package/documentation/testing.md +17 -0
- package/documentation/workflow-events.md +403 -9
- package/package.json +32 -20
- package/releases/2.51.0.json +32 -0
- package/releases/2.54.4.json +30 -0
- package/releases/2.54.6.json +30 -0
- package/releases/2.55.0.json +34 -0
- package/releases/2.55.1.json +32 -0
- package/releases/2.55.5.json +30 -0
- package/releases/2.55.6.json +32 -0
- package/releases/2.57.1.json +31 -0
- package/releases/2.58.0.json +37 -0
- package/releases/2.58.1.json +31 -0
- package/releases/2.59.0.json +36 -0
- package/releases/2.59.1.json +37 -0
- package/skills/manifest.json +1 -1
- package/skills/openxiangda-v2/SKILL.md +4 -4
- package/skills/openxiangda-v2/references/administration.md +40 -1
- package/skills/openxiangda-v2/references/backend.md +189 -3
- package/skills/openxiangda-v2/references/data-authz.md +72 -2
- package/skills/openxiangda-v2/references/declarations-cheatsheet.md +11 -1
- package/skills/openxiangda-v2/references/development.md +7 -0
- package/skills/openxiangda-v2/references/field-components.md +80 -0
- package/skills/openxiangda-v2/references/frontend.md +8 -0
- package/skills/openxiangda-v2/references/getting-started.md +60 -7
- package/skills/openxiangda-v2/references/managed-concurrency-frontend.md +10 -4
- package/skills/openxiangda-v2/references/testing.md +17 -0
- package/skills/openxiangda-v2/references/workflow-events.md +403 -9
- package/releases/2.48.1.json +0 -30
- package/releases/2.48.2.json +0 -29
|
@@ -3,6 +3,7 @@ import { applicationCode } from '../../runtime-meta';
|
|
|
3
3
|
import { readPendingWorkflowSubmission, writePendingWorkflowSubmission, clearPendingWorkflowSubmission, workflowSubmissionScope, submissionLocatorStorage, standardProcessWasRejected } from '../../workflow-submission-recovery';
|
|
4
4
|
import { readPendingWorkflowTaskCommand, writePendingWorkflowTaskCommand, clearPendingWorkflowTaskCommand, workflowTaskCommandScope, workflowCommandTokenDigest, workflowTaskCommandWasRejected } from '../../workflow-task-command-recovery';
|
|
5
5
|
import { WorkflowTaskForm, useWorkflowTaskForm } from './WorkflowTaskForm';
|
|
6
|
+
import { retainedWorkflowOperation, workflowOperationSignature, workflowOperationStringError, workflowOperationInputFailure } from '../../workflow-operation-input';
|
|
6
7
|
import { WorkflowPendingCommandGuard, WorkflowTaskCommandReceipt } from './WorkflowTaskCommandReceipt';
|
|
7
8
|
import { PresentationTime, usePresentationTimeZone } from '../../presentation-time';
|
|
8
9
|
import { ArrowLeftOutlined, ClockCircleOutlined, EllipsisOutlined, ReloadOutlined, CloseOutlined, ExpandOutlined, CompressOutlined, ExportOutlined, } from '@ant-design/icons';
|
|
@@ -26,8 +27,9 @@ import { ResourceFormContent } from '../resource/ResourceFormFrame';
|
|
|
26
27
|
import { WorkflowRecordEditor } from './WorkflowRecordEditor';
|
|
27
28
|
import { PlatformAvatar } from '../PlatformAvatar';
|
|
28
29
|
import { SubtableField } from '../platform-fields/SubtableField';
|
|
29
|
-
import { standardProcessFormValues } from './standard-process-values';
|
|
30
|
+
import { assertNamedSubtableReadonlyFields, namedProcessFormValues, standardProcessFormValues } from './standard-process-values';
|
|
30
31
|
import { fieldWritable } from '../resource/resource-page-helpers';
|
|
32
|
+
import { workflowSubmissionFormProjection, workflowSubmissionPrefill, workflowSubmissionValueLinkage } from './workflow-submission-form';
|
|
31
33
|
/**
|
|
32
34
|
* An advanced task command has completed the current task and may have
|
|
33
35
|
* created a new task. Reading the old task Surface in that case is expected
|
|
@@ -53,10 +55,13 @@ export async function completeWorkflowCommand(kind, result, options) {
|
|
|
53
55
|
catch (error) {
|
|
54
56
|
refreshFailure = error;
|
|
55
57
|
}
|
|
56
|
-
|
|
58
|
+
const failure = refreshFailure;
|
|
59
|
+
const taskSurfaceUnavailable = kind === 'task' && ((failure?.status === 403 && failure.code === 'WORKFLOW_V2_TASK_FORBIDDEN') ||
|
|
60
|
+
(failure?.status === 404 && failure.code === 'WORKFLOW_V2_TASK_NOT_FOUND'));
|
|
61
|
+
await options.onCommandCompleted?.(result, { taskSurfaceUnavailable });
|
|
57
62
|
if (nextSurface)
|
|
58
63
|
await options.onCompleted?.(nextSurface, result);
|
|
59
|
-
if (refreshFailure)
|
|
64
|
+
if (refreshFailure && !(taskSurfaceUnavailable && options.onCommandCompleted))
|
|
60
65
|
throw refreshFailure;
|
|
61
66
|
return nextSurface;
|
|
62
67
|
}
|
|
@@ -87,6 +92,7 @@ function workflowStatus(status) {
|
|
|
87
92
|
running: { label: '审批中', color: 'processing' },
|
|
88
93
|
returned: { label: '已退回', color: 'orange' },
|
|
89
94
|
completed: { label: '已完成', color: 'success' },
|
|
95
|
+
skipped: { label: '已跳过', color: 'default' },
|
|
90
96
|
approved: { label: '已同意', color: 'success' },
|
|
91
97
|
rejected: { label: '已拒绝', color: 'error' },
|
|
92
98
|
withdrawn: { label: '已撤回', color: 'default' },
|
|
@@ -132,25 +138,6 @@ function workflowSurfaceMatchesInstance(surface, instanceId) {
|
|
|
132
138
|
function surfaceBusinessDetail(surface) {
|
|
133
139
|
return normalizeWorkflowBusinessDetail(surface.presentation.businessDetail);
|
|
134
140
|
}
|
|
135
|
-
function workflowOperationSignature(operation) {
|
|
136
|
-
return JSON.stringify({
|
|
137
|
-
key: operation.key,
|
|
138
|
-
kind: operation.kind,
|
|
139
|
-
label: operation.label,
|
|
140
|
-
placement: operation.placement,
|
|
141
|
-
group: operation.group,
|
|
142
|
-
audience: operation.audience,
|
|
143
|
-
emphasis: operation.emphasis,
|
|
144
|
-
tone: operation.tone,
|
|
145
|
-
visible: operation.visible,
|
|
146
|
-
enabled: operation.enabled,
|
|
147
|
-
disabledReason: operation.disabledReason,
|
|
148
|
-
inputSchema: operation.inputSchema,
|
|
149
|
-
uiSchema: operation.uiSchema,
|
|
150
|
-
execute: operation.execute,
|
|
151
|
-
refresh: operation.refresh,
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
141
|
const WORKFLOW_APP_ACTION_DISABLED_REASON = '应用动作由应用页面负责';
|
|
155
142
|
function standardWorkflowOperations(surface) {
|
|
156
143
|
return surface.operations
|
|
@@ -320,7 +307,7 @@ export function WorkflowTimelineSection({ timeline, }) {
|
|
|
320
307
|
const operations = item.operations || [];
|
|
321
308
|
const visiblePeople = visibleWorkflowTimelinePeople(item, operations);
|
|
322
309
|
const businessStep = item.nodeKind === 'action' ? item.result.businessStep : undefined;
|
|
323
|
-
return (_jsxs("div", { className: timelineTone(item.status), children: [_jsx("span", { className: "oxa-workflow-timeline-dot", children: _jsx(WorkflowStatusIcon, { status: item.status }) }), _jsxs("div", { children: [_jsxs("div", { className: "oxa-workflow-timeline-title", children: [_jsx("strong", { children: item.title }), businessStep?.status === 'waiting' ? _jsx(Tag, { color: "processing", children: "\u7B49\u5F85\u4E1A\u52A1\u7ED3\u679C" }) : businessStep?.status === 'result_ready' ? _jsx(Tag, { color: "warning", children: "\u63A8\u8FDB\u53D7\u963B" }) : _jsx(StatusTag, { status: item.status })] }), businessStep?.status === 'result_ready' && _jsx(Typography.Text, { type: "secondary", children: "\u8BA1\u7B97\u7ED3\u679C\u5DF2\u6536\u5230\uFF0C\u540E\u7EED\u6D41\u8F6C\u7B49\u5F85\u4FEE\u590D\u3002" }), visiblePeople.length > 0 && (_jsx("div", { className: "oxa-workflow-timeline-assignees", children: visiblePeople.map((person, index) => (_jsxs("div", { children: [_jsx(PlatformAvatar, { avatarUrl: person.avatarUrl, size: 20 }), _jsxs("span", { children: [_jsx(Typography.Text, { children: person.displayName }), person.departmentDisplayName && (_jsx("small", { children: person.departmentDisplayName }))] })] }, `${person.userId || person.displayName}:${index}`))) })), operations.length > 0 && (_jsx("div", { className: "oxa-workflow-timeline-operations", children: operations.map((operation) => (_jsxs("div", { children: [_jsx(PlatformAvatar, { avatarUrl: operation.actor.avatarUrl, size: 20 }), _jsxs("span", { className: "oxa-workflow-operation-content", children: [_jsxs("span", { children: [_jsx(Typography.Text, { children: operation.actor.displayName }), _jsx("strong", { children: operation.operationLabel })] }), operation.reason && (_jsxs("span", { className: "oxa-workflow-operation-opinion", children: [_jsx(Typography.Text, { type: "secondary", children: "\u610F\u89C1" }), _jsx(Typography.Text, { children: operation.reason })] }))] })] }, operation.id))) })), item.terminalReason && !operations.some(operation => operation.reason) && (_jsxs(Typography.Text, { type: "secondary", children: ["\u539F\u56E0\uFF1A", item.terminalReason] })), item.primaryDisplayTime && (_jsx("time", { className: "oxa-workflow-primary-time", children: formatTime(item.primaryDisplayTime) }))] })] }, item.key));
|
|
310
|
+
return (_jsxs("div", { className: timelineTone(item.status), children: [_jsx("span", { className: "oxa-workflow-timeline-dot", children: _jsx(WorkflowStatusIcon, { status: item.status }) }), _jsxs("div", { children: [_jsxs("div", { className: "oxa-workflow-timeline-title", children: [_jsx("strong", { children: item.title }), businessStep?.status === 'waiting' ? _jsx(Tag, { color: "processing", children: "\u7B49\u5F85\u4E1A\u52A1\u7ED3\u679C" }) : businessStep?.status === 'result_ready' ? _jsx(Tag, { color: "warning", children: "\u63A8\u8FDB\u53D7\u963B" }) : _jsx(StatusTag, { status: item.status })] }), item.status === 'skipped' && _jsx(Typography.Text, { type: "secondary", children: "\u65E0\u5BA1\u6279\u4EBA\uFF0C\u6309\u6B64\u8282\u70B9\u7684\u89C4\u5219\u81EA\u52A8\u7EE7\u7EED\u3002" }), businessStep?.status === 'result_ready' && _jsx(Typography.Text, { type: "secondary", children: "\u8BA1\u7B97\u7ED3\u679C\u5DF2\u6536\u5230\uFF0C\u540E\u7EED\u6D41\u8F6C\u7B49\u5F85\u4FEE\u590D\u3002" }), visiblePeople.length > 0 && (_jsx("div", { className: "oxa-workflow-timeline-assignees", children: visiblePeople.map((person, index) => (_jsxs("div", { children: [_jsx(PlatformAvatar, { avatarUrl: person.avatarUrl, size: 20 }), _jsxs("span", { children: [_jsx(Typography.Text, { children: person.displayName }), person.departmentDisplayName && (_jsx("small", { children: person.departmentDisplayName }))] })] }, `${person.userId || person.displayName}:${index}`))) })), operations.length > 0 && (_jsx("div", { className: "oxa-workflow-timeline-operations", children: operations.map((operation) => (_jsxs("div", { children: [_jsx(PlatformAvatar, { avatarUrl: operation.actor.avatarUrl, size: 20 }), _jsxs("span", { className: "oxa-workflow-operation-content", children: [_jsxs("span", { children: [_jsx(Typography.Text, { children: operation.actor.displayName }), _jsx("strong", { children: operation.operationLabel })] }), operation.reason && (_jsxs("span", { className: "oxa-workflow-operation-opinion", children: [_jsx(Typography.Text, { type: "secondary", children: "\u610F\u89C1" }), _jsx(Typography.Text, { children: operation.reason })] }))] })] }, operation.id))) })), item.terminalReason && !operations.some(operation => operation.reason) && (_jsxs(Typography.Text, { type: "secondary", children: ["\u539F\u56E0\uFF1A", item.terminalReason] })), item.primaryDisplayTime && (_jsx("time", { className: "oxa-workflow-primary-time", children: formatTime(item.primaryDisplayTime) }))] })] }, item.key));
|
|
324
311
|
}) })) : (_jsx(Empty, { description: "\u6682\u65E0\u6D41\u7A0B\u8F68\u8FF9", image: Empty.PRESENTED_IMAGE_SIMPLE })) }));
|
|
325
312
|
}
|
|
326
313
|
function WorkflowMemberPicker({ id, placeholder, value, onChange, multiple = false, variant }) {
|
|
@@ -347,6 +334,11 @@ function OperationFields({ operation, variant, }) {
|
|
|
347
334
|
return (_jsx(_Fragment, { children: schemaProperties(operation).map(({ key, property, required, ui }) => {
|
|
348
335
|
const rules = [
|
|
349
336
|
...(required ? [{ required: true, message: `请填写${property.title || key}` }] : []),
|
|
337
|
+
...(property.type === 'string' ? [{ validator: async (_rule, value) => {
|
|
338
|
+
const error = workflowOperationStringError(property, value, required, property.title || key);
|
|
339
|
+
if (error)
|
|
340
|
+
throw new Error(error);
|
|
341
|
+
} }] : []),
|
|
350
342
|
...(property.type === 'array' ? [{ validator: async (_rule, value) => {
|
|
351
343
|
if (value == null && !required)
|
|
352
344
|
return;
|
|
@@ -370,7 +362,7 @@ function OperationFields({ operation, variant, }) {
|
|
|
370
362
|
else if (property.format === 'date-time') {
|
|
371
363
|
control = _jsx(Input, { type: "datetime-local" });
|
|
372
364
|
}
|
|
373
|
-
return (_jsx(Form.Item, { initialValue: property.default, label: property.title || key, name: key, rules: rules, children: control }, key));
|
|
365
|
+
return (_jsx(Form.Item, { initialValue: property.default, label: property.title || key, name: key, rules: rules, validateFirst: true, children: control }, key));
|
|
374
366
|
}) }));
|
|
375
367
|
}
|
|
376
368
|
function OperationDialog({ error, operation, submitting, variant, onCancel, onSubmit, }) {
|
|
@@ -392,6 +384,7 @@ function WorkflowOperations({ surface, variant, onRefresh, onCommandSuccess, pen
|
|
|
392
384
|
const [selectedState, setSelectedState] = useState(null);
|
|
393
385
|
const [moreOpen, setMoreOpen] = useState(false);
|
|
394
386
|
const [operationError, setOperationError] = useState('');
|
|
387
|
+
const [retainRefusedInput, setRetainRefusedInput] = useState(false);
|
|
395
388
|
const [submitting, setSubmitting] = useState(false);
|
|
396
389
|
const [draftLocked, setDraftLocked] = useState(false);
|
|
397
390
|
const [fileLocked, setFileLocked] = useState(false);
|
|
@@ -429,16 +422,14 @@ function WorkflowOperations({ surface, variant, onRefresh, onCommandSuccess, pen
|
|
|
429
422
|
timer = setTimeout(tick, Math.min(commandExpiresAt - Date.now(), 2_147_483_647));
|
|
430
423
|
return () => clearTimeout(timer);
|
|
431
424
|
}, [surface.commandToken, commandExpiresAt]);
|
|
432
|
-
const selected =
|
|
433
|
-
|
|
434
|
-
workflowOperationSignature(operation) ===
|
|
435
|
-
selectedState.operationSignature) || null
|
|
436
|
-
: null;
|
|
425
|
+
const selected = commandNeedsRefresh ? null
|
|
426
|
+
: retainedWorkflowOperation(selectedState, surface, operations, retainRefusedInput);
|
|
437
427
|
useEffect(() => {
|
|
438
428
|
if (selectedState && !selected && !submitting && !unknown)
|
|
439
429
|
setSelectedState(null);
|
|
440
430
|
}, [selected, selectedState, submitting, unknown]);
|
|
441
431
|
const selectOperation = (operation) => {
|
|
432
|
+
setRetainRefusedInput(false);
|
|
442
433
|
setOperationError('');
|
|
443
434
|
setSelectedState({
|
|
444
435
|
operation,
|
|
@@ -519,13 +510,13 @@ function WorkflowOperations({ surface, variant, onRefresh, onCommandSuccess, pen
|
|
|
519
510
|
if (rejected) {
|
|
520
511
|
clearLocator(wire.key);
|
|
521
512
|
request.current = null;
|
|
522
|
-
|
|
513
|
+
setRetainRefusedInput(true);
|
|
523
514
|
}
|
|
524
515
|
else {
|
|
525
516
|
setUnknown(true);
|
|
526
517
|
setSelectedState(null);
|
|
527
518
|
}
|
|
528
|
-
const failure = errorMessage(error, '任务操作尚未完成');
|
|
519
|
+
const failure = workflowOperationInputFailure(error) || errorMessage(error, '任务操作尚未完成');
|
|
529
520
|
setOperationError(failure);
|
|
530
521
|
if (rejected) {
|
|
531
522
|
message.error(failure);
|
|
@@ -607,12 +598,12 @@ function WorkflowOperations({ surface, variant, onRefresh, onCommandSuccess, pen
|
|
|
607
598
|
return (order[left.key] ?? 10) - (order[right.key] ?? 10);
|
|
608
599
|
});
|
|
609
600
|
const secondary = operations.filter((operation) => operation.placement !== 'primary');
|
|
610
|
-
const content = surface.taskForm || commandNeedsRefresh || ((submitting || unknown) && !pendingLocator) || unknown || refreshFailure ? (_jsxs(_Fragment, { children: [commandNeedsRefresh && !unknown && !pendingLocator && !refreshFailure && _jsx(Alert, { type: "info", showIcon: true, title: "\u8BF7\u5237\u65B0\u64CD\u4F5C\u540E\u7EE7\u7EED\u529E\u7406", description: "\u9875\u9762\u505C\u7559\u8F83\u4E45\uFF0C\u9700\u91CD\u65B0\u786E\u8BA4\u5F53\u524D\u529E\u7406\u6743\u9650\u3002\u5237\u65B0\u4F1A\u4FDD\u7559\u5C1A\u672A\u4FDD\u5B58\u7684\u8F93\u5165\u3002", action: _jsx(Button, { disabled: locked, loading: submitting, onClick: () => void refreshConfirmed(), children: "\u5237\u65B0\u64CD\u4F5C" }) }), surface.taskForm && _jsx(WorkflowTaskForm, { controller: taskForm, disabled: locked, variant: variant, taskId: surface.task?.id ? String(surface.task.id) : undefined, draftDisabled: commandLocked, onRefresh: onRefresh, onDraftBusyChange: busy => { setDraftLocked(busy); onBusyChange(busy || fileLocked || busyRef.current); }, onFileBusyChange: busy => { setFileLocked(busy); onBusyChange(busy || draftLocked || busyRef.current); }, resourceCode: surface.presentation.businessDetail.resourceCode || undefined, recordId: surface.presentation.businessDetail.recordId || undefined }), (submitting || unknown) && !pendingLocator && _jsx(WorkflowPendingCommandGuard, {}), unknown && _jsx(Alert, { type: "warning", showIcon: true, title: "\u4FDD\u7559\u4E86\u539F\u63D0\u4EA4\u8BF7\u6C42", description: operationError || '提交结果待确认,请重试原请求或查询原结果。', action: _jsx(Button, { disabled: submitting, loading: submitting, onClick: () => void sendRequest(true), children: "\u91CD\u8BD5\u539F\u8BF7\u6C42" }) }), refreshFailure && _jsx(Alert, { type: "warning", showIcon: true, title: refreshFailure.outcome === 'succeeded'
|
|
601
|
+
const content = surface.taskForm || commandNeedsRefresh || ((submitting || unknown) && !pendingLocator) || unknown || refreshFailure ? (_jsxs(_Fragment, { children: [commandNeedsRefresh && !unknown && !pendingLocator && !refreshFailure && _jsx(Alert, { type: "info", showIcon: true, title: "\u8BF7\u5237\u65B0\u64CD\u4F5C\u540E\u7EE7\u7EED\u529E\u7406", description: "\u9875\u9762\u505C\u7559\u8F83\u4E45\uFF0C\u9700\u91CD\u65B0\u786E\u8BA4\u5F53\u524D\u529E\u7406\u6743\u9650\u3002\u5237\u65B0\u4F1A\u4FDD\u7559\u5C1A\u672A\u4FDD\u5B58\u7684\u8F93\u5165\u3002", action: _jsx(Button, { disabled: locked, loading: submitting, onClick: () => void refreshConfirmed(), children: "\u5237\u65B0\u64CD\u4F5C" }) }), surface.taskForm && _jsx(WorkflowTaskForm, { controller: taskForm, disabled: locked, variant: variant, operationPending: submitting || unknown || Boolean(pendingLocator) || draftLocked || fileLocked, taskVersion: surface.task?.version ? Number(surface.task.version) : undefined, taskId: surface.task?.id ? String(surface.task.id) : undefined, draftDisabled: commandLocked, onRefresh: onRefresh, onDraftBusyChange: busy => { setDraftLocked(busy); onBusyChange(busy || fileLocked || busyRef.current); }, onFileBusyChange: busy => { setFileLocked(busy); onBusyChange(busy || draftLocked || busyRef.current); }, resourceCode: surface.presentation.businessDetail.resourceCode || undefined, recordId: surface.presentation.businessDetail.recordId || undefined }), (submitting || unknown) && !pendingLocator && _jsx(WorkflowPendingCommandGuard, {}), unknown && _jsx(Alert, { type: "warning", showIcon: true, title: "\u4FDD\u7559\u4E86\u539F\u63D0\u4EA4\u8BF7\u6C42", description: operationError || '提交结果待确认,请重试原请求或查询原结果。', action: _jsx(Button, { disabled: submitting, loading: submitting, onClick: () => void sendRequest(true), children: "\u91CD\u8BD5\u539F\u8BF7\u6C42" }) }), refreshFailure && _jsx(Alert, { type: "warning", showIcon: true, title: refreshFailure.outcome === 'succeeded'
|
|
611
602
|
? '任务操作已成功,但资料刷新失败' : '本次未提交,最新资料读取失败', description: refreshFailure.message, action: _jsx(Button, { disabled: submitting, onClick: () => void refreshConfirmed(), children: "\u5237\u65B0\u8D44\u6599" }) })] })) : null;
|
|
612
603
|
const actions = (_jsxs(_Fragment, { children: [_jsxs("div", { className: `oxa-workflow-actions oxa-workflow-actions-${variant}`, children: [primary.map((operation) => (_jsx(Button, { danger: operation.emphasis === 'danger', disabled: locked || commandNeedsRefresh || taskForm.stale || taskForm.draftNeedsSave || !operation.enabled, onClick: () => selectOperation(operation), title: operation.disabledReason, type: operation.emphasis === 'primary' ? 'primary' : 'default', children: operation.label }, operation.key))), secondary.length > 0 && (_jsx(Button, { className: "oxa-workflow-more-trigger", disabled: locked || commandNeedsRefresh || taskForm.stale || taskForm.draftNeedsSave, icon: _jsx(EllipsisOutlined, {}), onClick: () => setMoreOpen((value) => !value), children: "\u66F4\u591A\u64CD\u4F5C" })), secondary.length > 0 && moreOpen && (_jsx("div", { className: "oxa-workflow-more-actions", children: secondary.map((operation) => (_jsx(Button, { disabled: locked || commandNeedsRefresh || taskForm.stale || taskForm.draftNeedsSave || !operation.enabled, onClick: () => {
|
|
613
604
|
setMoreOpen(false);
|
|
614
605
|
selectOperation(operation);
|
|
615
|
-
}, type: "text", title: operation.disabledReason, children: operation.label }, operation.key))) }))] }), _jsx(OperationDialog, { error: operationError, onCancel: () => setSelectedState(null), onSubmit: (values) => void submit(values), operation: selected, submitting: submitting, variant: variant })] }));
|
|
606
|
+
}, type: "text", title: operation.disabledReason, children: operation.label }, operation.key))) }))] }), _jsx(OperationDialog, { error: operationError, onCancel: () => { setRetainRefusedInput(false); setSelectedState(null); }, onSubmit: (values) => void submit(values), operation: selected, submitting: submitting, variant: variant })] }));
|
|
616
607
|
return renderLayout({ content, actions });
|
|
617
608
|
}
|
|
618
609
|
/**
|
|
@@ -633,6 +624,7 @@ function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: su
|
|
|
633
624
|
const [writing, setWriting] = useState(false);
|
|
634
625
|
const [settlement, setSettlement] = useState(null);
|
|
635
626
|
const [confirmedReadFailure, setConfirmedReadFailure] = useState(null);
|
|
627
|
+
const [taskCommandSettled, setTaskCommandSettled] = useState(false);
|
|
636
628
|
const locator = locatorState.scope === locatorScope ? locatorState.value : null;
|
|
637
629
|
useEffect(() => {
|
|
638
630
|
setLocatorState({ scope: locatorScope, value: locatorScope ? readPendingWorkflowTaskCommand(submissionLocatorStorage(), locatorScope) : null });
|
|
@@ -760,6 +752,7 @@ function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: su
|
|
|
760
752
|
]);
|
|
761
753
|
useEffect(() => {
|
|
762
754
|
activeRef.current = true;
|
|
755
|
+
setTaskCommandSettled(false);
|
|
763
756
|
requestSequence.current += 1;
|
|
764
757
|
if (!normalizedIdentifier) {
|
|
765
758
|
setSurface(null);
|
|
@@ -842,11 +835,19 @@ function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: su
|
|
|
842
835
|
? nextSurface
|
|
843
836
|
: null;
|
|
844
837
|
},
|
|
845
|
-
onCommandCompleted: async (commandResult) => {
|
|
838
|
+
onCommandCompleted: async (commandResult, context) => {
|
|
846
839
|
if (!isCurrentGeneration(identifierAtStart, generationAtStart)) {
|
|
847
840
|
return;
|
|
848
841
|
}
|
|
849
|
-
|
|
842
|
+
if (context?.taskSurfaceUnavailable) {
|
|
843
|
+
setSurface(null);
|
|
844
|
+
setSurfaceIdentifier(identifierAtStart);
|
|
845
|
+
setLoading(false);
|
|
846
|
+
setLoadingIdentifier(null);
|
|
847
|
+
setErrorState(null);
|
|
848
|
+
setTaskCommandSettled(true);
|
|
849
|
+
}
|
|
850
|
+
await onCommandCompletedRef.current?.(commandResult, context);
|
|
850
851
|
},
|
|
851
852
|
onCompleted: async (nextSurface, commandResult) => {
|
|
852
853
|
if (!isCurrentGeneration(identifierAtStart, generationAtStart)) {
|
|
@@ -919,7 +920,7 @@ function WorkflowOperationsPanel({ identifier, surfaceKind, variant, surface: su
|
|
|
919
920
|
return layout({ actions: null, content: (_jsxs(_Fragment, { children: [receipt, confirmedWarning, _jsx(Alert, { action: _jsx(Button, { onClick: () => void refresh().catch(() => undefined), children: "\u91CD\u8BD5" }), description: renderError, showIcon: true, type: "error" })] })) });
|
|
920
921
|
}
|
|
921
922
|
if (!renderSurface)
|
|
922
|
-
return layout({ content: receipt || confirmedWarning ? _jsxs(_Fragment, { children: [receipt, confirmedWarning] }) : null, actions: null });
|
|
923
|
+
return layout({ content: receipt || confirmedWarning || taskCommandSettled ? _jsxs(_Fragment, { children: [receipt, confirmedWarning, taskCommandSettled && !confirmedWarning && _jsx(Alert, { type: "success", showIcon: true, title: "\u4EFB\u52A1\u64CD\u4F5C\u5DF2\u6210\u529F", description: "\u4F60\u7684\u672C\u6B21\u529E\u7406\u5DF2\u5B8C\u6210\uFF0C\u6D41\u7A0B\u53EF\u80FD\u4ECD\u5728\u7B49\u5F85\u5176\u4ED6\u5904\u7406\u4EBA\u3002" })] }) : null, actions: null });
|
|
923
924
|
return (_jsx(WorkflowOperations, { onCommandSuccess: completed, onRefresh: refreshCurrent, surface: renderSurface, variant: variant, pendingLocator: locator, persistLocator: persistLocator, clearLocator: clearLocator, onBusyChange: setWriting, settlement: settlement, confirmedReadPending: Boolean(confirmedReadFailure), renderLayout: ({ content, actions }) => layout({ actions, content: receipt || confirmedWarning || renderError || content ? _jsxs(_Fragment, { children: [receipt, confirmedWarning, renderError && _jsx(Alert, { description: renderError, showIcon: true, type: "warning" }), content] }) : null }) }, `${normalizedIdentifier}:${locatorScope}:${runtime?.identityEpoch || 0}`));
|
|
924
925
|
}
|
|
925
926
|
/** Public, embeddable Workflow task operation surface. */
|
|
@@ -945,12 +946,14 @@ function StandardWorkflowDetailRenderer({ surface, timeline, warning, operations
|
|
|
945
946
|
const timeZone = usePresentationTimeZone();
|
|
946
947
|
const [tab, setTab] = useState('application');
|
|
947
948
|
const instance = surfaceInstance(surface);
|
|
949
|
+
const deadline = surfaceTask(surface)?.completionDeadline;
|
|
948
950
|
const business = surfaceBusinessDetail(surface);
|
|
949
951
|
const summary = surface.presentation.summary;
|
|
950
952
|
const titleField = workflowDetailGroups(business).flatMap(group => group.fields).find(field => field.type === 'text.short');
|
|
951
953
|
const title = titleField && business.record[titleField.key] ? String(business.record[titleField.key]) : summary.title;
|
|
952
954
|
const activeNode = timeline?.display?.entries.find(entry => entry.status === 'active');
|
|
953
|
-
const
|
|
955
|
+
const activeFlow = activeNode && timeline?.flow.find(entry => entry.key === activeNode.key && entry.status === 'active');
|
|
956
|
+
const people = activeFlow?.assignees.filter(person => person.status === 'active').map(person => person.displayName).filter(Boolean).join('、');
|
|
954
957
|
const statusTone = workflowStatus(instance.status).color;
|
|
955
958
|
const bannerTone = statusTone === 'error' ? 'error' : 'active';
|
|
956
959
|
const edit = workflowHasEnded(instance) ? onEdit : undefined;
|
|
@@ -958,7 +961,7 @@ function StandardWorkflowDetailRenderer({ surface, timeline, warning, operations
|
|
|
958
961
|
const loadAudit = useCallback(() => loadWorkflowDataAudit(instance.id), [instance.id]);
|
|
959
962
|
const back = onClose || (() => navigate(variant === 'mobile' ? '/m/work-center' : '/work-center'));
|
|
960
963
|
return _jsxs(RecordDetailFrame, { variant: variant, title: "\u7533\u8BF7\u8BE6\u60C5", heading: title, status: _jsx(StatusTag, { status: instance.status }), metadata: [summary.initiator?.displayName || summary.initiatorDisplayName, summary.initiator?.departmentDisplayName || summary.departmentDisplayName,
|
|
961
|
-
summary.submittedAt ? `${detailTime(summary.submittedAt, timeZone)} 创建` : ''].filter(Boolean).join(' · '), updatedAt: business.record.updated_at || instance.completedAt || instance.startedAt, onClose: back, onEdit: edit, editing: editing, busy: busy, drawer: drawer, drawerState: drawerState, newPageHref: newPageHref, footer: operations || hasActions || edit ? _jsxs(_Fragment, { children: [operations, edit && _jsx(Button, { onClick: edit, children: "\u7F16\u8F91" })] }) : null, children: [warning && _jsx(Alert, { description: warning, showIcon: true, type: "warning" }), !workflowHasEnded(instance) && activeNode && _jsxs("div", { className: `oxa-workflow-current-node is-${bannerTone}`, role: "status", children: [_jsxs("span", { children: [_jsx(ClockCircleOutlined, {}), "\u5F53\u524D\u8282\u70B9\uFF1A", activeNode.title] }), people && _jsxs("span", { children: ["\u7B49\u5F85", people, "\u5904\u7406"] })] }), _jsx(Tabs, { activeKey: tab, onChange: setTab, items: [
|
|
964
|
+
summary.submittedAt ? `${detailTime(summary.submittedAt, timeZone)} 创建` : ''].filter(Boolean).join(' · '), updatedAt: business.record.updated_at || instance.completedAt || instance.startedAt, onClose: back, onEdit: edit, editing: editing, busy: busy, drawer: drawer, drawerState: drawerState, newPageHref: newPageHref, footer: operations || hasActions || edit ? _jsxs(_Fragment, { children: [operations, edit && _jsx(Button, { onClick: edit, children: "\u7F16\u8F91" })] }) : null, children: [warning && _jsx(Alert, { description: warning, showIcon: true, type: "warning" }), !workflowHasEnded(instance) && activeNode && _jsxs("div", { className: `oxa-workflow-current-node is-${bannerTone}`, role: "status", children: [_jsxs("span", { children: [_jsx(ClockCircleOutlined, {}), "\u5F53\u524D\u8282\u70B9\uFF1A", activeNode.title] }), people && _jsxs("span", { children: ["\u7B49\u5F85", people, "\u5904\u7406"] })] }), deadline && !workflowHasEnded(instance) && ['pending', 'failed'].includes(deadline.status) && _jsx(Alert, { showIcon: true, type: deadline.status === 'failed' ? 'warning' : 'info', title: deadline.status === 'failed' ? '到期自动处理未成功' : `未提前完成时,将于 ${detailTime(deadline.deadlineAt, timeZone)} 自动同意`, description: deadline.status === 'failed' ? '流程仍可人工办理。自动处理已停止,请联系流程管理员查看执行记录。' : '可以在此之前填写可选内容并手动提交;自动处理不会代替任何人记录审批意见。' }), _jsx(Tabs, { activeKey: tab, onChange: setTab, items: [
|
|
962
965
|
{ key: 'application', label: '申请内容', children: _jsx(WorkflowBusinessDetailSections, { surface: surface, variant: variant }) },
|
|
963
966
|
{ key: 'history', label: '审批历史', children: _jsx(WorkflowTimelineSection, { timeline: timeline }) },
|
|
964
967
|
{ key: 'changes', label: '变更记录', children: tab === 'changes' && business.surface && business.resourceCode && business.recordId
|
|
@@ -1044,6 +1047,16 @@ function WorkflowDetailPage({ kind, variant, resourceCode, recordId, onDismiss,
|
|
|
1044
1047
|
const id = recordId || String(kind === 'task' ? params.taskId || '' : kind === 'record' ? params.id || '' : params.instanceId || '');
|
|
1045
1048
|
const navigate = useNavigate();
|
|
1046
1049
|
const { identity } = useRuntime();
|
|
1050
|
+
const commandHostKey = `${identity.identityScope}:${identity.environment.id}:${kind}:${id}`;
|
|
1051
|
+
const commandHostRef = useRef(null);
|
|
1052
|
+
useEffect(() => {
|
|
1053
|
+
const host = { key: commandHostKey };
|
|
1054
|
+
commandHostRef.current = host;
|
|
1055
|
+
return () => {
|
|
1056
|
+
if (commandHostRef.current === host)
|
|
1057
|
+
commandHostRef.current = null;
|
|
1058
|
+
};
|
|
1059
|
+
}, [commandHostKey]);
|
|
1047
1060
|
const [editing, setEditing] = useState(false);
|
|
1048
1061
|
const [editBusy, setEditBusy] = useState(false);
|
|
1049
1062
|
const { detail, surface, timeline, loading, error, refresh } = useWorkflowDetail({ kind, id, resourceCode });
|
|
@@ -1053,12 +1066,17 @@ function WorkflowDetailPage({ kind, variant, resourceCode, recordId, onDismiss,
|
|
|
1053
1066
|
const detailPath = surface ? (variant === 'mobile' ? surface.detailNavigation.mobilePath : surface.detailNavigation.desktopPath) : '';
|
|
1054
1067
|
const newPageHref = useHref(detailPath || '.');
|
|
1055
1068
|
const close = onDismiss || (() => navigate(returnPath || (detail ? variant === 'mobile' ? detail.navigationContext.mobileReturnPath : detail.navigationContext.desktopReturnPath : variant === 'mobile' ? '/m/work-center' : '/work-center')));
|
|
1056
|
-
const onCommandCompleted = async (result) => {
|
|
1057
|
-
|
|
1058
|
-
|
|
1069
|
+
const onCommandCompleted = async (result, context) => {
|
|
1070
|
+
const host = commandHostRef.current;
|
|
1071
|
+
if (!host || host.key !== commandHostKey)
|
|
1072
|
+
return;
|
|
1073
|
+
if (kind === 'task' && (result.advanced === true || context?.taskSurfaceUnavailable)) {
|
|
1074
|
+
const nextId = String(result.instanceId || surface?.instance.id || '').trim();
|
|
1059
1075
|
if (!nextId)
|
|
1060
|
-
|
|
1076
|
+
throw new Error('WORKFLOW_INSTANCE_ID_REQUIRED_AFTER_COMMAND');
|
|
1061
1077
|
const next = await loadWorkflowInstanceDetail(nextId);
|
|
1078
|
+
if (commandHostRef.current !== host)
|
|
1079
|
+
return;
|
|
1062
1080
|
navigate(variant === 'mobile' ? next.surface.detailNavigation.mobilePath : next.surface.detailNavigation.desktopPath, { replace: true });
|
|
1063
1081
|
}
|
|
1064
1082
|
else
|
|
@@ -1254,7 +1272,7 @@ async function workflowContextPrefillValue(field, rawValue, resources) {
|
|
|
1254
1272
|
}
|
|
1255
1273
|
return workflowContextScalarValue(field, value);
|
|
1256
1274
|
}
|
|
1257
|
-
export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWorkflowCode, variant = 'desktop', onDismiss, onCompleted, } = {}) {
|
|
1275
|
+
export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWorkflowCode, variant = 'desktop', onDismiss, onCompleted, formOptions, } = {}) {
|
|
1258
1276
|
const { workflowCode: routeWorkflowCode = '' } = useParams();
|
|
1259
1277
|
const workflowCode = declaredWorkflowCode || routeWorkflowCode;
|
|
1260
1278
|
const definition = useWorkflowDefinition(workflowCode);
|
|
@@ -1265,6 +1283,10 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
|
|
|
1265
1283
|
const { message } = App.useApp();
|
|
1266
1284
|
const [subjectForm] = Form.useForm();
|
|
1267
1285
|
const [requirementForm] = Form.useForm();
|
|
1286
|
+
const watchedFormValues = Form.useWatch([], { form: subjectForm, preserve: true });
|
|
1287
|
+
const [prefillError, setPrefillError] = useState(false);
|
|
1288
|
+
const [linkageError, setLinkageError] = useState(false);
|
|
1289
|
+
const prefill = useRef({ context: '', applied: new Set() });
|
|
1268
1290
|
const [loading, setLoading] = useState(false);
|
|
1269
1291
|
const [launchSurface, setLaunchSurface] = useState(null);
|
|
1270
1292
|
const [launchError, setLaunchError] = useState(null);
|
|
@@ -1357,6 +1379,57 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
|
|
|
1357
1379
|
namedIntent,
|
|
1358
1380
|
subjectDefinition,
|
|
1359
1381
|
]);
|
|
1382
|
+
const formProjection = useMemo(() => {
|
|
1383
|
+
try {
|
|
1384
|
+
const values = subjectDefinition ? normalizeFormValues(watchedFormValues || {}, subjectDefinition.surface) : {};
|
|
1385
|
+
return { ...workflowSubmissionFormProjection(fields, values, formOptions?.fieldState?.(values)), error: false };
|
|
1386
|
+
}
|
|
1387
|
+
catch {
|
|
1388
|
+
return { fields, values: {}, hiddenValues: {}, error: true };
|
|
1389
|
+
}
|
|
1390
|
+
}, [fields, formOptions?.fieldState, subjectDefinition, watchedFormValues]);
|
|
1391
|
+
const prefillContext = JSON.stringify([recoveryScope, identity.environment.activeAppVersionId, submissionMode, subjectId]);
|
|
1392
|
+
useEffect(() => { setLinkageError(false); }, [prefillContext]);
|
|
1393
|
+
const onValuesChange = (changed, values) => {
|
|
1394
|
+
if (!formOptions?.valueLinkage || loading || commandId || pendingSubmission || !subjectDefinition)
|
|
1395
|
+
return;
|
|
1396
|
+
try {
|
|
1397
|
+
const canonical = normalizeFormValues(values, subjectDefinition.surface);
|
|
1398
|
+
const visible = workflowSubmissionFormProjection(fields, canonical, formOptions.fieldState?.(canonical)).fields;
|
|
1399
|
+
const patch = workflowSubmissionValueLinkage(visible, normalizeFormValues(changed, subjectDefinition.surface), canonical, formOptions.valueLinkage);
|
|
1400
|
+
const normalized = normalizeRecordForForm(patch, subjectDefinition.surface);
|
|
1401
|
+
subjectForm.setFields(Object.entries(normalized).map(([name, value]) => ({ name, value, touched: true, errors: [] })));
|
|
1402
|
+
setLinkageError(false);
|
|
1403
|
+
}
|
|
1404
|
+
catch {
|
|
1405
|
+
setLinkageError(true);
|
|
1406
|
+
}
|
|
1407
|
+
};
|
|
1408
|
+
useEffect(() => {
|
|
1409
|
+
if (!launchSurface || commandId || pendingSubmission || formOptions?.preparation || mutationMode !== 'create' || !subjectDefinition ||
|
|
1410
|
+
launchSurface.environmentId !== identity.environment.id || (generatedNamedSubmission && !namedIntent))
|
|
1411
|
+
return;
|
|
1412
|
+
if (prefill.current.context !== prefillContext)
|
|
1413
|
+
prefill.current = { context: prefillContext, applied: new Set() };
|
|
1414
|
+
try {
|
|
1415
|
+
const values = workflowSubmissionPrefill(fields, formOptions?.initialValues || {}, prefill.current.applied, key => subjectForm.isFieldTouched(key));
|
|
1416
|
+
const normalized = normalizeRecordForForm(values, subjectDefinition.surface);
|
|
1417
|
+
subjectForm.setFields(Object.entries(normalized).map(([name, value]) => ({ name, value, touched: false })));
|
|
1418
|
+
setPrefillError(false);
|
|
1419
|
+
}
|
|
1420
|
+
catch {
|
|
1421
|
+
setPrefillError(true);
|
|
1422
|
+
}
|
|
1423
|
+
}, [commandId, fields, formOptions?.initialValues, formOptions?.preparation, generatedNamedSubmission, identity.environment.id,
|
|
1424
|
+
launchSurface, mutationMode, namedIntent, pendingSubmission, prefillContext, subjectDefinition, subjectForm]);
|
|
1425
|
+
useEffect(() => {
|
|
1426
|
+
if (loading || commandId || pendingSubmission || !launchSurface || formProjection.error || !subjectDefinition)
|
|
1427
|
+
return;
|
|
1428
|
+
const values = normalizeRecordForForm(formProjection.hiddenValues, subjectDefinition.surface);
|
|
1429
|
+
const changes = Object.entries(values).filter(([key, value]) => JSON.stringify(subjectForm.getFieldValue(key)) !== JSON.stringify(value));
|
|
1430
|
+
if (changes.length)
|
|
1431
|
+
subjectForm.setFields(changes.map(([name, value]) => ({ name, value, errors: [] })));
|
|
1432
|
+
}, [commandId, formProjection, launchSurface, loading, pendingSubmission, subjectDefinition, subjectForm]);
|
|
1360
1433
|
useEffect(() => {
|
|
1361
1434
|
let active = true;
|
|
1362
1435
|
setLaunchSurface(null);
|
|
@@ -1691,6 +1764,8 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
|
|
|
1691
1764
|
: '当前部署版本未声明 launch 配置;请升级工具链后为 workflows.definitions[].launch 显式声明 mode' }));
|
|
1692
1765
|
if (!subjectDefinition)
|
|
1693
1766
|
return frame(_jsx(Result, { status: "500", title: "\u6807\u51C6\u6D41\u7A0B\u5408\u540C\u4E0D\u5B8C\u6574" }));
|
|
1767
|
+
if (!commandId && formOptions?.preparation)
|
|
1768
|
+
return frame(formOptions.preparation);
|
|
1694
1769
|
if (launchError && !commandId)
|
|
1695
1770
|
return frame(_jsx(Result, { status: "error", title: "\u53D1\u8D77\u9875\u9762\u5F53\u524D\u4E0D\u53EF\u7528", subTitle: launchError }));
|
|
1696
1771
|
if (!launchSurface && !commandId)
|
|
@@ -1706,11 +1781,12 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
|
|
|
1706
1781
|
launchSurface?.submission.kind !== 'standard-process')) {
|
|
1707
1782
|
return frame(_jsx(Result, { status: "500", title: "\u6807\u51C6\u6D41\u7A0B\u5408\u540C\u4E0D\u5B8C\u6574" }));
|
|
1708
1783
|
}
|
|
1709
|
-
if (!commandId && namedIntent && fields.some(field => field.type === 'subtable'
|
|
1710
|
-
|
|
1784
|
+
if (!commandId && namedIntent && fields.some(field => field.type === 'subtable' &&
|
|
1785
|
+
(submissionMode !== 'create' || namedIntent.ownedSubject?.resourceCode !== subjectDefinition.code || !namedIntent.ownedSubject.subtables.some(table => table.fieldCode === field.key)))) {
|
|
1786
|
+
return frame(_jsx(Result, { status: "error", title: "\u5B50\u8868\u63D0\u4EA4\u914D\u7F6E\u4E0D\u5B8C\u6574", subTitle: "\u8BF7\u8054\u7CFB\u5E94\u7528\u7BA1\u7406\u5458\u68C0\u67E5\u53D1\u8D77\u914D\u7F6E\u3002" }));
|
|
1711
1787
|
}
|
|
1712
1788
|
const submit = async (values) => {
|
|
1713
|
-
if (submitInFlight.current || commandId || completion || !launchSurface)
|
|
1789
|
+
if (submitInFlight.current || commandId || completion || !launchSurface || formOptions?.preparation || prefillError || linkageError || formProjection.error)
|
|
1714
1790
|
return;
|
|
1715
1791
|
const unresolved = readPendingWorkflowSubmission(submissionLocatorStorage(), recoveryScope);
|
|
1716
1792
|
if (pendingSubmission || unresolved) {
|
|
@@ -1730,9 +1806,9 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
|
|
|
1730
1806
|
setRecoveryObservedAbsent(false);
|
|
1731
1807
|
};
|
|
1732
1808
|
try {
|
|
1733
|
-
const encoded = namedIntent ?
|
|
1809
|
+
const encoded = namedIntent ? namedProcessFormValues(values, subjectDefinition.surface, resources, namedIntent.ownedSubject, formOptions?.subtableReadonlyFields)
|
|
1734
1810
|
: standardProcessFormValues(values, subjectDefinition.surface, resources, (field, mode) => fieldWritable(field, mode === 'create' ? 'create' : 'edit', hasCapability, identity.isAppSuperAdmin));
|
|
1735
|
-
const data =
|
|
1811
|
+
const data = workflowSubmissionFormProjection(fields, encoded, formOptions?.fieldState?.(encoded)).values;
|
|
1736
1812
|
const signature = JSON.stringify({ workflowCode, subjectId, environmentKey: identity.environment.key, submissionMode, data });
|
|
1737
1813
|
if (submissionAttempt.current?.signature !== signature) {
|
|
1738
1814
|
submissionAttempt.current = { signature, idempotencyKey: randomId(`process:${workflowCode}`), requestedAt: new Date().toISOString() };
|
|
@@ -1920,8 +1996,20 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
|
|
|
1920
1996
|
const uploadOperationCode = namedIntent?.operationCode || definition.processOperationCode;
|
|
1921
1997
|
const fieldRenderers = uploadOperationCode
|
|
1922
1998
|
? {
|
|
1923
|
-
|
|
1999
|
+
renderSubtable: ({ field, disabled, operation, recordId }) => {
|
|
2000
|
+
const grant = namedIntent?.ownedSubject?.subtables.find(table => table.fieldCode === field.key);
|
|
2001
|
+
assertNamedSubtableReadonlyFields(subjectDefinition.surface, namedIntent?.ownedSubject, formOptions?.subtableReadonlyFields || {});
|
|
2002
|
+
return _jsx(SubtableField, { field: field, disabled: disabled, operation: operation, parentRecordId: recordId, mobile: variant === 'mobile', ...(namedIntent && grant ? { launch: { fieldCodes: grant.fieldCodes,
|
|
2003
|
+
readonlyFieldCodes: formOptions?.subtableReadonlyFields?.[field.key],
|
|
2004
|
+
reference: { workflowCode: definition.code, operationCode: namedIntent.operationCode, subtableFieldCode: field.key },
|
|
2005
|
+
upload: (childField, file) => uploadOperationManagedFile({
|
|
2006
|
+
operationCode: uploadOperationCode, resourceCode: field.subtable.resourceCode,
|
|
2007
|
+
fieldCode: childField.key, intent: 'create', file,
|
|
2008
|
+
}),
|
|
2009
|
+
} } : {}) });
|
|
2010
|
+
},
|
|
1924
2011
|
...(namedIntent ? { referenceLaunch: { workflowCode: definition.code, operationCode: namedIntent.operationCode } } : {}),
|
|
2012
|
+
candidateScopeValues: { ...formOptions?.candidateScopeValues, ...formProjection.values },
|
|
1925
2013
|
upload: async (field, file) => await uploadOperationManagedFile({
|
|
1926
2014
|
operationCode: uploadOperationCode,
|
|
1927
2015
|
resourceCode: subjectDefinition.code,
|
|
@@ -1945,7 +2033,7 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
|
|
|
1945
2033
|
(namedIntent
|
|
1946
2034
|
? '业务操作将在服务端完成校验、幂等提交,并按规则决定是否进入审批'
|
|
1947
2035
|
: '业务数据和流程发起意图将原子提交'),
|
|
1948
|
-
fields,
|
|
2036
|
+
fields: formProjection.fields,
|
|
1949
2037
|
subjectDefinition,
|
|
1950
2038
|
subjectForm,
|
|
1951
2039
|
requirementForm,
|
|
@@ -1959,9 +2047,13 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
|
|
|
1959
2047
|
completion,
|
|
1960
2048
|
embedded: Boolean(onDismiss),
|
|
1961
2049
|
fieldRenderers,
|
|
2050
|
+
formIntro: formOptions?.intro,
|
|
2051
|
+
formError: prefillError || linkageError || formProjection.error ? '表单规则暂不可用,请联系管理员。' : undefined,
|
|
1962
2052
|
recordId: mutationMode === 'update' ? subjectId : undefined,
|
|
2053
|
+
expectedRevision: mutationMode === 'update' ? loadedSubjectRevision : undefined,
|
|
1963
2054
|
onBack: onDismiss ? dismissDrawer : () => navigate(-1),
|
|
1964
2055
|
onSubmit: submit,
|
|
2056
|
+
onValuesChange,
|
|
1965
2057
|
onAnswer: answer,
|
|
1966
2058
|
onRetry: retry,
|
|
1967
2059
|
onRefresh: () => { setProcessError(null); setProcessRefreshAttempt(value => value + 1); },
|
|
@@ -1988,7 +2080,7 @@ function StandardProcessSubmissionRenderer(props) {
|
|
|
1988
2080
|
}, new Map())].map(([section, fields]) => ({ section, fields }));
|
|
1989
2081
|
if (props.completion)
|
|
1990
2082
|
return _jsx(Result, { status: "success", title: "\u63D0\u4EA4\u6210\u529F", extra: _jsx(Button, { onClick: props.onBack, children: "\u8FD4\u56DE" }) });
|
|
1991
|
-
return _jsxs("div", { className: props.variant === 'mobile' ? 'oxa-mobile-page oxa-mobile-entry oxa-workflow-submission-mobile' : 'oxa-workflow-entry', children: [!props.embedded && _jsxs("header", { className: props.variant === 'mobile' ? 'oxa-mobile-header' : 'oxa-workflow-entry-header', children: [_jsx(Button, { type: "text", "aria-label": "\u8FD4\u56DE", icon: _jsx(ArrowLeftOutlined, {}), disabled: props.processing, onClick: props.onBack }), _jsx("h3", { children: props.title }), _jsx("span", {})] }), _jsx(ResourceFormContent, { variant: props.variant, mode: props.mutationMode === 'update' ? 'edit' : 'create', resourceCode: props.subjectDefinition.code, recordId: props.recordId, groups: groups, form: props.subjectForm, busy: props.loading || props.submitted, pending: props.loading || props.processing, submitDisabled: props.submitted, submitLabel: "\u63D0\u4EA4\u5BA1\u6279", canWriteField: () => true, renderers: props.fieldRenderers, feedback: _jsxs(_Fragment, { children: [props.launchControls, props.processing && _jsx(Alert, { type: "info", showIcon: true, title: "\u6B63\u5728\u63D0\u4EA4\u7533\u8BF7\uFF0C\u8BF7\u7A0D\u5019\u2026" })] }), onSubmit: values => void props.onSubmit(values) }), _jsx(ProcessCommandPanel, { error: props.processError, form: props.requirementForm, loading: props.loading, onAnswer: props.onAnswer, onRetry: props.onRetry, onRefresh: props.onRefresh, surface: props.processSurface })] });
|
|
2083
|
+
return _jsxs("div", { className: props.variant === 'mobile' ? 'oxa-mobile-page oxa-mobile-entry oxa-workflow-submission-mobile' : 'oxa-workflow-entry', children: [!props.embedded && _jsxs("header", { className: props.variant === 'mobile' ? 'oxa-mobile-header' : 'oxa-workflow-entry-header', children: [_jsx(Button, { type: "text", "aria-label": "\u8FD4\u56DE", icon: _jsx(ArrowLeftOutlined, {}), disabled: props.processing, onClick: props.onBack }), _jsx("h3", { children: props.title }), _jsx("span", {})] }), _jsx(ResourceFormContent, { variant: props.variant, mode: props.mutationMode === 'update' ? 'edit' : 'create', resourceCode: props.subjectDefinition.code, recordId: props.recordId, groups: groups, expectedRevision: props.expectedRevision, initialValues: props.mutationMode === 'update' ? undefined : Object.fromEntries(props.fields.filter(field => field.type === 'user.multiple').map(field => [field.key, []])), form: props.subjectForm, busy: props.loading || props.submitted, pending: props.loading || props.processing, submitDisabled: props.submitted || Boolean(props.formError), submitLabel: "\u63D0\u4EA4\u5BA1\u6279", canWriteField: () => true, renderers: props.fieldRenderers, feedback: _jsxs(_Fragment, { children: [props.formIntro, props.launchControls, props.formError && _jsx(Alert, { type: "error", showIcon: true, title: props.formError }), props.processing && _jsx(Alert, { type: "info", showIcon: true, title: "\u6B63\u5728\u63D0\u4EA4\u7533\u8BF7\uFF0C\u8BF7\u7A0D\u5019\u2026" })] }), onValuesChange: props.onValuesChange, onSubmit: values => void props.onSubmit(values) }), _jsx(ProcessCommandPanel, { error: props.processError, form: props.requirementForm, loading: props.loading, onAnswer: props.onAnswer, onRetry: props.onRetry, onRefresh: props.onRefresh, surface: props.processSurface })] });
|
|
1992
2084
|
}
|
|
1993
2085
|
function DesktopProcessSubmissionRenderer(props) { return _jsx(StandardProcessSubmissionRenderer, { ...props, variant: "desktop" }); }
|
|
1994
2086
|
function MobileProcessSubmissionRenderer(props) { return _jsx(StandardProcessSubmissionRenderer, { ...props, variant: "mobile" }); }
|