openxiangda 2.0.0-alpha.98 → 2.0.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 (150) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +23 -20
  3. package/bin/distribution/commands.js +55 -0
  4. package/bin/distribution/launcher.js +49 -0
  5. package/bin/distribution/migrate.js +60 -0
  6. package/bin/distribution/releases.js +52 -0
  7. package/bin/distribution/skills.js +80 -0
  8. package/bin/distribution/update.js +68 -0
  9. package/bin/distribution/workspace.js +85 -0
  10. package/bin/run.js +9 -11
  11. package/dist/browser/AuthoritativeSelector.d.ts +3 -2
  12. package/dist/browser/AuthoritativeSelector.d.ts.map +1 -1
  13. package/dist/browser/AuthoritativeSelector.js +39 -24
  14. package/dist/browser/AuthoritativeSelector.js.map +1 -1
  15. package/dist/browser/Shell.d.ts.map +1 -1
  16. package/dist/browser/Shell.js +24 -18
  17. package/dist/browser/Shell.js.map +1 -1
  18. package/dist/browser/admin-information-architecture.d.ts +4 -2
  19. package/dist/browser/admin-information-architecture.d.ts.map +1 -1
  20. package/dist/browser/admin-information-architecture.js +2 -2
  21. package/dist/browser/admin-information-architecture.js.map +1 -1
  22. package/dist/browser/application.d.ts.map +1 -1
  23. package/dist/browser/application.js +3 -3
  24. package/dist/browser/application.js.map +1 -1
  25. package/dist/browser/components/platform-fields/AttachmentFileList.d.ts.map +1 -1
  26. package/dist/browser/components/platform-fields/AttachmentFileList.js +5 -1
  27. package/dist/browser/components/platform-fields/AttachmentFileList.js.map +1 -1
  28. package/dist/browser/components/platform-fields/MobileFieldControls.d.ts.map +1 -1
  29. package/dist/browser/components/platform-fields/MobileFieldControls.js +2 -2
  30. package/dist/browser/components/platform-fields/MobileFieldControls.js.map +1 -1
  31. package/dist/browser/components/platform-fields/ResourceReferenceField.d.ts +4 -2
  32. package/dist/browser/components/platform-fields/ResourceReferenceField.d.ts.map +1 -1
  33. package/dist/browser/components/platform-fields/ResourceReferenceField.js +2 -2
  34. package/dist/browser/components/platform-fields/ResourceReferenceField.js.map +1 -1
  35. package/dist/browser/components/platform-fields/SubtableField.d.ts.map +1 -1
  36. package/dist/browser/components/platform-fields/SubtableField.js +60 -80
  37. package/dist/browser/components/platform-fields/SubtableField.js.map +1 -1
  38. package/dist/browser/components/platform-fields/rich-text-value.d.ts.map +1 -1
  39. package/dist/browser/components/platform-fields/rich-text-value.js +11 -1
  40. package/dist/browser/components/platform-fields/rich-text-value.js.map +1 -1
  41. package/dist/browser/components/resource/GeneratedResourceCrud.d.ts.map +1 -1
  42. package/dist/browser/components/resource/GeneratedResourceCrud.js +33 -114
  43. package/dist/browser/components/resource/GeneratedResourceCrud.js.map +1 -1
  44. package/dist/browser/components/resource/GeneratedResourceForm.d.ts +3 -1
  45. package/dist/browser/components/resource/GeneratedResourceForm.d.ts.map +1 -1
  46. package/dist/browser/components/resource/GeneratedResourceForm.js +11 -4
  47. package/dist/browser/components/resource/GeneratedResourceForm.js.map +1 -1
  48. package/dist/browser/components/resource/RecordChangeHistory.d.ts +12 -0
  49. package/dist/browser/components/resource/RecordChangeHistory.d.ts.map +1 -0
  50. package/dist/browser/components/resource/RecordChangeHistory.js +61 -0
  51. package/dist/browser/components/resource/RecordChangeHistory.js.map +1 -0
  52. package/dist/browser/components/resource/RecordDetailFrame.d.ts +30 -0
  53. package/dist/browser/components/resource/RecordDetailFrame.d.ts.map +1 -0
  54. package/dist/browser/components/resource/RecordDetailFrame.js +25 -0
  55. package/dist/browser/components/resource/RecordDetailFrame.js.map +1 -0
  56. package/dist/browser/components/resource/ResourceFormFrame.d.ts +2 -1
  57. package/dist/browser/components/resource/ResourceFormFrame.d.ts.map +1 -1
  58. package/dist/browser/components/resource/ResourceFormFrame.js +4 -4
  59. package/dist/browser/components/resource/ResourceFormFrame.js.map +1 -1
  60. package/dist/browser/components/resource/StandardResourcePages.d.ts +2 -4
  61. package/dist/browser/components/resource/StandardResourcePages.d.ts.map +1 -1
  62. package/dist/browser/components/resource/StandardResourcePages.js +8 -26
  63. package/dist/browser/components/resource/StandardResourcePages.js.map +1 -1
  64. package/dist/browser/components/resource/SurfaceFields.d.ts +5 -3
  65. package/dist/browser/components/resource/SurfaceFields.d.ts.map +1 -1
  66. package/dist/browser/components/resource/SurfaceFields.js +37 -12
  67. package/dist/browser/components/resource/SurfaceFields.js.map +1 -1
  68. package/dist/browser/components/resource/resource-import.d.ts +16 -1
  69. package/dist/browser/components/resource/resource-import.d.ts.map +1 -1
  70. package/dist/browser/components/resource/resource-import.js +58 -34
  71. package/dist/browser/components/resource/resource-import.js.map +1 -1
  72. package/dist/browser/components/resource/useResourceFormDrafts.d.ts.map +1 -1
  73. package/dist/browser/components/todo/ApplicationTodoCenterPage.d.ts +5 -2
  74. package/dist/browser/components/todo/ApplicationTodoCenterPage.d.ts.map +1 -1
  75. package/dist/browser/components/todo/ApplicationTodoCenterPage.js +19 -14
  76. package/dist/browser/components/todo/ApplicationTodoCenterPage.js.map +1 -1
  77. package/dist/browser/components/workflow/StandardWorkflowPages.d.ts +21 -5
  78. package/dist/browser/components/workflow/StandardWorkflowPages.d.ts.map +1 -1
  79. package/dist/browser/components/workflow/StandardWorkflowPages.js +175 -192
  80. package/dist/browser/components/workflow/StandardWorkflowPages.js.map +1 -1
  81. package/dist/browser/components/workflow/WorkflowRecordEditor.d.ts +2 -1
  82. package/dist/browser/components/workflow/WorkflowRecordEditor.d.ts.map +1 -1
  83. package/dist/browser/components/workflow/WorkflowRecordEditor.js +7 -4
  84. package/dist/browser/components/workflow/WorkflowRecordEditor.js.map +1 -1
  85. package/dist/browser/platform-client.d.ts +10 -4
  86. package/dist/browser/platform-client.d.ts.map +1 -1
  87. package/dist/browser/platform-client.js +78 -17
  88. package/dist/browser/platform-client.js.map +1 -1
  89. package/dist/browser/record-detail.css +115 -0
  90. package/dist/browser/runtime.d.ts.map +1 -1
  91. package/dist/browser/runtime.js +26 -2
  92. package/dist/browser/runtime.js.map +1 -1
  93. package/dist/browser/styles.css +1 -0
  94. package/dist/browser/workflow-launch.d.ts +4 -1
  95. package/dist/browser/workflow-launch.d.ts.map +1 -1
  96. package/dist/browser/workflow-launch.js +32 -0
  97. package/dist/browser/workflow-launch.js.map +1 -1
  98. package/dist/core.d.ts +1 -1
  99. package/dist/core.d.ts.map +1 -1
  100. package/dist/core.js.map +1 -1
  101. package/documentation/AGENTS.md +26 -0
  102. package/documentation/administration.md +27 -0
  103. package/documentation/application-foundation.md +162 -0
  104. package/documentation/appspec.md +152 -0
  105. package/documentation/backend.md +132 -0
  106. package/documentation/concepts.md +61 -0
  107. package/documentation/data-authz.md +62 -0
  108. package/documentation/delivery.md +110 -0
  109. package/documentation/development.md +32 -0
  110. package/documentation/field-components.md +236 -0
  111. package/documentation/frontend.md +269 -0
  112. package/documentation/getting-started.md +66 -0
  113. package/documentation/interaction-patterns.md +56 -0
  114. package/documentation/manifest.json +120 -0
  115. package/documentation/product-design.md +142 -0
  116. package/documentation/public-access.md +167 -0
  117. package/documentation/reference/cli.md +27 -0
  118. package/documentation/reference/mcp.md +649 -0
  119. package/documentation/testing.md +63 -0
  120. package/documentation/upgrading.md +39 -0
  121. package/documentation/workflow-events.md +181 -0
  122. package/launcher-skill/openxiangda/SKILL.md +24 -0
  123. package/package.json +72 -9
  124. package/releases/2.0.0.json +50 -0
  125. package/skills/manifest.json +2 -2
  126. package/skills/openxiangda-v2/SKILL.md +64 -51
  127. package/skills/openxiangda-v2/agents/openai.yaml +2 -2
  128. package/skills/openxiangda-v2/references/administration.md +27 -0
  129. package/skills/openxiangda-v2/references/application-foundation.md +162 -0
  130. package/skills/openxiangda-v2/references/appspec.md +132 -47
  131. package/skills/openxiangda-v2/references/backend.md +101 -248
  132. package/skills/openxiangda-v2/references/cli.md +27 -0
  133. package/skills/openxiangda-v2/references/concepts.md +61 -0
  134. package/skills/openxiangda-v2/references/data-authz.md +36 -388
  135. package/skills/openxiangda-v2/references/delivery.md +110 -49
  136. package/skills/openxiangda-v2/references/development.md +32 -0
  137. package/skills/openxiangda-v2/references/field-components.md +236 -0
  138. package/skills/openxiangda-v2/references/frontend.md +254 -280
  139. package/skills/openxiangda-v2/references/getting-started.md +66 -0
  140. package/skills/openxiangda-v2/references/interaction-patterns.md +56 -0
  141. package/skills/openxiangda-v2/references/mcp.md +649 -0
  142. package/skills/openxiangda-v2/references/product-design.md +142 -0
  143. package/skills/openxiangda-v2/references/public-access.md +92 -84
  144. package/skills/openxiangda-v2/references/testing.md +45 -56
  145. package/skills/openxiangda-v2/references/upgrading.md +39 -0
  146. package/skills/openxiangda-v2/references/workflow-events.md +143 -266
  147. package/skills/openxiangda-v2/references/architecture.md +0 -9
  148. package/skills/openxiangda-v2/references/commands.md +0 -21
  149. package/skills/openxiangda-v2/references/discovery.md +0 -15
  150. package/skills/openxiangda-v2/references/workspace.md +0 -62
@@ -1,18 +1,23 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { ArrowLeftOutlined, EllipsisOutlined, ReloadOutlined, CloseOutlined, ExpandOutlined, CompressOutlined, ExportOutlined, } from '@ant-design/icons';
3
- import { Alert, App, Button, Card, Descriptions, Drawer, Empty, Form, Input, Modal, Result, Segmented, Select, Space, Spin, Table, Tag, Tooltip, Typography, } from 'antd';
2
+ import { ArrowLeftOutlined, ClockCircleOutlined, EllipsisOutlined, ReloadOutlined, CloseOutlined, ExpandOutlined, CompressOutlined, ExportOutlined, } from '@ant-design/icons';
3
+ import { Alert, App, Button, Card, Drawer, Empty, Form, Input, Modal, Result, Segmented, Select, Space, Spin, Table, Tabs, Tag, Tooltip, Typography, } from 'antd';
4
4
  import { normalizeWorkflowBusinessDetail, } from 'openxiangda-contracts/browser';
5
5
  import { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
6
6
  import { Link, Navigate, useHref, useNavigate, useParams, useSearchParams, } from 'react-router-dom';
7
- import { Shell } from '../../Shell';
8
- import { executeWorkflowOperation, executeWorkflowLaunchNamedOperation, loadWorkflowInstanceDetail, loadWorkflowInstanceSurface, loadWorkflowLaunchSurface, loadWorkflowTaskSurface, loadWorkflowTaskDetail, loadWorkflowWorkCenter, answerBusinessProcessCommand, commitStandardProcess, createNativeResourceClient, loadProcessCommandSurface, retryBusinessProcessCommand, searchDirectory, uploadOperationManagedFile, } from '../../platform-client';
7
+ import { PlatformDirectoryPicker } from '../platform-fields/PlatformDirectoryPicker';
8
+ import { directoryStoredValues } from '../platform-fields/directory-value';
9
+ import { executeWorkflowOperation, executeWorkflowLaunchNamedOperation, loadWorkflowInstanceDetail, loadWorkflowRecordDetail, loadWorkflowDataAudit, loadWorkflowInstanceSurface, loadWorkflowLaunchSurface, loadWorkflowTaskSurface, loadWorkflowTaskDetail, loadWorkflowWorkCenter, answerBusinessProcessCommand, commitStandardProcess, createNativeResourceClient, loadProcessCommandSurface, retryBusinessProcessCommand, uploadOperationManagedFile, } from '../../platform-client';
9
10
  import { useWorkflowDefinition, } from '../../workflow-definitions';
10
11
  import { useResourceDefinitions } from '../../resource-definitions';
11
12
  import { selectedSurfaceFields } from '../resource/resource-field-selection';
12
13
  import { useRuntime } from '../../runtime';
13
- import { MobileSurfaceFieldControl, SurfaceFieldControl, SurfaceFieldValue, } from '../resource/SurfaceFields';
14
- import { buildWorkflowNamedOperationInput, parseWorkflowNamedOperationResult, workflowLaunchContractJsonEqual, } from '../../workflow-launch';
14
+ import { SurfaceFieldValue, } from '../resource/SurfaceFields';
15
+ import { workflowContextScalarValue, processStatusReadRetryDelay, buildWorkflowNamedOperationInput, parseWorkflowNamedOperationResult, workflowLaunchContractJsonEqual, } from '../../workflow-launch';
15
16
  import { normalizeFormValues, normalizeRecordForForm, } from '../platform-fields/field-form-codec';
17
+ import { RecordDetailFrame, RecordDetailSections, detailTime } from '../resource/RecordDetailFrame';
18
+ import { RecordChangeHistory } from '../resource/RecordChangeHistory';
19
+ import { ResourceFormContent } from '../resource/ResourceFormFrame';
20
+ import { WorkflowRecordEditor } from './WorkflowRecordEditor';
16
21
  import { PlatformAvatar } from '../PlatformAvatar';
17
22
  /**
18
23
  * An advanced task command has completed the current task and may have
@@ -71,6 +76,9 @@ function workflowStatus(status) {
71
76
  };
72
77
  return states[status] || { label: status || '未知', color: 'default' };
73
78
  }
79
+ function workflowHasEnded(instance) {
80
+ return ['completed', 'approved', 'rejected', 'withdrawn', 'terminated', 'cancelled', 'expired'].includes(instance.status);
81
+ }
74
82
  function StatusTag({ status }) {
75
83
  const state = workflowStatus(status);
76
84
  return _jsx(Tag, { color: state.color, children: state.label });
@@ -176,7 +184,7 @@ function WorkflowSubtableValue({ detail, field, instanceId, variant, }) {
176
184
  : [];
177
185
  });
178
186
  if (variant === 'mobile') {
179
- return (_jsx("div", { className: "oxa-workflow-subtable-mobile", children: subtable.rows.map((row, index) => (_jsxs("div", { children: [_jsxs("strong", { children: ["\u660E\u7EC6 ", index + 1] }), fields.map((child) => (_jsxs("div", { children: [_jsx(Typography.Text, { type: "secondary", children: child.label }), _jsx(SurfaceFieldValue, { field: child, presentation: "workflow-detail", resourceCode: subtable.resourceCode, value: row[child.key], workflowFileBinding: {
187
+ return (_jsx("div", { className: "oxa-workflow-subtable-mobile", children: subtable.rows.map((row, index) => (_jsxs("div", { children: [_jsxs("strong", { children: ["\u660E\u7EC6 ", index + 1] }), fields.map((child) => (_jsxs("div", { children: [_jsx(Typography.Text, { type: "secondary", children: child.label }), _jsx(SurfaceFieldValue, { field: child, mobile: true, presentation: "workflow-detail", resourceCode: subtable.resourceCode, value: row[child.key], workflowFileBinding: {
180
188
  instanceId,
181
189
  resourceCode: subtable.resourceCode,
182
190
  recordId: String(row.id),
@@ -214,22 +222,9 @@ export function WorkflowBusinessDetailSections({ surface, variant, }) {
214
222
  if (detail.status === 'unavailable' || !detail.surface) {
215
223
  return _jsx(Result, { status: "warning", title: "\u4E1A\u52A1\u8BE6\u60C5\u6682\u65F6\u4E0D\u53EF\u7528" });
216
224
  }
217
- return (_jsx("div", { className: `oxa-workflow-business-sections oxa-workflow-business-sections-${variant}`, children: groups.map((group) => (_jsx(Card, { className: "oxa-workflow-business", title: group.section, children: variant === 'desktop' ? (_jsx(Descriptions, { column: { xs: 1, md: 2 }, colon: false, items: group.fields.map((field) => ({
218
- key: field.key,
219
- label: field.label,
220
- span: field.type === 'subtable' || ['file', 'image', 'text.rich', 'signature'].includes(field.type) ? 2 : 1,
221
- children: field.type === 'subtable' ? (_jsx(WorkflowSubtableValue, { detail: detail, field: field, instanceId: instance.id, variant: variant })) : (_jsx(SurfaceFieldValue, { field: field, presentation: "workflow-detail", resourceCode: detail.resourceCode || undefined, value: detail.record[field.key], workflowFileBinding: {
222
- instanceId: instance.id,
223
- resourceCode: String(detail.resourceCode),
224
- recordId: String(detail.recordId),
225
- fieldCode: field.key,
226
- } })),
227
- })) })) : (_jsx("div", { className: "oxa-workflow-mobile-fields", children: group.fields.map((field) => (_jsxs("div", { className: field.type === 'subtable' ? 'is-wide' : undefined, children: [_jsx(Typography.Text, { type: "secondary", children: field.label }), field.type === 'subtable' ? (_jsx(WorkflowSubtableValue, { detail: detail, field: field, instanceId: instance.id, variant: variant })) : (_jsx(SurfaceFieldValue, { field: field, presentation: "workflow-detail", resourceCode: detail.resourceCode || undefined, value: detail.record[field.key], workflowFileBinding: {
228
- instanceId: instance.id,
229
- resourceCode: String(detail.resourceCode),
230
- recordId: String(detail.recordId),
231
- fieldCode: field.key,
232
- } }))] }, field.key))) })) }, group.section))) }));
225
+ return _jsx(RecordDetailSections, { groups: groups, renderValue: field => field.type === 'subtable'
226
+ ? _jsx(WorkflowSubtableValue, { detail: detail, field: field, instanceId: instance.id, variant: variant })
227
+ : _jsx(SurfaceFieldValue, { field: field, presentation: "workflow-detail", mobile: variant === 'mobile', resourceCode: detail.resourceCode || undefined, value: detail.record[field.key], workflowFileBinding: { instanceId: instance.id, resourceCode: String(detail.resourceCode), recordId: String(detail.recordId), fieldCode: field.key } }) });
233
228
  }
234
229
  function timelineTone(status) {
235
230
  return `is-${status}`;
@@ -311,25 +306,15 @@ export function WorkflowTimelineSection({ timeline, }) {
311
306
  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));
312
307
  }) })) : (_jsx(Empty, { description: "\u6682\u65E0\u6D41\u7A0B\u8F68\u8FF9", image: Empty.PRESENTED_IMAGE_SIMPLE })) }));
313
308
  }
314
- function WorkflowUserSelect({ value, onChange, }) {
315
- const [options, setOptions] = useState([]);
316
- const [loading, setLoading] = useState(false);
317
- const sequence = useRef(0);
318
- const search = (keyword) => {
319
- const current = ++sequence.current;
320
- setLoading(true);
321
- void searchDirectory('user', { keyword })
322
- .then((page) => {
323
- if (current !== sequence.current)
324
- return;
325
- setOptions(page.items.map((item) => ({ label: item.label, value: item.id })));
326
- })
327
- .finally(() => {
328
- if (current === sequence.current)
329
- setLoading(false);
330
- });
331
- };
332
- return (_jsx(Select, { filterOption: false, loading: loading, onChange: onChange, onSearch: search, options: options, placeholder: "\u641C\u7D22\u5E76\u9009\u62E9\u4EBA\u5458", showSearch: true, value: value }));
309
+ function WorkflowMemberPicker({ id, placeholder, value, onChange, multiple = false, variant }) {
310
+ const [snapshots, setSnapshots] = useState([]);
311
+ const ids = Array.isArray(value) ? value : value ? [value] : [];
312
+ const selected = ids.map(id => snapshots.find(item => item.value === id) || { value: id, label: '已选成员' });
313
+ return _jsx(PlatformDirectoryPicker, { id: id, kind: "user", mobile: variant === 'mobile', multiple: multiple, placeholder: placeholder || '请选择成员', value: multiple ? selected : selected[0], onChange: next => {
314
+ const items = directoryStoredValues(next);
315
+ setSnapshots(items);
316
+ onChange?.(multiple ? items.map(item => item.value) : items[0]?.value);
317
+ } });
333
318
  }
334
319
  function schemaProperties(operation) {
335
320
  const schema = operation.inputSchema;
@@ -341,14 +326,21 @@ function schemaProperties(operation) {
341
326
  required: schema.required?.includes(key) === true,
342
327
  }));
343
328
  }
344
- function OperationFields({ operation, }) {
329
+ function OperationFields({ operation, variant, }) {
345
330
  return (_jsx(_Fragment, { children: schemaProperties(operation).map(({ key, property, required, ui }) => {
346
- const rules = required
347
- ? [{ required: true, message: `请填写${property.title || key}` }]
348
- : [];
331
+ const rules = [
332
+ ...(required ? [{ required: true, message: `请填写${property.title || key}` }] : []),
333
+ ...(property.type === 'array' ? [{ validator: async (_rule, value) => {
334
+ if (value == null && !required)
335
+ return;
336
+ if (!Array.isArray(value) || value.length < (property.minItems ?? 0) || value.length > (property.maxItems ?? 200)) {
337
+ throw new Error(`请选择 ${property.minItems ?? 0} 至 ${property.maxItems ?? 200} 位人员`);
338
+ }
339
+ } }] : []),
340
+ ];
349
341
  let control = _jsx(Input, { placeholder: ui.placeholder });
350
342
  if (ui.component === 'user_select')
351
- control = _jsx(WorkflowUserSelect, {});
343
+ control = _jsx(WorkflowMemberPicker, { variant: variant, multiple: property.type === 'array' || ui.mode === 'multiple', placeholder: ui.placeholder || `请选择${property.title || '成员'}` });
352
344
  else if (property.enum) {
353
345
  control = (_jsx(Select, { options: property.enum.map((value, index) => ({
354
346
  label: property.enumNames?.[index] || value,
@@ -372,9 +364,9 @@ function OperationDialog({ operation, submitting, variant, onCancel, onSubmit, }
372
364
  if (!operation)
373
365
  return null;
374
366
  const ui = operation.uiSchema;
375
- const formNode = (_jsxs(Form, { form: form, layout: "vertical", onFinish: onSubmit, children: [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 })] }));
367
+ const formNode = (_jsxs(Form, { form: form, layout: "vertical", onFinish: onSubmit, children: [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 })] }));
376
368
  if (variant === 'mobile') {
377
- return (_jsx(Drawer, { extra: _jsx(Button, { loading: submitting, onClick: () => form.submit(), type: "primary", children: ui.confirmText || `确认${operation.label}` }), onClose: onCancel, open: true, placement: "bottom", size: "large", title: operation.label, children: formNode }));
369
+ 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 }));
378
370
  }
379
371
  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 }));
380
372
  }
@@ -722,20 +714,40 @@ export function WorkflowSummarySection({ surface }) {
722
714
  };
723
715
  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] }))] }));
724
716
  }
725
- export function DesktopWorkflowDetailRenderer({ surface, timeline, warning, operations, }) {
717
+ function StandardWorkflowDetailRenderer({ surface, timeline, warning, operations, variant, onClose, onEdit, drawer, drawerState, newPageHref, editing, busy, }) {
718
+ const navigate = useNavigate();
719
+ const [tab, setTab] = useState('application');
726
720
  const instance = surfaceInstance(surface);
727
- const task = surfaceTask(surface);
721
+ const business = surfaceBusinessDetail(surface);
728
722
  const summary = surface.presentation.summary;
729
- return (_jsxs("div", { className: "oxa-workflow-detail oxa-workflow-detail-desktop oxa-workflow-desktop-standalone-page", children: [_jsxs(Card, { className: "oxa-workflow-summary-card", children: [_jsxs("div", { className: "oxa-workflow-summary-title", children: [_jsx(Typography.Title, { level: 2, children: summary.title }), _jsx(StatusTag, { status: task?.status || instance.status })] }), _jsx(WorkflowSummarySection, { surface: surface })] }), warning && _jsx(Alert, { description: warning, showIcon: true, type: "warning" }), _jsx(WorkflowBusinessDetailSections, { surface: surface, variant: "desktop" }), _jsx(WorkflowTimelineSection, { timeline: timeline }), operations] }));
723
+ const titleField = workflowDetailGroups(business).flatMap(group => group.fields).find(field => field.type === 'text.short');
724
+ const title = titleField && business.record[titleField.key] ? String(business.record[titleField.key]) : summary.title;
725
+ const activeNode = timeline?.display?.entries.find(entry => entry.status === 'active');
726
+ const people = activeNode?.people.map(person => person.displayName).filter(Boolean).join('、');
727
+ const statusTone = workflowStatus(instance.status).color;
728
+ const bannerTone = statusTone === 'error' ? 'error' : 'active';
729
+ const edit = workflowHasEnded(instance) ? onEdit : undefined;
730
+ const hasActions = standardWorkflowOperations(surface).length > 0;
731
+ const loadAudit = useCallback(() => loadWorkflowDataAudit(instance.id), [instance.id]);
732
+ const back = onClose || (() => navigate(variant === 'mobile' ? '/m/work-center' : '/work-center'));
733
+ 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,
734
+ summary.submittedAt ? `${detailTime(summary.submittedAt)} 创建` : ''].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: [
735
+ { key: 'application', label: '申请内容', children: _jsx(WorkflowBusinessDetailSections, { surface: surface, variant: variant }) },
736
+ { key: 'history', label: '审批历史', children: _jsx(WorkflowTimelineSection, { timeline: timeline }) },
737
+ { key: 'changes', label: '变更记录', children: tab === 'changes' && business.surface && business.resourceCode && business.recordId
738
+ ? _jsx(RecordChangeHistory, { resourceCode: business.resourceCode, recordId: business.recordId, surface: business.surface, readable: () => true, loadPage: loadAudit })
739
+ : _jsx(Empty, { description: "\u6682\u65E0\u53D8\u66F4\u8BB0\u5F55" }) },
740
+ ] })] });
730
741
  }
731
- export function MobileWorkflowDetailRenderer({ surface, timeline, warning, operations, }) {
732
- const instance = surfaceInstance(surface);
733
- const task = surfaceTask(surface);
734
- const summary = surface.presentation.summary;
735
- return (_jsxs("div", { className: "oxa-workflow-detail-mobile-native", children: [_jsxs("main", { children: [_jsxs(Card, { className: "oxa-workflow-summary-card-mobile", children: [_jsxs("div", { className: "oxa-workflow-summary-title-mobile", children: [_jsx(Typography.Title, { level: 3, children: summary.title }), _jsx(StatusTag, { status: task?.status || instance.status })] }), _jsx(WorkflowSummarySection, { surface: surface })] }), warning && _jsx(Alert, { description: warning, showIcon: true, type: "warning" }), _jsx(WorkflowBusinessDetailSections, { surface: surface, variant: "mobile" }), _jsx(WorkflowTimelineSection, { timeline: timeline })] }), operations] }));
742
+ export function DesktopWorkflowDetailRenderer(props) {
743
+ return _jsx(StandardWorkflowDetailRenderer, { ...props, variant: "desktop" });
736
744
  }
737
- export function useWorkflowDetail({ kind, id, }) {
738
- const detailKey = `${kind}:${id}`;
745
+ export function MobileWorkflowDetailRenderer(props) {
746
+ return _jsx(StandardWorkflowDetailRenderer, { ...props, variant: "mobile" });
747
+ }
748
+ export function useWorkflowDetail({ kind, id, resourceCode }) {
749
+ const { identity } = useRuntime();
750
+ const detailKey = `${identity.identityScope}:${identity.environment.id}:${kind}:${resourceCode || ''}:${id}`;
739
751
  const [state, setState] = useState({ detailKey, detail: null, error: '' });
740
752
  const [loading, setLoading] = useState(true);
741
753
  const requestSequence = useRef(0);
@@ -748,7 +760,7 @@ export function useWorkflowDetail({ kind, id, }) {
748
760
  ? { ...current, error: '' }
749
761
  : { detailKey, detail: null, error: '' });
750
762
  try {
751
- const detail = kind === 'task'
763
+ const detail = kind === 'record' ? await loadWorkflowRecordDetail(resourceCode, id) : kind === 'task'
752
764
  ? await loadWorkflowTaskDetail(id)
753
765
  : await loadWorkflowInstanceDetail(id);
754
766
  if (!isCurrent())
@@ -777,7 +789,7 @@ export function useWorkflowDetail({ kind, id, }) {
777
789
  if (isCurrent())
778
790
  setLoading(false);
779
791
  }
780
- }, [detailKey, id, kind]);
792
+ }, [detailKey, id, kind, resourceCode]);
781
793
  useEffect(() => {
782
794
  activeRef.current = true;
783
795
  void refresh();
@@ -800,128 +812,90 @@ export function useWorkflowDetail({ kind, id, }) {
800
812
  refresh,
801
813
  };
802
814
  }
803
- function WorkflowDetailPage({ kind, variant, }) {
815
+ function WorkflowDetailPage({ kind, variant, resourceCode, recordId, onDismiss, drawerState }) {
804
816
  const params = useParams();
805
- const id = String(kind === 'task' ? params.taskId || '' : params.instanceId || '');
817
+ const id = recordId || String(kind === 'task' ? params.taskId || '' : kind === 'record' ? params.id || '' : params.instanceId || '');
806
818
  const navigate = useNavigate();
807
- const { detail, surface, timeline, loading, error, refresh } = useWorkflowDetail({ kind, id });
808
- let content;
809
- if (loading && !surface)
810
- content = (_jsx("div", { className: "oxa-page-loading", children: _jsx(Spin, {}) }));
811
- else if (error && !surface) {
812
- content = (_jsx(Result, { extra: _jsx(Button, { onClick: () => void refresh(), children: "\u91CD\u8BD5" }), status: "error", subTitle: error, title: "\u6D41\u7A0B\u8BE6\u60C5\u52A0\u8F7D\u5931\u8D25" }));
813
- }
814
- else if (!surface || !detail)
815
- content = _jsx(Result, { status: "404", title: "\u6D41\u7A0B\u4E0D\u5B58\u5728" });
816
- else {
817
- if (surface.detailNavigation.custom) {
818
- return (_jsx(Navigate, { replace: true, to: variant === 'mobile'
819
- ? surface.detailNavigation.mobilePath
820
- : surface.detailNavigation.desktopPath }));
821
- }
822
- const instance = surfaceInstance(surface);
823
- const task = surfaceTask(surface);
824
- const onCommandCompleted = async (result) => {
825
- if (kind === 'task' && result.advanced === true) {
826
- const instanceId = String(result.instanceId || '').trim();
827
- if (!instanceId)
828
- return;
829
- const nextDetail = await loadWorkflowInstanceDetail(instanceId);
830
- navigate(variant === 'mobile'
831
- ? nextDetail.surface.detailNavigation.mobilePath
832
- : nextDetail.surface.detailNavigation.desktopPath, { replace: true });
819
+ const { identity } = useRuntime();
820
+ const [editing, setEditing] = useState(false);
821
+ const [editBusy, setEditBusy] = useState(false);
822
+ const { detail, surface, timeline, loading, error, refresh } = useWorkflowDetail({ kind, id, resourceCode });
823
+ const instance = surface ? surfaceInstance(surface) : null;
824
+ const task = surface ? surfaceTask(surface) : null;
825
+ const business = surface ? surfaceBusinessDetail(surface) : null;
826
+ const detailPath = surface ? (variant === 'mobile' ? surface.detailNavigation.mobilePath : surface.detailNavigation.desktopPath) : '';
827
+ const newPageHref = useHref(detailPath || '.');
828
+ const close = onDismiss || (() => navigate(detail ? variant === 'mobile' ? detail.navigationContext.mobileReturnPath : detail.navigationContext.desktopReturnPath : variant === 'mobile' ? '/m/work-center' : '/work-center'));
829
+ if (!surface || !detail || !instance)
830
+ 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" }) }) });
831
+ if (surface.detailNavigation.custom)
832
+ return _jsx(Navigate, { replace: true, to: detailPath });
833
+ const onCommandCompleted = async (result) => {
834
+ if (kind === 'task' && result.advanced === true) {
835
+ const nextId = String(result.instanceId || '').trim();
836
+ if (!nextId)
833
837
  return;
834
- }
838
+ const next = await loadWorkflowInstanceDetail(nextId);
839
+ navigate(variant === 'mobile' ? next.surface.detailNavigation.mobilePath : next.surface.detailNavigation.desktopPath, { replace: true });
840
+ }
841
+ else
835
842
  await refresh();
836
- };
837
- const operations = kind === 'task' ? (task ? (_jsx(WorkflowTaskOperationsPanel, { onCommandCompleted: onCommandCompleted, surface: surface, taskId: task.id, variant: variant })) : null) : (_jsx(WorkflowInstanceOperationsPanel, { instanceId: instance.id, onCommandCompleted: onCommandCompleted, surface: surface, variant: variant }));
838
- const rendererProps = {
839
- surface,
840
- timeline,
841
- warning: error,
842
- operations,
843
- };
844
- content = variant === 'desktop' ? (_jsx(DesktopWorkflowDetailRenderer, { ...rendererProps })) : (_jsx(MobileWorkflowDetailRenderer, { ...rendererProps }));
845
- }
846
- return variant === 'desktop' ? (_jsx("div", { className: "oxa-workflow-desktop-page", children: content })) : (_jsx("div", { className: "oxa-workflow-mobile-page oxa-workflow-mobile-detail-page", children: content }));
843
+ };
844
+ const operations = task
845
+ ? _jsx(WorkflowTaskOperationsPanel, { onCommandCompleted: onCommandCompleted, surface: surface, taskId: task.id, variant: variant }, task.id)
846
+ : _jsx(WorkflowInstanceOperationsPanel, { instanceId: instance.id, onCommandCompleted: onCommandCompleted, surface: surface, variant: variant });
847
+ return _jsx(StandardWorkflowDetailRenderer, { surface: surface, timeline: timeline, warning: error, operations: operations, variant: variant, drawer: Boolean(onDismiss), drawerState: drawerState, newPageHref: newPageHref, onClose: editing ? () => { if (!editBusy)
848
+ 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 });
847
849
  }
850
+ export function WorkflowRecordDetailPage({ variant = 'desktop', ...props }) { return _jsx(WorkflowDetailPage, { ...props, variant: variant, kind: "record" }); }
848
851
  export function WorkflowTaskPage({ variant = 'desktop', }) {
849
852
  return _jsx(WorkflowDetailPage, { kind: "task", variant: variant });
850
853
  }
851
854
  export function WorkflowInstancePage({ variant = 'desktop', }) {
852
855
  return _jsx(WorkflowDetailPage, { kind: "instance", variant: variant });
853
856
  }
854
- function WorkCenterMobile({ page, loading, status, onStatus, onPage, }) {
855
- const navigate = useNavigate();
856
- const current = Math.floor((page?.offset || 0) / (page?.limit || 20)) + 1;
857
- return (_jsxs("div", { className: "oxa-workflow-mobile-page oxa-workflow-work-center-mobile", children: [_jsxs("header", { children: [_jsx(Typography.Title, { level: 2, children: "\u5BA1\u6279\u4E2D\u5FC3" }), _jsx(Typography.Text, { type: "secondary", children: "\u5F53\u524D\u7528\u6237\u89D2\u8272\u5E76\u96C6\u4E0B\u7684\u6D41\u7A0B\u4EFB\u52A1" })] }), _jsx(Segmented, { block: true, onChange: (value) => onStatus(value), options: [
858
- { label: '待我审批', value: 'pending' },
859
- { label: '我已处理', value: 'completed' },
860
- ], value: status }), loading && !page ? (_jsx("div", { className: "oxa-page-loading", children: _jsx(Spin, {}) })) : null, _jsx("div", { className: "oxa-workflow-mobile-tasks", children: (page?.items || []).map((task) => (_jsxs(Card, { hoverable: true, onClick: () => navigate(task.detailNavigation.mobilePath), children: [_jsxs("div", { className: "oxa-workflow-mobile-task-heading", children: [_jsx("strong", { children: task.title }), _jsx(StatusTag, { status: task.status })] }), _jsx(Typography.Text, { children: task.businessKey }), _jsxs(Typography.Text, { type: "secondary", children: [task.workflowCode, " \u00B7 ", formatTime(task.updatedAt)] })] }, task.id))) }), !loading && !page?.items.length && _jsx(Empty, { description: "\u6682\u65E0\u6D41\u7A0B\u4EFB\u52A1" }), _jsxs("div", { className: "oxa-mobile-pagination", children: [_jsx(Button, { disabled: current <= 1, onClick: () => onPage(current - 1), children: "\u4E0A\u4E00\u9875" }), _jsxs(Typography.Text, { children: ["\u7B2C ", current, " \u9875"] }), _jsx(Button, { disabled: current * (page?.limit || 20) >= (page?.total || 0), onClick: () => onPage(current + 1), children: "\u4E0B\u4E00\u9875" })] })] }));
861
- }
862
- export function WorkflowWorkCenterPage({ variant = 'desktop', }) {
857
+ const WORK_VIEWS = [
858
+ { value: 'created', label: '我创建的' }, { value: 'pending', label: '待我审批' },
859
+ { value: 'handled', label: '我已处理' }, { value: 'cc', label: '抄送我的' },
860
+ ];
861
+ export function WorkflowWorkCenterPage({ variant = 'desktop' }) {
863
862
  const navigate = useNavigate();
864
- const [status, setStatus] = useState('pending');
863
+ const [view, setView] = useState('pending');
865
864
  const [pageNumber, setPageNumber] = useState(1);
866
865
  const [pageSize, setPageSize] = useState(20);
867
866
  const [page, setPage] = useState(null);
868
867
  const [loading, setLoading] = useState(true);
869
868
  const [error, setError] = useState('');
870
- const load = useCallback(async () => {
869
+ const [attempt, setAttempt] = useState(0);
870
+ useEffect(() => {
871
+ let active = true;
871
872
  setLoading(true);
872
873
  setError('');
873
- try {
874
- setPage(await loadWorkflowWorkCenter({
875
- status,
876
- limit: pageSize,
877
- offset: (pageNumber - 1) * pageSize,
878
- }));
879
- }
880
- catch (reason) {
881
- setError(errorMessage(reason, '工作台加载失败'));
882
- }
883
- finally {
884
- setLoading(false);
885
- }
886
- }, [pageNumber, pageSize, status]);
887
- useEffect(() => {
888
- void load();
889
- }, [load]);
890
- const changeStatus = (value) => {
891
- setStatus(value);
892
- setPageNumber(1);
893
- };
894
- if (variant === 'mobile') {
895
- return (_jsxs(_Fragment, { children: [error && _jsx(Alert, { description: error, showIcon: true, type: "error" }), _jsx(WorkCenterMobile, { loading: loading, onPage: setPageNumber, onStatus: changeStatus, page: page, status: status })] }));
896
- }
874
+ setPage(null);
875
+ void loadWorkflowWorkCenter({ view, limit: pageSize, offset: (pageNumber - 1) * pageSize })
876
+ .then(result => { if (active)
877
+ setPage(result); })
878
+ .catch(reason => { if (active)
879
+ setError(errorMessage(reason, '待办加载失败')); })
880
+ .finally(() => { if (active)
881
+ setLoading(false); });
882
+ return () => { active = false; };
883
+ }, [attempt, pageNumber, pageSize, view]);
884
+ const open = (item) => navigate(variant === 'mobile' ? item.detailNavigation.mobilePath : item.detailNavigation.desktopPath);
897
885
  const columns = [
898
- {
899
- title: '审批事项',
900
- dataIndex: 'title',
901
- render: (value, task) => (_jsx(Link, { to: task.detailNavigation.desktopPath, children: String(value) })),
902
- },
903
- { title: '业务标识', dataIndex: 'businessKey' },
904
- { title: '流程', dataIndex: 'workflowCode' },
905
- {
906
- title: '状态',
907
- dataIndex: 'status',
908
- render: (value) => _jsx(StatusTag, { status: String(value) }),
909
- },
910
- { title: '更新时间', dataIndex: 'updatedAt', render: formatTime },
886
+ { title: '申请事项', key: 'title', render: (_, item) => _jsx(Button, { type: "link", className: "oxa-work-item-link", onClick: () => open(item), children: item.title }) },
887
+ { title: '流程', dataIndex: 'workflowTitle', render: value => value || '—' },
888
+ { title: '当前节点', dataIndex: 'taskTitle', render: value => value || '—' },
889
+ { title: '状态', dataIndex: 'instanceStatus', render: (value, item) => _jsx(StatusTag, { status: String(value || item.status) }) },
890
+ { title: view === 'created' ? '创建时间' : view === 'cc' ? '抄送时间' : '更新时间', dataIndex: 'occurredAt', render: formatTime },
911
891
  ];
912
- return (_jsx(Shell, { children: _jsxs(Card, { className: "oxa-workflow-work-center", children: [_jsxs("div", { className: "oxa-workflow-page-heading", children: [_jsxs("div", { children: [_jsx(Typography.Title, { level: 2, children: "\u5BA1\u6279\u5DE5\u4F5C\u53F0" }), _jsx(Typography.Text, { type: "secondary", children: "\u6309\u7167\u5F53\u524D\u767B\u5F55\u7528\u6237\u5728\u5E94\u7528\u4E2D\u7684\u5168\u90E8\u6709\u6548\u89D2\u8272\u67E5\u8BE2" })] }), _jsx(Button, { icon: _jsx(ReloadOutlined, {}), loading: loading, onClick: () => void load(), children: "\u5237\u65B0" })] }), _jsx(Segmented, { onChange: (value) => changeStatus(value), options: [
913
- { label: '待我审批', value: 'pending' },
914
- { label: '我已处理', value: 'completed' },
915
- ], value: status }), error && _jsx(Alert, { description: error, showIcon: true, type: "error" }), _jsx(Table, { columns: columns, dataSource: page?.items || [], loading: loading, pagination: {
916
- current: pageNumber,
917
- pageSize,
918
- total: page?.total || 0,
919
- showSizeChanger: true,
920
- onChange: (next, size) => {
921
- setPageNumber(next);
922
- setPageSize(size);
923
- },
924
- }, rowKey: "id" })] }) }));
892
+ const content = _jsxs("main", { className: `oxa-application-work-center oxa-application-work-center-${variant}`, children: [_jsxs("header", { children: [_jsx("h1", { children: "\u5F85\u529E\u4E2D\u5FC3" }), _jsx(Button, { icon: _jsx(ReloadOutlined, {}), loading: loading, onClick: () => setAttempt(value => value + 1), children: "\u5237\u65B0" })] }), _jsx(Tabs, { activeKey: view, onChange: value => { setView(value); setPageNumber(1); }, items: WORK_VIEWS.map(item => ({
893
+ key: item.value, label: _jsxs("span", { children: [item.label, page?.counts && _jsx("small", { className: "oxa-work-count", children: page.counts[item.value] })] }),
894
+ })) }), error && _jsx(Alert, { title: "\u5F85\u529E\u52A0\u8F7D\u5931\u8D25", description: error, type: "error", showIcon: true, action: _jsx(Button, { onClick: () => setAttempt(value => value + 1), children: "\u91CD\u8BD5" }) }), variant === 'desktop' ? _jsx(Table, { rowKey: "id", columns: columns, dataSource: page?.items || [], loading: loading, pagination: {
895
+ current: pageNumber, pageSize, total: page?.total || 0, showSizeChanger: true,
896
+ onChange: (number, size) => { setPageNumber(number); setPageSize(size); },
897
+ } }) : _jsxs(_Fragment, { children: [loading && _jsx(Spin, {}), _jsx("div", { className: "oxa-work-center-mobile-list", children: page?.items.map(item => _jsxs("button", { type: "button", className: "oxa-work-center-item", onClick: () => open(item), children: [_jsxs("div", { children: [_jsx("strong", { children: item.title }), _jsx(StatusTag, { status: item.instanceStatus || item.status })] }), _jsx("p", { children: [item.workflowTitle, item.taskTitle].filter(Boolean).join(' · ') }), _jsx("time", { children: formatTime(item.occurredAt) })] }, item.id)) }), !loading && !page?.items.length && !error && _jsx(Empty, { description: "\u6682\u65E0\u76F8\u5173\u7533\u8BF7" }), Boolean(page && page.total > pageSize) && _jsxs("div", { className: "oxa-mobile-pagination", children: [_jsx(Button, { disabled: pageNumber <= 1, onClick: () => setPageNumber(value => value - 1), children: "\u4E0A\u4E00\u9875" }), _jsxs("span", { children: ["\u7B2C ", pageNumber, " \u9875"] }), _jsx(Button, { disabled: pageNumber * pageSize >= (page?.total || 0), onClick: () => setPageNumber(value => value + 1), children: "\u4E0B\u4E00\u9875" })] })] })] });
898
+ return content;
925
899
  }
926
900
  function RequirementFields({ requirements, }) {
927
901
  return (_jsx(_Fragment, { children: requirements.map((requirement) => (_jsx(Form.Item, { label: requirement.title, name: requirement.id, rules: requirement.required
@@ -1043,20 +1017,7 @@ async function workflowContextPrefillValue(field, rawValue, resources) {
1043
1017
  ...(Object.keys(snapshot).length ? { snapshot } : {}),
1044
1018
  };
1045
1019
  }
1046
- if (field.type === 'number.integer' || field.type === 'number.decimal') {
1047
- const parsed = Number(value);
1048
- if (!Number.isFinite(parsed)) {
1049
- throw new Error('OPENXIANGDA_WORKFLOW_LAUNCH_CONTEXT_VALUE_INVALID');
1050
- }
1051
- return parsed;
1052
- }
1053
- if (field.type === 'boolean') {
1054
- if (!['true', 'false'].includes(value)) {
1055
- throw new Error('OPENXIANGDA_WORKFLOW_LAUNCH_CONTEXT_VALUE_INVALID');
1056
- }
1057
- return value === 'true';
1058
- }
1059
- return value;
1020
+ return workflowContextScalarValue(field, value);
1060
1021
  }
1061
1022
  export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWorkflowCode, variant = 'desktop', onDismiss, onCompleted, } = {}) {
1062
1023
  const { workflowCode: routeWorkflowCode = '' } = useParams();
@@ -1282,16 +1243,29 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
1282
1243
  }
1283
1244
  let active = true;
1284
1245
  let timer;
1246
+ const startedAt = Date.now();
1247
+ let failures = 0;
1248
+ let controller;
1285
1249
  const refresh = async () => {
1250
+ if (!active)
1251
+ return;
1252
+ if (Date.now() - startedAt >= 60_000) {
1253
+ setProcessError('申请已提交,处理尚未结束;可稍后刷新状态,无需重复提交');
1254
+ return;
1255
+ }
1256
+ controller = new AbortController();
1257
+ const timeout = setTimeout(() => controller?.abort(new DOMException('状态读取超时', 'TimeoutError')), Math.min(10_000, 60_000 - (Date.now() - startedAt)));
1286
1258
  try {
1287
- const next = await loadProcessCommandSurface(commandId);
1259
+ const next = await loadProcessCommandSurface(commandId, controller.signal);
1288
1260
  if (!active)
1289
1261
  return;
1290
- if (next.command.environmentKey !== identity.environment.key ||
1262
+ if (next.command.id !== commandId ||
1263
+ next.command.environmentKey !== identity.environment.key ||
1291
1264
  next.command.workflowCode !== workflowCode ||
1292
1265
  next.command.subject.resourceCode !== definition?.subject.resourceCode) {
1293
1266
  throw new Error('OPENXIANGDA_PROCESS_COMMAND_SCOPE_MISMATCH');
1294
1267
  }
1268
+ failures = 0;
1295
1269
  setProcessSurface(next);
1296
1270
  setProcessError(null);
1297
1271
  if (next.command.status === 'started' && next.command.workflowInstanceId) {
@@ -1314,13 +1288,21 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
1314
1288
  }
1315
1289
  }
1316
1290
  catch (error) {
1317
- if (active)
1318
- setProcessError(errorMessage(error, '命令状态读取失败'));
1291
+ if (!active)
1292
+ return;
1293
+ const delay = processStatusReadRetryDelay(error, ++failures, Date.now() - startedAt);
1294
+ setProcessError(delay === null ? '申请已提交,暂时无法读取处理状态;请稍后刷新,无需重复提交' : '申请已提交,正在重新读取处理状态,请勿重复提交');
1295
+ if (delay !== null)
1296
+ timer = setTimeout(() => void refresh(), delay);
1297
+ }
1298
+ finally {
1299
+ clearTimeout(timeout);
1319
1300
  }
1320
1301
  };
1321
1302
  void refresh();
1322
1303
  return () => {
1323
1304
  active = false;
1305
+ controller?.abort();
1324
1306
  if (timer)
1325
1307
  clearTimeout(timer);
1326
1308
  };
@@ -1357,7 +1339,7 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
1357
1339
  const drawerBusy = loading || Boolean(commandId && (!processSurface || ['accepted', 'resolving', 'ready', 'starting', 'retry_wait'].includes(processSurface.command.status)) && !processError);
1358
1340
  const dismissDrawer = () => { if (!submitInFlight.current && !drawerBusy)
1359
1341
  onDismiss?.(); };
1360
- const frame = (content) => onDismiss ? _jsx(Drawer, { open: true, title: definition?.title || '新增流程申请', closable: false, rootClassName: "oxa-resource-drawer oxa-workflow-launch-drawer", size: fullScreen || variant === 'mobile' ? '100vw' : 'min(960px, calc(100vw - 48px))', styles: { body: { padding: 0, overflow: 'auto' } }, keyboard: !drawerBusy, mask: { closable: !drawerBusy }, onClose: dismissDrawer, extra: _jsxs(Space, { size: 4, children: [_jsx(Button, { type: "text", "aria-label": fullScreen ? '退出全屏' : '全屏', icon: fullScreen ? _jsx(CompressOutlined, {}) : _jsx(ExpandOutlined, {}), onClick: () => setFullScreen(value => !value) }), launchPagePath.current && _jsx(Tooltip, { title: commandId ? '新开页面继续查看当前申请' : '新开页面(未提交内容不会带入)', children: _jsx(Button, { type: "text", "aria-label": "\u65B0\u5F00\u9875\u9762", icon: _jsx(ExportOutlined, {}), href: newPageHref, target: "_blank", rel: "noopener noreferrer", disabled: loading }) }), _jsx(Button, { type: "text", "aria-label": "\u5173\u95ED", disabled: drawerBusy, icon: _jsx(CloseOutlined, {}), onClick: dismissDrawer })] }), children: content }) : variant === 'desktop' ? (_jsx("div", { className: "oxa-workflow-desktop-page oxa-workflow-submission-standalone-page", children: content })) : content;
1342
+ const frame = (content) => onDismiss ? _jsx(Drawer, { open: true, title: definition?.title || '新增流程申请', closable: false, rootClassName: "oxa-resource-drawer oxa-workflow-launch-drawer", size: fullScreen || variant === 'mobile' ? '100vw' : 'min(850px, calc(100vw - 48px))', styles: { body: { padding: 0, overflow: 'auto' } }, keyboard: !drawerBusy, mask: { closable: !drawerBusy }, onClose: dismissDrawer, extra: _jsxs(Space, { size: 4, children: [_jsx(Button, { type: "text", "aria-label": fullScreen ? '退出全屏' : '全屏', icon: fullScreen ? _jsx(CompressOutlined, {}) : _jsx(ExpandOutlined, {}), onClick: () => setFullScreen(value => !value) }), launchPagePath.current && _jsx(Tooltip, { title: commandId ? '新开页面继续查看当前申请' : '新开页面(未提交内容不会带入)', children: _jsx(Button, { type: "text", "aria-label": "\u65B0\u5F00\u9875\u9762", icon: _jsx(ExportOutlined, {}), href: newPageHref, target: "_blank", rel: "noopener noreferrer", disabled: loading }) }), _jsx(Button, { type: "text", "aria-label": "\u5173\u95ED", disabled: drawerBusy, icon: _jsx(CloseOutlined, {}), onClick: dismissDrawer })] }), children: content }) : variant === 'desktop' ? (_jsx("div", { className: "oxa-workflow-desktop-page oxa-workflow-submission-standalone-page", children: content })) : content;
1361
1343
  if (!definition)
1362
1344
  return frame(_jsx(Result, { status: "404", title: "\u672A\u58F0\u660E\u8BE5\u5DE5\u4F5C\u6D41" }));
1363
1345
  if (definition.launch.mode !== 'standalone' &&
@@ -1564,6 +1546,7 @@ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWor
1564
1546
  const uploadOperationCode = namedIntent?.operationCode || definition.processOperationCode;
1565
1547
  const fieldRenderers = uploadOperationCode
1566
1548
  ? {
1549
+ ...(namedIntent ? { referenceLaunch: { workflowCode: definition.code, operationCode: namedIntent.operationCode } } : {}),
1567
1550
  upload: async (field, file) => await uploadOperationManagedFile({
1568
1551
  operationCode: uploadOperationCode,
1569
1552
  resourceCode: subjectDefinition.code,
@@ -1622,16 +1605,16 @@ function ProcessCommandPanel({ form, surface, error, loading, onAnswer, onRetry,
1622
1605
  }
1623
1606
  return (_jsxs("div", { className: "oxa-workflow-submission-followup", children: [command.status === 'awaiting_input' ? (_jsx(Card, { title: "\u8FD8\u9700\u8981\u8865\u5145\u4E00\u4E9B\u4FE1\u606F", children: _jsxs(Form, { form: form, layout: "vertical", onFinish: values => void onAnswer(values), children: [_jsx(RequirementFields, { requirements: surface.requirements }), _jsx(Button, { htmlType: "submit", loading: loading, type: "primary", children: "\u63D0\u4EA4\u8865\u5145\u4FE1\u606F" })] }) })) : null, ['cancelled', 'dead_letter'].includes(command.status) ? (_jsx(Result, { extra: surface.resume.retry ? (_jsx(Button, { loading: loading, onClick: () => void onRetry(), type: "primary", children: "\u91CD\u65B0\u5C1D\u8BD5" })) : null, status: "error", subTitle: "\u8BF7\u7A0D\u540E\u91CD\u8BD5\uFF1B\u5982\u95EE\u9898\u6301\u7EED\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u3002", title: "\u7533\u8BF7\u6682\u672A\u53D1\u8D77\u6210\u529F" })) : null] }));
1624
1607
  }
1625
- function DesktopProcessSubmissionRenderer(props) {
1626
- if (props.completion) {
1627
- return (_jsx("div", { className: "oxa-workflow-submission oxa-workflow-submission-desktop", children: _jsx(Result, { extra: _jsx(Button, { onClick: props.onBack, children: "\u8FD4\u56DE" }), status: "success", subTitle: `业务记录 ${props.completion.subjectId} 已提交,本次无需进入审批流程。`, title: "\u4E1A\u52A1\u63D0\u4EA4\u6210\u529F" }) }));
1628
- }
1629
- return (_jsx("div", { className: "oxa-workflow-submission oxa-workflow-submission-desktop", children: _jsx(Spin, { description: "\u6B63\u5728\u63D0\u4EA4\u7533\u8BF7\uFF0C\u8BF7\u7A0D\u5019\u2026", spinning: props.processing, children: _jsxs(Card, { className: "oxa-workflow-submission-card", children: [!props.embedded && _jsx(Typography.Title, { level: 2, children: props.title }), props.launchControls, _jsxs(Form, { form: props.subjectForm, layout: "vertical", onFinish: values => void props.onSubmit(values), children: [_jsx("div", { className: "oxa-grid", children: props.fields.map(field => (_jsx(SurfaceFieldControl, { disabled: props.loading || props.submitted, field: field, operation: props.mutationMode === 'update' ? 'update' : 'create', recordId: props.recordId, renderers: props.fieldRenderers, resourceCode: props.subjectDefinition.code }, field.key))) }), _jsxs(Space, { children: [!props.embedded && _jsx(Button, { disabled: props.processing, icon: _jsx(ArrowLeftOutlined, {}), onClick: props.onBack, children: "\u8FD4\u56DE" }), _jsx(Button, { disabled: props.submitted, htmlType: "submit", loading: props.loading || props.processing, type: "primary", children: "\u63D0\u4EA4\u5BA1\u6279" })] })] }), _jsx(ProcessCommandPanel, { error: props.processError, form: props.requirementForm, loading: props.loading, onAnswer: props.onAnswer, onRetry: props.onRetry, onRefresh: props.onRefresh, surface: props.processSurface })] }) }) }));
1630
- }
1631
- function MobileProcessSubmissionRenderer(props) {
1632
- if (props.completion) {
1633
- return (_jsx("div", { className: "oxa-mobile-page", children: _jsx(Result, { extra: _jsx(Button, { onClick: props.onBack, children: "\u8FD4\u56DE" }), status: "success", subTitle: `业务记录 ${props.completion.subjectId} 已提交,本次无需进入审批流程。`, title: "\u4E1A\u52A1\u63D0\u4EA4\u6210\u529F" }) }));
1634
- }
1635
- return (_jsx(Spin, { description: "\u6B63\u5728\u63D0\u4EA4\u7533\u8BF7\uFF0C\u8BF7\u7A0D\u5019\u2026", spinning: props.processing, children: _jsxs("div", { className: "oxa-mobile-page oxa-workflow-submission-mobile", children: [!props.embedded && _jsx(Typography.Title, { level: 3, children: props.title }), props.launchControls, _jsxs(Form, { className: "oxa-mobile-form", form: props.subjectForm, layout: "vertical", onFinish: values => void props.onSubmit(values), children: [props.fields.map(field => (_jsx(MobileSurfaceFieldControl, { disabled: props.loading || props.submitted, field: field, operation: props.mutationMode === 'update' ? 'update' : 'create', recordId: props.recordId, renderers: props.fieldRenderers, resourceCode: props.subjectDefinition.code }, field.key))), _jsx(Button, { block: true, disabled: props.submitted, htmlType: "submit", loading: props.loading || props.processing, type: "primary", children: "\u63D0\u4EA4\u5BA1\u6279" })] }), _jsx(ProcessCommandPanel, { error: props.processError, form: props.requirementForm, loading: props.loading, onAnswer: props.onAnswer, onRetry: props.onRetry, onRefresh: props.onRefresh, surface: props.processSurface })] }) }));
1608
+ function StandardProcessSubmissionRenderer(props) {
1609
+ const groups = [...props.fields.reduce((groups, field) => {
1610
+ const section = field.section || 'default';
1611
+ groups.set(section, [...(groups.get(section) || []), field]);
1612
+ return groups;
1613
+ }, new Map())].map(([section, fields]) => ({ section, fields }));
1614
+ if (props.completion)
1615
+ return _jsx(Result, { status: "success", title: "\u63D0\u4EA4\u6210\u529F", extra: _jsx(Button, { onClick: props.onBack, children: "\u8FD4\u56DE" }) });
1616
+ 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 })] });
1636
1617
  }
1618
+ function DesktopProcessSubmissionRenderer(props) { return _jsx(StandardProcessSubmissionRenderer, { ...props, variant: "desktop" }); }
1619
+ function MobileProcessSubmissionRenderer(props) { return _jsx(StandardProcessSubmissionRenderer, { ...props, variant: "mobile" }); }
1637
1620
  //# sourceMappingURL=StandardWorkflowPages.js.map