@chenchaolong/plugin-trade-compliance-workbench 1.0.24 → 1.0.26

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 (76) hide show
  1. package/dist/lib/import-jobs/import-job-callback.processor.d.ts.map +1 -1
  2. package/dist/lib/import-jobs/import-job-callback.processor.js +5 -4
  3. package/dist/lib/import-jobs/import-job-callback.processor.js.map +1 -1
  4. package/dist/lib/remote-components/trade-compliance-workbench/app.css +1 -1
  5. package/dist/lib/remote-components/trade-compliance-workbench/app.js +5 -5
  6. package/dist/lib/remote-ui/action-execution.d.ts +5 -2
  7. package/dist/lib/remote-ui/action-execution.d.ts.map +1 -1
  8. package/dist/lib/remote-ui/action-execution.js +2 -2
  9. package/dist/lib/remote-ui/action-execution.js.map +1 -1
  10. package/dist/lib/remote-ui/app-source.d.ts.map +1 -1
  11. package/dist/lib/remote-ui/app-source.js +134 -35
  12. package/dist/lib/remote-ui/app-source.js.map +1 -1
  13. package/dist/lib/remote-ui/bulk-delete.d.ts +56 -0
  14. package/dist/lib/remote-ui/bulk-delete.d.ts.map +1 -0
  15. package/dist/lib/remote-ui/bulk-delete.js +138 -0
  16. package/dist/lib/remote-ui/bulk-delete.js.map +1 -0
  17. package/dist/lib/remote-ui/components/bulk-delete.d.ts +21 -0
  18. package/dist/lib/remote-ui/components/bulk-delete.d.ts.map +1 -0
  19. package/dist/lib/remote-ui/components/bulk-delete.js +9 -0
  20. package/dist/lib/remote-ui/components/bulk-delete.js.map +1 -0
  21. package/dist/lib/remote-ui/components/data-grid.d.ts +12 -1
  22. package/dist/lib/remote-ui/components/data-grid.d.ts.map +1 -1
  23. package/dist/lib/remote-ui/components/data-grid.js +24 -5
  24. package/dist/lib/remote-ui/components/data-grid.js.map +1 -1
  25. package/dist/lib/remote-ui/components/task-status.d.ts +10 -1
  26. package/dist/lib/remote-ui/components/task-status.d.ts.map +1 -1
  27. package/dist/lib/remote-ui/components/task-status.js +79 -10
  28. package/dist/lib/remote-ui/components/task-status.js.map +1 -1
  29. package/dist/lib/remote-ui/customer-contract-contracts.d.ts +7 -2
  30. package/dist/lib/remote-ui/customer-contract-contracts.d.ts.map +1 -1
  31. package/dist/lib/remote-ui/customer-contract-contracts.js.map +1 -1
  32. package/dist/lib/remote-ui/import-task-status.d.ts +1 -1
  33. package/dist/lib/remote-ui/import-task-status.d.ts.map +1 -1
  34. package/dist/lib/remote-ui/import-task-status.js +2 -2
  35. package/dist/lib/remote-ui/import-task-status.js.map +1 -1
  36. package/dist/lib/remote-ui/model.d.ts +1 -0
  37. package/dist/lib/remote-ui/model.d.ts.map +1 -1
  38. package/dist/lib/remote-ui/model.js +3 -0
  39. package/dist/lib/remote-ui/model.js.map +1 -1
  40. package/dist/lib/remote-ui/pages/catalogs.css +91 -0
  41. package/dist/lib/remote-ui/pages/catalogs.d.ts +49 -3
  42. package/dist/lib/remote-ui/pages/catalogs.d.ts.map +1 -1
  43. package/dist/lib/remote-ui/pages/catalogs.js +123 -12
  44. package/dist/lib/remote-ui/pages/catalogs.js.map +1 -1
  45. package/dist/lib/remote-ui/pages/contracts.css +42 -6
  46. package/dist/lib/remote-ui/pages/contracts.d.ts +61 -3
  47. package/dist/lib/remote-ui/pages/contracts.d.ts.map +1 -1
  48. package/dist/lib/remote-ui/pages/contracts.js +213 -11
  49. package/dist/lib/remote-ui/pages/contracts.js.map +1 -1
  50. package/dist/lib/remote-ui/pages/files.d.ts +52 -10
  51. package/dist/lib/remote-ui/pages/files.d.ts.map +1 -1
  52. package/dist/lib/remote-ui/pages/files.js +122 -12
  53. package/dist/lib/remote-ui/pages/files.js.map +1 -1
  54. package/dist/lib/remote-ui/pages/home.js +4 -4
  55. package/dist/lib/remote-ui/pages/home.js.map +1 -1
  56. package/dist/lib/remote-ui/pages/procurement.d.ts +87 -2
  57. package/dist/lib/remote-ui/pages/procurement.d.ts.map +1 -1
  58. package/dist/lib/remote-ui/pages/procurement.js +225 -21
  59. package/dist/lib/remote-ui/pages/procurement.js.map +1 -1
  60. package/dist/lib/remote-ui/pages/settings.d.ts +39 -6
  61. package/dist/lib/remote-ui/pages/settings.d.ts.map +1 -1
  62. package/dist/lib/remote-ui/pages/settings.js +94 -5
  63. package/dist/lib/remote-ui/pages/settings.js.map +1 -1
  64. package/dist/lib/remote-ui/polling.d.ts +1 -0
  65. package/dist/lib/remote-ui/polling.d.ts.map +1 -1
  66. package/dist/lib/remote-ui/polling.js +6 -1
  67. package/dist/lib/remote-ui/polling.js.map +1 -1
  68. package/dist/lib/remote-ui/procurement-contracts.d.ts +6 -2
  69. package/dist/lib/remote-ui/procurement-contracts.d.ts.map +1 -1
  70. package/dist/lib/remote-ui/procurement-contracts.js +11 -1
  71. package/dist/lib/remote-ui/procurement-contracts.js.map +1 -1
  72. package/dist/lib/remote-ui/styles.css +55 -19
  73. package/dist/lib/workbench.service.d.ts.map +1 -1
  74. package/dist/lib/workbench.service.js +20 -23
  75. package/dist/lib/workbench.service.js.map +1 -1
  76. package/package.json +2 -1
@@ -13,14 +13,17 @@ type ActionNotificationPolicy = 'silent' | {
13
13
  tone: 'success';
14
14
  fallback: string;
15
15
  };
16
+ export type ActionExecutionOptions = {
17
+ suppressRefresh?: boolean;
18
+ };
16
19
  export declare const ACTION_NOTIFICATION_POLICIES: Record<WorkbenchActionKey, ActionNotificationPolicy>;
17
- export declare function executeWorkbenchAction<T>({ actionKey, mutate, refresh, notify, onResult, resolveMessage }: {
20
+ export declare function executeWorkbenchAction<T>({ actionKey, mutate, refresh, notify, onResult, resolveMessage, suppressRefresh }: {
18
21
  actionKey: WorkbenchActionKey;
19
22
  mutate: () => Promise<ActionResult<T>>;
20
23
  refresh: () => void | Promise<void>;
21
24
  notify?: Notify;
22
25
  onResult?: (result: ActionResult<T>) => void;
23
26
  resolveMessage?: (result: ActionResult<T>) => string;
24
- }): Promise<ActionResult<T>>;
27
+ } & ActionExecutionOptions): Promise<ActionResult<T>>;
25
28
  export {};
26
29
  //# sourceMappingURL=action-execution.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"action-execution.d.ts","sourceRoot":"","sources":["../../../src/lib/remote-ui/action-execution.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,kBAAkB,EAAE,MAAM,iCAAiC,CAAA;AAC/F,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAE9C,MAAM,MAAM,YAAY,CAAC,CAAC,GAAG,OAAO,IAAI;IACtC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,IAAI,CAAC,EAAE,CAAC,CAAA;IACR,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,CAAA;AAED,KAAK,wBAAwB,GAAG,QAAQ,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAA;AAEhF,eAAO,MAAM,4BAA4B,EAAE,MAAM,CAAC,kBAAkB,EAAE,wBAAwB,CAsC7F,CAAA;AAiBD,wBAAsB,sBAAsB,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAqC,EAAE,EAAE;IACvI,SAAS,EAAE,kBAAkB,CAAA;IAC7B,MAAM,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAA;IACtC,OAAO,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACnC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;IAC5C,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,MAAM,CAAA;CACrD,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAiB3B"}
1
+ {"version":3,"file":"action-execution.d.ts","sourceRoot":"","sources":["../../../src/lib/remote-ui/action-execution.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,kBAAkB,EAAE,MAAM,iCAAiC,CAAA;AAC/F,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAE9C,MAAM,MAAM,YAAY,CAAC,CAAC,GAAG,OAAO,IAAI;IACtC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,IAAI,CAAC,EAAE,CAAC,CAAA;IACR,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,CAAA;AAED,KAAK,wBAAwB,GAAG,QAAQ,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAA;AAChF,MAAM,MAAM,sBAAsB,GAAG;IAAE,eAAe,CAAC,EAAE,OAAO,CAAA;CAAE,CAAA;AAElE,eAAO,MAAM,4BAA4B,EAAE,MAAM,CAAC,kBAAkB,EAAE,wBAAwB,CAsC7F,CAAA;AAiBD,wBAAsB,sBAAsB,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAqC,EAAE,eAAe,EAAE,EAAE;IACxJ,SAAS,EAAE,kBAAkB,CAAA;IAC7B,MAAM,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAA;IACtC,OAAO,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACnC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;IAC5C,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,MAAM,CAAA;CACrD,GAAG,sBAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAiBpD"}
@@ -52,7 +52,7 @@ const refreshActions = new Set([
52
52
  'update_customer_contract_review',
53
53
  'delete_entity'
54
54
  ]);
55
- export async function executeWorkbenchAction({ actionKey, mutate, refresh, notify, onResult, resolveMessage = defaultActionMessage }) {
55
+ export async function executeWorkbenchAction({ actionKey, mutate, refresh, notify, onResult, resolveMessage = defaultActionMessage, suppressRefresh }) {
56
56
  try {
57
57
  if (!isWorkbenchActionKey(actionKey))
58
58
  throw new Error(`未配置操作通知策略:${String(actionKey)}`);
@@ -61,7 +61,7 @@ export async function executeWorkbenchAction({ actionKey, mutate, refresh, notif
61
61
  if (result.success === false)
62
62
  throw new Error(resolveMessage(result) || '操作失败');
63
63
  onResult?.(result);
64
- if (result.refresh && refreshActions.has(actionKey)) {
64
+ if (result.refresh && refreshActions.has(actionKey) && !suppressRefresh) {
65
65
  try {
66
66
  await refresh();
67
67
  }
@@ -1 +1 @@
1
- {"version":3,"file":"action-execution.js","sourceRoot":"","sources":["../../../src/lib/remote-ui/action-execution.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"action-execution.js","sourceRoot":"","sources":["../../../src/lib/remote-ui/action-execution.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"app-source.d.ts","sourceRoot":"","sources":["../../../src/lib/remote-ui/app-source.tsx"],"names":[],"mappings":"AA8BA,OAAO,cAAc,CAAA"}
1
+ {"version":3,"file":"app-source.d.ts","sourceRoot":"","sources":["../../../src/lib/remote-ui/app-source.tsx"],"names":[],"mappings":"AA+BA,OAAO,cAAc,CAAA"}
@@ -1,9 +1,9 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from 'react';
3
3
  import { createRoot } from 'react-dom/client';
4
4
  import { createRemoteBridge } from './bridge.js';
5
5
  import { resolvePaginationPageSize } from './contracts.js';
6
- import { clampPageAfterSingleDeletion, createBlobUrlOwner, createKeyedLatestRequestGate, createLatestCommittedRequest, createLatestRequestGate, generatedFileHistoryKey, normalizeWorkbenchData, routeDataRequest, runKeyedLatestRequest, unwrapResponse } from './model.js';
6
+ import { clampPageAfterSingleDeletion, createBlobUrlOwner, createKeyedLatestRequestGate, createLatestCommittedRequest, createLatestRequestGate, generatedFileHistoryKey, isCurrentSettingsRouteRevision, normalizeWorkbenchData, routeDataRequest, runKeyedLatestRequest, unwrapResponse } from './model.js';
7
7
  import { encodeViewFilters } from '../view-parameters.js';
8
8
  import { InfoNotice } from './components/data-grid.js';
9
9
  import { LoadingBlocker, ToastStack } from './components/forms.js';
@@ -13,8 +13,9 @@ import { buildBreadcrumbs, WorkbenchShell } from './components/layout.js';
13
13
  import { CatalogCommands, CatalogImportError, ControlledGoodsPage, findActiveCatalogImportTasks, findLatestFailedCatalogImportTask, runCatalogImportOrchestration, SanctionedCompaniesPage } from './pages/catalogs.js';
14
14
  import { HomePage } from './pages/home.js';
15
15
  import { ProcurementPage } from './pages/procurement.js';
16
+ import { procurementRouteForQuery } from './procurement-contracts.js';
16
17
  import { adaptPurchaseReview } from './purchase-review-adapter.js';
17
- import { CustomerContractsPage, adaptCustomerContractReview } from './pages/contracts.js';
18
+ import { CustomerContractsPage, adaptCustomerContractReview, createCustomerContractQueryCancelCoordinator, customerContractRouteEntryQuery, reconcileLatestCustomerContractQuery } from './pages/contracts.js';
18
19
  import { SalesPage } from './pages/sales.js';
19
20
  import { SalesFilesPage } from './pages/files.js';
20
21
  import { AnalyticsPage, AnalyticsPrototypeBoard } from './pages/analytics.js';
@@ -23,6 +24,7 @@ import { ContractImportError, findRecoverableContractImport, findResumableContra
23
24
  import { createTaskChatSynchronizer, importTaskFromStart, importTaskProgressPercent, isLocalUploadPlaceholder, mergeImportTaskItems, removeImportTaskItem } from './import-task-status.js';
24
25
  import { compareTaskBusinessOrder, createPendingResultQueue, createPollingTaskTracker, isAbortError, isPollingObservationTimeout, submitAndRefreshTaskState } from './polling.js';
25
26
  import { ACTION_NOTIFICATION_POLICIES, executeWorkbenchAction } from './action-execution.js';
27
+ import { createDeferredBulkReconcileCoordinator, runBulkDelete } from './bulk-delete.js';
26
28
  import './styles.css';
27
29
  const pageMeta = {
28
30
  home: { title: '首页', subtitle: '查看系统风险操作提示与外贸合规业务流程。' },
@@ -60,6 +62,7 @@ let excelEditorRuntimePromise = null;
60
62
  function App() {
61
63
  const [route, setRoute] = React.useState({ type: 'page', page: 'home' });
62
64
  const routeRef = React.useRef(route);
65
+ const routeRevisionRef = React.useRef(0);
63
66
  const contextRef = React.useRef(null);
64
67
  const requestGateRef = React.useRef(createLatestRequestGate());
65
68
  const taskStateRequestGateRef = React.useRef(createLatestRequestGate());
@@ -68,7 +71,15 @@ function App() {
68
71
  const settingsRatesRequestGateRef = React.useRef(createLatestRequestGate());
69
72
  const generatedFilesRequestGateRef = React.useRef(createKeyedLatestRequestGate());
70
73
  const activeGeneratedFilesKeyRef = React.useRef(null);
74
+ const latestGeneratedFilesQueryRef = React.useRef({ page: 1, pageSize: 20, filters: {} });
75
+ const generatedFilesBulkViewActiveRef = React.useRef(false);
76
+ const generatedFilesBulkReconcileRef = React.useRef(createDeferredBulkReconcileCoordinator());
77
+ const latestCustomerContractQueryRef = React.useRef({ page: 1, pageSize: 20, filters: {} });
78
+ const customerContractQueryCancelCoordinatorRef = React.useRef(createCustomerContractQueryCancelCoordinator());
71
79
  const settingsRatesQueryRef = React.useRef({ page: 1, pageSize: 20 });
80
+ const latestSettingsRatesQueryRef = React.useRef({ page: 1, pageSize: 20 });
81
+ const settingsRatesBulkViewActiveRef = React.useRef(false);
82
+ const settingsRatesBulkReconcileRef = React.useRef(createDeferredBulkReconcileCoordinator());
72
83
  const analyticsRequestsRef = React.useRef(createLatestCommittedRequest(initialAnalyticsQuery));
73
84
  const [analyticsCommittedQuery, setAnalyticsCommittedQuery] = React.useState(initialAnalyticsQuery);
74
85
  const analyticsPrototypeListenerCleanupRef = React.useRef(null);
@@ -77,8 +88,14 @@ function App() {
77
88
  const [salesFilesPrototypeState, setSalesFilesPrototypeState] = React.useState(undefined);
78
89
  const [data, setData] = React.useState(() => normalizeWorkbenchData(null));
79
90
  const importTaskItemsRef = React.useRef([]);
91
+ const deletedImportTaskIdsRef = React.useRef(new Set());
80
92
  const [busy, setBusy] = React.useState(false);
81
93
  const [actionBusy, setActionBusy] = React.useState(false);
94
+ const [customerContractBulkBusy, setCustomerContractBulkBusy] = React.useState(false);
95
+ const [salesFileBulkBusy, setSalesFileBulkBusy] = React.useState(false);
96
+ const [settingsRateBulkBusy, setSettingsRateBulkBusy] = React.useState(false);
97
+ const [salesFileBulkReconcileBusy, setSalesFileBulkReconcileBusy] = React.useState(false);
98
+ const [settingsRateBulkReconcileBusy, setSettingsRateBulkReconcileBusy] = React.useState(false);
82
99
  const [catalogCommand, setCatalogCommand] = React.useState(null);
83
100
  const [importCompletions, setImportCompletions] = React.useState({ CONTROLLED_CATALOG: null, SANCTION_CATALOG: null });
84
101
  const [importFailures, setImportFailures] = React.useState({ CONTROLLED_CATALOG: null, SANCTION_CATALOG: null });
@@ -135,7 +152,7 @@ function App() {
135
152
  }, [setToast]);
136
153
  const upsertLocalImportTask = React.useCallback((task) => {
137
154
  setData(current => {
138
- const items = mergeImportTaskItems(current.importTasks.items, [task]);
155
+ const items = mergeImportTaskItems(current.importTasks.items, [task], deletedImportTaskIdsRef.current);
139
156
  return {
140
157
  ...current,
141
158
  importTasks: {
@@ -207,7 +224,7 @@ function App() {
207
224
  const requestedPageSize = resolvePaginationPageSize(pageSize);
208
225
  const request = routeDataRequest(targetRoute, page, requestedPageSize, filters);
209
226
  if (!request)
210
- return;
227
+ return false;
211
228
  const collection = String(asRecord(request.parameters).collection);
212
229
  const requests = collection === 'analytics' ? analyticsRequestsRef.current : routeRequestGateRef.current;
213
230
  const analyticsCandidate = collection === 'analytics' ? analyticsQueryFromRoute(page, requestedPageSize, filters) : null;
@@ -216,16 +233,18 @@ function App() {
216
233
  try {
217
234
  const payload = asRecord(unwrapResponse(await bridge.requestData(request)));
218
235
  if (!requests.commit(requestId))
219
- return;
236
+ return false;
220
237
  if (routeRef.current !== targetRoute && JSON.stringify(routeRef.current) !== JSON.stringify(targetRoute))
221
- return;
238
+ return false;
222
239
  if (collection === 'analytics') {
223
240
  setAnalyticsCommittedQuery(analyticsRequestsRef.current.committed());
224
241
  setData(current => ({ ...current, analytics: normalizeWorkbenchData({ summary: { analytics: payload } }).analytics }));
225
242
  }
226
243
  else if (collection === 'settings') {
227
244
  const settings = normalizeWorkbenchData({ summary: { settings: payload } }).settings;
228
- settingsRatesQueryRef.current = { page: settings.rates.page, pageSize: settings.rates.pageSize };
245
+ const committedRatesQuery = { page: settings.rates.page, pageSize: settings.rates.pageSize };
246
+ settingsRatesQueryRef.current = committedRatesQuery;
247
+ latestSettingsRatesQueryRef.current = committedRatesQuery;
229
248
  setData(current => ({ ...current, settings }));
230
249
  }
231
250
  else
@@ -243,6 +262,7 @@ function App() {
243
262
  order: payload.order && typeof payload.order === 'object' ? payload.order : null,
244
263
  metrics: payload.metrics && typeof payload.metrics === 'object' ? payload.metrics : targetRoute.level === 'orders' ? null : current.metrics
245
264
  }));
265
+ return true;
246
266
  }
247
267
  catch (error) {
248
268
  if (requests.isLatest(requestId)) {
@@ -250,6 +270,7 @@ function App() {
250
270
  setAnalyticsCommittedQuery({ ...analyticsRequestsRef.current.committed() });
251
271
  setToast({ message: error instanceof Error ? `页面加载失败:${error.message}` : '页面加载失败', tone: 'error' });
252
272
  }
273
+ return false;
253
274
  }
254
275
  finally {
255
276
  requests.finish(requestId);
@@ -269,7 +290,7 @@ function App() {
269
290
  const normalized = normalizeWorkbenchData(response);
270
291
  const importTasks = await loadImportTasksFallback(normalized);
271
292
  bridge.configure({ uploadPolicy: normalized.uploadPolicy });
272
- setData(current => mergeWorkbenchDataWithLocalTasks(current, { ...normalized, importTasks }));
293
+ setData(current => mergeWorkbenchDataWithLocalTasks(current, { ...normalized, importTasks }, deletedImportTaskIdsRef.current));
273
294
  await loadRoute(routeRef.current);
274
295
  }
275
296
  }
@@ -302,7 +323,7 @@ function App() {
302
323
  sanctionBatches: normalized.sanctionBatches,
303
324
  importTasks: {
304
325
  ...importTasks,
305
- items: mergeImportTaskItems(current.importTasks.items, importTasks.items),
326
+ items: mergeImportTaskItems(current.importTasks.items, importTasks.items, deletedImportTaskIdsRef.current),
306
327
  total: Math.max(numberValue(importTasks.total, 0), importTasks.items.length, current.importTasks.items.length)
307
328
  },
308
329
  customerContractAnalyses: normalized.customerContractAnalyses
@@ -362,7 +383,7 @@ function App() {
362
383
  contextRef.current = nextContext;
363
384
  const normalized = normalizeWorkbenchData(nextContext.payload);
364
385
  bridge.configure({ uploadPolicy: normalized.uploadPolicy });
365
- setData(current => mergeWorkbenchDataWithLocalTasks(current, normalized));
386
+ setData(current => mergeWorkbenchDataWithLocalTasks(current, normalized, deletedImportTaskIdsRef.current));
366
387
  void load(nextContext);
367
388
  },
368
389
  onHostEvent() {
@@ -386,7 +407,7 @@ function App() {
386
407
  React.useEffect(() => {
387
408
  toastTimers.sync(toasts);
388
409
  }, [toastTimers, toasts]);
389
- const executeAction = React.useCallback(async (actionKey, input) => {
410
+ const executeAction = React.useCallback(async (actionKey, input, options = {}) => {
390
411
  const blocksPage = ACTION_NOTIFICATION_POLICIES[actionKey] !== 'silent';
391
412
  if (blocksPage)
392
413
  setActionBusy(true);
@@ -398,7 +419,8 @@ function App() {
398
419
  notify: setToast,
399
420
  resolveMessage: resolveActionMessage,
400
421
  onResult: result => { if (actionKey === 'download_import_issues' || actionKey === 'download_sales_file')
401
- downloadActionFile(result.data); }
422
+ downloadActionFile(result.data); },
423
+ ...options
402
424
  });
403
425
  }
404
426
  finally {
@@ -406,7 +428,7 @@ function App() {
406
428
  setActionBusy(false);
407
429
  }
408
430
  }, [load]);
409
- const executeSalesFileAction = React.useCallback((actionKey, input) => executeAction(actionKey, input), [executeAction]);
431
+ const executeSalesFileAction = React.useCallback((actionKey, input, options = {}) => executeAction(actionKey, input, options), [executeAction]);
410
432
  const schedulePendingReviewPresentation = React.useCallback(() => {
411
433
  if (reviewPresentationTimerRef.current != null)
412
434
  return;
@@ -658,7 +680,7 @@ function App() {
658
680
  throw error;
659
681
  }
660
682
  }, [loadTaskState, notifyTaskRecoveryRefreshError]);
661
- const deleteImportTask = React.useCallback(async (task) => {
683
+ const deleteImportTaskRecord = React.useCallback(async (task, options) => {
662
684
  const result = asActionResult(unwrapResponse(await bridge.executeAction('delete_import_task', { taskId: task.id })));
663
685
  if (result.success === false)
664
686
  throw actionResponseError(result, '任务删除失败');
@@ -675,8 +697,23 @@ function App() {
675
697
  clientMessageId: `trade-task:${task.id}:deleted`
676
698
  }).catch(() => undefined);
677
699
  }
678
- await loadTaskState();
700
+ if (options.refresh)
701
+ await loadTaskState();
679
702
  }, [loadTaskState, removeLocalImportTask]);
703
+ const deleteImportTask = React.useCallback(async (task) => {
704
+ await deleteImportTaskRecord(task, { refresh: true });
705
+ }, [deleteImportTaskRecord]);
706
+ const bulkDeleteImportTasks = React.useCallback(async (items) => {
707
+ const tasksById = new Map(data.importTasks.items.map(task => [task.id, task]));
708
+ const result = await runBulkDelete(items.map(item => item.id), async (id) => {
709
+ const task = tasksById.get(id);
710
+ if (!task)
711
+ throw new Error('任务已不存在');
712
+ await deleteImportTaskRecord(task, { refresh: false });
713
+ });
714
+ await loadTaskState();
715
+ return result.failed.map(failure => ({ id: failure.key, message: failure.message }));
716
+ }, [data.importTasks.items, deleteImportTaskRecord, loadTaskState]);
680
717
  const openImportTaskFromStatus = React.useCallback(async (task) => {
681
718
  if (task.status === 'FAILED' || task.status === 'TIMED_OUT') {
682
719
  if (task.documentType === 'PURCHASE_CONTRACT' || task.documentType === 'CUSTOMER_CONTRACT')
@@ -905,13 +942,13 @@ function App() {
905
942
  const maxPage = Math.max(1, Math.ceil(total / requestedPageSize));
906
943
  if (query.page > maxPage) {
907
944
  if (!gate.isLatest(requestId))
908
- return;
945
+ return false;
909
946
  committedPage = maxPage;
910
947
  payload = await requestCatalogPage(maxPage);
911
948
  total = numberValue(payload.total, 0);
912
949
  }
913
950
  if (!gate.isLatest(requestId))
914
- return;
951
+ return false;
915
952
  setData((current) => ({
916
953
  ...current,
917
954
  [collection]: {
@@ -922,10 +959,12 @@ function App() {
922
959
  pageSize: requestedPageSize
923
960
  }
924
961
  }));
962
+ return true;
925
963
  }
926
964
  catch (error) {
927
965
  if (gate.isLatest(requestId))
928
966
  setToast({ message: error instanceof Error ? `查询失败:${error.message}` : '查询失败', tone: 'error' });
967
+ return false;
929
968
  }
930
969
  finally {
931
970
  gate.finish(requestId);
@@ -933,44 +972,69 @@ function App() {
933
972
  }
934
973
  }, []);
935
974
  const queryMonthlyRates = React.useCallback(async (query) => {
975
+ latestSettingsRatesQueryRef.current = query;
936
976
  const requestedPageSize = resolvePaginationPageSize(query.pageSize);
937
977
  const gate = settingsRatesRequestGateRef.current;
978
+ const requestRouteRevision = routeRevisionRef.current;
938
979
  const requestId = gate.begin();
939
980
  setBusy(true);
940
981
  try {
941
982
  const payload = asRecord(unwrapResponse(await bridge.requestData({ page: query.page, pageSize: requestedPageSize, parameters: { collection: 'monthlyRates' } })));
942
983
  if (!gate.commit(requestId))
943
- return;
944
- settingsRatesQueryRef.current = { page: numberValue(payload.page, query.page), pageSize: requestedPageSize };
984
+ return false;
985
+ if (!isCurrentSettingsRouteRevision(requestRouteRevision, routeRevisionRef.current, routeRef.current))
986
+ return false;
987
+ const committedRatesQuery = { page: numberValue(payload.page, query.page), pageSize: requestedPageSize };
988
+ settingsRatesQueryRef.current = committedRatesQuery;
989
+ latestSettingsRatesQueryRef.current = committedRatesQuery;
945
990
  setData(current => ({ ...current, settings: normalizeWorkbenchData({ summary: { settings: { ...current.settings, rates: { ...payload, pageSize: requestedPageSize } } } }).settings }));
991
+ return true;
946
992
  }
947
993
  catch (error) {
948
994
  if (gate.isLatest(requestId))
949
995
  setToast({ message: error instanceof Error ? `月度汇率加载失败:${error.message}` : '月度汇率加载失败', tone: 'error' });
996
+ return false;
950
997
  }
951
998
  finally {
952
999
  gate.finish(requestId);
953
1000
  setBusy(gate.activeCount() + requestGateRef.current.activeCount() + routeRequestGateRef.current.activeCount() > 0);
954
1001
  }
955
1002
  }, []);
956
- const queryProcurement = React.useCallback(async (_collection, query) => {
957
- await loadRoute(routeRef.current, query.page, query.pageSize, query.filters);
958
- }, [loadImportTasksFallback, loadRoute]);
1003
+ const queryProcurement = React.useCallback(async (collection, query) => {
1004
+ const targetRoute = procurementRouteForQuery(collection, query);
1005
+ if (!targetRoute)
1006
+ return false;
1007
+ return await loadRoute(targetRoute, query.page, query.pageSize, query.filters);
1008
+ }, [loadRoute]);
959
1009
  const queryCustomerContracts = React.useCallback(async (query) => {
960
- await loadRoute(routeRef.current, query.page, query.pageSize, query.filters);
1010
+ latestCustomerContractQueryRef.current = query;
1011
+ return await loadRoute({ type: 'page', page: 'customer-contract-review' }, query.page, query.pageSize, query.filters);
961
1012
  }, [loadRoute]);
1013
+ const rememberCustomerContractQuery = React.useCallback((query) => {
1014
+ latestCustomerContractQueryRef.current = query;
1015
+ }, []);
1016
+ const registerCustomerContractQueryCancel = React.useCallback((cancel) => customerContractQueryCancelCoordinatorRef.current.register(cancel), []);
1017
+ const reconcileCustomerContractsAfterBulk = React.useCallback(() => {
1018
+ const isActive = routeRef.current.type === 'page' && routeRef.current.page === 'customer-contract-review';
1019
+ if (isActive)
1020
+ customerContractQueryCancelCoordinatorRef.current.cancelCurrent();
1021
+ return reconcileLatestCustomerContractQuery({
1022
+ isActive: () => isActive,
1023
+ latestQuery: () => latestCustomerContractQueryRef.current,
1024
+ query: queryCustomerContracts
1025
+ });
1026
+ }, [queryCustomerContracts]);
962
1027
  const querySales = React.useCallback(async (_collection, query) => {
963
1028
  await loadRoute(routeRef.current, query.page, query.pageSize, query.filters);
964
1029
  }, [loadRoute]);
965
1030
  const querySalesFiles = React.useCallback(async (collection, query) => {
966
1031
  const requestedPageSize = resolvePaginationPageSize(query.pageSize);
967
- if (collection === 'salesFileOrders') {
968
- await loadRoute({ type: 'page', page: 'sales-files' }, query.page, requestedPageSize, query.filters);
969
- return;
970
- }
1032
+ if (collection === 'salesFileOrders')
1033
+ return await loadRoute({ type: 'page', page: 'sales-files' }, query.page, requestedPageSize, query.filters);
1034
+ latestGeneratedFilesQueryRef.current = { ...query, pageSize: requestedPageSize };
971
1035
  const key = generatedFileHistoryKey(query.parameters);
972
1036
  activeGeneratedFilesKeyRef.current = key;
973
- await runKeyedLatestRequest({
1037
+ return await runKeyedLatestRequest({
974
1038
  gate: generatedFilesRequestGateRef.current,
975
1039
  key,
976
1040
  request: async () => asRecord(unwrapResponse(await bridge.requestData({ page: query.page, pageSize: requestedPageSize, parameters: { collection, ...query.parameters, filters: encodeViewFilters(query.filters) } }))),
@@ -981,6 +1045,30 @@ function App() {
981
1045
  onFinish: () => setBusy(generatedFilesRequestGateRef.current.activeCount() + requestGateRef.current.activeCount() + routeRequestGateRef.current.activeCount() + analyticsRequestsRef.current.activeCount() > 0)
982
1046
  });
983
1047
  }, [loadRoute]);
1048
+ const generatedFilesBulkReconcileOptions = React.useCallback(() => ({
1049
+ isActive: () => routeRef.current.type === 'page' && routeRef.current.page === 'sales-files' && generatedFilesBulkViewActiveRef.current,
1050
+ latestQuery: () => latestGeneratedFilesQueryRef.current,
1051
+ query: (query) => querySalesFiles('generatedFiles', query),
1052
+ onBusyChange: setSalesFileBulkReconcileBusy
1053
+ }), [querySalesFiles]);
1054
+ const settingsRatesBulkReconcileOptions = React.useCallback(() => ({
1055
+ isActive: () => routeRef.current.type === 'page' && routeRef.current.page === 'settings' && settingsRatesBulkViewActiveRef.current,
1056
+ latestQuery: () => latestSettingsRatesQueryRef.current,
1057
+ query: queryMonthlyRates,
1058
+ onBusyChange: setSettingsRateBulkReconcileBusy
1059
+ }), [queryMonthlyRates]);
1060
+ const setGeneratedFilesBulkViewActive = React.useCallback((active) => {
1061
+ generatedFilesBulkViewActiveRef.current = active;
1062
+ if (active)
1063
+ void generatedFilesBulkReconcileRef.current.activate(generatedFilesBulkReconcileOptions());
1064
+ }, [generatedFilesBulkReconcileOptions]);
1065
+ const setSettingsRatesBulkViewActive = React.useCallback((active) => {
1066
+ settingsRatesBulkViewActiveRef.current = active;
1067
+ if (active)
1068
+ void settingsRatesBulkReconcileRef.current.activate(settingsRatesBulkReconcileOptions());
1069
+ }, [settingsRatesBulkReconcileOptions]);
1070
+ const reconcileGeneratedFilesAfterBulk = React.useCallback(() => generatedFilesBulkReconcileRef.current.reconcile(generatedFilesBulkReconcileOptions()), [generatedFilesBulkReconcileOptions]);
1071
+ const reconcileSettingsRatesAfterBulk = React.useCallback(() => settingsRatesBulkReconcileRef.current.reconcile(settingsRatesBulkReconcileOptions()), [settingsRatesBulkReconcileOptions]);
984
1072
  const queryAnalytics = React.useCallback(async (query) => {
985
1073
  const requestedQuery = { ...query, pageSize: resolvePaginationPageSize(query.pageSize) };
986
1074
  const requests = analyticsRequestsRef.current;
@@ -1040,10 +1128,12 @@ function App() {
1040
1128
  throw new Error(resolveActionMessage(result) || '恢复系统默认失败');
1041
1129
  await loadRoute({ type: 'page', page: 'settings' }, settingsRatesQueryRef.current.page, settingsRatesQueryRef.current.pageSize);
1042
1130
  }, [loadRoute]);
1043
- const deleteSettingsRate = React.useCallback(async (request) => {
1131
+ const deleteSettingsRate = React.useCallback(async (request, options = {}) => {
1044
1132
  const result = asActionResult(unwrapResponse(await bridge.executeAction('delete_settings_rate', request)));
1045
1133
  if (result.success === false)
1046
1134
  throw new Error(resolveActionMessage(result) || '月度汇率删除失败');
1135
+ if (options.suppressRefresh)
1136
+ return;
1047
1137
  const { page, pageSize } = settingsRatesQueryRef.current;
1048
1138
  const clampedPage = clampPageAfterSingleDeletion(page, pageSize, data.settings.rates.total);
1049
1139
  await loadRoute({ type: 'page', page: 'settings' }, clampedPage, pageSize);
@@ -1079,6 +1169,11 @@ function App() {
1079
1169
  setActiveSalesFileOrder(order);
1080
1170
  }, []);
1081
1171
  const changeRoute = React.useCallback((nextRoute) => {
1172
+ routeRevisionRef.current += 1;
1173
+ settingsRatesRequestGateRef.current.invalidate();
1174
+ latestCustomerContractQueryRef.current = customerContractRouteEntryQuery(nextRoute) ?? latestCustomerContractQueryRef.current;
1175
+ if (nextRoute.type === 'page' && nextRoute.page === 'settings')
1176
+ latestSettingsRatesQueryRef.current = { page: 1, pageSize: 20 };
1082
1177
  routeRef.current = nextRoute;
1083
1178
  setRoute(nextRoute);
1084
1179
  void loadRoute(nextRoute);
@@ -1092,11 +1187,11 @@ function App() {
1092
1187
  ? _jsx(ContractUploadCommand, { label: "\u4E0A\u4F20\u91C7\u8D2D\u5408\u540C", busy: busy || actionBusy, onFile: file => executeFileAction('upload_purchase_contract', file, { documentType: 'PURCHASE_CONTRACT' }), onError: message => setToast({ message, tone: 'error' }) })
1093
1188
  : undefined
1094
1189
  : page === 'customer-contract-review'
1095
- ? _jsx(ContractUploadCommand, { label: "\u4E0A\u4F20\u5BA2\u6237\u5408\u540C", busy: busy || actionBusy, onFile: file => executeFileAction('upload_customer_contract', file, { documentType: 'CUSTOMER_CONTRACT' }), onError: message => setToast({ message, tone: 'error' }) })
1190
+ ? _jsx(ContractUploadCommand, { label: "\u4E0A\u4F20\u5BA2\u6237\u5408\u540C", busy: busy || actionBusy || customerContractBulkBusy, onFile: file => executeFileAction('upload_customer_contract', file, { documentType: 'CUSTOMER_CONTRACT' }), onError: message => setToast({ message, tone: 'error' }) })
1096
1191
  : undefined;
1097
1192
  if (analyticsPrototypeVisible)
1098
- return (_jsx(WorkbenchShell, { route: route, breadcrumbs: buildBreadcrumbs(route), title: meta.title, topbarActions: _jsx(TaskStatusMenu, { items: taskStatusItems, busy: busy || actionBusy, onActionError: notifyTaskStatusActionError }), onRouteChange: changeRoute, onRefresh: () => void load(), children: _jsx(AnalyticsPrototypeBoard, { onClose: () => setAnalyticsPrototypeVisible(false) }) }));
1099
- return (_jsxs(_Fragment, { children: [_jsxs(WorkbenchShell, { route: route, breadcrumbs: buildBreadcrumbs(route), title: meta.title, subtitle: page === 'home' ? undefined : meta.subtitle, topbarActions: _jsx(TaskStatusMenu, { items: taskStatusItems, busy: busy || actionBusy, onActionError: notifyTaskStatusActionError }), commands: (page === 'controlled-goods' || page === 'sanctioned-companies') ? _jsx(CatalogCommands, { busy: busy || actionBusy, onUpload: () => setCatalogCommand('upload'), onCreate: () => setCatalogCommand('create') }) : contractCommands, busy: busy || actionBusy, onRouteChange: changeRoute, onRefresh: () => void load(), children: [page === 'home' && _jsx(HomePage, {}), page === 'controlled-goods' && _jsx(ControlledGoodsPage, { collection: data.controlledGoods, batches: data.controlledBatches.items, busy: busy || actionBusy, onAction: executeAction, onFileAction: executeCatalogFileAction, onRefresh: load, onQuery: (query) => queryCatalog('controlledGoods', query), onIssueQuery: queryImportIssues, importCompletion: importCompletions.CONTROLLED_CATALOG, importFailure: importFailures.CONTROLLED_CATALOG, onRetryImport: retryImport, command: catalogCommand, onCommandHandled: () => setCatalogCommand(null), onNotify: setToast }), page === 'sanctioned-companies' && _jsx(SanctionedCompaniesPage, { collection: data.sanctionedCompanies, batches: data.sanctionBatches.items, busy: busy || actionBusy, onAction: executeAction, onFileAction: executeCatalogFileAction, onRefresh: load, onQuery: (query) => queryCatalog('sanctionedCompanies', query), onIssueQuery: queryImportIssues, importCompletion: importCompletions.SANCTION_CATALOG, importFailure: importFailures.SANCTION_CATALOG, onRetryImport: retryImport, command: catalogCommand, onCommandHandled: () => setCatalogCommand(null), onNotify: setToast }), page === 'procurement' && route.type === 'procurement' && _jsx(ProcurementPage, { route: route, suppliers: data.suppliers, purchaseOrders: data.purchaseOrders, purchaseLines: data.purchaseLines, supplier: procurementContext.supplier, order: procurementContext.order, metrics: procurementContext.metrics, importTask: purchaseImportTask, reviewDraft: purchaseReviewDraft, busy: busy || actionBusy, onQuery: queryProcurement, onAction: executeAction, onFileAction: executeFileAction, onRouteChange: changeRoute, onRetryImport: retryContractImport, onReviewClose: closePurchaseReview, onNotify: setToast }), page === 'customer-contract-review' && _jsx(CustomerContractsPage, { collection: data.customerContracts, busy: busy || actionBusy, importReviewState: customerImportState, onQuery: queryCustomerContracts, onAction: executeAction, onFileAction: executeFileAction, onReanalyze: reanalyzeCustomerContract, onLoadReview: loadCustomerContractReview, onReviewClose: closeCustomerReview, reanalysisState: customerReanalysisState, onRetryImport: retryContractImport, onNotify: setToast }), page === 'sales' && route.type === 'sales' && _jsx(SalesPage, { route: route, customers: data.salesCustomers, orders: data.salesOrders, lines: data.salesLines, busy: busy || actionBusy, onQuery: querySales, onAction: executeAction, onRouteChange: changeRoute, onOpenFiles: openSalesFiles, onNotify: setToast }), page === 'sales-files' && _jsx(SalesFilesPage, { orders: data.salesFileOrders, history: data.generatedFiles, activeOrder: activeSalesFileOrder, visualPrototypeState: salesFilesPrototypeState, onPrototypeStateClose: () => setSalesFilesPrototypeState(undefined), banks: data.settings.banks, busy: busy || actionBusy, onQuery: querySalesFiles, onAction: executeSalesFileAction, onOpenSalesOrder: selectSalesFileOrder, onDownload: file => executeAction('download_sales_file', { id: file.id }).then(() => undefined), onEdit: async (file) => setEditingSalesFile(file), onNotify: setToast }), page === 'analytics' && _jsx(AnalyticsPage, { data: data.analytics, committedQuery: analyticsCommittedQuery, busy: busy || actionBusy, error: analyticsError, onQuery: queryAnalytics, onExportXlsx: exportAnalytics }), page === 'settings' && _jsx(SettingsPage, { data: data.settings, busy: busy || actionBusy, onSave: saveSettings, onRestoreDefault: restoreSettings, onDeleteRate: deleteSettingsRate, onRateQuery: queryMonthlyRates, onUploadLogo: uploadCompanyLogo, onDiscardLogo: discardCompanyLogo, onNotify: setToast }), !['home', 'controlled-goods', 'sanctioned-companies', 'procurement', 'customer-contract-review', 'sales', 'sales-files', 'analytics', 'settings'].includes(page) && _jsxs("section", { className: "tcw-page-placeholder", "data-page": page, children: [_jsx(InfoNotice, { children: "\u9875\u9762\u57FA\u7840\u7ED3\u6784\u5DF2\u5C31\u7EEA\uFF0C\u4E1A\u52A1\u5185\u5BB9\u5C06\u5728\u5BF9\u5E94\u9875\u9762\u6A21\u5757\u4E2D\u52A0\u8F7D\u3002" }), _jsx("div", { className: "tcw-placeholder-body", "aria-label": `${meta.title}页面内容` })] })] }), _jsx(LoadingBlocker, { visible: busy || actionBusy, label: actionBusy ? '操作处理中' : '数据加载中' }), _jsx(ToastStack, { items: toasts, onClose: closeToast }), editingSalesFile && _jsx(SalesFileEditor, { file: editingSalesFile, onNotify: setToast, onClose: () => setEditingSalesFile(null), onSaved: () => { setEditingSalesFile(null); if (activeSalesFileOrder)
1193
+ return (_jsxs(_Fragment, { children: [_jsx(WorkbenchShell, { route: route, breadcrumbs: buildBreadcrumbs(route), title: meta.title, topbarActions: _jsx(TaskStatusMenu, { items: taskStatusItems, busy: busy || actionBusy, onBulkDelete: bulkDeleteImportTasks, onActionError: notifyTaskStatusActionError }), onRouteChange: changeRoute, onRefresh: () => void load(), children: _jsx(AnalyticsPrototypeBoard, { onClose: () => setAnalyticsPrototypeVisible(false) }) }), _jsx(LoadingBlocker, { visible: busy || actionBusy || customerContractBulkBusy, label: actionBusy || customerContractBulkBusy ? '操作处理中' : '数据加载中' }), _jsx(ToastStack, { items: toasts, onClose: closeToast })] }));
1194
+ return (_jsxs(_Fragment, { children: [_jsxs(WorkbenchShell, { route: route, breadcrumbs: buildBreadcrumbs(route), title: meta.title, subtitle: page === 'home' ? undefined : meta.subtitle, topbarActions: _jsx(TaskStatusMenu, { items: taskStatusItems, busy: busy || actionBusy, onBulkDelete: bulkDeleteImportTasks, onActionError: notifyTaskStatusActionError }), commands: (page === 'controlled-goods' || page === 'sanctioned-companies') ? _jsx(CatalogCommands, { busy: busy || actionBusy, onUpload: () => setCatalogCommand('upload'), onCreate: () => setCatalogCommand('create') }) : contractCommands, busy: busy || actionBusy, onRouteChange: changeRoute, onRefresh: () => void load(), children: [page === 'home' && _jsx(HomePage, {}), page === 'controlled-goods' && _jsx(ControlledGoodsPage, { collection: data.controlledGoods, batches: data.controlledBatches.items, busy: busy || actionBusy, onAction: executeAction, onFileAction: executeCatalogFileAction, onRefresh: load, onQuery: (query) => queryCatalog('controlledGoods', query), onIssueQuery: queryImportIssues, importCompletion: importCompletions.CONTROLLED_CATALOG, importFailure: importFailures.CONTROLLED_CATALOG, onRetryImport: retryImport, command: catalogCommand, onCommandHandled: () => setCatalogCommand(null), onNotify: setToast }), page === 'sanctioned-companies' && _jsx(SanctionedCompaniesPage, { collection: data.sanctionedCompanies, batches: data.sanctionBatches.items, busy: busy || actionBusy, onAction: executeAction, onFileAction: executeCatalogFileAction, onRefresh: load, onQuery: (query) => queryCatalog('sanctionedCompanies', query), onIssueQuery: queryImportIssues, importCompletion: importCompletions.SANCTION_CATALOG, importFailure: importFailures.SANCTION_CATALOG, onRetryImport: retryImport, command: catalogCommand, onCommandHandled: () => setCatalogCommand(null), onNotify: setToast }), page === 'procurement' && route.type === 'procurement' && _jsx(ProcurementPage, { route: route, suppliers: data.suppliers, purchaseOrders: data.purchaseOrders, purchaseLines: data.purchaseLines, supplier: procurementContext.supplier, order: procurementContext.order, metrics: procurementContext.metrics, importTask: purchaseImportTask, reviewDraft: purchaseReviewDraft, busy: busy || actionBusy, onQuery: queryProcurement, onAction: executeAction, onFileAction: executeFileAction, onRouteChange: changeRoute, onRetryImport: retryContractImport, onReviewClose: closePurchaseReview, onNotify: setToast }), page === 'customer-contract-review' && _jsx(CustomerContractsPage, { collection: data.customerContracts, busy: busy || actionBusy || customerContractBulkBusy, importReviewState: customerImportState, onQuery: queryCustomerContracts, onAction: executeAction, onFileAction: executeFileAction, onReanalyze: reanalyzeCustomerContract, onLoadReview: loadCustomerContractReview, onReviewClose: closeCustomerReview, reanalysisState: customerReanalysisState, onRetryImport: retryContractImport, onQueryRequested: rememberCustomerContractQuery, onQueryCancelRegistration: registerCustomerContractQueryCancel, onBulkReconcile: reconcileCustomerContractsAfterBulk, onBulkBusyChange: setCustomerContractBulkBusy, onNotify: setToast }), page === 'sales' && route.type === 'sales' && _jsx(SalesPage, { route: route, customers: data.salesCustomers, orders: data.salesOrders, lines: data.salesLines, busy: busy || actionBusy, onQuery: querySales, onAction: executeAction, onRouteChange: changeRoute, onOpenFiles: openSalesFiles, onNotify: setToast }), page === 'sales-files' && _jsx(SalesFilesPage, { orders: data.salesFileOrders, history: data.generatedFiles, activeOrder: activeSalesFileOrder, visualPrototypeState: salesFilesPrototypeState, onPrototypeStateClose: () => setSalesFilesPrototypeState(undefined), banks: data.settings.banks, busy: busy || actionBusy, externalBulkBusy: salesFileBulkBusy || salesFileBulkReconcileBusy, onQuery: querySalesFiles, onBulkReconcile: reconcileGeneratedFilesAfterBulk, onBulkBusyChange: setSalesFileBulkBusy, onBulkViewActiveChange: setGeneratedFilesBulkViewActive, onAction: executeSalesFileAction, onOpenSalesOrder: selectSalesFileOrder, onDownload: file => executeAction('download_sales_file', { id: file.id }).then(() => undefined), onEdit: async (file) => setEditingSalesFile(file), onNotify: setToast }), page === 'analytics' && _jsx(AnalyticsPage, { data: data.analytics, committedQuery: analyticsCommittedQuery, busy: busy || actionBusy, error: analyticsError, onQuery: queryAnalytics, onExportXlsx: exportAnalytics }), page === 'settings' && _jsx(SettingsPage, { data: data.settings, busy: busy || actionBusy, externalBulkBusy: settingsRateBulkBusy || settingsRateBulkReconcileBusy, onSave: saveSettings, onRestoreDefault: restoreSettings, onDeleteRate: deleteSettingsRate, onRateQuery: queryMonthlyRates, onBulkReconcile: reconcileSettingsRatesAfterBulk, onBulkBusyChange: setSettingsRateBulkBusy, onBulkViewActiveChange: setSettingsRatesBulkViewActive, onUploadLogo: uploadCompanyLogo, onDiscardLogo: discardCompanyLogo, onNotify: setToast }), !['home', 'controlled-goods', 'sanctioned-companies', 'procurement', 'customer-contract-review', 'sales', 'sales-files', 'analytics', 'settings'].includes(page) && _jsxs("section", { className: "tcw-page-placeholder", "data-page": page, children: [_jsx(InfoNotice, { children: "\u9875\u9762\u57FA\u7840\u7ED3\u6784\u5DF2\u5C31\u7EEA\uFF0C\u4E1A\u52A1\u5185\u5BB9\u5C06\u5728\u5BF9\u5E94\u9875\u9762\u6A21\u5757\u4E2D\u52A0\u8F7D\u3002" }), _jsx("div", { className: "tcw-placeholder-body", "aria-label": `${meta.title}页面内容` })] })] }), _jsx(LoadingBlocker, { visible: busy || actionBusy || customerContractBulkBusy, label: actionBusy || customerContractBulkBusy ? '操作处理中' : '数据加载中' }), _jsx(ToastStack, { items: toasts, onClose: closeToast }), editingSalesFile && _jsx(SalesFileEditor, { file: editingSalesFile, onNotify: setToast, onClose: () => setEditingSalesFile(null), onSaved: () => { setEditingSalesFile(null); if (activeSalesFileOrder)
1100
1195
  void querySalesFiles('generatedFiles', { page: 1, pageSize: 20, filters: {}, parameters: { customerContractId: activeSalesFileOrder.customerContractId, purchaseOrderId: activeSalesFileOrder.purchaseOrderId } }); } })] }));
1101
1196
  }
1102
1197
  function routePage(route) {
@@ -1108,6 +1203,7 @@ function routePage(route) {
1108
1203
  }
1109
1204
  function buildTaskStatusItems(tasks, onOpen, onSelect, onDelete) {
1110
1205
  return [...tasks]
1206
+ .filter(task => ['QUEUED', 'EXTRACTING', 'AI_PARSING', 'VALIDATING', 'PERSISTING', 'AWAITING_REVIEW', 'FAILED', 'TIMED_OUT'].includes(task.status))
1111
1207
  .sort((left, right) => String(right.updatedAt ?? right.queuedAt ?? right.createdAt ?? '').localeCompare(String(left.updatedAt ?? left.queuedAt ?? left.createdAt ?? '')))
1112
1208
  .map(task => {
1113
1209
  const tone = taskTone(task.status);
@@ -1158,8 +1254,8 @@ function formatTaskTime(value) {
1158
1254
  return value;
1159
1255
  return new Intl.DateTimeFormat('zh-CN', { timeZone: 'Asia/Shanghai', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', hour12: false }).format(date).replace(/\//g, '/');
1160
1256
  }
1161
- function mergeWorkbenchDataWithLocalTasks(current, incoming) {
1162
- const items = mergeImportTaskItems(current.importTasks.items, incoming.importTasks.items);
1257
+ function mergeWorkbenchDataWithLocalTasks(current, incoming, deletedIds) {
1258
+ const items = mergeImportTaskItems(current.importTasks.items, incoming.importTasks.items, deletedIds);
1163
1259
  return {
1164
1260
  ...incoming,
1165
1261
  importTasks: {
@@ -1217,6 +1313,9 @@ function actionResponseError(result, fallback) {
1217
1313
  function asRecord(value) {
1218
1314
  return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
1219
1315
  }
1316
+ function stringArray(value) {
1317
+ return Array.isArray(value) ? value.filter((item) => typeof item === 'string' && Boolean(item.trim())) : [];
1318
+ }
1220
1319
  function summaryRequestParameters(value) {
1221
1320
  const parameters = { ...asRecord(value) };
1222
1321
  delete parameters.collection;