openxiangda 2.0.0-alpha.70 → 2.0.0-alpha.72

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 (48) hide show
  1. package/dist/browser/Shell.d.ts.map +1 -1
  2. package/dist/browser/Shell.js +1 -4
  3. package/dist/browser/Shell.js.map +1 -1
  4. package/dist/browser/admin-access.d.ts +7 -0
  5. package/dist/browser/admin-access.d.ts.map +1 -0
  6. package/dist/browser/admin-access.js +13 -0
  7. package/dist/browser/admin-access.js.map +1 -0
  8. package/dist/browser/application.d.ts +3 -2
  9. package/dist/browser/application.d.ts.map +1 -1
  10. package/dist/browser/application.js +36 -31
  11. package/dist/browser/application.js.map +1 -1
  12. package/dist/browser/components/workflow/StandardWorkflowPages.d.ts +2 -1
  13. package/dist/browser/components/workflow/StandardWorkflowPages.d.ts.map +1 -1
  14. package/dist/browser/components/workflow/StandardWorkflowPages.js +362 -16
  15. package/dist/browser/components/workflow/StandardWorkflowPages.js.map +1 -1
  16. package/dist/browser/platform-client.d.ts +3 -1
  17. package/dist/browser/platform-client.d.ts.map +1 -1
  18. package/dist/browser/platform-client.js +13 -0
  19. package/dist/browser/platform-client.js.map +1 -1
  20. package/dist/browser/route-manifest.d.ts +1 -7
  21. package/dist/browser/route-manifest.d.ts.map +1 -1
  22. package/dist/browser/route-manifest.js +1 -43
  23. package/dist/browser/route-manifest.js.map +1 -1
  24. package/dist/browser/workflow-definitions.d.ts +25 -0
  25. package/dist/browser/workflow-definitions.d.ts.map +1 -1
  26. package/dist/browser/workflow-definitions.js +9 -3
  27. package/dist/browser/workflow-definitions.js.map +1 -1
  28. package/dist/browser/workflow-launch.d.ts +36 -0
  29. package/dist/browser/workflow-launch.d.ts.map +1 -0
  30. package/dist/browser/workflow-launch.js +103 -0
  31. package/dist/browser/workflow-launch.js.map +1 -0
  32. package/dist/config.d.ts +1 -1
  33. package/dist/config.d.ts.map +1 -1
  34. package/dist/config.js +1 -1
  35. package/dist/config.js.map +1 -1
  36. package/dist/nest.d.ts +1 -1
  37. package/dist/nest.d.ts.map +1 -1
  38. package/dist/nest.js +1 -1
  39. package/dist/nest.js.map +1 -1
  40. package/dist/react.d.ts +2 -0
  41. package/dist/react.d.ts.map +1 -1
  42. package/dist/react.js +2 -0
  43. package/dist/react.js.map +1 -1
  44. package/package.json +7 -7
  45. package/skills/openxiangda-v2/SKILL.md +3 -3
  46. package/skills/openxiangda-v2/references/frontend.md +14 -9
  47. package/skills/openxiangda-v2/references/workflow-events.md +52 -14
  48. package/skills/openxiangda-v2/references/workspace.md +1 -1
@@ -5,11 +5,12 @@ import { normalizeWorkflowBusinessDetail, } from 'openxiangda-contracts/browser'
5
5
  import { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
6
6
  import { Link, Navigate, useNavigate, useParams, useSearchParams, } from 'react-router-dom';
7
7
  import { Shell } from '../../Shell';
8
- import { executeWorkflowOperation, loadWorkflowInstanceDetail, loadWorkflowInstanceSurface, loadWorkflowLaunchSurface, loadWorkflowTaskSurface, loadWorkflowTaskDetail, loadWorkflowWorkCenter, answerBusinessProcessCommand, commitStandardProcess, createNativeResourceClient, loadProcessCommandSurface, retryBusinessProcessCommand, searchDirectory, } from '../../platform-client';
9
- import { useWorkflowDefinition } from '../../workflow-definitions';
8
+ import { executeWorkflowOperation, executeWorkflowLaunchNamedOperation, loadWorkflowInstanceDetail, loadWorkflowInstanceSurface, loadWorkflowLaunchSurface, loadWorkflowTaskSurface, loadWorkflowTaskDetail, loadWorkflowWorkCenter, answerBusinessProcessCommand, commitStandardProcess, createNativeResourceClient, loadProcessCommandSurface, retryBusinessProcessCommand, searchDirectory, uploadOperationManagedFile, } from '../../platform-client';
9
+ import { useWorkflowDefinition, } from '../../workflow-definitions';
10
10
  import { useResourceDefinitions } from '../../resource-definitions';
11
11
  import { useRuntime } from '../../runtime';
12
12
  import { MobileSurfaceFieldControl, SurfaceFieldControl, SurfaceFieldValue, } from '../resource/SurfaceFields';
13
+ import { buildWorkflowNamedOperationInput, parseWorkflowNamedOperationResult, } from '../../workflow-launch';
13
14
  import { normalizeFormValues, normalizeRecordForForm, } from '../platform-fields/field-form-codec';
14
15
  import { PlatformAvatar } from '../PlatformAvatar';
15
16
  /**
@@ -915,7 +916,131 @@ function RequirementFields({ requirements, }) {
915
916
  value: 'userId' in candidate ? candidate.userId : candidate.value,
916
917
  })) }) }, requirement.id))) }));
917
918
  }
918
- export function WorkflowSubmissionPage({ workflowCode: declaredWorkflowCode, variant = 'desktop', } = {}) {
919
+ function sameJson(left, right) {
920
+ return JSON.stringify(left) === JSON.stringify(right);
921
+ }
922
+ function namedLaunchIntentMatches(generated, surface) {
923
+ if (!generated || !surface)
924
+ return generated === surface;
925
+ return (generated.operationCode === surface.operationCode &&
926
+ generated.method === surface.method &&
927
+ generated.path === surface.href &&
928
+ generated.requiredCapability === surface.requiredCapability &&
929
+ generated.requestSchemaDigest === surface.requestSchemaDigest &&
930
+ generated.responseSchemaDigest === surface.responseSchemaDigest &&
931
+ sameJson(generated.inputs, surface.inputs) &&
932
+ sameJson(generated.output, surface.output));
933
+ }
934
+ function fieldValueLabel(value) {
935
+ if (value === undefined || value === null || value === '')
936
+ return '';
937
+ if (typeof value === 'object' && !Array.isArray(value)) {
938
+ const item = value;
939
+ if (typeof item.label === 'string')
940
+ return item.label;
941
+ }
942
+ if (Array.isArray(value))
943
+ return value.map(fieldValueLabel).filter(Boolean).join('、');
944
+ return String(value);
945
+ }
946
+ function subjectRecordLabel(record, definition) {
947
+ const preferred = definition.surface.list?.searchableFields || [];
948
+ const candidates = [
949
+ ...preferred,
950
+ ...(definition.surface.form?.fieldOrder || []),
951
+ ...Object.keys(definition.surface.fields),
952
+ ];
953
+ for (const fieldCode of candidates) {
954
+ const label = fieldValueLabel(record[fieldCode]);
955
+ if (label)
956
+ return label;
957
+ }
958
+ return String(record.id || '业务记录');
959
+ }
960
+ function SubjectRecordSelector({ definition, value, onChange, }) {
961
+ const [keyword, setKeyword] = useState('');
962
+ const [options, setOptions] = useState([]);
963
+ const [loading, setLoading] = useState(false);
964
+ useEffect(() => {
965
+ let active = true;
966
+ const timer = window.setTimeout(() => {
967
+ setLoading(true);
968
+ void createNativeResourceClient(definition.code, definition.surface)
969
+ .list({ page: 1, pageSize: 20, ...(keyword.trim() ? { keyword } : {}) })
970
+ .then(page => {
971
+ if (!active)
972
+ return;
973
+ setOptions(page.rows
974
+ .map(record => ({
975
+ value: String(record.id || ''),
976
+ label: subjectRecordLabel(record, definition),
977
+ }))
978
+ .filter(option => option.value));
979
+ })
980
+ .catch(() => {
981
+ if (active)
982
+ setOptions([]);
983
+ })
984
+ .finally(() => {
985
+ if (active)
986
+ setLoading(false);
987
+ });
988
+ }, 250);
989
+ return () => {
990
+ active = false;
991
+ window.clearTimeout(timer);
992
+ };
993
+ }, [definition, keyword]);
994
+ return (_jsx(Select, { allowClear: true, filterOption: false, loading: loading, onChange: next => onChange(next || undefined), onSearch: setKeyword, options: options, placeholder: "\u641C\u7D22\u5E76\u9009\u62E9\u5DF2\u6709\u4E1A\u52A1\u8BB0\u5F55", showSearch: true, value: value || undefined }));
995
+ }
996
+ async function workflowContextPrefillValue(field, rawValue, resources) {
997
+ const value = rawValue.trim();
998
+ if (!value || value.length > 500) {
999
+ throw new Error('OPENXIANGDA_WORKFLOW_LAUNCH_CONTEXT_VALUE_INVALID');
1000
+ }
1001
+ if (field.type === 'resource-ref.single') {
1002
+ const source = field.source;
1003
+ const target = source ? resources[source.resourceCode] : undefined;
1004
+ if (!source || !target) {
1005
+ throw new Error('OPENXIANGDA_WORKFLOW_LAUNCH_CONTEXT_SOURCE_REQUIRED');
1006
+ }
1007
+ const record = await createNativeResourceClient(target.code, target.surface).get(value);
1008
+ const label = fieldValueLabel(record[source.labelField]);
1009
+ if (!label || String(record.id || '') !== value) {
1010
+ throw new Error('OPENXIANGDA_WORKFLOW_LAUNCH_CONTEXT_RECORD_INVALID');
1011
+ }
1012
+ const description = (source.descriptionFields || [])
1013
+ .map(fieldCode => fieldValueLabel(record[fieldCode]))
1014
+ .filter(Boolean)
1015
+ .join(' · ');
1016
+ const snapshot = Object.fromEntries((source.snapshotFields || []).map(fieldCode => [
1017
+ fieldCode,
1018
+ record[fieldCode],
1019
+ ]));
1020
+ return {
1021
+ resourceCode: source.resourceCode,
1022
+ value,
1023
+ label,
1024
+ ...(description ? { description } : {}),
1025
+ ...(Object.keys(snapshot).length ? { snapshot } : {}),
1026
+ };
1027
+ }
1028
+ if (field.type === 'number.integer' || field.type === 'number.decimal') {
1029
+ const parsed = Number(value);
1030
+ if (!Number.isFinite(parsed)) {
1031
+ throw new Error('OPENXIANGDA_WORKFLOW_LAUNCH_CONTEXT_VALUE_INVALID');
1032
+ }
1033
+ return parsed;
1034
+ }
1035
+ if (field.type === 'boolean') {
1036
+ if (!['true', 'false'].includes(value)) {
1037
+ throw new Error('OPENXIANGDA_WORKFLOW_LAUNCH_CONTEXT_VALUE_INVALID');
1038
+ }
1039
+ return value === 'true';
1040
+ }
1041
+ return value;
1042
+ }
1043
+ export function WorkflowSubmissionPage({ instancePath, workflowCode: declaredWorkflowCode, variant = 'desktop', } = {}) {
919
1044
  const { workflowCode: routeWorkflowCode = '' } = useParams();
920
1045
  const workflowCode = declaredWorkflowCode || routeWorkflowCode;
921
1046
  const definition = useWorkflowDefinition(workflowCode);
@@ -931,13 +1056,40 @@ export function WorkflowSubmissionPage({ workflowCode: declaredWorkflowCode, var
931
1056
  const [launchError, setLaunchError] = useState(null);
932
1057
  const [processSurface, setProcessSurface] = useState(null);
933
1058
  const [processError, setProcessError] = useState(null);
1059
+ const [completion, setCompletion] = useState(null);
1060
+ const submitInFlight = useRef(false);
934
1061
  const pathCommandId = useParams().commandId;
935
1062
  const commandId = pathCommandId || searchParams.get('processCommandId') || '';
936
1063
  const subjectId = searchParams.get('subjectId') || '';
1064
+ const generatedNamedSubmission = definition?.launch.submission;
1065
+ const [submissionMode, setSubmissionMode] = useState(subjectId ? 'existing' : 'create');
937
1066
  const subjectDefinition = definition
938
1067
  ? resources[definition.subject.resourceCode]
939
1068
  : undefined;
940
- const mutationMode = subjectId ? 'update' : 'create';
1069
+ useEffect(() => {
1070
+ if (!generatedNamedSubmission)
1071
+ return;
1072
+ if (subjectId && generatedNamedSubmission.existing) {
1073
+ setSubmissionMode('existing');
1074
+ return;
1075
+ }
1076
+ if (submissionMode === 'create' && generatedNamedSubmission.create)
1077
+ return;
1078
+ if (submissionMode === 'existing' && generatedNamedSubmission.existing)
1079
+ return;
1080
+ setSubmissionMode(generatedNamedSubmission.create ? 'create' : 'existing');
1081
+ }, [generatedNamedSubmission, subjectId, submissionMode]);
1082
+ const surfaceNamedSubmission = launchSurface?.submission.kind === 'named-operation'
1083
+ ? launchSurface.submission
1084
+ : undefined;
1085
+ const namedIntent = surfaceNamedSubmission?.[submissionMode];
1086
+ const mutationMode = generatedNamedSubmission
1087
+ ? submissionMode === 'existing'
1088
+ ? 'update'
1089
+ : 'create'
1090
+ : subjectId
1091
+ ? 'update'
1092
+ : 'create';
941
1093
  const fields = useMemo(() => {
942
1094
  if (!subjectDefinition)
943
1095
  return [];
@@ -955,6 +1107,9 @@ export function WorkflowSubmissionPage({ workflowCode: declaredWorkflowCode, var
955
1107
  .filter((field) => Boolean(field))
956
1108
  .filter(field => !field.system && field.widget !== 'readonly')
957
1109
  .filter(field => {
1110
+ if (namedIntent) {
1111
+ return Object.values(namedIntent.inputs).some(binding => binding.source === 'field' && binding.fieldCode === field.key);
1112
+ }
958
1113
  if (identity.isAppSuperAdmin)
959
1114
  return true;
960
1115
  const capabilities = mutationMode === 'create'
@@ -962,7 +1117,13 @@ export function WorkflowSubmissionPage({ workflowCode: declaredWorkflowCode, var
962
1117
  : field.updateCapabilities;
963
1118
  return capabilities.length > 0 && capabilities.every(hasCapability);
964
1119
  });
965
- }, [hasCapability, identity.isAppSuperAdmin, mutationMode, subjectDefinition]);
1120
+ }, [
1121
+ hasCapability,
1122
+ identity.isAppSuperAdmin,
1123
+ mutationMode,
1124
+ namedIntent,
1125
+ subjectDefinition,
1126
+ ]);
966
1127
  useEffect(() => {
967
1128
  let active = true;
968
1129
  setLaunchSurface(null);
@@ -981,10 +1142,19 @@ export function WorkflowSubmissionPage({ workflowCode: declaredWorkflowCode, var
981
1142
  const actualSummaryFields = [
982
1143
  ...(surface.subject.summaryFields || []),
983
1144
  ].sort();
1145
+ const generatedSubmission = definition?.launch.submission;
1146
+ const submissionMatches = generatedSubmission
1147
+ ? surface.submission.kind === 'named-operation' &&
1148
+ namedLaunchIntentMatches(generatedSubmission.create, surface.submission.create) &&
1149
+ namedLaunchIntentMatches(generatedSubmission.existing, surface.submission.existing) &&
1150
+ sameJson(generatedSubmission.context || [], surface.submission.context)
1151
+ : surface.submission.kind === 'standard-process' &&
1152
+ surface.submission.processOperationCode ===
1153
+ definition?.processOperationCode;
984
1154
  if (!definition ||
985
1155
  surface.environmentKey !== identity.environment.key ||
986
1156
  surface.environmentId !== identity.environment.id ||
987
- surface.processOperationCode !== definition.processOperationCode ||
1157
+ !submissionMatches ||
988
1158
  surface.subject.resourceCode !== definition.subject.resourceCode ||
989
1159
  JSON.stringify(actualProjection) !== JSON.stringify(expectedProjection) ||
990
1160
  JSON.stringify(actualSummaryFields) !==
@@ -1003,7 +1173,10 @@ export function WorkflowSubmissionPage({ workflowCode: declaredWorkflowCode, var
1003
1173
  };
1004
1174
  }, [commandId, definition, identity.environment.id, identity.environment.key, workflowCode]);
1005
1175
  useEffect(() => {
1006
- if (!subjectDefinition || !subjectId || commandId)
1176
+ if (!subjectDefinition ||
1177
+ !subjectId ||
1178
+ commandId ||
1179
+ mutationMode !== 'update')
1007
1180
  return;
1008
1181
  let active = true;
1009
1182
  const client = createNativeResourceClient(subjectDefinition.code, subjectDefinition.surface);
@@ -1021,7 +1194,56 @@ export function WorkflowSubmissionPage({ workflowCode: declaredWorkflowCode, var
1021
1194
  return () => {
1022
1195
  active = false;
1023
1196
  };
1024
- }, [commandId, subjectDefinition, subjectForm, subjectId]);
1197
+ }, [commandId, mutationMode, subjectDefinition, subjectForm, subjectId]);
1198
+ useEffect(() => {
1199
+ if (!surfaceNamedSubmission ||
1200
+ !namedIntent ||
1201
+ !subjectDefinition ||
1202
+ commandId ||
1203
+ mutationMode !== 'create')
1204
+ return;
1205
+ const declaredFields = new Set(Object.values(namedIntent.inputs).flatMap(binding => binding.source === 'field' ? [binding.fieldCode] : []));
1206
+ const requested = surfaceNamedSubmission.context
1207
+ .map(context => ({
1208
+ ...context,
1209
+ value: searchParams.get(context.queryParameter),
1210
+ }))
1211
+ .filter((context) => Boolean(context.value) && declaredFields.has(context.fieldCode));
1212
+ if (!requested.length)
1213
+ return;
1214
+ let active = true;
1215
+ void Promise.all(requested.map(async (context) => {
1216
+ const field = subjectDefinition.surface.fields[context.fieldCode];
1217
+ if (!field || field.system || field.widget === 'readonly') {
1218
+ throw new Error('OPENXIANGDA_WORKFLOW_LAUNCH_CONTEXT_FIELD_INVALID');
1219
+ }
1220
+ return [
1221
+ context.fieldCode,
1222
+ await workflowContextPrefillValue({ key: context.fieldCode, ...field }, context.value, resources),
1223
+ ];
1224
+ }))
1225
+ .then(entries => {
1226
+ if (active)
1227
+ subjectForm.setFieldsValue(Object.fromEntries(entries));
1228
+ })
1229
+ .catch(error => {
1230
+ if (active) {
1231
+ setLaunchError(errorMessage(error, '上下文预填失败'));
1232
+ }
1233
+ });
1234
+ return () => {
1235
+ active = false;
1236
+ };
1237
+ }, [
1238
+ commandId,
1239
+ mutationMode,
1240
+ namedIntent,
1241
+ resources,
1242
+ searchParams,
1243
+ subjectDefinition,
1244
+ subjectForm,
1245
+ surfaceNamedSubmission,
1246
+ ]);
1025
1247
  useEffect(() => {
1026
1248
  if (!commandId) {
1027
1249
  setProcessSurface(null);
@@ -1043,7 +1265,11 @@ export function WorkflowSubmissionPage({ workflowCode: declaredWorkflowCode, var
1043
1265
  setProcessSurface(next);
1044
1266
  setProcessError(null);
1045
1267
  if (next.command.status === 'started' && next.command.workflowInstanceId) {
1046
- navigate(`/${variant === 'mobile' ? 'm' : 'admin'}/workflows/${encodeURIComponent(next.command.workflowInstanceId)}`, { replace: true });
1268
+ if (!instancePath ||
1269
+ (instancePath.match(/:instanceId/g) || []).length !== 1) {
1270
+ throw new Error('OPENXIANGDA_WORKFLOW_INSTANCE_ROUTE_MISSING');
1271
+ }
1272
+ navigate(instancePath.replace(':instanceId', encodeURIComponent(next.command.workflowInstanceId)), { replace: true });
1047
1273
  return;
1048
1274
  }
1049
1275
  if (['accepted', 'resolving', 'ready', 'starting', 'retry_wait'].includes(next.command.status)) {
@@ -1061,26 +1287,91 @@ export function WorkflowSubmissionPage({ workflowCode: declaredWorkflowCode, var
1061
1287
  if (timer)
1062
1288
  clearTimeout(timer);
1063
1289
  };
1064
- }, [commandId, definition, identity.environment.key, navigate, variant, workflowCode]);
1065
- const frame = (content) => variant === 'desktop' ? _jsx(Shell, { children: content }) : content;
1290
+ }, [
1291
+ commandId,
1292
+ definition,
1293
+ identity.environment.key,
1294
+ instancePath,
1295
+ navigate,
1296
+ workflowCode,
1297
+ ]);
1298
+ const frame = (content) => variant === 'desktop' ? (_jsx("div", { className: "oxa-workflow-desktop-page oxa-workflow-submission-standalone-page", children: content })) : content;
1066
1299
  if (!definition)
1067
1300
  return frame(_jsx(Result, { status: "404", title: "\u672A\u58F0\u660E\u8BE5\u5DE5\u4F5C\u6D41" }));
1068
1301
  if (definition.launch.mode !== 'standalone' &&
1069
1302
  definition.launch.mode !== 'hidden-handoff')
1070
1303
  return frame(_jsx(Result, { status: "404", title: "\u8BE5\u5DE5\u4F5C\u6D41\u4E0D\u63D0\u4F9B\u6807\u51C6\u53D1\u8D77\u9875\u9762" }));
1071
- if (!definition.processOperationCode || !subjectDefinition)
1304
+ if (!subjectDefinition)
1072
1305
  return frame(_jsx(Result, { status: "500", title: "\u6807\u51C6\u6D41\u7A0B\u5408\u540C\u4E0D\u5B8C\u6574" }));
1073
1306
  if (launchError && !commandId)
1074
1307
  return frame(_jsx(Result, { status: "error", title: "\u53D1\u8D77\u9875\u9762\u5F53\u524D\u4E0D\u53EF\u7528", subTitle: launchError }));
1075
1308
  if (!launchSurface && !commandId)
1076
1309
  return frame(_jsx(Spin, { className: "oxa-workflow-launch-loading", size: "large" }));
1310
+ if (!commandId &&
1311
+ generatedNamedSubmission &&
1312
+ (!surfaceNamedSubmission || !namedIntent)) {
1313
+ return frame(_jsx(Result, { status: "500", title: "Named Action \u63D0\u4EA4\u5408\u540C\u4E0D\u5B8C\u6574" }));
1314
+ }
1315
+ if (!commandId &&
1316
+ !generatedNamedSubmission &&
1317
+ (!definition.processOperationCode ||
1318
+ launchSurface?.submission.kind !== 'standard-process')) {
1319
+ return frame(_jsx(Result, { status: "500", title: "\u6807\u51C6\u6D41\u7A0B\u5408\u540C\u4E0D\u5B8C\u6574" }));
1320
+ }
1077
1321
  if (!commandId && fields.some(field => field.type === 'subtable')) {
1078
- return frame(_jsx(Result, { status: "error", title: "\u6807\u51C6\u6D41\u7A0B\u4E0D\u652F\u6301\u5305\u542B\u53EF\u5199\u5B50\u8868\u7684\u4E3B\u4F53\u8868\u5355", subTitle: "\u8BF7\u6539\u7528\u58F0\u660E\u4E86 Workflow \u4F9D\u8D56\u7684 Named Action\u3002" }));
1322
+ return frame(_jsx(Result, { status: "error", title: "\u6807\u51C6\u6D41\u7A0B\u4E0D\u652F\u6301\u5305\u542B\u53EF\u5199\u5B50\u8868\u7684\u4E3B\u4F53\u8868\u5355", subTitle: "\u5F53\u524D\u6807\u51C6\u63D0\u4EA4 Surface \u5C1A\u672A\u58F0\u660E\u53EF\u5199\u5B50\u8868\u6E32\u67D3\u5668\u3002" }));
1079
1323
  }
1080
1324
  const submit = async (values) => {
1325
+ if (submitInFlight.current)
1326
+ return;
1327
+ submitInFlight.current = true;
1081
1328
  setLoading(true);
1082
1329
  try {
1083
1330
  const data = normalizeFormValues(values, subjectDefinition.surface);
1331
+ if (namedIntent && launchSurface) {
1332
+ let subject;
1333
+ if (submissionMode === 'existing') {
1334
+ if (!subjectId) {
1335
+ throw new Error('OPENXIANGDA_WORKFLOW_EXISTING_SUBJECT_REQUIRED');
1336
+ }
1337
+ const current = await createNativeResourceClient(subjectDefinition.code, subjectDefinition.surface).get(subjectId);
1338
+ const revision = Number(current.revision);
1339
+ if (!Number.isSafeInteger(revision) || revision < 1) {
1340
+ throw new Error('OPENXIANGDA_WORKFLOW_EXISTING_SUBJECT_REVISION_REQUIRED');
1341
+ }
1342
+ subject = { id: subjectId, revision };
1343
+ }
1344
+ const response = await executeWorkflowLaunchNamedOperation(namedIntent, buildWorkflowNamedOperationInput(namedIntent, {
1345
+ values: data,
1346
+ idempotencyKey: randomId(`workflow-action:${workflowCode}`),
1347
+ requestedAt: new Date().toISOString(),
1348
+ subjectProfile: identity.subjectProfile,
1349
+ ...(subject ? { subject } : {}),
1350
+ }));
1351
+ const outcome = parseWorkflowNamedOperationResult(namedIntent, response, {
1352
+ appCode: launchSurface.appCode,
1353
+ environmentKey: launchSurface.environmentKey,
1354
+ workflowCode,
1355
+ resourceCode: subjectDefinition.code,
1356
+ });
1357
+ if (outcome.kind === 'workflow-command') {
1358
+ const next = new URLSearchParams(searchParams);
1359
+ next.set('processCommandId', outcome.command.id);
1360
+ next.delete('subjectId');
1361
+ setSearchParams(next, { replace: true });
1362
+ message.success(outcome.command.replayed ? '已恢复原提交命令' : '提交命令已受理');
1363
+ }
1364
+ else {
1365
+ setCompletion({
1366
+ subjectId: outcome.subjectId,
1367
+ ...(outcome.subjectRevision
1368
+ ? { subjectRevision: outcome.subjectRevision }
1369
+ : {}),
1370
+ });
1371
+ message.success('业务提交成功,无需审批');
1372
+ }
1373
+ return;
1374
+ }
1084
1375
  let mutation;
1085
1376
  if (subjectId) {
1086
1377
  const current = await createNativeResourceClient(subjectDefinition.code, subjectDefinition.surface).get(subjectId);
@@ -1114,6 +1405,7 @@ export function WorkflowSubmissionPage({ workflowCode: declaredWorkflowCode, var
1114
1405
  message.error(errorMessage(error, '业务与流程命令提交失败'));
1115
1406
  }
1116
1407
  finally {
1408
+ submitInFlight.current = false;
1117
1409
  setLoading(false);
1118
1410
  }
1119
1411
  };
@@ -1153,9 +1445,53 @@ export function WorkflowSubmissionPage({ workflowCode: declaredWorkflowCode, var
1153
1445
  setLoading(false);
1154
1446
  }
1155
1447
  };
1448
+ const selectMode = (nextMode) => {
1449
+ if (!generatedNamedSubmission?.[nextMode])
1450
+ return;
1451
+ setSubmissionMode(nextMode);
1452
+ setCompletion(null);
1453
+ subjectForm.resetFields();
1454
+ const next = new URLSearchParams(searchParams);
1455
+ next.delete('processCommandId');
1456
+ if (nextMode === 'create')
1457
+ next.delete('subjectId');
1458
+ setSearchParams(next, { replace: true });
1459
+ };
1460
+ const selectSubject = (nextSubjectId) => {
1461
+ setCompletion(null);
1462
+ subjectForm.resetFields();
1463
+ const next = new URLSearchParams(searchParams);
1464
+ next.delete('processCommandId');
1465
+ if (nextSubjectId)
1466
+ next.set('subjectId', nextSubjectId);
1467
+ else
1468
+ next.delete('subjectId');
1469
+ setSearchParams(next, { replace: true });
1470
+ };
1471
+ const launchControls = generatedNamedSubmission ? (_jsxs(Space, { direction: "vertical", size: "middle", style: { width: '100%' }, children: [generatedNamedSubmission.create && generatedNamedSubmission.existing ? (_jsx(Segmented, { onChange: value => selectMode(value), options: [
1472
+ { label: '新建并提交', value: 'create' },
1473
+ { label: '选择已有记录', value: 'existing' },
1474
+ ], value: submissionMode })) : null, submissionMode === 'existing' && subjectDefinition ? (_jsx(SubjectRecordSelector, { definition: subjectDefinition, onChange: selectSubject, value: subjectId })) : null] })) : null;
1475
+ const fieldRenderers = namedIntent
1476
+ ? {
1477
+ upload: async (field, file) => await uploadOperationManagedFile({
1478
+ operationCode: namedIntent.operationCode,
1479
+ resourceCode: subjectDefinition.code,
1480
+ fieldCode: field.key,
1481
+ intent: submissionMode === 'existing' ? 'update' : 'create',
1482
+ file,
1483
+ ...(submissionMode === 'existing' && subjectId
1484
+ ? { recordId: subjectId }
1485
+ : {}),
1486
+ }),
1487
+ }
1488
+ : undefined;
1156
1489
  const shared = {
1157
1490
  title: launchSurface?.title || definition.title,
1158
- description: definition.description || '业务数据和流程发起意图将原子提交',
1491
+ description: definition.description ||
1492
+ (namedIntent
1493
+ ? '业务操作将在服务端完成校验、幂等提交,并按规则决定是否进入审批'
1494
+ : '业务数据和流程发起意图将原子提交'),
1159
1495
  fields,
1160
1496
  subjectDefinition,
1161
1497
  subjectForm,
@@ -1164,6 +1500,10 @@ export function WorkflowSubmissionPage({ workflowCode: declaredWorkflowCode, var
1164
1500
  processError,
1165
1501
  loading,
1166
1502
  mutationMode,
1503
+ launchControls,
1504
+ completion,
1505
+ fieldRenderers,
1506
+ recordId: mutationMode === 'update' ? subjectId : undefined,
1167
1507
  onBack: () => navigate(-1),
1168
1508
  onSubmit: submit,
1169
1509
  onAnswer: answer,
@@ -1180,15 +1520,21 @@ function ProcessCommandPanel({ form, surface, error, loading, onAnswer, onRetry,
1180
1520
  return (_jsxs(Card, { title: `提交状态 · ${command.status}`, children: [command.lastError ? (_jsx(Alert, { description: command.lastError.preview, message: command.lastError.code, showIcon: true, type: "error" })) : null, command.status === 'awaiting_input' ? (_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, surface.resume.retry ? (_jsx(Button, { loading: loading, onClick: () => void onRetry(), children: "\u91CD\u8BD5\u539F\u547D\u4EE4" })) : null, ['cancelled', 'dead_letter'].includes(command.status) ? (_jsx(Result, { status: "error", title: "\u6D41\u7A0B\u672A\u53D1\u8D77", subTitle: "\u4E1A\u52A1\u6570\u636E\u5DF2\u4FDD\u7559\uFF0C\u8BF7\u6309\u547D\u4EE4\u72B6\u6001\u5904\u7406\u3002" })) : null, !['awaiting_input', 'cancelled', 'dead_letter'].includes(command.status) ? (_jsx(Typography.Text, { type: "secondary", children: "\u547D\u4EE4\u53EF\u5B89\u5168\u6062\u590D\uFF0C\u9875\u9762\u4F1A\u6309 commandId \u81EA\u52A8\u5237\u65B0\u3002" })) : null] }));
1181
1521
  }
1182
1522
  function DesktopProcessSubmissionRenderer(props) {
1523
+ if (props.completion) {
1524
+ 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" }) }));
1525
+ }
1183
1526
  if (props.processSurface || props.processError) {
1184
1527
  return (_jsx("div", { className: "oxa-workflow-submission oxa-workflow-submission-desktop", children: _jsx(ProcessCommandPanel, { error: props.processError, form: props.requirementForm, loading: props.loading, onAnswer: props.onAnswer, onRetry: props.onRetry, surface: props.processSurface }) }));
1185
1528
  }
1186
- return (_jsx("div", { className: "oxa-workflow-submission oxa-workflow-submission-desktop", children: _jsxs(Card, { children: [_jsx(Typography.Title, { level: 2, children: props.title }), _jsx(Typography.Text, { type: "secondary", children: props.description }), _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: false, field: field, operation: props.mutationMode === 'update' ? 'update' : 'create', resourceCode: props.subjectDefinition.code }, field.key))) }), _jsxs(Space, { children: [_jsx(Button, { icon: _jsx(ArrowLeftOutlined, {}), onClick: props.onBack, children: "\u8FD4\u56DE" }), _jsx(Button, { htmlType: "submit", loading: props.loading, type: "primary", children: "\u63D0\u4EA4\u5BA1\u6279" })] })] })] }) }));
1529
+ return (_jsx("div", { className: "oxa-workflow-submission oxa-workflow-submission-desktop", children: _jsxs(Card, { children: [_jsx(Typography.Title, { level: 2, children: props.title }), _jsx(Typography.Text, { type: "secondary", children: props.description }), 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: false, field: field, operation: props.mutationMode === 'update' ? 'update' : 'create', recordId: props.recordId, renderers: props.fieldRenderers, resourceCode: props.subjectDefinition.code }, field.key))) }), _jsxs(Space, { children: [_jsx(Button, { icon: _jsx(ArrowLeftOutlined, {}), onClick: props.onBack, children: "\u8FD4\u56DE" }), _jsx(Button, { htmlType: "submit", loading: props.loading, type: "primary", children: "\u63D0\u4EA4\u5BA1\u6279" })] })] })] }) }));
1187
1530
  }
1188
1531
  function MobileProcessSubmissionRenderer(props) {
1532
+ if (props.completion) {
1533
+ 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" }) }));
1534
+ }
1189
1535
  if (props.processSurface || props.processError) {
1190
1536
  return (_jsx("div", { className: "oxa-mobile-page", children: _jsx(ProcessCommandPanel, { error: props.processError, form: props.requirementForm, loading: props.loading, onAnswer: props.onAnswer, onRetry: props.onRetry, surface: props.processSurface }) }));
1191
1537
  }
1192
- return (_jsxs("div", { className: "oxa-mobile-page oxa-workflow-submission-mobile", children: [_jsx(Typography.Title, { level: 3, children: props.title }), _jsx(Typography.Text, { type: "secondary", children: props.description }), _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: false, field: field, operation: props.mutationMode === 'update' ? 'update' : 'create', resourceCode: props.subjectDefinition.code }, field.key))), _jsx(Button, { block: true, htmlType: "submit", loading: props.loading, type: "primary", children: "\u63D0\u4EA4\u5BA1\u6279" })] })] }));
1538
+ return (_jsxs("div", { className: "oxa-mobile-page oxa-workflow-submission-mobile", children: [_jsx(Typography.Title, { level: 3, children: props.title }), _jsx(Typography.Text, { type: "secondary", children: props.description }), 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: false, field: field, operation: props.mutationMode === 'update' ? 'update' : 'create', recordId: props.recordId, renderers: props.fieldRenderers, resourceCode: props.subjectDefinition.code }, field.key))), _jsx(Button, { block: true, htmlType: "submit", loading: props.loading, type: "primary", children: "\u63D0\u4EA4\u5BA1\u6279" })] })] }));
1193
1539
  }
1194
1540
  //# sourceMappingURL=StandardWorkflowPages.js.map