@chenchaolong/plugin-trade-compliance-workbench 1.0.52 → 1.0.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/lib/analytics-export.d.ts.map +1 -1
- package/dist/lib/analytics-export.js +1 -3
- package/dist/lib/analytics-export.js.map +1 -1
- package/dist/lib/analytics-query.d.ts.map +1 -1
- package/dist/lib/analytics-query.js +13 -20
- package/dist/lib/analytics-query.js.map +1 -1
- package/dist/lib/catalog-import/confidence.d.ts +1 -0
- package/dist/lib/catalog-import/confidence.d.ts.map +1 -1
- package/dist/lib/catalog-import/confidence.js +12 -0
- package/dist/lib/catalog-import/confidence.js.map +1 -1
- package/dist/lib/catalog-import/docx.parser.d.ts.map +1 -1
- package/dist/lib/catalog-import/docx.parser.js +7 -6
- package/dist/lib/catalog-import/docx.parser.js.map +1 -1
- package/dist/lib/catalog-import/pdf-layout.parser.d.ts.map +1 -1
- package/dist/lib/catalog-import/pdf-layout.parser.js +8 -7
- package/dist/lib/catalog-import/pdf-layout.parser.js.map +1 -1
- package/dist/lib/catalog-import/spreadsheet.parser.d.ts.map +1 -1
- package/dist/lib/catalog-import/spreadsheet.parser.js +12 -13
- package/dist/lib/catalog-import/spreadsheet.parser.js.map +1 -1
- package/dist/lib/demo-data.definitions.js +1 -1
- package/dist/lib/demo-data.definitions.js.map +1 -1
- package/dist/lib/entities/catalog.entity.d.ts +7 -1
- package/dist/lib/entities/catalog.entity.d.ts.map +1 -1
- package/dist/lib/entities/catalog.entity.js +26 -2
- package/dist/lib/entities/catalog.entity.js.map +1 -1
- package/dist/lib/entities/customer.entity.js.map +1 -1
- package/dist/lib/entities/procurement.entity.js.map +1 -1
- package/dist/lib/remote-components/trade-compliance-workbench/app.css +1 -1
- package/dist/lib/remote-components/trade-compliance-workbench/app.js +8 -8
- package/dist/lib/remote-ui/action-execution.d.ts +3 -0
- package/dist/lib/remote-ui/action-execution.d.ts.map +1 -1
- package/dist/lib/remote-ui/action-execution.js +21 -1
- package/dist/lib/remote-ui/action-execution.js.map +1 -1
- package/dist/lib/remote-ui/app-source.js +168 -151
- package/dist/lib/remote-ui/app-source.js.map +1 -1
- package/dist/lib/remote-ui/bridge.d.ts.map +1 -1
- package/dist/lib/remote-ui/components/forms.d.ts +5 -2
- package/dist/lib/remote-ui/components/forms.d.ts.map +1 -1
- package/dist/lib/remote-ui/components/forms.js +4 -4
- package/dist/lib/remote-ui/components/forms.js.map +1 -1
- package/dist/lib/remote-ui/components/layout.d.ts.map +1 -1
- package/dist/lib/remote-ui/components/layout.js +31 -4
- package/dist/lib/remote-ui/components/layout.js.map +1 -1
- package/dist/lib/remote-ui/components/task-status.js +2 -2
- package/dist/lib/remote-ui/components/task-status.js.map +1 -1
- package/dist/lib/remote-ui/contract-import.d.ts +1 -0
- package/dist/lib/remote-ui/contract-import.d.ts.map +1 -1
- package/dist/lib/remote-ui/contract-import.js.map +1 -1
- package/dist/lib/remote-ui/contracts.d.ts +11 -0
- package/dist/lib/remote-ui/contracts.d.ts.map +1 -1
- package/dist/lib/remote-ui/import-task-status.d.ts +5 -2
- package/dist/lib/remote-ui/import-task-status.d.ts.map +1 -1
- package/dist/lib/remote-ui/import-task-status.js +13 -8
- package/dist/lib/remote-ui/import-task-status.js.map +1 -1
- package/dist/lib/remote-ui/pages/analytics-detail-list.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/analytics-detail-list.js.map +1 -1
- package/dist/lib/remote-ui/pages/catalog-import-history.d.ts +47 -2
- package/dist/lib/remote-ui/pages/catalog-import-history.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/catalog-import-history.js +128 -8
- package/dist/lib/remote-ui/pages/catalog-import-history.js.map +1 -1
- package/dist/lib/remote-ui/pages/catalogs.css +55 -1
- package/dist/lib/remote-ui/pages/catalogs.d.ts +7 -4
- package/dist/lib/remote-ui/pages/catalogs.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/catalogs.js +40 -19
- package/dist/lib/remote-ui/pages/catalogs.js.map +1 -1
- package/dist/lib/remote-ui/pages/contracts.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/contracts.js +0 -16
- package/dist/lib/remote-ui/pages/contracts.js.map +1 -1
- package/dist/lib/remote-ui/pages/files-prototype.css +2 -2
- package/dist/lib/remote-ui/pages/files.d.ts +1 -1
- package/dist/lib/remote-ui/pages/files.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/files.js +6 -6
- package/dist/lib/remote-ui/pages/files.js.map +1 -1
- package/dist/lib/remote-ui/pages/home.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/home.js +2 -2
- package/dist/lib/remote-ui/pages/home.js.map +1 -1
- package/dist/lib/remote-ui/pages/procurement.d.ts +6 -1
- package/dist/lib/remote-ui/pages/procurement.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/procurement.js +77 -16
- package/dist/lib/remote-ui/pages/procurement.js.map +1 -1
- package/dist/lib/remote-ui/procurement-contracts.d.ts +7 -1
- package/dist/lib/remote-ui/procurement-contracts.d.ts.map +1 -1
- package/dist/lib/remote-ui/procurement-contracts.js.map +1 -1
- package/dist/lib/remote-ui/purchase-review-adapter.d.ts +6 -2
- package/dist/lib/remote-ui/purchase-review-adapter.d.ts.map +1 -1
- package/dist/lib/remote-ui/purchase-review-adapter.js +5 -3
- package/dist/lib/remote-ui/purchase-review-adapter.js.map +1 -1
- package/dist/lib/remote-ui/sidebar-state.d.ts +1 -0
- package/dist/lib/remote-ui/sidebar-state.d.ts.map +1 -1
- package/dist/lib/remote-ui/sidebar-state.js +3 -0
- package/dist/lib/remote-ui/sidebar-state.js.map +1 -1
- package/dist/lib/sales-file.service.js +1 -1
- package/dist/lib/sales-file.service.js.map +1 -1
- package/dist/lib/schema-maintenance.d.ts.map +1 -1
- package/dist/lib/schema-maintenance.js +125 -0
- package/dist/lib/schema-maintenance.js.map +1 -1
- package/dist/lib/types.d.ts +3 -0
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/types.js +1 -0
- package/dist/lib/types.js.map +1 -1
- package/dist/lib/workbench-action-contract.d.ts +1 -1
- package/dist/lib/workbench-action-contract.d.ts.map +1 -1
- package/dist/lib/workbench-action-contract.js +3 -1
- package/dist/lib/workbench-action-contract.js.map +1 -1
- package/dist/lib/workbench-view.provider.d.ts +12 -9
- package/dist/lib/workbench-view.provider.d.ts.map +1 -1
- package/dist/lib/workbench-view.provider.js +38 -6
- package/dist/lib/workbench-view.provider.js.map +1 -1
- package/dist/lib/workbench.middleware.d.ts +444 -15
- package/dist/lib/workbench.middleware.d.ts.map +1 -1
- package/dist/lib/workbench.middleware.js +38 -7
- package/dist/lib/workbench.middleware.js.map +1 -1
- package/dist/lib/workbench.service.d.ts +31 -3
- package/dist/lib/workbench.service.d.ts.map +1 -1
- package/dist/lib/workbench.service.js +765 -147
- package/dist/lib/workbench.service.js.map +1 -1
- package/package.json +2 -1
|
@@ -16,6 +16,9 @@ type ActionNotificationPolicy = 'silent' | {
|
|
|
16
16
|
export type ActionExecutionOptions = {
|
|
17
17
|
suppressRefresh?: boolean;
|
|
18
18
|
};
|
|
19
|
+
export declare function createMutationCoordinator(): {
|
|
20
|
+
run<T>(operation: () => Promise<T>, onBusyChange?: (busy: boolean) => void): Promise<T>;
|
|
21
|
+
};
|
|
19
22
|
export declare const ACTION_NOTIFICATION_POLICIES: Record<WorkbenchActionKey, ActionNotificationPolicy>;
|
|
20
23
|
export declare function executeWorkbenchAction<T>({ actionKey, mutate, refresh, notify, onResult, resolveMessage, suppressRefresh }: {
|
|
21
24
|
actionKey: WorkbenchActionKey;
|
|
@@ -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;AAChF,MAAM,MAAM,sBAAsB,GAAG;IAAE,eAAe,CAAC,EAAE,OAAO,CAAA;CAAE,CAAA;AAElE,eAAO,MAAM,4BAA4B,EAAE,MAAM,CAAC,kBAAkB,EAAE,wBAAwB,
|
|
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,wBAAgB,yBAAyB;QAG3B,CAAC,aAAa,MAAM,OAAO,CAAC,CAAC,CAAC,iBAAgB,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,GAAqB,OAAO,CAAC,CAAC,CAAC;EAOjH;AAED,eAAO,MAAM,4BAA4B,EAAE,MAAM,CAAC,kBAAkB,EAAE,wBAAwB,CAwC7F,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"}
|
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
import { isWorkbenchActionKey } from '../workbench-action-contract.js';
|
|
2
|
+
export function createMutationCoordinator() {
|
|
3
|
+
let running = false;
|
|
4
|
+
return {
|
|
5
|
+
async run(operation, onBusyChange = () => undefined) {
|
|
6
|
+
if (running)
|
|
7
|
+
throw new Error('操作正在执行,请稍候');
|
|
8
|
+
running = true;
|
|
9
|
+
onBusyChange(true);
|
|
10
|
+
try {
|
|
11
|
+
return await operation();
|
|
12
|
+
}
|
|
13
|
+
finally {
|
|
14
|
+
running = false;
|
|
15
|
+
onBusyChange(false);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
2
20
|
export const ACTION_NOTIFICATION_POLICIES = {
|
|
3
21
|
refresh: 'silent',
|
|
4
22
|
load_excel_editor_runtime: 'silent',
|
|
@@ -31,12 +49,14 @@ export const ACTION_NOTIFICATION_POLICIES = {
|
|
|
31
49
|
download_sales_file: { tone: 'success', fallback: '销售文件下载成功' },
|
|
32
50
|
download_import_issues: { tone: 'success', fallback: '导入问题 Excel 下载成功' },
|
|
33
51
|
retry_import_task: 'silent',
|
|
52
|
+
resolve_stalled_catalog_import: 'silent',
|
|
34
53
|
delete_import_task: 'silent',
|
|
35
54
|
download_source_file: 'silent',
|
|
36
55
|
preview_source_file: 'silent',
|
|
37
56
|
save_edited_sales_file: 'silent',
|
|
38
57
|
search_hs_code: 'silent',
|
|
39
|
-
get_hs_code_detail: 'silent'
|
|
58
|
+
get_hs_code_detail: 'silent',
|
|
59
|
+
translate_purchase_product_name: 'silent'
|
|
40
60
|
};
|
|
41
61
|
const refreshActions = new Set([
|
|
42
62
|
'save_controlled_goods',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action-execution.js","sourceRoot":"","sources":["../../../src/lib/remote-ui/action-execution.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAA2B,MAAM,iCAAiC,CAAA;AAc/F,MAAM,CAAC,MAAM,4BAA4B,GAAyD;IAChG,OAAO,EAAE,QAAQ;IACjB,yBAAyB,EAAE,QAAQ;IACnC,cAAc,EAAE,QAAQ;IACxB,qBAAqB,EAAE,QAAQ;IAC/B,uBAAuB,EAAE,QAAQ;IACjC,uBAAuB,EAAE,QAAQ;IACjC,yBAAyB,EAAE,QAAQ;IACnC,oBAAoB,EAAE,QAAQ;IAC9B,6BAA6B,EAAE,QAAQ;IACvC,eAAe,EAAE,QAAQ;IACzB,qBAAqB,EAAE,QAAQ;IAC/B,iBAAiB,EAAE,QAAQ;IAC3B,oBAAoB,EAAE,QAAQ;IAC9B,eAAe,EAAE,QAAQ;IACzB,yBAAyB,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE;IACrE,wBAAwB,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE;IACrE,4BAA4B,EAAE,QAAQ;IACtC,+BAA+B,EAAE,QAAQ;IACzC,2BAA2B,EAAE,QAAQ;IACrC,2CAA2C,EAAE,QAAQ;IACrD,aAAa,EAAE,QAAQ;IACvB,aAAa,EAAE,QAAQ;IACvB,wBAAwB,EAAE,QAAQ;IAClC,oBAAoB,EAAE,QAAQ;IAC9B,oBAAoB,EAAE,QAAQ;IAC9B,gBAAgB,EAAE,QAAQ;IAC1B,oBAAoB,EAAE,QAAQ;IAC9B,mBAAmB,EAAE,QAAQ;IAC7B,mBAAmB,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE;IAC9D,sBAAsB,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,iBAAiB,EAAE;IACxE,iBAAiB,EAAE,QAAQ;IAC3B,kBAAkB,EAAE,QAAQ;IAC5B,oBAAoB,EAAE,QAAQ;IAC9B,mBAAmB,EAAE,QAAQ;IAC7B,sBAAsB,EAAE,QAAQ;IAChC,cAAc,EAAE,QAAQ;IACxB,kBAAkB,EAAE,QAAQ;
|
|
1
|
+
{"version":3,"file":"action-execution.js","sourceRoot":"","sources":["../../../src/lib/remote-ui/action-execution.ts"],"names":[],"mappings":"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"}
|
|
@@ -24,7 +24,7 @@ import { AnalyticsComplianceCategoryKeys } from './analytics-contracts.js';
|
|
|
24
24
|
import { ContractImportError, findRecoverableContractImport, findResumableContractImports, runContractImportOrchestration, runCustomerContractReanalysis, taskTransition } from './contract-import.js';
|
|
25
25
|
import { createTaskChatSynchronizer, importTaskFromStart, importTaskProgressPercent, isLocalUploadPlaceholder, mergeImportTaskItems, removeImportTaskItem, taskBusinessRefreshRevision, taskRouteRefreshTarget } from './import-task-status.js';
|
|
26
26
|
import { compareTaskBusinessOrder, createPendingResultQueue, createPollingTaskTracker, isAbortError, isPollingObservationTimeout, submitAndRefreshTaskState } from './polling.js';
|
|
27
|
-
import {
|
|
27
|
+
import { createMutationCoordinator, executeWorkbenchAction } from './action-execution.js';
|
|
28
28
|
import { createDeferredBulkReconcileCoordinator, runBulkDelete } from './bulk-delete.js';
|
|
29
29
|
import './styles.css';
|
|
30
30
|
const pageMeta = {
|
|
@@ -93,6 +93,7 @@ function App() {
|
|
|
93
93
|
const deletedImportTaskIdsRef = React.useRef(new Set());
|
|
94
94
|
const [busy, setBusy] = React.useState(false);
|
|
95
95
|
const [actionBusy, setActionBusy] = React.useState(false);
|
|
96
|
+
const mutationCoordinatorRef = React.useRef(createMutationCoordinator());
|
|
96
97
|
const [customerContractBulkBusy, setCustomerContractBulkBusy] = React.useState(false);
|
|
97
98
|
const [salesFileBulkBusy, setSalesFileBulkBusy] = React.useState(false);
|
|
98
99
|
const [settingsRateBulkBusy, setSettingsRateBulkBusy] = React.useState(false);
|
|
@@ -122,8 +123,13 @@ function App() {
|
|
|
122
123
|
key: 'tradeComplianceTask', context: { task, relatedRecords }
|
|
123
124
|
});
|
|
124
125
|
},
|
|
125
|
-
|
|
126
|
-
await bridge.invokeClientCommand('assistant.
|
|
126
|
+
openConversation: async (conversationId, task) => {
|
|
127
|
+
await bridge.invokeClientCommand('assistant.conversation.open', {
|
|
128
|
+
target: 'assistant.conversation',
|
|
129
|
+
conversationId,
|
|
130
|
+
contextKey: 'tradeComplianceTask',
|
|
131
|
+
context: { task }
|
|
132
|
+
});
|
|
127
133
|
}
|
|
128
134
|
});
|
|
129
135
|
const changeRouteRef = React.useRef(() => undefined);
|
|
@@ -420,27 +426,19 @@ function App() {
|
|
|
420
426
|
React.useEffect(() => {
|
|
421
427
|
toastTimers.sync(toasts);
|
|
422
428
|
}, [toastTimers, toasts]);
|
|
429
|
+
const runMutation = React.useCallback((operation) => mutationCoordinatorRef.current.run(operation, setActionBusy), []);
|
|
423
430
|
const executeAction = React.useCallback(async (actionKey, input, options = {}) => {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
downloadActionFile(result.data); },
|
|
436
|
-
...options
|
|
437
|
-
});
|
|
438
|
-
}
|
|
439
|
-
finally {
|
|
440
|
-
if (blocksPage)
|
|
441
|
-
setActionBusy(false);
|
|
442
|
-
}
|
|
443
|
-
}, [load]);
|
|
431
|
+
return runMutation(() => executeWorkbenchAction({
|
|
432
|
+
actionKey,
|
|
433
|
+
mutate: async () => asActionResult(unwrapResponse(await bridge.executeAction(actionKey, input))),
|
|
434
|
+
refresh: load,
|
|
435
|
+
notify: setToast,
|
|
436
|
+
resolveMessage: resolveActionMessage,
|
|
437
|
+
onResult: result => { if (actionKey === 'download_import_issues' || actionKey === 'download_sales_file')
|
|
438
|
+
downloadActionFile(result.data); },
|
|
439
|
+
...options
|
|
440
|
+
}));
|
|
441
|
+
}, [load, runMutation]);
|
|
444
442
|
const executeSalesFileAction = React.useCallback((actionKey, input, options = {}) => executeAction(actionKey, input, options), [executeAction]);
|
|
445
443
|
const schedulePendingReviewPresentation = React.useCallback(() => {
|
|
446
444
|
if (reviewPresentationTimerRef.current != null)
|
|
@@ -602,35 +600,37 @@ function App() {
|
|
|
602
600
|
return false;
|
|
603
601
|
}, [upsertLocalImportTask]);
|
|
604
602
|
const executeFileAction = React.useCallback(async (actionKey, file, input, notifyError = true) => {
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
603
|
+
return runMutation(async () => {
|
|
604
|
+
try {
|
|
605
|
+
const raw = await bridge.executeFileAction(actionKey, file, input);
|
|
606
|
+
const start = asActionResult(unwrapResponse(raw));
|
|
607
|
+
const task = importTaskFromStart(start, actionKey, input, file);
|
|
608
|
+
if (task) {
|
|
609
|
+
upsertLocalImportTask(task);
|
|
610
|
+
if (isLocalUploadPlaceholder(task))
|
|
611
|
+
scheduleTaskStateRecovery();
|
|
612
|
+
else
|
|
613
|
+
startTrackingUploadedImportTask(task);
|
|
614
|
+
}
|
|
615
|
+
if (start.success === false)
|
|
616
|
+
throw actionResponseError(start, '上传失败');
|
|
617
|
+
await loadTaskState().catch((refreshError) => {
|
|
618
|
+
notifyTaskRecoveryRefreshError(refreshError instanceof Error ? refreshError : new Error(String(refreshError)));
|
|
619
|
+
});
|
|
620
|
+
await presentImmediateContractUploadReview(actionKey, start);
|
|
621
|
+
setToast({ message: resolveActionMessage(start) || '解析任务已提交', tone: 'info' });
|
|
622
|
+
return start;
|
|
615
623
|
}
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
await loadTaskState().catch((refreshError) => {
|
|
627
|
-
notifyTaskRecoveryRefreshError(refreshError instanceof Error ? refreshError : new Error(String(refreshError)));
|
|
628
|
-
});
|
|
629
|
-
if (notifyError && !(error instanceof CatalogImportError))
|
|
630
|
-
setToast({ message: error instanceof Error ? error.message : '上传失败', tone: 'error' });
|
|
631
|
-
throw error;
|
|
632
|
-
}
|
|
633
|
-
}, [loadTaskState, notifyTaskRecoveryRefreshError, presentImmediateContractUploadReview, scheduleTaskStateRecovery, startTrackingUploadedImportTask, upsertLocalImportTask]);
|
|
624
|
+
catch (error) {
|
|
625
|
+
await loadTaskState().catch((refreshError) => {
|
|
626
|
+
notifyTaskRecoveryRefreshError(refreshError instanceof Error ? refreshError : new Error(String(refreshError)));
|
|
627
|
+
});
|
|
628
|
+
if (notifyError && !(error instanceof CatalogImportError))
|
|
629
|
+
setToast({ message: error instanceof Error ? error.message : '上传失败', tone: 'error' });
|
|
630
|
+
throw error;
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
}, [loadTaskState, notifyTaskRecoveryRefreshError, presentImmediateContractUploadReview, runMutation, scheduleTaskStateRecovery, startTrackingUploadedImportTask, upsertLocalImportTask]);
|
|
634
634
|
const executeCatalogFileAction = React.useCallback((actionKey, file, input) => executeFileAction(actionKey, file, input, false), [executeFileAction]);
|
|
635
635
|
React.useEffect(() => {
|
|
636
636
|
const tasks = data.importTasks.items;
|
|
@@ -673,52 +673,58 @@ function App() {
|
|
|
673
673
|
}
|
|
674
674
|
}, [data.importTasks.items, handleBackgroundImportTrackingError, orchestrateContractImport]);
|
|
675
675
|
const retryImport = React.useCallback(async (taskId) => {
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
676
|
+
return runMutation(async () => {
|
|
677
|
+
try {
|
|
678
|
+
const raw = await submitAndRefreshTaskState(() => bridge.executeAction('retry_import_task', { taskId }), loadTaskState, notifyTaskRecoveryRefreshError);
|
|
679
|
+
const start = asActionResult(unwrapResponse(raw));
|
|
680
|
+
if (start.success === false)
|
|
681
|
+
throw actionResponseError(start, '重新解析失败');
|
|
682
|
+
setToast({ message: resolveActionMessage(start) || '重新解析任务已提交', tone: 'info' });
|
|
683
|
+
}
|
|
684
|
+
catch (error) {
|
|
685
|
+
if (!(error instanceof CatalogImportError))
|
|
686
|
+
setToast({ message: error instanceof Error ? error.message : '重新解析失败', tone: 'error' });
|
|
687
|
+
throw error;
|
|
688
|
+
}
|
|
689
|
+
});
|
|
690
|
+
}, [loadTaskState, notifyTaskRecoveryRefreshError, runMutation]);
|
|
689
691
|
const retryContractImport = React.useCallback(async (taskId) => {
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
692
|
+
return runMutation(async () => {
|
|
693
|
+
try {
|
|
694
|
+
const raw = await submitAndRefreshTaskState(() => bridge.executeAction('retry_import_task', { taskId }), loadTaskState, notifyTaskRecoveryRefreshError);
|
|
695
|
+
const start = asActionResult(unwrapResponse(raw));
|
|
696
|
+
if (start.success === false)
|
|
697
|
+
throw actionResponseError(start, '重新解析失败');
|
|
698
|
+
setToast({ message: resolveActionMessage(start) || '重新解析任务已提交', tone: 'info' });
|
|
699
|
+
}
|
|
700
|
+
catch (error) {
|
|
701
|
+
setToast({ message: error instanceof Error ? error.message : '重新解析失败', tone: 'error' });
|
|
702
|
+
throw error;
|
|
703
|
+
}
|
|
704
|
+
});
|
|
705
|
+
}, [loadTaskState, notifyTaskRecoveryRefreshError, runMutation]);
|
|
702
706
|
const deleteImportTaskRecord = React.useCallback(async (task, options) => {
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
taskChatSynchronizerRef.current.
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
707
|
+
return runMutation(async () => {
|
|
708
|
+
const result = asActionResult(unwrapResponse(await bridge.executeAction('delete_import_task', { taskId: task.id })));
|
|
709
|
+
if (result.success === false)
|
|
710
|
+
throw actionResponseError(result, '任务删除失败');
|
|
711
|
+
catalogTaskTrackerRef.current.abort(task.id);
|
|
712
|
+
contractTaskTrackerRef.current.abort(task.id);
|
|
713
|
+
removeLocalImportTask(task.id);
|
|
714
|
+
if (taskChatSynchronizerRef.current?.isSelected(task.id)) {
|
|
715
|
+
taskChatSynchronizerRef.current.clear();
|
|
716
|
+
await bridge.invokeClientCommand('assistant.context.set', {
|
|
717
|
+
key: 'tradeComplianceTask', clear: true
|
|
718
|
+
}).catch(() => undefined);
|
|
719
|
+
await bridge.invokeClientCommand('assistant.chat.send_message', {
|
|
720
|
+
text: `任务“${task.sourceFileName || documentTypeLabel(task.documentType)}”已取消并删除。`,
|
|
721
|
+
clientMessageId: `trade-task:${task.id}:deleted`
|
|
722
|
+
}).catch(() => undefined);
|
|
723
|
+
}
|
|
724
|
+
if (options.refresh)
|
|
725
|
+
await loadTaskState();
|
|
726
|
+
});
|
|
727
|
+
}, [loadTaskState, removeLocalImportTask, runMutation]);
|
|
722
728
|
const deleteImportTask = React.useCallback(async (task) => {
|
|
723
729
|
await deleteImportTaskRecord(task, { refresh: true });
|
|
724
730
|
}, [deleteImportTaskRecord]);
|
|
@@ -781,14 +787,16 @@ function App() {
|
|
|
781
787
|
return attached;
|
|
782
788
|
}, []);
|
|
783
789
|
const consumeCustomerContractReanalysisResult = React.useCallback(async (runId) => {
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
790
|
+
return runMutation(async () => {
|
|
791
|
+
const consumed = asActionResult(unwrapResponse(await bridge.executeAction('consume_customer_contract_reanalysis_result', { runId })));
|
|
792
|
+
if (consumed.success === false)
|
|
793
|
+
throw new Error(resolveActionMessage(consumed) || '确认重新分析结果失败');
|
|
794
|
+
});
|
|
795
|
+
}, [runMutation]);
|
|
788
796
|
const reanalyzeCustomerContract = React.useCallback(async (input) => {
|
|
789
797
|
setCustomerReanalysisState({ status: 'RUNNING', message: 'AI 重新分析中' });
|
|
790
798
|
try {
|
|
791
|
-
const raw = await submitAndRefreshTaskState(() => bridge.executeAction('reanalyze_customer_contract', { reviewSource: input.reviewSource, ...(input.customerContractId ? { id: input.customerContractId } : {}), ...(input.importTaskId ? { importTaskId: input.importTaskId } : {}) }), loadTaskState, notifyTaskRecoveryRefreshError);
|
|
799
|
+
const raw = await runMutation(() => submitAndRefreshTaskState(() => bridge.executeAction('reanalyze_customer_contract', { reviewSource: input.reviewSource, ...(input.customerContractId ? { id: input.customerContractId } : {}), ...(input.importTaskId ? { importTaskId: input.importTaskId } : {}) }), loadTaskState, notifyTaskRecoveryRefreshError));
|
|
792
800
|
const response = asActionResult(unwrapResponse(raw));
|
|
793
801
|
if (response.success === false || !response.data)
|
|
794
802
|
throw actionResponseError(response, 'AI 重新分析启动失败');
|
|
@@ -829,7 +837,7 @@ function App() {
|
|
|
829
837
|
setToast({ message: error instanceof Error ? error.message : 'AI 重新分析失败', tone: 'error' });
|
|
830
838
|
throw error;
|
|
831
839
|
}
|
|
832
|
-
}, [applyCustomerContractReanalysisResult, consumeCustomerContractReanalysisResult, loadTaskState, scheduleTaskStateRecovery]);
|
|
840
|
+
}, [applyCustomerContractReanalysisResult, consumeCustomerContractReanalysisResult, loadTaskState, runMutation, scheduleTaskStateRecovery]);
|
|
833
841
|
React.useEffect(() => {
|
|
834
842
|
for (const recovery of data.customerContractAnalyses.items) {
|
|
835
843
|
const recoverable = recovery.recoveryType === 'ACTIVE_POLL' || recovery.recoveryType === 'SUCCEEDED_RESULT';
|
|
@@ -1000,6 +1008,9 @@ function App() {
|
|
|
1000
1008
|
setBusy(gate.activeCount() > 0);
|
|
1001
1009
|
}
|
|
1002
1010
|
}, []);
|
|
1011
|
+
const resolveStalledCatalogImport = React.useCallback(async (taskId, decision) => {
|
|
1012
|
+
return runMutation(async () => asActionResult(unwrapResponse(await bridge.executeAction('resolve_stalled_catalog_import', { taskId, decision }))));
|
|
1013
|
+
}, [runMutation]);
|
|
1003
1014
|
const queryMonthlyRates = React.useCallback(async (query) => {
|
|
1004
1015
|
latestSettingsRatesQueryRef.current = query;
|
|
1005
1016
|
const requestedPageSize = resolvePaginationPageSize(query.pageSize);
|
|
@@ -1164,24 +1175,21 @@ function App() {
|
|
|
1164
1175
|
return analyticsDetailPage(payload, query.page, pageSize);
|
|
1165
1176
|
}, []);
|
|
1166
1177
|
const exportAnalytics = React.useCallback(async (_request) => {
|
|
1167
|
-
setActionBusy(true);
|
|
1168
1178
|
try {
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1179
|
+
await runMutation(async () => {
|
|
1180
|
+
const committed = analyticsRequestsRef.current.committed();
|
|
1181
|
+
const request = { filters: committed.filters, format: 'XLSX' };
|
|
1182
|
+
const result = asActionResult(unwrapResponse(await bridge.executeAction('export_analytics', request)));
|
|
1183
|
+
if (result.success === false)
|
|
1184
|
+
throw new Error(resolveActionMessage(result) || '分析导出失败');
|
|
1185
|
+
downloadActionFile(result.data);
|
|
1186
|
+
setToast({ message: '分析数据导出成功', tone: 'success' });
|
|
1187
|
+
});
|
|
1176
1188
|
}
|
|
1177
1189
|
catch (error) {
|
|
1178
1190
|
setToast({ message: error instanceof Error ? error.message : '分析导出失败', tone: 'error' });
|
|
1179
|
-
throw error;
|
|
1180
1191
|
}
|
|
1181
|
-
|
|
1182
|
-
setActionBusy(false);
|
|
1183
|
-
}
|
|
1184
|
-
}, []);
|
|
1192
|
+
}, [runMutation]);
|
|
1185
1193
|
const openAnalyticsDetailAction = React.useCallback((row) => {
|
|
1186
1194
|
const action = row.action;
|
|
1187
1195
|
if (action.kind === 'CUSTOMER_CONTRACT_REVIEW') {
|
|
@@ -1229,38 +1237,48 @@ function App() {
|
|
|
1229
1237
|
setToast({ message: '当前明细缺少可跳转的业务标识', tone: 'error' });
|
|
1230
1238
|
}, [openImportTaskFromStatus, setToast]);
|
|
1231
1239
|
const saveSettings = React.useCallback(async (request) => {
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1240
|
+
return runMutation(async () => {
|
|
1241
|
+
const result = asActionResult(unwrapResponse(await bridge.executeAction('save_settings', request)));
|
|
1242
|
+
if (result.success === false)
|
|
1243
|
+
throw new Error(resolveActionMessage(result) || '设置保存失败');
|
|
1244
|
+
await loadRoute({ type: 'page', page: 'settings' }, settingsRatesQueryRef.current.page, settingsRatesQueryRef.current.pageSize);
|
|
1245
|
+
});
|
|
1246
|
+
}, [loadRoute, runMutation]);
|
|
1237
1247
|
const restoreSettings = React.useCallback(async (request) => {
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1248
|
+
return runMutation(async () => {
|
|
1249
|
+
const result = asActionResult(unwrapResponse(await bridge.executeAction('restore_settings_default', request)));
|
|
1250
|
+
if (result.success === false)
|
|
1251
|
+
throw new Error(resolveActionMessage(result) || '恢复系统默认失败');
|
|
1252
|
+
await loadRoute({ type: 'page', page: 'settings' }, settingsRatesQueryRef.current.page, settingsRatesQueryRef.current.pageSize);
|
|
1253
|
+
});
|
|
1254
|
+
}, [loadRoute, runMutation]);
|
|
1243
1255
|
const deleteSettingsRate = React.useCallback(async (request, options = {}) => {
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1256
|
+
return runMutation(async () => {
|
|
1257
|
+
const result = asActionResult(unwrapResponse(await bridge.executeAction('delete_settings_rate', request)));
|
|
1258
|
+
if (result.success === false)
|
|
1259
|
+
throw new Error(resolveActionMessage(result) || '月度汇率删除失败');
|
|
1260
|
+
if (options.suppressRefresh)
|
|
1261
|
+
return;
|
|
1262
|
+
const { page, pageSize } = settingsRatesQueryRef.current;
|
|
1263
|
+
const clampedPage = clampPageAfterSingleDeletion(page, pageSize, data.settings.rates.total);
|
|
1264
|
+
await loadRoute({ type: 'page', page: 'settings' }, clampedPage, pageSize);
|
|
1265
|
+
});
|
|
1266
|
+
}, [data.settings.rates.total, loadRoute, runMutation]);
|
|
1253
1267
|
const uploadCompanyLogo = React.useCallback(async (file) => {
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1268
|
+
return runMutation(async () => {
|
|
1269
|
+
const result = asActionResult(unwrapResponse(await bridge.executeFileAction('upload_company_logo', file, {})));
|
|
1270
|
+
if (result.success === false || !result.data?.logoFileId)
|
|
1271
|
+
throw new Error(resolveActionMessage(result) || '公司 Logo 上传失败');
|
|
1272
|
+
return result.data.logoFileId;
|
|
1273
|
+
});
|
|
1274
|
+
}, [runMutation]);
|
|
1259
1275
|
const discardCompanyLogo = React.useCallback(async (logoFileId) => {
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1276
|
+
return runMutation(async () => {
|
|
1277
|
+
const result = asActionResult(unwrapResponse(await bridge.executeAction('discard_company_logo', { logoFileId })));
|
|
1278
|
+
if (result.success === false)
|
|
1279
|
+
throw new Error(resolveActionMessage(result) || '公司 Logo 草稿清理失败');
|
|
1280
|
+
});
|
|
1281
|
+
}, [runMutation]);
|
|
1264
1282
|
const openSalesFiles = React.useCallback((order) => {
|
|
1265
1283
|
setActiveSalesFileOrder(order);
|
|
1266
1284
|
if (!order)
|
|
@@ -1301,7 +1319,7 @@ function App() {
|
|
|
1301
1319
|
: page === 'customer-contract-review'
|
|
1302
1320
|
? _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' }) })
|
|
1303
1321
|
: undefined;
|
|
1304
|
-
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'), onHistory: () => setCatalogCommand('history') }) : 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, onHistoryQuery: queryCatalogHistory, 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, onHistoryQuery: queryCatalogHistory, 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, detailRefreshKey: analyticsDetailRefreshKey, busy: busy || actionBusy, error: analyticsError, onQuery: queryAnalytics, onDetailQuery: queryAnalyticsDetails, onDetailAction: openAnalyticsDetailAction, 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: () => {
|
|
1322
|
+
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'), onHistory: () => setCatalogCommand('history') }) : 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, onHistoryQuery: queryCatalogHistory, tasks: data.importTasks.items, onResolveStalledImport: resolveStalledCatalogImport, onRefreshTasks: loadTaskState, 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, onHistoryQuery: queryCatalogHistory, tasks: data.importTasks.items, onResolveStalledImport: resolveStalledCatalogImport, onRefreshTasks: loadTaskState, 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, detailRefreshKey: analyticsDetailRefreshKey, busy: busy || actionBusy, error: analyticsError, onQuery: queryAnalytics, onDetailQuery: queryAnalyticsDetails, onDetailAction: openAnalyticsDetailAction, 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, runMutation: runMutation, onNotify: setToast, onClose: () => setEditingSalesFile(null), onSaved: () => {
|
|
1305
1323
|
setEditingSalesFile(null);
|
|
1306
1324
|
if (page === 'analytics') {
|
|
1307
1325
|
setAnalyticsDetailRefreshKey(current => current + 1);
|
|
@@ -1492,9 +1510,6 @@ function actionResponseError(result, fallback) {
|
|
|
1492
1510
|
function asRecord(value) {
|
|
1493
1511
|
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
1494
1512
|
}
|
|
1495
|
-
function stringArray(value) {
|
|
1496
|
-
return Array.isArray(value) ? value.filter((item) => typeof item === 'string' && Boolean(item.trim())) : [];
|
|
1497
|
-
}
|
|
1498
1513
|
function summaryRequestParameters(value) {
|
|
1499
1514
|
const parameters = { ...asRecord(value) };
|
|
1500
1515
|
delete parameters.collection;
|
|
@@ -1635,7 +1650,7 @@ async function ensureExcelEditorRuntime() {
|
|
|
1635
1650
|
});
|
|
1636
1651
|
return excelEditorRuntimePromise;
|
|
1637
1652
|
}
|
|
1638
|
-
function SalesFileEditor({ file, onClose, onSaved, onNotify }) {
|
|
1653
|
+
function SalesFileEditor({ file, runMutation, onClose, onSaved, onNotify }) {
|
|
1639
1654
|
const containerRef = React.useRef(null);
|
|
1640
1655
|
const editorRef = React.useRef(null);
|
|
1641
1656
|
const [error, setError] = React.useState('');
|
|
@@ -1657,9 +1672,11 @@ function SalesFileEditor({ file, onClose, onSaved, onNotify }) {
|
|
|
1657
1672
|
if (cancelled || !container)
|
|
1658
1673
|
return;
|
|
1659
1674
|
const editor = await runtime.open({ container, base64: payload.base64, onSave: async (base64) => {
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1675
|
+
await runMutation(async () => {
|
|
1676
|
+
const saved = asActionResult(unwrapResponse(await bridge.executeAction('save_edited_sales_file', { id: file.id, base64, editVersion: payload.editVersion, expectedStorageFileId: payload.storageFileId })));
|
|
1677
|
+
if (saved.success === false)
|
|
1678
|
+
throw new Error(resolveActionMessage(saved) || '工作簿保存失败');
|
|
1679
|
+
});
|
|
1663
1680
|
} });
|
|
1664
1681
|
if (cancelled)
|
|
1665
1682
|
editor.dispose();
|
|
@@ -1674,7 +1691,7 @@ function SalesFileEditor({ file, onClose, onSaved, onNotify }) {
|
|
|
1674
1691
|
}
|
|
1675
1692
|
})();
|
|
1676
1693
|
return () => { cancelled = true; editorRef.current?.dispose(); editorRef.current = null; };
|
|
1677
|
-
}, [file.id]);
|
|
1694
|
+
}, [file.id, runMutation]);
|
|
1678
1695
|
const save = async () => { if (!editorRef.current)
|
|
1679
1696
|
return; setSaving(true); try {
|
|
1680
1697
|
await editorRef.current.save();
|