@chenchaolong/plugin-trade-compliance-workbench 1.0.6 → 1.0.7
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/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/adapters/hsbianma.provider.d.ts +6 -8
- package/dist/lib/adapters/hsbianma.provider.d.ts.map +1 -1
- package/dist/lib/adapters/hsbianma.provider.js +159 -53
- package/dist/lib/adapters/hsbianma.provider.js.map +1 -1
- package/dist/lib/adapters/translation.provider.d.ts +7 -1
- package/dist/lib/adapters/translation.provider.d.ts.map +1 -1
- package/dist/lib/adapters/translation.provider.js +15 -5
- package/dist/lib/adapters/translation.provider.js.map +1 -1
- package/dist/lib/domain/catalog-localization.d.ts +14 -0
- package/dist/lib/domain/catalog-localization.d.ts.map +1 -0
- package/dist/lib/domain/catalog-localization.js +19 -0
- package/dist/lib/domain/catalog-localization.js.map +1 -0
- package/dist/lib/domain/file-name.d.ts +8 -0
- package/dist/lib/domain/file-name.d.ts.map +1 -0
- package/dist/lib/domain/file-name.js +111 -0
- package/dist/lib/domain/file-name.js.map +1 -0
- package/dist/lib/domain/import-issues.d.ts +4 -0
- package/dist/lib/domain/import-issues.d.ts.map +1 -1
- package/dist/lib/domain/import-issues.js +14 -3
- package/dist/lib/domain/import-issues.js.map +1 -1
- package/dist/lib/entities/catalog.entity.d.ts +5 -1
- package/dist/lib/entities/catalog.entity.d.ts.map +1 -1
- package/dist/lib/entities/catalog.entity.js +16 -0
- package/dist/lib/entities/catalog.entity.js.map +1 -1
- package/dist/lib/entities/customer.entity.d.ts +3 -0
- package/dist/lib/entities/customer.entity.d.ts.map +1 -1
- package/dist/lib/entities/customer.entity.js +12 -0
- package/dist/lib/entities/customer.entity.js.map +1 -1
- package/dist/lib/entities/shared.entity.d.ts +9 -0
- package/dist/lib/entities/shared.entity.d.ts.map +1 -1
- package/dist/lib/entities/shared.entity.js +33 -0
- package/dist/lib/entities/shared.entity.js.map +1 -1
- package/dist/lib/import-jobs/agent-handoff.dispatcher.d.ts +15 -0
- package/dist/lib/import-jobs/agent-handoff.dispatcher.d.ts.map +1 -0
- package/dist/lib/import-jobs/agent-handoff.dispatcher.js +114 -0
- package/dist/lib/import-jobs/agent-handoff.dispatcher.js.map +1 -0
- package/dist/lib/import-jobs/contracts.d.ts +76 -0
- package/dist/lib/import-jobs/contracts.d.ts.map +1 -0
- package/dist/lib/import-jobs/contracts.js +31 -0
- package/dist/lib/import-jobs/contracts.js.map +1 -0
- package/dist/lib/import-jobs/import-command.builder.d.ts +30 -0
- package/dist/lib/import-jobs/import-command.builder.d.ts.map +1 -0
- package/dist/lib/import-jobs/import-command.builder.js +97 -0
- package/dist/lib/import-jobs/import-command.builder.js.map +1 -0
- package/dist/lib/import-jobs/import-job-callback.processor.d.ts +15 -0
- package/dist/lib/import-jobs/import-job-callback.processor.d.ts.map +1 -0
- package/dist/lib/import-jobs/import-job-callback.processor.js +107 -0
- package/dist/lib/import-jobs/import-job-callback.processor.js.map +1 -0
- package/dist/lib/import-jobs/import-job.coordinator.d.ts +17 -0
- package/dist/lib/import-jobs/import-job.coordinator.d.ts.map +1 -0
- package/dist/lib/import-jobs/import-job.coordinator.js +256 -0
- package/dist/lib/import-jobs/import-job.coordinator.js.map +1 -0
- package/dist/lib/import-jobs/import-job.processor.d.ts +24 -0
- package/dist/lib/import-jobs/import-job.processor.d.ts.map +1 -0
- package/dist/lib/import-jobs/import-job.processor.js +213 -0
- package/dist/lib/import-jobs/import-job.processor.js.map +1 -0
- package/dist/lib/import-jobs/import-timeout.processor.d.ts +12 -0
- package/dist/lib/import-jobs/import-timeout.processor.d.ts.map +1 -0
- package/dist/lib/import-jobs/import-timeout.processor.js +37 -0
- package/dist/lib/import-jobs/import-timeout.processor.js.map +1 -0
- package/dist/lib/import-jobs/organization-lease.d.ts +39 -0
- package/dist/lib/import-jobs/organization-lease.d.ts.map +1 -0
- package/dist/lib/import-jobs/organization-lease.js +136 -0
- package/dist/lib/import-jobs/organization-lease.js.map +1 -0
- package/dist/lib/plugin-context.token.d.ts +4 -0
- package/dist/lib/plugin-context.token.d.ts.map +1 -0
- package/dist/lib/plugin-context.token.js +2 -0
- package/dist/lib/plugin-context.token.js.map +1 -0
- package/dist/lib/public-dto.d.ts +6 -0
- package/dist/lib/public-dto.d.ts.map +1 -0
- package/dist/lib/public-dto.js +33 -0
- package/dist/lib/public-dto.js.map +1 -0
- package/dist/lib/remote-components/trade-compliance-workbench/app.css +1 -1
- package/dist/lib/remote-components/trade-compliance-workbench/app.js +5 -5
- package/dist/lib/remote-ui/action-execution.d.ts +26 -0
- package/dist/lib/remote-ui/action-execution.d.ts.map +1 -0
- package/dist/lib/remote-ui/action-execution.js +85 -0
- package/dist/lib/remote-ui/action-execution.js.map +1 -0
- package/dist/lib/remote-ui/analytics-contracts.d.ts +3 -2
- package/dist/lib/remote-ui/analytics-contracts.d.ts.map +1 -1
- package/dist/lib/remote-ui/analytics-contracts.js.map +1 -1
- package/dist/lib/remote-ui/app-source.d.ts.map +1 -1
- package/dist/lib/remote-ui/app-source.js +426 -169
- package/dist/lib/remote-ui/app-source.js.map +1 -1
- package/dist/lib/remote-ui/company-logo-lifecycle.d.ts +14 -0
- package/dist/lib/remote-ui/company-logo-lifecycle.d.ts.map +1 -0
- package/dist/lib/remote-ui/company-logo-lifecycle.js +52 -0
- package/dist/lib/remote-ui/company-logo-lifecycle.js.map +1 -0
- package/dist/lib/remote-ui/components/data-grid.d.ts +9 -4
- package/dist/lib/remote-ui/components/data-grid.d.ts.map +1 -1
- package/dist/lib/remote-ui/components/data-grid.js +41 -9
- package/dist/lib/remote-ui/components/data-grid.js.map +1 -1
- package/dist/lib/remote-ui/components/forms.d.ts +6 -1
- package/dist/lib/remote-ui/components/forms.d.ts.map +1 -1
- package/dist/lib/remote-ui/components/forms.js +6 -1
- package/dist/lib/remote-ui/components/forms.js.map +1 -1
- package/dist/lib/remote-ui/contract-import.d.ts +37 -32
- package/dist/lib/remote-ui/contract-import.d.ts.map +1 -1
- package/dist/lib/remote-ui/contract-import.js +51 -59
- package/dist/lib/remote-ui/contract-import.js.map +1 -1
- package/dist/lib/remote-ui/contracts.d.ts +20 -27
- package/dist/lib/remote-ui/contracts.d.ts.map +1 -1
- package/dist/lib/remote-ui/contracts.js +8 -0
- package/dist/lib/remote-ui/contracts.js.map +1 -1
- package/dist/lib/remote-ui/customer-contract-contracts.d.ts +5 -11
- package/dist/lib/remote-ui/customer-contract-contracts.d.ts.map +1 -1
- package/dist/lib/remote-ui/customer-contract-contracts.js.map +1 -1
- package/dist/lib/remote-ui/date-time.d.ts +3 -0
- package/dist/lib/remote-ui/date-time.d.ts.map +1 -0
- package/dist/lib/remote-ui/date-time.js +58 -0
- package/dist/lib/remote-ui/date-time.js.map +1 -0
- package/dist/lib/remote-ui/model.d.ts +26 -1
- package/dist/lib/remote-ui/model.d.ts.map +1 -1
- package/dist/lib/remote-ui/model.js +151 -15
- package/dist/lib/remote-ui/model.js.map +1 -1
- package/dist/lib/remote-ui/pages/analytics.d.ts +2 -0
- package/dist/lib/remote-ui/pages/analytics.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/analytics.js +11 -6
- package/dist/lib/remote-ui/pages/analytics.js.map +1 -1
- package/dist/lib/remote-ui/pages/catalogs.d.ts +35 -14
- package/dist/lib/remote-ui/pages/catalogs.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/catalogs.js +132 -85
- package/dist/lib/remote-ui/pages/catalogs.js.map +1 -1
- package/dist/lib/remote-ui/pages/contracts.d.ts +8 -5
- package/dist/lib/remote-ui/pages/contracts.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/contracts.js +23 -22
- package/dist/lib/remote-ui/pages/contracts.js.map +1 -1
- package/dist/lib/remote-ui/pages/files.d.ts +11 -3
- package/dist/lib/remote-ui/pages/files.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/files.js +50 -12
- package/dist/lib/remote-ui/pages/files.js.map +1 -1
- package/dist/lib/remote-ui/pages/procurement.d.ts +6 -4
- package/dist/lib/remote-ui/pages/procurement.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/procurement.js +44 -50
- package/dist/lib/remote-ui/pages/procurement.js.map +1 -1
- package/dist/lib/remote-ui/pages/sales.d.ts +5 -0
- package/dist/lib/remote-ui/pages/sales.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/sales.js +34 -15
- package/dist/lib/remote-ui/pages/sales.js.map +1 -1
- package/dist/lib/remote-ui/pages/settings.d.ts +15 -13
- package/dist/lib/remote-ui/pages/settings.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/settings.js +50 -68
- package/dist/lib/remote-ui/pages/settings.js.map +1 -1
- package/dist/lib/remote-ui/polling.d.ts +41 -0
- package/dist/lib/remote-ui/polling.d.ts.map +1 -0
- package/dist/lib/remote-ui/polling.js +148 -0
- package/dist/lib/remote-ui/polling.js.map +1 -0
- package/dist/lib/remote-ui/procurement-contracts.d.ts +4 -9
- package/dist/lib/remote-ui/procurement-contracts.d.ts.map +1 -1
- package/dist/lib/remote-ui/sales-contracts.d.ts +2 -2
- package/dist/lib/remote-ui/sales-contracts.d.ts.map +1 -1
- package/dist/lib/remote-ui/settings-contracts.d.ts +9 -1
- package/dist/lib/remote-ui/settings-contracts.d.ts.map +1 -1
- package/dist/lib/remote-ui/settings-contracts.js.map +1 -1
- package/dist/lib/remote-ui/toast-queue.d.ts +29 -0
- package/dist/lib/remote-ui/toast-queue.d.ts.map +1 -0
- package/dist/lib/remote-ui/toast-queue.js +71 -0
- package/dist/lib/remote-ui/toast-queue.js.map +1 -0
- package/dist/lib/sales-file.service.d.ts.map +1 -1
- package/dist/lib/sales-file.service.js +5 -5
- package/dist/lib/sales-file.service.js.map +1 -1
- package/dist/lib/types.d.ts +5 -0
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/types.js +3 -0
- package/dist/lib/types.js.map +1 -1
- package/dist/lib/workbench-action-contract.d.ts +4 -0
- package/dist/lib/workbench-action-contract.d.ts.map +1 -0
- package/dist/lib/workbench-action-contract.js +43 -0
- package/dist/lib/workbench-action-contract.js.map +1 -0
- package/dist/lib/workbench-view.provider.d.ts +57 -78
- package/dist/lib/workbench-view.provider.d.ts.map +1 -1
- package/dist/lib/workbench-view.provider.js +89 -91
- package/dist/lib/workbench-view.provider.js.map +1 -1
- package/dist/lib/workbench.middleware.d.ts +129 -72
- package/dist/lib/workbench.middleware.d.ts.map +1 -1
- package/dist/lib/workbench.middleware.js +16 -3
- package/dist/lib/workbench.middleware.js.map +1 -1
- package/dist/lib/workbench.plugin.d.ts +3 -1
- package/dist/lib/workbench.plugin.d.ts.map +1 -1
- package/dist/lib/workbench.plugin.js +13 -4
- package/dist/lib/workbench.plugin.js.map +1 -1
- package/dist/lib/workbench.service.d.ts +136 -40
- package/dist/lib/workbench.service.d.ts.map +1 -1
- package/dist/lib/workbench.service.js +985 -71
- package/dist/lib/workbench.service.js.map +1 -1
- package/package.json +6 -5
|
@@ -2,8 +2,13 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { DataGrid, FilterPanel, InfoNotice } from '../components/data-grid.js';
|
|
4
4
|
import { ConfirmDialog, FieldGrid, FormField, Modal, ReviewSplit, UploadZone } from '../components/forms.js';
|
|
5
|
+
import { isPaginationPageSize } from '../contracts.js';
|
|
5
6
|
import { getConfiguredUploadPolicy } from '../upload-policy.js';
|
|
7
|
+
import { formatBeijingDateTime, formatBusinessDate } from '../date-time.js';
|
|
6
8
|
import './catalogs.css';
|
|
9
|
+
import { createLatestRequestGate } from '../model.js';
|
|
10
|
+
import { abortablePollingDelay, createPollingObservation, pollWithRetry } from '../polling.js';
|
|
11
|
+
import { runNotifiedOperation } from '../toast-queue.js';
|
|
7
12
|
const PAGE_SIZE = 20;
|
|
8
13
|
const ACCEPTED_CATALOG_FILES = '.pdf,.doc,.docx,.xls,.xlsx,.png,.jpg,.jpeg,.webp';
|
|
9
14
|
const formatNotice = () => `支持 PDF、DOC、DOCX、XLS、XLSX、PNG、JPEG、WEBP,单文件最大 ${getConfiguredUploadPolicy().maxUploadMb} MB。`;
|
|
@@ -45,7 +50,6 @@ export function createCatalogActionApi({ onAction, onFileAction, onRefresh }) {
|
|
|
45
50
|
const mutate = async (actionKey, input) => {
|
|
46
51
|
const response = await onAction(actionKey, input);
|
|
47
52
|
assertActionSucceeded(response);
|
|
48
|
-
await onRefresh();
|
|
49
53
|
return response;
|
|
50
54
|
};
|
|
51
55
|
const mutateFile = async (actionKey, file, input) => {
|
|
@@ -80,26 +84,22 @@ export class CatalogImportError extends Error {
|
|
|
80
84
|
this.name = 'CatalogImportError';
|
|
81
85
|
}
|
|
82
86
|
}
|
|
83
|
-
export async function runCatalogImportOrchestration({ start,
|
|
87
|
+
export async function runCatalogImportOrchestration({ start, pollTask, refresh, wait = abortablePollingDelay, now = Date.now, intervalMs = 2000, timeoutMs = MAX_IMPORT_TIMEOUT_MS, onTransition, onPollError, signal }) {
|
|
84
88
|
assertActionSucceeded(start);
|
|
85
89
|
if (!isCatalogImportStartData(start.data))
|
|
86
90
|
throw new Error('上传响应缺少解析任务信息');
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const deadline = now() + timeoutMs;
|
|
100
|
-
while (now() < deadline) {
|
|
101
|
-
await wait(intervalMs);
|
|
102
|
-
const completion = await pollTask(start.data.task.id);
|
|
91
|
+
const observation = createPollingObservation({ now, timeoutMs, maxTimeoutMs: MAX_IMPORT_TIMEOUT_MS });
|
|
92
|
+
intervalMs = Math.max(1, intervalMs);
|
|
93
|
+
let previous = start.data.task;
|
|
94
|
+
const taskId = previous.id;
|
|
95
|
+
while (observation.active()) {
|
|
96
|
+
await wait(intervalMs, signal);
|
|
97
|
+
const attempt = await pollWithRetry(requestSignal => requestSignal ? pollTask(taskId, requestSignal) : pollTask(taskId), onPollError, signal);
|
|
98
|
+
if (!attempt.ok)
|
|
99
|
+
continue;
|
|
100
|
+
const completion = attempt.value;
|
|
101
|
+
onTransition?.(previous, completion.task);
|
|
102
|
+
previous = completion.task;
|
|
103
103
|
const status = completion.task.status;
|
|
104
104
|
if (status === 'SUCCEEDED') {
|
|
105
105
|
if (!completion.batch)
|
|
@@ -112,13 +112,16 @@ export async function runCatalogImportOrchestration({ start, invokeClientCommand
|
|
|
112
112
|
if (!ACTIVE_IMPORT_STATUSES.has(status))
|
|
113
113
|
throw new Error(`不支持的导入任务状态:${status}`);
|
|
114
114
|
}
|
|
115
|
-
throw
|
|
115
|
+
throw observation.expired('目录任务观察窗口已结束', previous);
|
|
116
116
|
}
|
|
117
117
|
export function findActiveCatalogImportTask(items) {
|
|
118
118
|
return items
|
|
119
|
-
.filter((task) => (task.documentType === 'CONTROLLED_CATALOG' || task.documentType === 'SANCTION_CATALOG') && ACTIVE_IMPORT_STATUSES.has(task.status)
|
|
119
|
+
.filter((task) => (task.documentType === 'CONTROLLED_CATALOG' || task.documentType === 'SANCTION_CATALOG') && ACTIVE_IMPORT_STATUSES.has(task.status))
|
|
120
120
|
.sort((left, right) => String(right.updatedAt ?? '').localeCompare(String(left.updatedAt ?? '')))[0] ?? null;
|
|
121
121
|
}
|
|
122
|
+
export function findActiveCatalogImportTasks(items) {
|
|
123
|
+
return items.filter((task) => (task.documentType === 'CONTROLLED_CATALOG' || task.documentType === 'SANCTION_CATALOG') && ACTIVE_IMPORT_STATUSES.has(task.status));
|
|
124
|
+
}
|
|
122
125
|
export function findLatestFailedCatalogImportTask(items, documentType) {
|
|
123
126
|
const retriedTaskIds = new Set(items.map((task) => task.retryOfTaskId).filter((id) => Boolean(id)));
|
|
124
127
|
return items
|
|
@@ -129,17 +132,19 @@ export function createCatalogQueryScheduler(query, debounceMs = 300) {
|
|
|
129
132
|
let timer = null;
|
|
130
133
|
const cancel = () => { if (timer)
|
|
131
134
|
clearTimeout(timer); timer = null; };
|
|
132
|
-
const
|
|
135
|
+
const run = (page, filters, pageSize = PAGE_SIZE) => { cancel(); query(page, filters, pageSize); };
|
|
133
136
|
return {
|
|
134
|
-
schedule(type, filters) {
|
|
137
|
+
schedule(type, filters, pageSize = PAGE_SIZE) {
|
|
135
138
|
cancel();
|
|
136
139
|
if (type !== 'text') {
|
|
137
|
-
|
|
140
|
+
run(1, filters, pageSize);
|
|
138
141
|
return;
|
|
139
142
|
}
|
|
140
|
-
timer = setTimeout(() => { timer = null; query(filters); }, debounceMs);
|
|
143
|
+
timer = setTimeout(() => { timer = null; query(1, filters, pageSize); }, debounceMs);
|
|
141
144
|
},
|
|
142
|
-
flush,
|
|
145
|
+
flush: (filters, pageSize = PAGE_SIZE) => run(1, filters, pageSize),
|
|
146
|
+
page: (page, filters, pageSize = PAGE_SIZE) => run(page, filters, pageSize),
|
|
147
|
+
pageSize: (pageSize, filters) => run(1, filters, pageSize),
|
|
143
148
|
cancel
|
|
144
149
|
};
|
|
145
150
|
}
|
|
@@ -217,41 +222,47 @@ export function ControlledGoodsPage(props) {
|
|
|
217
222
|
if (props.command)
|
|
218
223
|
props.onCommandHandled?.();
|
|
219
224
|
}, [props.command, props.onCommandHandled]);
|
|
220
|
-
const query = React.useCallback((page, nextFilters) => props.onQuery({ page, pageSize
|
|
221
|
-
const scheduler = React.useMemo(() => createCatalogQueryScheduler((nextFilters) => void query(
|
|
225
|
+
const query = React.useCallback((page, nextFilters, pageSize) => props.onQuery({ page, pageSize, filters: nextFilters }), [props.onQuery]);
|
|
226
|
+
const scheduler = React.useMemo(() => createCatalogQueryScheduler((page, nextFilters, pageSize) => void query(page, nextFilters, pageSize)), [query]);
|
|
222
227
|
React.useEffect(() => () => scheduler.cancel(), [scheduler]);
|
|
223
228
|
React.useEffect(() => { issueControllerRef.current?.mount(); return () => issueControllerRef.current?.dispose(); }, []);
|
|
224
229
|
const changeFilter = (key, value) => {
|
|
225
230
|
const nextFilters = { ...filters, [key]: value };
|
|
226
231
|
setFilters(nextFilters);
|
|
227
|
-
scheduler.schedule(controlledGoodsFilters.find((definition) => definition.key === key)?.type ?? 'text', nextFilters);
|
|
232
|
+
scheduler.schedule(controlledGoodsFilters.find((definition) => definition.key === key)?.type ?? 'text', nextFilters, pagination.pageSize);
|
|
228
233
|
};
|
|
229
|
-
const queryIssues = async (batchId, issueType, page = 1) => {
|
|
234
|
+
const queryIssues = async (batchId, issueType, page = 1, pageSize = PAGE_SIZE) => {
|
|
230
235
|
if (!props.onIssueQuery)
|
|
231
236
|
return;
|
|
232
|
-
await issueControllerRef.current?.load(props.onIssueQuery, { documentType: 'CONTROLLED_CATALOG', batchId, issueType, page, pageSize
|
|
237
|
+
await issueControllerRef.current?.load(props.onIssueQuery, { documentType: 'CONTROLLED_CATALOG', batchId, issueType, page, pageSize });
|
|
233
238
|
};
|
|
234
239
|
const displayedSummary = summary ?? (props.importCompletion?.task.documentType === 'CONTROLLED_CATALOG' ? props.importCompletion.batch ?? null : null);
|
|
235
240
|
const columns = [
|
|
236
|
-
{ type: 'custom', key: 'productName', header: '商品名称', width: 180, render: (_, row) => _jsx(LinkButton, { icon: "ri-search-line", title:
|
|
237
|
-
{ type: 'custom', key: 'hsCode', header: '海关编码', width: 130, render: (_, row) => row.hsCode ? _jsx(LinkButton, { icon: "ri-search-line", title:
|
|
238
|
-
{ type: 'text', key: 'catalogType', header: '目录类型', width: 130, format: (value) => labelFor(value, catalogTypeLabels) },
|
|
241
|
+
{ type: 'custom', key: 'productName', header: '商品名称', width: 180, title: (_, row) => row.productName || undefined, render: (_, row) => _jsx(LinkButton, { icon: "ri-search-line", title: row.productName || '—', ariaLabel: `按商品名称查询海关编码:${row.productName || '—'}`, onClick: () => setHsQuery(hsLookupQuery(row, 'productName')), children: row.productName || '—' }) },
|
|
242
|
+
{ type: 'custom', key: 'hsCode', header: '海关编码', width: 130, title: (_, row) => row.hsCode || undefined, render: (_, row) => row.hsCode ? _jsx(LinkButton, { icon: "ri-search-line", title: row.hsCode, ariaLabel: `按编码查询海关编码:${row.hsCode}`, onClick: () => setHsQuery(hsLookupQuery(row, 'hsCode')), children: row.hsCode }) : '—' },
|
|
243
|
+
{ type: 'text', key: 'catalogType', header: '目录类型', width: 130, truncate: false, format: (value) => labelFor(value, catalogTypeLabels) },
|
|
239
244
|
{ type: 'ellipsis', key: 'controlDescription', header: '管控说明', width: 220, maxWidth: 220 },
|
|
240
|
-
{ type: 'text', key: 'sourceType', header: '来源类型', width: 110, format: (value) => labelFor(value, sourceTypeLabels) },
|
|
245
|
+
{ type: 'text', key: 'sourceType', header: '来源类型', width: 110, truncate: false, format: (value) => labelFor(value, sourceTypeLabels) },
|
|
241
246
|
{ type: 'ellipsis', key: 'sourceFileName', header: '来源文件', width: 190, maxWidth: 190 },
|
|
242
|
-
{ type: 'text', key: 'updatedAt', header: '更新时间', width: 170 }
|
|
247
|
+
{ type: 'text', key: 'updatedAt', header: '更新时间', width: 170, truncate: false, format: formatBeijingDateTime }
|
|
243
248
|
];
|
|
244
|
-
return (_jsxs("section", { className: "tcw-catalog-page tcw-catalog-page--controlled", "aria-label": "\u7BA1\u63A7\u5546\u54C1\u76EE\u5F55", children: [_jsx(FilterPanel, { definitions: controlledGoodsFilters, values: filters, onChange: changeFilter, onQuery: () => scheduler.flush(filters), onReset: () => { setFilters({}); scheduler.flush({}); } }), _jsx(InfoNotice, { children: formatNotice() }), props.importFailure?.documentType === 'CONTROLLED_CATALOG' && _jsx(CatalogImportStatusNotice, { task: props.importFailure, busy: props.busy, onRetry: (taskId) => props.onRetryImport?.(taskId) ?? Promise.resolve() }), displayedSummary && _jsx(CatalogImportSummaryNotice, { documentType: "CONTROLLED_CATALOG", batch: displayedSummary, busy: props.busy, onDownload: (documentType, batchId) => api.downloadImportIssues(documentType, batchId), onView: (batchId, issueType) => queryIssues(batchId, issueType) }), _jsx(DataGrid, { columns: columns, rows: rows, rowKey: "id", actionWidth: 160, actions: (row) => _jsxs(_Fragment, { children: [_jsxs("button", { type: "button", className: "tcw-catalog-operation-button", disabled: props.busy, onClick: () => setForm({ mode: 'review', item: row }), children: [_jsx("i", { className: "ri-edit-line", "aria-hidden": "true" }), "\u5BA1\u6838"] }), _jsxs("button", { type: "button", className: "tcw-catalog-operation-button is-danger", disabled: props.busy, onClick: () => setDeleteItem(row), children: [_jsx("i", { className: "ri-delete-bin-line", "aria-hidden": "true" }), "\u5220\u9664"] })] }), pagination: pagination, onPageChange: (nextPage) =>
|
|
249
|
+
return (_jsxs("section", { className: "tcw-catalog-page tcw-catalog-page--controlled", "aria-label": "\u7BA1\u63A7\u5546\u54C1\u76EE\u5F55", children: [_jsx(FilterPanel, { definitions: controlledGoodsFilters, values: filters, onChange: changeFilter, onQuery: () => scheduler.flush(filters, pagination.pageSize), onReset: () => { setFilters({}); scheduler.flush({}, pagination.pageSize); } }), _jsx(InfoNotice, { children: formatNotice() }), props.importFailure?.documentType === 'CONTROLLED_CATALOG' && _jsx(CatalogImportStatusNotice, { task: props.importFailure, busy: props.busy, onRetry: (taskId) => props.onRetryImport?.(taskId) ?? Promise.resolve() }), displayedSummary && _jsx(CatalogImportSummaryNotice, { documentType: "CONTROLLED_CATALOG", batch: displayedSummary, busy: props.busy, onDownload: (documentType, batchId) => api.downloadImportIssues(documentType, batchId), onView: (batchId, issueType) => queryIssues(batchId, issueType) }), _jsx(DataGrid, { columns: columns, rows: rows, rowKey: "id", actionWidth: 160, actions: (row) => _jsxs(_Fragment, { children: [_jsxs("button", { type: "button", className: "tcw-catalog-operation-button", disabled: props.busy, onClick: () => setForm({ mode: 'review', item: row }), children: [_jsx("i", { className: "ri-edit-line", "aria-hidden": "true" }), "\u5BA1\u6838"] }), _jsxs("button", { type: "button", className: "tcw-catalog-operation-button is-danger", disabled: props.busy, onClick: () => setDeleteItem(row), children: [_jsx("i", { className: "ri-delete-bin-line", "aria-hidden": "true" }), "\u5220\u9664"] })] }), pagination: pagination, onPageChange: (nextPage) => scheduler.page(nextPage, filters, pagination.pageSize), onPageSizeChange: (pageSize) => scheduler.pageSize(pageSize, filters), emptyText: "\u6682\u65E0\u7BA1\u63A7\u5546\u54C1\u6570\u636E" }), _jsx(CatalogUploadDialog, { open: uploadOpen, kind: "controlled", batches: props.batches, busy: props.busy, onClose: () => setUploadOpen(false), onNotify: props.onNotify, onUpload: async (file, input) => {
|
|
245
250
|
const response = await api.uploadControlledCatalog(file, input);
|
|
246
251
|
const completion = asImportCompletion(response.data);
|
|
247
252
|
if (completion.batch)
|
|
248
253
|
setSummary(completion.batch);
|
|
249
254
|
setUploadOpen(false);
|
|
250
|
-
} }), _jsx(ControlledGoodsFormDialog, { open: Boolean(form), mode: form?.mode ?? 'create', item: form?.item ?? null, busy: props.busy, onClose: () => setForm(null), onSave: async (input) => {
|
|
251
|
-
|
|
255
|
+
} }), _jsx(ControlledGoodsFormDialog, { open: Boolean(form), mode: form?.mode ?? 'create', item: form?.item ?? null, busy: props.busy, onClose: () => setForm(null), onSave: async (input) => {
|
|
256
|
+
const saved = await runNotifiedOperation(async () => { form?.mode === 'review' && form.item ? await api.updateControlledGoods({ ...input, id: form.item.id }) : await api.saveControlledGoods(input); }, props.onNotify, { success: form?.mode === 'review' ? '管控商品保存成功' : '管控商品新增成功', error: '管控商品保存失败' });
|
|
257
|
+
if (saved)
|
|
258
|
+
setForm(null);
|
|
259
|
+
} }), _jsx(ConfirmDialog, { open: Boolean(deleteItem), title: "\u786E\u8BA4\u5220\u9664\u7BA1\u63A7\u5546\u54C1", message: `删除“${deleteItem?.productName ?? ''}”后,该记录将被逻辑删除。`, confirmText: "\u786E\u8BA4\u5220\u9664", danger: true, onCancel: () => setDeleteItem(null), onConfirm: () => { if (deleteItem)
|
|
260
|
+
void runNotifiedOperation(() => api.deleteEntity('controlledGoods', deleteItem.id).then(() => undefined), props.onNotify, { success: '管控商品删除成功', error: '管控商品删除失败' }).then((deleted) => { if (deleted)
|
|
261
|
+
setDeleteItem(null); }); } }), _jsx(HsCodeLookupDialog, { open: hsQuery !== null, initialQuery: hsQuery ?? '', onAction: props.onAction, onClose: () => setHsQuery(null) }), _jsx(CatalogImportIssuesDialog, { open: Boolean(issueState), documentType: "CONTROLLED_CATALOG", batchId: issueState?.batchId ?? '', issueType: issueState?.issueType ?? 'DUPLICATE', collection: issueState?.collection ?? { items: [] }, loading: issueState?.loading ?? false, error: issueState?.error ?? '', busy: props.busy, onClose: () => { issueControllerRef.current?.close(); setIssueState(null); }, onIssueTypeChange: (issueType) => { if (issueState)
|
|
252
262
|
void queryIssues(issueState.batchId, issueType); }, onPageChange: (page) => { if (issueState)
|
|
253
|
-
void queryIssues(issueState.batchId, issueState.issueType, page); },
|
|
254
|
-
void queryIssues(issueState.batchId, issueState.issueType,
|
|
263
|
+
void queryIssues(issueState.batchId, issueState.issueType, page, collectionPagination(issueState.collection).pageSize); }, onPageSizeChange: (pageSize) => { if (issueState)
|
|
264
|
+
void queryIssues(issueState.batchId, issueState.issueType, 1, pageSize); }, onRetry: () => { if (issueState)
|
|
265
|
+
void queryIssues(issueState.batchId, issueState.issueType, Number(issueState.collection.page) || 1, collectionPagination(issueState.collection).pageSize); } })] }));
|
|
255
266
|
}
|
|
256
267
|
export function SanctionedCompaniesPage(props) {
|
|
257
268
|
const [filters, setFilters] = React.useState({});
|
|
@@ -274,47 +285,53 @@ export function SanctionedCompaniesPage(props) {
|
|
|
274
285
|
if (props.command)
|
|
275
286
|
props.onCommandHandled?.();
|
|
276
287
|
}, [props.command, props.onCommandHandled]);
|
|
277
|
-
const query = React.useCallback((page, nextFilters) => props.onQuery({ page, pageSize
|
|
278
|
-
const scheduler = React.useMemo(() => createCatalogQueryScheduler((nextFilters) => void query(
|
|
288
|
+
const query = React.useCallback((page, nextFilters, pageSize) => props.onQuery({ page, pageSize, filters: nextFilters }), [props.onQuery]);
|
|
289
|
+
const scheduler = React.useMemo(() => createCatalogQueryScheduler((page, nextFilters, pageSize) => void query(page, nextFilters, pageSize)), [query]);
|
|
279
290
|
React.useEffect(() => () => scheduler.cancel(), [scheduler]);
|
|
280
291
|
React.useEffect(() => { issueControllerRef.current?.mount(); return () => issueControllerRef.current?.dispose(); }, []);
|
|
281
292
|
const changeFilter = (key, value) => {
|
|
282
293
|
const nextFilters = { ...filters, [key]: value };
|
|
283
294
|
setFilters(nextFilters);
|
|
284
|
-
scheduler.schedule(sanctionedCompanyFilters.find((definition) => definition.key === key)?.type ?? 'text', nextFilters);
|
|
295
|
+
scheduler.schedule(sanctionedCompanyFilters.find((definition) => definition.key === key)?.type ?? 'text', nextFilters, pagination.pageSize);
|
|
285
296
|
};
|
|
286
|
-
const queryIssues = async (batchId, issueType, page = 1) => {
|
|
297
|
+
const queryIssues = async (batchId, issueType, page = 1, pageSize = PAGE_SIZE) => {
|
|
287
298
|
if (!props.onIssueQuery)
|
|
288
299
|
return;
|
|
289
|
-
await issueControllerRef.current?.load(props.onIssueQuery, { documentType: 'SANCTION_CATALOG', batchId, issueType, page, pageSize
|
|
300
|
+
await issueControllerRef.current?.load(props.onIssueQuery, { documentType: 'SANCTION_CATALOG', batchId, issueType, page, pageSize });
|
|
290
301
|
};
|
|
291
302
|
const displayedSummary = summary ?? (props.importCompletion?.task.documentType === 'SANCTION_CATALOG' ? props.importCompletion.batch ?? null : null);
|
|
292
303
|
const columns = [
|
|
293
|
-
{ type: 'custom', key: 'companyName', header: '公司名称', width: 210, render: (_, row) => _jsx(LinkButton, { icon: "ri-eye-line", title:
|
|
304
|
+
{ type: 'custom', key: 'companyName', header: '公司名称', width: 210, title: (_, row) => row.companyName || undefined, render: (_, row) => _jsx(LinkButton, { icon: "ri-eye-line", title: row.companyName || '—', ariaLabel: `查看制裁公司详情:${row.companyName || '—'}`, onClick: () => setForm({ mode: 'detail', item: row }), children: row.companyName || '—' }) },
|
|
294
305
|
{ type: 'text', key: 'countryOrRegion', header: '国家或地区', width: 120 },
|
|
295
306
|
{ type: 'ellipsis', key: 'sanctionListSource', header: '制裁名单来源', width: 190, maxWidth: 190 },
|
|
296
|
-
{ type: 'text', key: 'effectiveDate', header: '生效日期', width: 120 },
|
|
307
|
+
{ type: 'text', key: 'effectiveDate', header: '生效日期', width: 120, truncate: false, format: formatBusinessDate },
|
|
297
308
|
{ type: 'ellipsis', key: 'sanctionReason', header: '制裁原因', width: 220, maxWidth: 220 },
|
|
298
|
-
{ type: 'text', key: 'sourceType', header: '来源类型', width: 110, format: (value) => labelFor(value, sourceTypeLabels) },
|
|
309
|
+
{ type: 'text', key: 'sourceType', header: '来源类型', width: 110, truncate: false, format: (value) => labelFor(value, sourceTypeLabels) },
|
|
299
310
|
{ type: 'ellipsis', key: 'sourceFileName', header: '来源文件', width: 190, maxWidth: 190 },
|
|
300
|
-
{ type: 'text', key: 'updatedAt', header: '更新时间', width: 170 }
|
|
311
|
+
{ type: 'text', key: 'updatedAt', header: '更新时间', width: 170, truncate: false, format: formatBeijingDateTime }
|
|
301
312
|
];
|
|
302
|
-
return (_jsxs("section", { className: "tcw-catalog-page tcw-catalog-page--sanction", "aria-label": "\u516C\u53F8\u5236\u88C1\u76EE\u5F55", children: [_jsx(FilterPanel, { definitions: sanctionedCompanyFilters, values: filters, onChange: changeFilter, onQuery: () => scheduler.flush(filters), onReset: () => { setFilters({}); scheduler.flush({}); } }), _jsx(InfoNotice, { children: formatNotice() }), props.importFailure?.documentType === 'SANCTION_CATALOG' && _jsx(CatalogImportStatusNotice, { task: props.importFailure, busy: props.busy, onRetry: (taskId) => props.onRetryImport?.(taskId) ?? Promise.resolve() }), displayedSummary && _jsx(CatalogImportSummaryNotice, { documentType: "SANCTION_CATALOG", batch: displayedSummary, busy: props.busy, onDownload: (documentType, batchId) => api.downloadImportIssues(documentType, batchId), onView: (batchId, issueType) => queryIssues(batchId, issueType) }), _jsx(DataGrid, { columns: columns, rows: rows, rowKey: "id", actionWidth: 160, actions: (row) => _jsxs(_Fragment, { children: [_jsxs("button", { type: "button", className: "tcw-catalog-operation-button", disabled: props.busy, onClick: () => setForm({ mode: 'review', item: row }), children: [_jsx("i", { className: "ri-edit-line", "aria-hidden": "true" }), "\u5BA1\u6838"] }), _jsxs("button", { type: "button", className: "tcw-catalog-operation-button is-danger", disabled: props.busy, onClick: () => setDeleteItem(row), children: [_jsx("i", { className: "ri-delete-bin-line", "aria-hidden": "true" }), "\u5220\u9664"] })] }), pagination: pagination, onPageChange: (nextPage) =>
|
|
313
|
+
return (_jsxs("section", { className: "tcw-catalog-page tcw-catalog-page--sanction", "aria-label": "\u516C\u53F8\u5236\u88C1\u76EE\u5F55", children: [_jsx(FilterPanel, { definitions: sanctionedCompanyFilters, values: filters, onChange: changeFilter, onQuery: () => scheduler.flush(filters, pagination.pageSize), onReset: () => { setFilters({}); scheduler.flush({}, pagination.pageSize); } }), _jsx(InfoNotice, { children: formatNotice() }), props.importFailure?.documentType === 'SANCTION_CATALOG' && _jsx(CatalogImportStatusNotice, { task: props.importFailure, busy: props.busy, onRetry: (taskId) => props.onRetryImport?.(taskId) ?? Promise.resolve() }), displayedSummary && _jsx(CatalogImportSummaryNotice, { documentType: "SANCTION_CATALOG", batch: displayedSummary, busy: props.busy, onDownload: (documentType, batchId) => api.downloadImportIssues(documentType, batchId), onView: (batchId, issueType) => queryIssues(batchId, issueType) }), _jsx(DataGrid, { columns: columns, rows: rows, rowKey: "id", actionWidth: 160, actions: (row) => _jsxs(_Fragment, { children: [_jsxs("button", { type: "button", className: "tcw-catalog-operation-button", disabled: props.busy, onClick: () => setForm({ mode: 'review', item: row }), children: [_jsx("i", { className: "ri-edit-line", "aria-hidden": "true" }), "\u5BA1\u6838"] }), _jsxs("button", { type: "button", className: "tcw-catalog-operation-button is-danger", disabled: props.busy, onClick: () => setDeleteItem(row), children: [_jsx("i", { className: "ri-delete-bin-line", "aria-hidden": "true" }), "\u5220\u9664"] })] }), pagination: pagination, onPageChange: (nextPage) => scheduler.page(nextPage, filters, pagination.pageSize), onPageSizeChange: (pageSize) => scheduler.pageSize(pageSize, filters), emptyText: "\u6682\u65E0\u5236\u88C1\u516C\u53F8\u6570\u636E" }), _jsx(CatalogUploadDialog, { open: uploadOpen, kind: "sanction", batches: props.batches, busy: props.busy, onClose: () => setUploadOpen(false), onNotify: props.onNotify, onUpload: async (file, input) => {
|
|
303
314
|
const response = await api.uploadSanctionCatalog(file, input);
|
|
304
315
|
const completion = asImportCompletion(response.data);
|
|
305
316
|
if (completion.batch)
|
|
306
317
|
setSummary(completion.batch);
|
|
307
318
|
setUploadOpen(false);
|
|
308
|
-
} }), _jsx(SanctionedCompanyDialog, { open: Boolean(form), mode: form?.mode ?? 'create', item: form?.item ?? null, busy: props.busy, onClose: () => setForm(null), onSave: async (input) => {
|
|
309
|
-
|
|
319
|
+
} }), _jsx(SanctionedCompanyDialog, { open: Boolean(form), mode: form?.mode ?? 'create', item: form?.item ?? null, busy: props.busy, onClose: () => setForm(null), onSave: async (input) => {
|
|
320
|
+
const saved = await runNotifiedOperation(async () => { form?.mode === 'review' && form.item ? await api.updateSanctionedCompany({ ...input, id: form.item.id }) : await api.saveSanctionedCompany(input); }, props.onNotify, { success: form?.mode === 'review' ? '制裁公司保存成功' : '制裁公司新增成功', error: '制裁公司保存失败' });
|
|
321
|
+
if (saved)
|
|
322
|
+
setForm(null);
|
|
323
|
+
} }), _jsx(ConfirmDialog, { open: Boolean(deleteItem), title: "\u786E\u8BA4\u5220\u9664\u5236\u88C1\u516C\u53F8", message: `删除“${deleteItem?.companyName ?? ''}”后,该记录将被逻辑删除。`, confirmText: "\u786E\u8BA4\u5220\u9664", danger: true, onCancel: () => setDeleteItem(null), onConfirm: () => { if (deleteItem)
|
|
324
|
+
void runNotifiedOperation(() => api.deleteEntity('sanctionedCompany', deleteItem.id).then(() => undefined), props.onNotify, { success: '制裁公司删除成功', error: '制裁公司删除失败' }).then((deleted) => { if (deleted)
|
|
325
|
+
setDeleteItem(null); }); } }), _jsx(CatalogImportIssuesDialog, { open: Boolean(issueState), documentType: "SANCTION_CATALOG", batchId: issueState?.batchId ?? '', issueType: issueState?.issueType ?? 'DUPLICATE', collection: issueState?.collection ?? { items: [] }, loading: issueState?.loading ?? false, error: issueState?.error ?? '', busy: props.busy, onClose: () => { issueControllerRef.current?.close(); setIssueState(null); }, onIssueTypeChange: (issueType) => { if (issueState)
|
|
310
326
|
void queryIssues(issueState.batchId, issueType); }, onPageChange: (page) => { if (issueState)
|
|
311
|
-
void queryIssues(issueState.batchId, issueState.issueType, page); },
|
|
312
|
-
void queryIssues(issueState.batchId, issueState.issueType,
|
|
327
|
+
void queryIssues(issueState.batchId, issueState.issueType, page, collectionPagination(issueState.collection).pageSize); }, onPageSizeChange: (pageSize) => { if (issueState)
|
|
328
|
+
void queryIssues(issueState.batchId, issueState.issueType, 1, pageSize); }, onRetry: () => { if (issueState)
|
|
329
|
+
void queryIssues(issueState.batchId, issueState.issueType, Number(issueState.collection.page) || 1, collectionPagination(issueState.collection).pageSize); } })] }));
|
|
313
330
|
}
|
|
314
331
|
export function CatalogCommands({ busy, onUpload, onCreate }) {
|
|
315
332
|
return _jsxs(_Fragment, { children: [_jsxs("button", { type: "button", className: "tcw-button tcw-button--primary", disabled: busy, onClick: onUpload, children: [_jsx("i", { className: "ri-upload-cloud-2-line", "aria-hidden": "true" }), "\u4E0A\u4F20\u76EE\u5F55\u6587\u4EF6"] }), _jsxs("button", { type: "button", className: "tcw-button tcw-button--secondary", disabled: busy, onClick: onCreate, children: [_jsx("i", { className: "ri-add-line", "aria-hidden": "true" }), "\u624B\u5DE5\u65B0\u589E"] })] });
|
|
316
333
|
}
|
|
317
|
-
export function CatalogUploadDialog({ open, kind, batches, busy = false, onClose, onUpload }) {
|
|
334
|
+
export function CatalogUploadDialog({ open, kind, batches, busy = false, onClose, onUpload, onNotify }) {
|
|
318
335
|
const [state, dispatch] = React.useReducer(catalogUploadReducer, initialCatalogUploadState);
|
|
319
336
|
const disabled = busy || state.submitting;
|
|
320
337
|
React.useEffect(() => {
|
|
@@ -335,7 +352,7 @@ export function CatalogUploadDialog({ open, kind, batches, busy = false, onClose
|
|
|
335
352
|
dispatch({ type: 'reset' });
|
|
336
353
|
}
|
|
337
354
|
catch (cause) {
|
|
338
|
-
|
|
355
|
+
onNotify?.({ message: errorMessage(cause, '目录上传失败'), tone: 'error' });
|
|
339
356
|
dispatch({ type: 'cancel-replace' });
|
|
340
357
|
}
|
|
341
358
|
finally {
|
|
@@ -361,7 +378,7 @@ export function CatalogUploadDialog({ open, kind, batches, busy = false, onClose
|
|
|
361
378
|
}
|
|
362
379
|
export function CatalogReplaceConfirmDialog({ open, kind, batch, fileName, busy = false, onCancel, onConfirm }) {
|
|
363
380
|
const typeLabel = batch?.catalogType ? catalogTypeLabels[batch.catalogType] : '公司制裁目录';
|
|
364
|
-
return (_jsx(Modal, { open: open, title: kind === 'controlled' ? '确认覆盖历史目录?' : '确认覆盖历史制裁目录?', onClose: onCancel, footer: _jsxs(_Fragment, { children: [_jsxs("button", { type: "button", className: "tcw-button tcw-button--secondary", onClick: onCancel, children: [_jsx("i", { className: "ri-close-line", "aria-hidden": "true" }), "\u53D6\u6D88"] }), _jsxs("button", { type: "button", className: "tcw-button tcw-button--danger", disabled: busy, onClick: onConfirm, children: [_jsx("i", { className: "ri-upload-cloud-2-line", "aria-hidden": "true" }), "\u786E\u8BA4\u4E0A\u4F20\u5E76\u8986\u76D6"] })] }), children: _jsxs("div", { className: `tcw-catalog-replace-dialog tcw-catalog-replace-dialog--${kind}`, children: [_jsxs("div", { className: "tcw-catalog-replace-warning tcw-notice--risk", role: "alert", children: [_jsx("i", { className: "ri-error-warning-fill", "aria-hidden": "true" }), _jsxs("span", { children: ["\u53EA\u6709\u65B0\u6587\u4EF6\u89E3\u6790\u6210\u529F\u5E76\u901A\u8FC7\u57FA\u7840\u6821\u9A8C\u540E\uFF0C\u65E7\u6279\u6B21\u53CA\u5176", kind === 'controlled' ? '文件导入商品' : '公司记录', "\u624D\u4F1A\u88AB\u903B\u8F91\u5220\u9664\u3002"] })] }), _jsxs("dl", { className: "tcw-catalog-replace-summary", children: [kind === 'controlled' && _jsxs(_Fragment, { children: [_jsx("dt", { children: "\u76EE\u5F55\u7C7B\u578B\uFF1A" }), _jsx("dd", { children: _jsx("span", { className: "tcw-catalog-type-badge", children: typeLabel }) })] }), _jsx("dt", { children: "\u88AB\u8986\u76D6\u6587\u4EF6\uFF1A" }), _jsx("dd", { children: batch?.sourceFileName || '—' }), _jsx("dt", { children: kind === 'controlled' ? '现有商品数量:' : '现有记录数量:' }), _jsx("dd", { className: "tcw-catalog-replace-count", children: batch?.totalCount ?? 0 }), _jsx("dt", { children: "\u6700\u8FD1\u66F4\u65B0\u65F6\u95F4\uFF1A" }), _jsx("dd", { children: batch?.updatedAt || batch?.createdAt
|
|
381
|
+
return (_jsx(Modal, { open: open, title: kind === 'controlled' ? '确认覆盖历史目录?' : '确认覆盖历史制裁目录?', onClose: onCancel, footer: _jsxs(_Fragment, { children: [_jsxs("button", { type: "button", className: "tcw-button tcw-button--secondary", onClick: onCancel, children: [_jsx("i", { className: "ri-close-line", "aria-hidden": "true" }), "\u53D6\u6D88"] }), _jsxs("button", { type: "button", className: "tcw-button tcw-button--danger", disabled: busy, onClick: onConfirm, children: [_jsx("i", { className: "ri-upload-cloud-2-line", "aria-hidden": "true" }), "\u786E\u8BA4\u4E0A\u4F20\u5E76\u8986\u76D6"] })] }), children: _jsxs("div", { className: `tcw-catalog-replace-dialog tcw-catalog-replace-dialog--${kind}`, children: [_jsxs("div", { className: "tcw-catalog-replace-warning tcw-notice--risk", role: "alert", children: [_jsx("i", { className: "ri-error-warning-fill", "aria-hidden": "true" }), _jsxs("span", { children: ["\u53EA\u6709\u65B0\u6587\u4EF6\u89E3\u6790\u6210\u529F\u5E76\u901A\u8FC7\u57FA\u7840\u6821\u9A8C\u540E\uFF0C\u65E7\u6279\u6B21\u53CA\u5176", kind === 'controlled' ? '文件导入商品' : '公司记录', "\u624D\u4F1A\u88AB\u903B\u8F91\u5220\u9664\u3002"] })] }), _jsxs("dl", { className: "tcw-catalog-replace-summary", children: [kind === 'controlled' && _jsxs(_Fragment, { children: [_jsx("dt", { children: "\u76EE\u5F55\u7C7B\u578B\uFF1A" }), _jsx("dd", { children: _jsx("span", { className: "tcw-catalog-type-badge", children: typeLabel }) })] }), _jsx("dt", { children: "\u88AB\u8986\u76D6\u6587\u4EF6\uFF1A" }), _jsx("dd", { children: batch?.sourceFileName || '—' }), _jsx("dt", { children: kind === 'controlled' ? '现有商品数量:' : '现有记录数量:' }), _jsx("dd", { className: "tcw-catalog-replace-count", children: batch?.totalCount ?? 0 }), _jsx("dt", { children: "\u6700\u8FD1\u66F4\u65B0\u65F6\u95F4\uFF1A" }), _jsx("dd", { children: formatBeijingDateTime(batch?.updatedAt || batch?.createdAt) }), _jsx("dt", { children: "\u65B0\u4E0A\u4F20\u6587\u4EF6\uFF1A" }), _jsx("dd", { children: fileName || '—' })] })] }) }));
|
|
365
382
|
}
|
|
366
383
|
export function ControlledGoodsFormDialog({ open, mode, item, busy = false, onClose, onSave }) {
|
|
367
384
|
const [form, setForm] = React.useState(() => controlledForm(item));
|
|
@@ -430,36 +447,43 @@ function CatalogManualSource() {
|
|
|
430
447
|
return _jsxs("div", { className: "tcw-catalog-manual-source", "aria-label": "\u6765\u6E90\u7C7B\u578B\uFF1A\u624B\u5DE5\u65B0\u589E", children: [_jsx("span", { children: "\u6765\u6E90\u7C7B\u578B\uFF1A" }), _jsx("strong", { className: "tcw-catalog-source-badge", children: "\u624B\u5DE5\u65B0\u589E" })] });
|
|
431
448
|
}
|
|
432
449
|
function SourceEvidence({ item }) {
|
|
433
|
-
return _jsxs(FieldGrid, { columns: 1, children: [_jsx(FormField, { label: "\u6765\u6E90\u7C7B\u578B", children: _jsx("input", { disabled: true, value: labelFor(item?.sourceType, sourceTypeLabels) }) }), _jsx(FormField, { label: "\u6765\u6E90\u6587\u4EF6", children: _jsx("input", { disabled: true, value: item?.sourceFileName ?? '—' }) }), _jsx(FormField, { label: "\u6765\u6E90\u4F4D\u7F6E", children: _jsx("input", { disabled: true, value: item?.sourceLocation ?? '—' }) })
|
|
450
|
+
return _jsxs(FieldGrid, { columns: 1, children: [_jsx(FormField, { label: "\u6765\u6E90\u7C7B\u578B", children: _jsx("input", { disabled: true, value: labelFor(item?.sourceType, sourceTypeLabels) }) }), _jsx(FormField, { label: "\u6765\u6E90\u6587\u4EF6", children: _jsx("input", { disabled: true, value: item?.sourceFileName ?? '—' }) }), _jsx(FormField, { label: "\u6765\u6E90\u4F4D\u7F6E", children: _jsx("input", { disabled: true, value: item?.sourceLocation ?? '—' }) })] });
|
|
434
451
|
}
|
|
435
452
|
export function CatalogImportSummaryNotice({ documentType, batch, busy, onDownload, onView }) {
|
|
436
453
|
const issueCount = (batch.duplicateCount ?? 0) + (batch.invalidCount ?? 0);
|
|
454
|
+
if (issueCount === 0)
|
|
455
|
+
return null;
|
|
437
456
|
const initialIssueType = (batch.duplicateCount ?? 0) > 0 ? 'DUPLICATE' : 'INVALID';
|
|
438
|
-
return _jsxs(InfoNotice, { children: [_jsxs("span", { children: ["\u5BFC\u5165\
|
|
457
|
+
return _jsxs(InfoNotice, { children: [_jsxs("span", { children: ["\u5BFC\u5165\u95EE\u9898\uFF1A\u91CD\u590D ", batch.duplicateCount ?? 0, "\uFF0C\u65E0\u6548 ", batch.invalidCount ?? 0, "\u3002"] }), _jsxs("button", { type: "button", className: "tcw-button tcw-button--secondary", disabled: busy || !onView, onClick: () => void onView?.(batch.id, initialIssueType), children: [_jsx("i", { className: "ri-list-check-2", "aria-hidden": "true" }), "\u67E5\u770B\u660E\u7EC6"] }), _jsxs("button", { type: "button", className: "tcw-button tcw-button--secondary", disabled: busy, onClick: () => void onDownload(documentType, batch.id).catch(() => undefined), children: [_jsx("i", { className: "ri-download-line", "aria-hidden": "true" }), "\u4E0B\u8F7D Excel"] })] });
|
|
439
458
|
}
|
|
440
459
|
export function CatalogImportStatusNotice({ task, busy, onRetry }) {
|
|
441
460
|
if (task.status !== 'FAILED' && task.status !== 'TIMED_OUT')
|
|
442
461
|
return null;
|
|
443
462
|
return _jsxs("div", { className: "tcw-catalog-import-failure", role: "alert", children: [_jsxs("div", { children: [_jsx("strong", { children: task.status === 'TIMED_OUT' ? '目录解析超时' : '目录解析失败' }), _jsx("p", { children: task.errorMessage || '解析任务未完成,请重新解析。' })] }), _jsxs("button", { type: "button", className: "tcw-button tcw-button--primary", disabled: busy, onClick: () => void onRetry(task.id).catch(() => undefined), children: [_jsx("i", { className: "ri-restart-line", "aria-hidden": "true" }), "\u91CD\u65B0\u89E3\u6790"] })] });
|
|
444
463
|
}
|
|
445
|
-
export function CatalogImportIssuesDialog({ open, documentType, batchId, issueType, collection, loading, error, busy, onClose, onIssueTypeChange, onPageChange, onRetry }) {
|
|
464
|
+
export function CatalogImportIssuesDialog({ open, documentType, batchId, issueType, collection, loading, error, busy, onClose, onIssueTypeChange, onPageChange, onPageSizeChange, onRetry }) {
|
|
446
465
|
const columns = [
|
|
447
|
-
{ type: 'text', key: 'issueType', header: '问题类型', width: 100, format: (value) => value === 'DUPLICATE' ? '重复' : '无效' },
|
|
448
|
-
{ type: 'text', key: 'sourceSequence', header: '来源序号', width: 100 },
|
|
466
|
+
{ type: 'text', key: 'issueType', header: '问题类型', width: 100, truncate: false, format: (value) => value === 'DUPLICATE' ? '重复' : '无效' },
|
|
467
|
+
{ type: 'text', key: 'sourceSequence', header: '来源序号', width: 100, truncate: false },
|
|
449
468
|
{ type: 'ellipsis', key: 'sourceLocation', header: '来源位置', width: 160, maxWidth: 160 },
|
|
450
|
-
{ type: 'ellipsis', key: 'sourceContent', header: '来源原文', width: 260, maxWidth: 260 },
|
|
451
469
|
{ type: 'ellipsis', key: 'message', header: '问题说明', width: 220, maxWidth: 220 }
|
|
452
470
|
];
|
|
453
|
-
return _jsxs(Modal, { open: open, title: "\u5BFC\u5165\u95EE\u9898\u660E\u7EC6", size: "wide", onClose: onClose, footer: _jsxs("button", { type: "button", className: "tcw-button tcw-button--primary", onClick: onClose, children: [_jsx("i", { className: "ri-close-line", "aria-hidden": "true" }), "\u5173\u95ED"] }), children: [_jsxs("div", { className: "tcw-catalog-issue-toolbar", "data-document-type": documentType, "data-batch-id": batchId, children: [_jsxs("button", { type: "button", className: issueType === 'DUPLICATE' ? 'is-active' : '', disabled: busy || loading, onClick: () => onIssueTypeChange('DUPLICATE'), children: [_jsx("i", { className: "ri-file-copy-line", "aria-hidden": "true" }), "\u91CD\u590D"] }), _jsxs("button", { type: "button", className: issueType === 'INVALID' ? 'is-active' : '', disabled: busy || loading, onClick: () => onIssueTypeChange('INVALID'), children: [_jsx("i", { className: "ri-error-warning-line", "aria-hidden": "true" }), "\u65E0\u6548"] })] }), loading && _jsx("p", { className: "tcw-catalog-issue-status", role: "status", children: "\u6B63\u5728\u52A0\u8F7D\u5BFC\u5165\u95EE\u9898\u660E\u7EC6..." }), error && _jsxs("div", { className: "tcw-catalog-issue-error", role: "alert", children: [_jsx("span", { children: error }), _jsxs("button", { type: "button", className: "tcw-button tcw-button--secondary", disabled: busy || loading, onClick: onRetry, children: [_jsx("i", { className: "ri-refresh-line", "aria-hidden": "true" }), "\u91CD\u65B0\u52A0\u8F7D"] })] }), !loading && !error && _jsx(DataGrid, { columns: columns, rows: collection.items, rowKey: "id", pagination: collectionPagination(collection), onPageChange: onPageChange, emptyText: issueType === 'DUPLICATE' ? '暂无重复数据' : '暂无无效数据' })] });
|
|
471
|
+
return _jsxs(Modal, { open: open, title: "\u5BFC\u5165\u95EE\u9898\u660E\u7EC6", size: "wide", onClose: onClose, footer: _jsxs("button", { type: "button", className: "tcw-button tcw-button--primary", onClick: onClose, children: [_jsx("i", { className: "ri-close-line", "aria-hidden": "true" }), "\u5173\u95ED"] }), children: [_jsxs("div", { className: "tcw-catalog-issue-toolbar", "data-document-type": documentType, "data-batch-id": batchId, children: [_jsxs("button", { type: "button", className: issueType === 'DUPLICATE' ? 'is-active' : '', disabled: busy || loading, onClick: () => onIssueTypeChange('DUPLICATE'), children: [_jsx("i", { className: "ri-file-copy-line", "aria-hidden": "true" }), "\u91CD\u590D"] }), _jsxs("button", { type: "button", className: issueType === 'INVALID' ? 'is-active' : '', disabled: busy || loading, onClick: () => onIssueTypeChange('INVALID'), children: [_jsx("i", { className: "ri-error-warning-line", "aria-hidden": "true" }), "\u65E0\u6548"] })] }), loading && _jsx("p", { className: "tcw-catalog-issue-status", role: "status", children: "\u6B63\u5728\u52A0\u8F7D\u5BFC\u5165\u95EE\u9898\u660E\u7EC6..." }), error && _jsxs("div", { className: "tcw-catalog-issue-error", role: "alert", children: [_jsx("span", { children: error }), _jsxs("button", { type: "button", className: "tcw-button tcw-button--secondary", disabled: busy || loading, onClick: onRetry, children: [_jsx("i", { className: "ri-refresh-line", "aria-hidden": "true" }), "\u91CD\u65B0\u52A0\u8F7D"] })] }), !loading && !error && _jsx(DataGrid, { columns: columns, rows: collection.items, rowKey: "id", pagination: collectionPagination(collection), onPageChange: onPageChange, onPageSizeChange: onPageSizeChange, emptyText: issueType === 'DUPLICATE' ? '暂无重复数据' : '暂无无效数据' })] });
|
|
472
|
+
}
|
|
473
|
+
export function hsSearchPageSizeRequest(keywords, pageSize) {
|
|
474
|
+
return { keywords: keywords.trim(), page: 1, pageSize };
|
|
475
|
+
}
|
|
476
|
+
function emptyHsSearchData(page = 1, pageSize = 20) {
|
|
477
|
+
return { items: [], total: 0, page, pageSize, pageCount: 1 };
|
|
454
478
|
}
|
|
455
479
|
export function HsCodeLookupDialog({ open, initialQuery, onAction, onClose }) {
|
|
456
480
|
const [query, setQuery] = React.useState(initialQuery);
|
|
457
|
-
const [
|
|
458
|
-
const [results, setResults] = React.useState([]);
|
|
481
|
+
const [collection, setCollection] = React.useState(() => emptyHsSearchData());
|
|
459
482
|
const [detail, setDetail] = React.useState(null);
|
|
460
483
|
const [error, setError] = React.useState('');
|
|
461
484
|
const [loading, setLoading] = React.useState(false);
|
|
462
|
-
const
|
|
485
|
+
const hsRequestGateRef = React.useRef(createLatestRequestGate());
|
|
486
|
+
const search = React.useCallback(async (keywords, nextPage = 1, pageSize = 20) => {
|
|
463
487
|
const normalized = keywords.trim();
|
|
464
488
|
if (!normalized) {
|
|
465
489
|
setError('请输入商品名称或海关编码');
|
|
@@ -468,56 +492,73 @@ export function HsCodeLookupDialog({ open, initialQuery, onAction, onClose }) {
|
|
|
468
492
|
setLoading(true);
|
|
469
493
|
setError('');
|
|
470
494
|
setDetail(null);
|
|
495
|
+
const requestId = hsRequestGateRef.current.begin();
|
|
471
496
|
try {
|
|
472
|
-
const response = await onAction('search_hs_code', { keywords: normalized, page: nextPage });
|
|
497
|
+
const response = await onAction('search_hs_code', { keywords: normalized, page: nextPage, pageSize });
|
|
473
498
|
if (response.success === false)
|
|
474
499
|
throw new Error(response.message || '海关编码查询失败');
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
500
|
+
if (!hsRequestGateRef.current.commit(requestId))
|
|
501
|
+
return;
|
|
502
|
+
setCollection(isHsSearchData(response.data) ? response.data : emptyHsSearchData(nextPage, pageSize));
|
|
478
503
|
}
|
|
479
504
|
catch (cause) {
|
|
505
|
+
if (!hsRequestGateRef.current.isLatest(requestId))
|
|
506
|
+
return;
|
|
480
507
|
setError(errorMessage(cause, '海关编码查询失败'));
|
|
481
|
-
|
|
508
|
+
setCollection(emptyHsSearchData(nextPage, pageSize));
|
|
482
509
|
}
|
|
483
510
|
finally {
|
|
484
|
-
|
|
511
|
+
hsRequestGateRef.current.finish(requestId);
|
|
512
|
+
if (hsRequestGateRef.current.isLatest(requestId))
|
|
513
|
+
setLoading(false);
|
|
485
514
|
}
|
|
486
515
|
}, [onAction]);
|
|
487
516
|
React.useEffect(() => {
|
|
488
|
-
if (!open)
|
|
517
|
+
if (!open) {
|
|
518
|
+
hsRequestGateRef.current.invalidate();
|
|
519
|
+
setLoading(false);
|
|
489
520
|
return;
|
|
521
|
+
}
|
|
490
522
|
setQuery(initialQuery);
|
|
491
|
-
|
|
523
|
+
setCollection(emptyHsSearchData());
|
|
492
524
|
void search(initialQuery, 1);
|
|
525
|
+
return () => hsRequestGateRef.current.invalidate();
|
|
493
526
|
}, [open, initialQuery, search]);
|
|
494
527
|
const showDetail = async (result) => {
|
|
495
528
|
setLoading(true);
|
|
496
529
|
setError('');
|
|
530
|
+
const requestId = hsRequestGateRef.current.begin();
|
|
497
531
|
try {
|
|
498
532
|
const response = await onAction('get_hs_code_detail', { code: result.code, ...(result.detailUrl ? { detailUrl: result.detailUrl } : {}) });
|
|
499
533
|
if (response.success === false)
|
|
500
534
|
throw new Error(response.message || '海关编码详情查询失败');
|
|
535
|
+
if (!hsRequestGateRef.current.commit(requestId))
|
|
536
|
+
return;
|
|
501
537
|
setDetail(isRecord(response.data) ? response.data : {});
|
|
502
538
|
}
|
|
503
539
|
catch (cause) {
|
|
540
|
+
if (!hsRequestGateRef.current.isLatest(requestId))
|
|
541
|
+
return;
|
|
504
542
|
setError(errorMessage(cause, '海关编码详情查询失败'));
|
|
505
543
|
}
|
|
506
544
|
finally {
|
|
507
|
-
|
|
545
|
+
hsRequestGateRef.current.finish(requestId);
|
|
546
|
+
if (hsRequestGateRef.current.isLatest(requestId))
|
|
547
|
+
setLoading(false);
|
|
508
548
|
}
|
|
509
549
|
};
|
|
550
|
+
const close = () => { hsRequestGateRef.current.invalidate(); setLoading(false); onClose(); };
|
|
510
551
|
const columns = [
|
|
511
552
|
{ type: 'text', key: 'code', header: '海关编码', width: 130 },
|
|
512
553
|
{ type: 'ellipsis', key: 'name', header: '商品名称', width: 220, maxWidth: 220 },
|
|
513
554
|
{ type: 'ellipsis', key: 'englishName', header: '英文名称', width: 220, maxWidth: 220 },
|
|
514
|
-
{ type: 'text', key: 'unit', header: '单位', width: 90 },
|
|
515
|
-
{ type: 'text', key: 'taxRefundRate', header: '退税率', width: 90 }
|
|
555
|
+
{ type: 'text', key: 'unit', header: '单位', width: 90, truncate: false },
|
|
556
|
+
{ type: 'text', key: 'taxRefundRate', header: '退税率', width: 90, truncate: false }
|
|
516
557
|
];
|
|
517
|
-
return (_jsxs(Modal, { open: open, title: "\u6D77\u5173\u7F16\u7801\u67E5\u8BE2", size: "wide", onClose:
|
|
558
|
+
return (_jsxs(Modal, { open: open, title: "\u6D77\u5173\u7F16\u7801\u67E5\u8BE2", size: "wide", onClose: close, footer: _jsxs("button", { type: "button", className: "tcw-button tcw-button--primary", onClick: close, children: [_jsx("i", { className: "ri-close-line", "aria-hidden": "true" }), "\u5173\u95ED"] }), children: [_jsx("form", { onSubmit: (event) => { event.preventDefault(); void search(query, 1); }, children: _jsxs(FieldGrid, { columns: 2, children: [_jsx(FormField, { label: "\u5546\u54C1\u540D\u79F0\u6216\u6D77\u5173\u7F16\u7801", required: true, children: _jsx("input", { value: query, onChange: (event) => setQuery(event.target.value) }) }), _jsx("div", { children: _jsxs("button", { type: "submit", className: "tcw-button tcw-button--primary", disabled: loading, children: [_jsx("i", { className: "ri-search-line", "aria-hidden": "true" }), "\u67E5\u8BE2"] }) })] }) }), error && _jsx("p", { role: "alert", children: error }), detail ? _jsx(HsCodeDetail, { detail: detail, onBack: () => setDetail(null) }) : _jsx(_Fragment, { children: _jsx(DataGrid, { columns: columns, rows: collection.items, rowKey: (row) => row.code, pagination: collection, onPageChange: (page) => void search(query, page, collection.pageSize), onPageSizeChange: (pageSize) => { const request = hsSearchPageSizeRequest(query, pageSize); void search(request.keywords, request.page, request.pageSize); }, actions: (row) => _jsxs("button", { type: "button", onClick: () => void showDetail(row), children: [_jsx("i", { className: "ri-eye-line", "aria-hidden": "true" }), "\u8BE6\u60C5"] }), emptyText: loading ? '查询中' : '暂无海关编码结果' }) })] }));
|
|
518
559
|
}
|
|
519
560
|
function HsCodeDetail({ detail, onBack }) {
|
|
520
|
-
return _jsxs("section", { children: [_jsxs("button", { type: "button", className: "tcw-button tcw-button--secondary", onClick: onBack, children: [_jsx("i", { className: "ri-arrow-left-line", "aria-hidden": "true" }), "\u8FD4\u56DE\u67E5\u8BE2\u7ED3\u679C"] }), _jsxs("h3", { children: [detail.code || '—', " ", detail.name || detail.title || ''] }), _jsxs("p", { children: [_jsx("strong", { children: "\u67E5\u8BE2\u65F6\u95F4\uFF1A" }), detail.queriedAt
|
|
561
|
+
return _jsxs("section", { children: [_jsxs("button", { type: "button", className: "tcw-button tcw-button--secondary", onClick: onBack, children: [_jsx("i", { className: "ri-arrow-left-line", "aria-hidden": "true" }), "\u8FD4\u56DE\u67E5\u8BE2\u7ED3\u679C"] }), _jsxs("h3", { children: [detail.code || '—', " ", detail.name || detail.title || ''] }), _jsxs("p", { children: [_jsx("strong", { children: "\u67E5\u8BE2\u65F6\u95F4\uFF1A" }), formatBeijingDateTime(detail.queriedAt)] }), detail.sourceUrl && _jsxs("p", { children: [_jsx("strong", { children: "\u6570\u636E\u6765\u6E90\uFF1A" }), _jsx("a", { href: detail.sourceUrl, target: "_blank", rel: "noreferrer", children: detail.sourceUrl })] }), detail.sections?.map((section, index) => _jsxs("section", { children: [_jsx("h4", { children: section.title || '详情' }), section.rows?.map((row, rowIndex) => _jsxs("p", { children: [_jsxs("strong", { children: [row.label || '字段', "\uFF1A"] }), row.value || '—'] }, `${row.label ?? 'row'}-${rowIndex}`))] }, `${section.title ?? 'detail'}-${index}`))] });
|
|
521
562
|
}
|
|
522
563
|
export function hsLookupQuery(row, field) {
|
|
523
564
|
return String(row[field] ?? '').trim();
|
|
@@ -540,8 +581,8 @@ function sanctionedForm(item) {
|
|
|
540
581
|
remarks: item?.remarks ?? ''
|
|
541
582
|
};
|
|
542
583
|
}
|
|
543
|
-
function LinkButton({ children, icon, title, onClick }) {
|
|
544
|
-
return _jsx("span", { className: "tcw-row-actions tcw-catalog-table-link", children: _jsxs("button", { type: "button", title: title, onClick: onClick, children: [_jsx("i", { className: icon, "aria-hidden": "true" }), children] }) });
|
|
584
|
+
function LinkButton({ children, icon, title, ariaLabel, onClick }) {
|
|
585
|
+
return _jsx("span", { className: "tcw-row-actions tcw-catalog-table-link", children: _jsxs("button", { type: "button", title: title, "aria-label": ariaLabel, onClick: onClick, children: [_jsx("i", { className: icon, "aria-hidden": "true" }), children] }) });
|
|
545
586
|
}
|
|
546
587
|
function options(labels) {
|
|
547
588
|
return Object.entries(labels).map(([value, label]) => ({ value, label: String(label) }));
|
|
@@ -550,7 +591,13 @@ function labelFor(value, labels) {
|
|
|
550
591
|
return typeof value === 'string' && value in labels ? labels[value] : value ? String(value) : '—';
|
|
551
592
|
}
|
|
552
593
|
function isHsSearchData(value) {
|
|
553
|
-
return isRecord(value)
|
|
594
|
+
return isRecord(value)
|
|
595
|
+
&& Array.isArray(value.items)
|
|
596
|
+
&& value.items.every((item) => isRecord(item) && typeof item.code === 'string')
|
|
597
|
+
&& Number.isInteger(value.total) && Number(value.total) >= 0
|
|
598
|
+
&& Number.isInteger(value.page) && Number(value.page) >= 1
|
|
599
|
+
&& isPaginationPageSize(Number(value.pageSize))
|
|
600
|
+
&& Number.isInteger(value.pageCount) && Number(value.pageCount) >= 1;
|
|
554
601
|
}
|
|
555
602
|
function isRecord(value) {
|
|
556
603
|
return Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
@@ -563,7 +610,7 @@ function assertActionSucceeded(response) {
|
|
|
563
610
|
throw new Error(typeof response.message === 'string' && response.message ? response.message : '操作失败');
|
|
564
611
|
}
|
|
565
612
|
function collectionPagination(collection) {
|
|
566
|
-
const pageSize =
|
|
613
|
+
const pageSize = collection.pageSize ?? PAGE_SIZE;
|
|
567
614
|
const total = Number(collection.total) >= 0 ? Number(collection.total) : collection.items.length;
|
|
568
615
|
const pageCount = Math.max(1, Math.ceil(total / pageSize));
|
|
569
616
|
const page = Math.min(pageCount, Math.max(1, Number(collection.page) || 1));
|
|
@@ -575,7 +622,7 @@ function asImportCompletion(value) {
|
|
|
575
622
|
return value;
|
|
576
623
|
}
|
|
577
624
|
function isCatalogImportStartData(value) {
|
|
578
|
-
return isRecord(value) && isRecord(value.task) && typeof value.task.id === 'string' && typeof value.task.documentType === 'string' && typeof value.task.status === 'string'
|
|
625
|
+
return isRecord(value) && isRecord(value.task) && typeof value.task.id === 'string' && typeof value.task.documentType === 'string' && typeof value.task.status === 'string';
|
|
579
626
|
}
|
|
580
627
|
function delay(milliseconds) {
|
|
581
628
|
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|