@chenchaolong/plugin-trade-compliance-workbench 1.0.48 → 1.0.50
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/dist/lib/entities/settings.entity.d.ts +2 -0
- package/dist/lib/entities/settings.entity.d.ts.map +1 -1
- package/dist/lib/entities/settings.entity.js +8 -0
- package/dist/lib/entities/settings.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 +4 -4
- package/dist/lib/remote-ui/analytics-contracts.d.ts +121 -145
- package/dist/lib/remote-ui/analytics-contracts.d.ts.map +1 -1
- package/dist/lib/remote-ui/analytics-contracts.js +8 -3
- package/dist/lib/remote-ui/analytics-contracts.js.map +1 -1
- package/dist/lib/remote-ui/app-source.js +130 -38
- package/dist/lib/remote-ui/app-source.js.map +1 -1
- package/dist/lib/remote-ui/model.d.ts.map +1 -1
- package/dist/lib/remote-ui/model.js +7 -43
- package/dist/lib/remote-ui/model.js.map +1 -1
- package/dist/lib/remote-ui/pages/analytics-charts.d.ts +12 -0
- package/dist/lib/remote-ui/pages/analytics-charts.d.ts.map +1 -0
- package/dist/lib/remote-ui/pages/analytics-charts.js +41 -0
- package/dist/lib/remote-ui/pages/analytics-charts.js.map +1 -0
- package/dist/lib/remote-ui/pages/analytics-dashboard.d.ts +11 -0
- package/dist/lib/remote-ui/pages/analytics-dashboard.d.ts.map +1 -0
- package/dist/lib/remote-ui/pages/analytics-dashboard.js +59 -0
- package/dist/lib/remote-ui/pages/analytics-dashboard.js.map +1 -0
- package/dist/lib/remote-ui/pages/analytics-detail-list.d.ts +13 -0
- package/dist/lib/remote-ui/pages/analytics-detail-list.d.ts.map +1 -0
- package/dist/lib/remote-ui/pages/analytics-detail-list.js +33 -0
- package/dist/lib/remote-ui/pages/analytics-detail-list.js.map +1 -0
- package/dist/lib/remote-ui/pages/analytics-format.d.ts +7 -0
- package/dist/lib/remote-ui/pages/analytics-format.d.ts.map +1 -0
- package/dist/lib/remote-ui/pages/analytics-format.js +31 -0
- package/dist/lib/remote-ui/pages/analytics-format.js.map +1 -0
- package/dist/lib/remote-ui/pages/analytics.d.ts +6 -9
- package/dist/lib/remote-ui/pages/analytics.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/analytics.js +77 -372
- package/dist/lib/remote-ui/pages/analytics.js.map +1 -1
- package/dist/lib/remote-ui/pages/catalogs.css +1 -3
- package/dist/lib/remote-ui/pages/catalogs.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/catalogs.js +0 -2
- package/dist/lib/remote-ui/pages/catalogs.js.map +1 -1
- package/dist/lib/remote-ui/pages/files-prototype.css +1 -1
- package/dist/lib/remote-ui/pages/files.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/files.js +9 -2
- package/dist/lib/remote-ui/pages/files.js.map +1 -1
- package/dist/lib/remote-ui/pages/sales-files.css +6 -0
- package/dist/lib/remote-ui/pages/sales.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/sales.js +1 -2
- package/dist/lib/remote-ui/pages/sales.js.map +1 -1
- package/dist/lib/remote-ui/pages/settings.d.ts +1 -0
- package/dist/lib/remote-ui/pages/settings.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/settings.js +5 -2
- package/dist/lib/remote-ui/pages/settings.js.map +1 -1
- package/dist/lib/remote-ui/styles.css +160 -249
- package/dist/lib/runtime-validation.d.ts.map +1 -1
- package/dist/lib/runtime-validation.js +6 -35
- package/dist/lib/runtime-validation.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 +10 -0
- package/dist/lib/schema-maintenance.d.ts.map +1 -0
- package/dist/lib/schema-maintenance.js +34 -0
- package/dist/lib/schema-maintenance.js.map +1 -0
- package/dist/lib/types.d.ts +72 -63
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/types.js +2 -3
- package/dist/lib/types.js.map +1 -1
- package/dist/lib/workbench-view.provider.d.ts +12 -62
- package/dist/lib/workbench-view.provider.d.ts.map +1 -1
- package/dist/lib/workbench-view.provider.js +20 -4
- package/dist/lib/workbench-view.provider.js.map +1 -1
- package/dist/lib/workbench.middleware.d.ts +8 -8
- package/dist/lib/workbench.plugin.d.ts +3 -3
- package/dist/lib/workbench.plugin.d.ts.map +1 -1
- package/dist/lib/workbench.plugin.js +6 -6
- package/dist/lib/workbench.plugin.js.map +1 -1
- package/dist/lib/workbench.service.d.ts +4 -33
- package/dist/lib/workbench.service.d.ts.map +1 -1
- package/dist/lib/workbench.service.js +236 -265
- package/dist/lib/workbench.service.js.map +1 -1
- package/package.json +1 -1
- package/dist/lib/legacy-schema-cleanup.d.ts +0 -9
- package/dist/lib/legacy-schema-cleanup.d.ts.map +0 -1
- package/dist/lib/legacy-schema-cleanup.js +0 -28
- package/dist/lib/legacy-schema-cleanup.js.map +0 -1
- package/dist/lib/remote-ui/pages/analytics-prototype.css +0 -307
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } 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';
|
|
@@ -18,8 +18,9 @@ import { adaptPurchaseReview } from './purchase-review-adapter.js';
|
|
|
18
18
|
import { CustomerContractsPage, adaptCustomerContractReview, createCustomerContractQueryCancelCoordinator, customerContractRouteEntryQuery, reconcileLatestCustomerContractQuery } from './pages/contracts.js';
|
|
19
19
|
import { SalesPage } from './pages/sales.js';
|
|
20
20
|
import { SalesFilesPage } from './pages/files.js';
|
|
21
|
-
import { AnalyticsPage
|
|
21
|
+
import { AnalyticsPage } from './pages/analytics.js';
|
|
22
22
|
import { SettingsPage } from './pages/settings.js';
|
|
23
|
+
import { AnalyticsComplianceCategoryKeys } from './analytics-contracts.js';
|
|
23
24
|
import { ContractImportError, findRecoverableContractImport, findResumableContractImports, runContractImportOrchestration, runCustomerContractReanalysis, taskTransition } from './contract-import.js';
|
|
24
25
|
import { createTaskChatSynchronizer, importTaskFromStart, importTaskProgressPercent, isLocalUploadPlaceholder, mergeImportTaskItems, removeImportTaskItem, taskBusinessRefreshRevision, taskRouteRefreshTarget } from './import-task-status.js';
|
|
25
26
|
import { compareTaskBusinessOrder, createPendingResultQueue, createPollingTaskTracker, isAbortError, isPollingObservationTimeout, submitAndRefreshTaskState } from './polling.js';
|
|
@@ -43,7 +44,7 @@ const procurementPageMeta = {
|
|
|
43
44
|
lines: { title: '采购商品列表', subtitle: '查看当前采购订单的商品明细及合规状态。' }
|
|
44
45
|
};
|
|
45
46
|
const bridge = createRemoteBridge();
|
|
46
|
-
const initialAnalyticsQuery = {
|
|
47
|
+
const initialAnalyticsQuery = { filters: {}, page: 1, pageSize: 20 };
|
|
47
48
|
let summaryRequestSequence = 0;
|
|
48
49
|
const DOWNLOAD_URL_REVOKE_DELAY_MS = 30_000;
|
|
49
50
|
function comparePendingReviewBusinessOrder(left, right) {
|
|
@@ -85,9 +86,7 @@ function App() {
|
|
|
85
86
|
const settingsRatesBulkReconcileRef = React.useRef(createDeferredBulkReconcileCoordinator());
|
|
86
87
|
const analyticsRequestsRef = React.useRef(createLatestCommittedRequest(initialAnalyticsQuery));
|
|
87
88
|
const [analyticsCommittedQuery, setAnalyticsCommittedQuery] = React.useState(initialAnalyticsQuery);
|
|
88
|
-
const analyticsPrototypeListenerCleanupRef = React.useRef(null);
|
|
89
89
|
const salesFilesPrototypeListenerCleanupRef = React.useRef(null);
|
|
90
|
-
const [analyticsPrototypeVisible, setAnalyticsPrototypeVisible] = React.useState(false);
|
|
91
90
|
const [salesFilesPrototypeState, setSalesFilesPrototypeState] = React.useState(undefined);
|
|
92
91
|
const [data, setData] = React.useState(() => normalizeWorkbenchData(null));
|
|
93
92
|
const importTaskItemsRef = React.useRef([]);
|
|
@@ -138,6 +137,7 @@ function App() {
|
|
|
138
137
|
const [toasts, setToasts] = React.useState([]);
|
|
139
138
|
const [toastTimers] = React.useState(() => createToastTimerRegistry((id) => setToasts((items) => removeToast(items, id))));
|
|
140
139
|
const [analyticsError, setAnalyticsError] = React.useState(null);
|
|
140
|
+
const [analyticsDetailRefreshKey, setAnalyticsDetailRefreshKey] = React.useState(0);
|
|
141
141
|
const setToast = React.useCallback((toast) => {
|
|
142
142
|
setToasts((items) => addToast(items, toast, createToastId));
|
|
143
143
|
}, []);
|
|
@@ -205,22 +205,16 @@ function App() {
|
|
|
205
205
|
return normalized.importTasks;
|
|
206
206
|
}
|
|
207
207
|
}, []);
|
|
208
|
-
const
|
|
209
|
-
analyticsPrototypeListenerCleanupRef.current?.();
|
|
210
|
-
analyticsPrototypeListenerCleanupRef.current = null;
|
|
208
|
+
const configureSalesFilesPrototypeCapability = React.useCallback((parameters) => {
|
|
211
209
|
salesFilesPrototypeListenerCleanupRef.current?.();
|
|
212
210
|
salesFilesPrototypeListenerCleanupRef.current = null;
|
|
213
|
-
setAnalyticsPrototypeVisible(false);
|
|
214
211
|
setSalesFilesPrototypeState(undefined);
|
|
215
212
|
if (parameters.visualCheck !== true)
|
|
216
213
|
return;
|
|
217
|
-
const showPrototypeBoard = () => setAnalyticsPrototypeVisible(true);
|
|
218
214
|
const showSalesFileUpload = () => setSalesFilesPrototypeState('upload');
|
|
219
215
|
const showSalesFileFormEditor = () => setSalesFilesPrototypeState('form-editor');
|
|
220
|
-
window.addEventListener('analytics-prototype-board', showPrototypeBoard);
|
|
221
216
|
window.addEventListener('sales-file-upload', showSalesFileUpload);
|
|
222
217
|
window.addEventListener('sales-file-form-editor', showSalesFileFormEditor);
|
|
223
|
-
analyticsPrototypeListenerCleanupRef.current = () => window.removeEventListener('analytics-prototype-board', showPrototypeBoard);
|
|
224
218
|
salesFilesPrototypeListenerCleanupRef.current = () => {
|
|
225
219
|
window.removeEventListener('sales-file-upload', showSalesFileUpload);
|
|
226
220
|
window.removeEventListener('sales-file-form-editor', showSalesFileFormEditor);
|
|
@@ -400,7 +394,7 @@ function App() {
|
|
|
400
394
|
React.useEffect(() => {
|
|
401
395
|
bridge.start({
|
|
402
396
|
onInit(nextContext) {
|
|
403
|
-
|
|
397
|
+
configureSalesFilesPrototypeCapability(asRecord(nextContext.initialQuery?.parameters));
|
|
404
398
|
contextRef.current = nextContext;
|
|
405
399
|
const normalized = normalizeWorkbenchData(nextContext.payload);
|
|
406
400
|
bridge.configure({ uploadPolicy: normalized.uploadPolicy });
|
|
@@ -412,14 +406,12 @@ function App() {
|
|
|
412
406
|
}
|
|
413
407
|
});
|
|
414
408
|
return () => {
|
|
415
|
-
analyticsPrototypeListenerCleanupRef.current?.();
|
|
416
|
-
analyticsPrototypeListenerCleanupRef.current = null;
|
|
417
409
|
salesFilesPrototypeListenerCleanupRef.current?.();
|
|
418
410
|
salesFilesPrototypeListenerCleanupRef.current = null;
|
|
419
411
|
blobUrlOwnerRef.current.dispose();
|
|
420
412
|
bridge.destroy();
|
|
421
413
|
};
|
|
422
|
-
}, [
|
|
414
|
+
}, [configureSalesFilesPrototypeCapability, load]);
|
|
423
415
|
React.useEffect(() => () => toastTimers.dispose(), [toastTimers]);
|
|
424
416
|
React.useEffect(() => {
|
|
425
417
|
const timer = window.setTimeout(() => bridge.reportResize(), 0);
|
|
@@ -1135,7 +1127,7 @@ function App() {
|
|
|
1135
1127
|
setBusy(true);
|
|
1136
1128
|
setAnalyticsError(null);
|
|
1137
1129
|
try {
|
|
1138
|
-
const payload = asRecord(unwrapResponse(await bridge.requestData({ page: requestedQuery.page, pageSize: requestedQuery.pageSize, parameters: { collection: 'analytics',
|
|
1130
|
+
const payload = asRecord(unwrapResponse(await bridge.requestData({ page: requestedQuery.page, pageSize: requestedQuery.pageSize, parameters: { collection: 'analytics', filters: encodeViewFilters(requestedQuery.filters) } })));
|
|
1139
1131
|
if (!requests.commit(requestId))
|
|
1140
1132
|
return;
|
|
1141
1133
|
setAnalyticsCommittedQuery(requests.committed());
|
|
@@ -1154,13 +1146,20 @@ function App() {
|
|
|
1154
1146
|
setBusy(requests.activeCount() + requestGateRef.current.activeCount() + routeRequestGateRef.current.activeCount() > 0);
|
|
1155
1147
|
}
|
|
1156
1148
|
}, [loadImportTasksFallback]);
|
|
1149
|
+
const queryAnalyticsDetails = React.useCallback(async (query) => {
|
|
1150
|
+
const pageSize = resolvePaginationPageSize(query.pageSize);
|
|
1151
|
+
const payload = asRecord(unwrapResponse(await bridge.requestData({
|
|
1152
|
+
page: query.page,
|
|
1153
|
+
pageSize,
|
|
1154
|
+
parameters: { collection: 'analyticsDetails', kind: query.kind, categoryKey: query.categoryKey, filters: encodeViewFilters(query.filters) }
|
|
1155
|
+
})));
|
|
1156
|
+
return analyticsDetailPage(payload, query.page, pageSize);
|
|
1157
|
+
}, []);
|
|
1157
1158
|
const exportAnalytics = React.useCallback(async (_request) => {
|
|
1158
1159
|
setActionBusy(true);
|
|
1159
1160
|
try {
|
|
1160
1161
|
const committed = analyticsRequestsRef.current.committed();
|
|
1161
|
-
const request = committed.
|
|
1162
|
-
? { mode: committed.mode, dimension: committed.dimension, filters: committed.filters, format: 'XLSX' }
|
|
1163
|
-
: { mode: committed.mode, dimension: committed.dimension, filters: committed.filters, format: 'XLSX' };
|
|
1162
|
+
const request = { filters: committed.filters, format: 'XLSX' };
|
|
1164
1163
|
const result = asActionResult(unwrapResponse(await bridge.executeAction('export_analytics', request)));
|
|
1165
1164
|
if (result.success === false)
|
|
1166
1165
|
throw new Error(resolveActionMessage(result) || '分析导出失败');
|
|
@@ -1175,6 +1174,52 @@ function App() {
|
|
|
1175
1174
|
setActionBusy(false);
|
|
1176
1175
|
}
|
|
1177
1176
|
}, []);
|
|
1177
|
+
const openAnalyticsDetailAction = React.useCallback((row) => {
|
|
1178
|
+
const action = row.action;
|
|
1179
|
+
if (action.kind === 'CUSTOMER_CONTRACT_REVIEW') {
|
|
1180
|
+
if (!action.importTaskId) {
|
|
1181
|
+
setToast({ message: '客户合同待审任务缺少任务标识', tone: 'error' });
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1184
|
+
void openImportTaskFromStatus({ id: action.importTaskId, documentType: 'CUSTOMER_CONTRACT', status: 'AWAITING_REVIEW', sourceFileName: row.referenceNo }).catch(error => setToast({ message: error instanceof Error ? error.message : '合同审核任务打开失败', tone: 'error' }));
|
|
1185
|
+
return;
|
|
1186
|
+
}
|
|
1187
|
+
if (action.kind === 'PURCHASE_CONTRACT_REVIEW') {
|
|
1188
|
+
if (!action.importTaskId) {
|
|
1189
|
+
setToast({ message: '采购合同待审任务缺少任务标识', tone: 'error' });
|
|
1190
|
+
return;
|
|
1191
|
+
}
|
|
1192
|
+
void openImportTaskFromStatus({ id: action.importTaskId, documentType: 'PURCHASE_CONTRACT', status: 'AWAITING_REVIEW', sourceFileName: row.referenceNo }).catch(error => setToast({ message: error instanceof Error ? error.message : '合同审核任务打开失败', tone: 'error' }));
|
|
1193
|
+
return;
|
|
1194
|
+
}
|
|
1195
|
+
if (action.kind === 'SALES_FILE_REVIEW') {
|
|
1196
|
+
if (!action.customerContractId || !action.purchaseOrderId || !action.salesOrderNoSnapshot || !action.fileType || !action.fileName || !action.storageFileId || action.editVersion == null || !action.generatedAt) {
|
|
1197
|
+
setToast({ message: '销售文件复核数据不完整', tone: 'error' });
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
setEditingSalesFile({
|
|
1201
|
+
id: row.id,
|
|
1202
|
+
customerContractId: action.customerContractId,
|
|
1203
|
+
purchaseOrderId: action.purchaseOrderId,
|
|
1204
|
+
salesOrderNoSnapshot: action.salesOrderNoSnapshot,
|
|
1205
|
+
fileType: action.fileType,
|
|
1206
|
+
fileName: action.fileName,
|
|
1207
|
+
storageFileId: action.storageFileId,
|
|
1208
|
+
editVersion: action.editVersion,
|
|
1209
|
+
generatedAt: action.generatedAt
|
|
1210
|
+
});
|
|
1211
|
+
return;
|
|
1212
|
+
}
|
|
1213
|
+
if (action.kind === 'PURCHASE_ORDER_VIEW' && action.supplierId && action.purchaseOrderId) {
|
|
1214
|
+
changeRouteRef.current({ type: 'procurement', level: 'lines', supplierId: action.supplierId, orderId: action.purchaseOrderId });
|
|
1215
|
+
return;
|
|
1216
|
+
}
|
|
1217
|
+
if (action.kind === 'SALES_ORDER_VIEW') {
|
|
1218
|
+
changeRouteRef.current({ type: 'sales', level: 'lines', customerId: action.customerId, orderKey: `${action.customerContractId}:${action.purchaseOrderId}` });
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
setToast({ message: '当前明细缺少可跳转的业务标识', tone: 'error' });
|
|
1222
|
+
}, [openImportTaskFromStatus, setToast]);
|
|
1178
1223
|
const saveSettings = React.useCallback(async (request) => {
|
|
1179
1224
|
const result = asActionResult(unwrapResponse(await bridge.executeAction('save_settings', request)));
|
|
1180
1225
|
if (result.success === false)
|
|
@@ -1248,10 +1293,16 @@ function App() {
|
|
|
1248
1293
|
: page === 'customer-contract-review'
|
|
1249
1294
|
? _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' }) })
|
|
1250
1295
|
: undefined;
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1296
|
+
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, 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: () => {
|
|
1297
|
+
setEditingSalesFile(null);
|
|
1298
|
+
if (page === 'analytics') {
|
|
1299
|
+
setAnalyticsDetailRefreshKey(current => current + 1);
|
|
1300
|
+
void queryAnalytics(analyticsRequestsRef.current.committed());
|
|
1301
|
+
}
|
|
1302
|
+
else if (activeSalesFileOrder) {
|
|
1303
|
+
void querySalesFiles('generatedFiles', { page: 1, pageSize: 20, filters: {}, parameters: { customerContractId: activeSalesFileOrder.customerContractId, purchaseOrderId: activeSalesFileOrder.purchaseOrderId } });
|
|
1304
|
+
}
|
|
1305
|
+
} })] }));
|
|
1255
1306
|
}
|
|
1256
1307
|
function routePage(route) {
|
|
1257
1308
|
if (route.type === 'procurement')
|
|
@@ -1357,23 +1408,64 @@ function analyticsQueryFromRoute(page, pageSize, values) {
|
|
|
1357
1408
|
const dateValue = values.orderDate;
|
|
1358
1409
|
const date = dateValue && typeof dateValue === 'object' && !Array.isArray(dateValue) ? dateValue : null;
|
|
1359
1410
|
const orderDate = date ? { from: optionalText(date.from), to: optionalText(date.to) } : undefined;
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1411
|
+
return { page, pageSize: requestedPageSize, filters: orderDate?.from || orderDate?.to ? { orderDate } : {} };
|
|
1412
|
+
}
|
|
1413
|
+
function optionalText(value) { const text = typeof value === 'string' ? value.trim() : ''; return text || undefined; }
|
|
1414
|
+
function analyticsDetailPage(payload, fallbackPage, pageSize) {
|
|
1415
|
+
const items = Array.isArray(payload.items) ? payload.items.map(analyticsDetailRow) : [];
|
|
1416
|
+
const total = Math.max(0, numberValue(payload.total, items.length));
|
|
1417
|
+
const pageCount = Math.max(1, numberValue(payload.pageCount, Math.ceil(total / pageSize)));
|
|
1418
|
+
return { items, total, page: Math.max(1, numberValue(payload.page, fallbackPage)), pageSize, pageCount };
|
|
1419
|
+
}
|
|
1420
|
+
function analyticsDetailRow(value) {
|
|
1421
|
+
const row = asRecord(value);
|
|
1422
|
+
const categoryKey = AnalyticsComplianceCategoryKeys.find(item => item === row.categoryKey) ?? (row.categoryKey === 'risk-order' ? 'risk-order' : null);
|
|
1423
|
+
if (!categoryKey)
|
|
1424
|
+
throw new Error('分析明细业务分类无效');
|
|
1370
1425
|
return {
|
|
1371
|
-
|
|
1372
|
-
|
|
1426
|
+
id: requiredAnalyticsText(row.id, '明细标识'),
|
|
1427
|
+
categoryKey,
|
|
1428
|
+
categoryLabel: requiredAnalyticsText(row.categoryLabel, '业务分类'),
|
|
1429
|
+
referenceNo: requiredAnalyticsText(row.referenceNo, '业务编号'),
|
|
1430
|
+
...(optionalText(row.subjectName) ? { subjectName: optionalText(row.subjectName) } : {}),
|
|
1431
|
+
...(optionalText(row.businessDate) ? { businessDate: optionalText(row.businessDate) } : {}),
|
|
1432
|
+
...(optionalText(row.reason) ? { reason: optionalText(row.reason) } : {}),
|
|
1433
|
+
...(row.amountCny == null ? {} : { amountCny: requiredAnalyticsText(row.amountCny, '风险金额') }),
|
|
1434
|
+
status: requiredAnalyticsText(row.status, '状态'),
|
|
1435
|
+
action: analyticsDetailAction(row.action)
|
|
1373
1436
|
};
|
|
1374
1437
|
}
|
|
1375
|
-
function
|
|
1376
|
-
|
|
1438
|
+
function analyticsDetailAction(value) {
|
|
1439
|
+
const action = asRecord(value);
|
|
1440
|
+
const id = requiredAnalyticsText(action.id, '操作标识');
|
|
1441
|
+
if (action.kind === 'CUSTOMER_CONTRACT_REVIEW')
|
|
1442
|
+
return { kind: action.kind, label: '审核', id, importTaskId: requiredAnalyticsText(action.importTaskId, '客户合同任务标识') };
|
|
1443
|
+
if (action.kind === 'PURCHASE_CONTRACT_REVIEW')
|
|
1444
|
+
return { kind: action.kind, label: '审核', id, importTaskId: requiredAnalyticsText(action.importTaskId, '采购合同任务标识') };
|
|
1445
|
+
if (action.kind === 'PURCHASE_ORDER_VIEW')
|
|
1446
|
+
return { kind: action.kind, label: analyticsActionLabel(action.label), id, purchaseOrderId: requiredAnalyticsText(action.purchaseOrderId, '采购订单标识'), supplierId: requiredAnalyticsText(action.supplierId, '供应商标识') };
|
|
1447
|
+
if (action.kind === 'SALES_ORDER_VIEW')
|
|
1448
|
+
return { kind: action.kind, label: analyticsActionLabel(action.label), id, customerId: requiredAnalyticsText(action.customerId, '客户标识'), customerContractId: requiredAnalyticsText(action.customerContractId, '客户合同标识'), purchaseOrderId: requiredAnalyticsText(action.purchaseOrderId, '采购订单标识') };
|
|
1449
|
+
if (action.kind === 'SALES_FILE_REVIEW') {
|
|
1450
|
+
const fileType = ['PI', 'CI', 'PL', 'CUSTOMS_DECLARATION'].find(item => item === action.fileType);
|
|
1451
|
+
const editVersion = Number(action.editVersion);
|
|
1452
|
+
if (!fileType || !Number.isInteger(editVersion) || editVersion < 0)
|
|
1453
|
+
throw new Error('销售文件复核操作数据无效');
|
|
1454
|
+
return { kind: action.kind, label: '复核', id, customerContractId: requiredAnalyticsText(action.customerContractId, '客户合同标识'), purchaseOrderId: requiredAnalyticsText(action.purchaseOrderId, '采购订单标识'), salesOrderNoSnapshot: requiredAnalyticsText(action.salesOrderNoSnapshot, '销售订单编号'), fileType, fileName: requiredAnalyticsText(action.fileName, '文件名'), storageFileId: requiredAnalyticsText(action.storageFileId, '存储文件标识'), editVersion, generatedAt: requiredAnalyticsText(action.generatedAt, '生成时间') };
|
|
1455
|
+
}
|
|
1456
|
+
throw new Error('分析明细操作类型无效');
|
|
1457
|
+
}
|
|
1458
|
+
function analyticsActionLabel(value) {
|
|
1459
|
+
if (value === '查看' || value === '跟进')
|
|
1460
|
+
return value;
|
|
1461
|
+
throw new Error('分析明细操作名称无效');
|
|
1462
|
+
}
|
|
1463
|
+
function requiredAnalyticsText(value, label) {
|
|
1464
|
+
const text = optionalText(value);
|
|
1465
|
+
if (!text)
|
|
1466
|
+
throw new Error(`${label}不能为空`);
|
|
1467
|
+
return text;
|
|
1468
|
+
}
|
|
1377
1469
|
function asActionResult(value) {
|
|
1378
1470
|
return value && typeof value === 'object' && !Array.isArray(value) ? value : { success: true, data: value };
|
|
1379
1471
|
}
|