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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/lib/import-jobs/import-job-callback.processor.d.ts.map +1 -1
  2. package/dist/lib/import-jobs/import-job-callback.processor.js +5 -4
  3. package/dist/lib/import-jobs/import-job-callback.processor.js.map +1 -1
  4. package/dist/lib/remote-components/trade-compliance-workbench/app.css +1 -1
  5. package/dist/lib/remote-components/trade-compliance-workbench/app.js +5 -5
  6. package/dist/lib/remote-ui/action-execution.d.ts +5 -2
  7. package/dist/lib/remote-ui/action-execution.d.ts.map +1 -1
  8. package/dist/lib/remote-ui/action-execution.js +2 -2
  9. package/dist/lib/remote-ui/action-execution.js.map +1 -1
  10. package/dist/lib/remote-ui/app-source.d.ts.map +1 -1
  11. package/dist/lib/remote-ui/app-source.js +134 -35
  12. package/dist/lib/remote-ui/app-source.js.map +1 -1
  13. package/dist/lib/remote-ui/bulk-delete.d.ts +56 -0
  14. package/dist/lib/remote-ui/bulk-delete.d.ts.map +1 -0
  15. package/dist/lib/remote-ui/bulk-delete.js +138 -0
  16. package/dist/lib/remote-ui/bulk-delete.js.map +1 -0
  17. package/dist/lib/remote-ui/components/bulk-delete.d.ts +21 -0
  18. package/dist/lib/remote-ui/components/bulk-delete.d.ts.map +1 -0
  19. package/dist/lib/remote-ui/components/bulk-delete.js +9 -0
  20. package/dist/lib/remote-ui/components/bulk-delete.js.map +1 -0
  21. package/dist/lib/remote-ui/components/data-grid.d.ts +12 -1
  22. package/dist/lib/remote-ui/components/data-grid.d.ts.map +1 -1
  23. package/dist/lib/remote-ui/components/data-grid.js +24 -5
  24. package/dist/lib/remote-ui/components/data-grid.js.map +1 -1
  25. package/dist/lib/remote-ui/components/task-status.d.ts +10 -1
  26. package/dist/lib/remote-ui/components/task-status.d.ts.map +1 -1
  27. package/dist/lib/remote-ui/components/task-status.js +79 -10
  28. package/dist/lib/remote-ui/components/task-status.js.map +1 -1
  29. package/dist/lib/remote-ui/customer-contract-contracts.d.ts +7 -2
  30. package/dist/lib/remote-ui/customer-contract-contracts.d.ts.map +1 -1
  31. package/dist/lib/remote-ui/customer-contract-contracts.js.map +1 -1
  32. package/dist/lib/remote-ui/import-task-status.d.ts +1 -1
  33. package/dist/lib/remote-ui/import-task-status.d.ts.map +1 -1
  34. package/dist/lib/remote-ui/import-task-status.js +2 -2
  35. package/dist/lib/remote-ui/import-task-status.js.map +1 -1
  36. package/dist/lib/remote-ui/model.d.ts +1 -0
  37. package/dist/lib/remote-ui/model.d.ts.map +1 -1
  38. package/dist/lib/remote-ui/model.js +3 -0
  39. package/dist/lib/remote-ui/model.js.map +1 -1
  40. package/dist/lib/remote-ui/pages/catalogs.css +91 -0
  41. package/dist/lib/remote-ui/pages/catalogs.d.ts +49 -3
  42. package/dist/lib/remote-ui/pages/catalogs.d.ts.map +1 -1
  43. package/dist/lib/remote-ui/pages/catalogs.js +123 -12
  44. package/dist/lib/remote-ui/pages/catalogs.js.map +1 -1
  45. package/dist/lib/remote-ui/pages/contracts.css +42 -6
  46. package/dist/lib/remote-ui/pages/contracts.d.ts +61 -3
  47. package/dist/lib/remote-ui/pages/contracts.d.ts.map +1 -1
  48. package/dist/lib/remote-ui/pages/contracts.js +213 -11
  49. package/dist/lib/remote-ui/pages/contracts.js.map +1 -1
  50. package/dist/lib/remote-ui/pages/files.d.ts +52 -10
  51. package/dist/lib/remote-ui/pages/files.d.ts.map +1 -1
  52. package/dist/lib/remote-ui/pages/files.js +122 -12
  53. package/dist/lib/remote-ui/pages/files.js.map +1 -1
  54. package/dist/lib/remote-ui/pages/home.js +4 -4
  55. package/dist/lib/remote-ui/pages/home.js.map +1 -1
  56. package/dist/lib/remote-ui/pages/procurement.d.ts +87 -2
  57. package/dist/lib/remote-ui/pages/procurement.d.ts.map +1 -1
  58. package/dist/lib/remote-ui/pages/procurement.js +225 -21
  59. package/dist/lib/remote-ui/pages/procurement.js.map +1 -1
  60. package/dist/lib/remote-ui/pages/settings.d.ts +39 -6
  61. package/dist/lib/remote-ui/pages/settings.d.ts.map +1 -1
  62. package/dist/lib/remote-ui/pages/settings.js +94 -5
  63. package/dist/lib/remote-ui/pages/settings.js.map +1 -1
  64. package/dist/lib/remote-ui/polling.d.ts +1 -0
  65. package/dist/lib/remote-ui/polling.d.ts.map +1 -1
  66. package/dist/lib/remote-ui/polling.js +6 -1
  67. package/dist/lib/remote-ui/polling.js.map +1 -1
  68. package/dist/lib/remote-ui/procurement-contracts.d.ts +6 -2
  69. package/dist/lib/remote-ui/procurement-contracts.d.ts.map +1 -1
  70. package/dist/lib/remote-ui/procurement-contracts.js +11 -1
  71. package/dist/lib/remote-ui/procurement-contracts.js.map +1 -1
  72. package/dist/lib/remote-ui/styles.css +55 -19
  73. package/dist/lib/workbench.service.d.ts.map +1 -1
  74. package/dist/lib/workbench.service.js +20 -23
  75. package/dist/lib/workbench.service.js.map +1 -1
  76. package/package.json +2 -1
@@ -7,7 +7,8 @@
7
7
  }
8
8
 
9
9
  .tcw-modal-backdrop:has(.tcw-contract-review-summary) .tcw-modal {
10
- height: min(568px, calc(100vh - 28px));
10
+ height: min(660px, calc(100vh - 28px));
11
+ max-height: none;
11
12
  transform: translate(18px, 8px);
12
13
  }
13
14
 
@@ -16,14 +17,35 @@
16
17
  }
17
18
 
18
19
  .tcw-modal:has(.tcw-contract-review-summary) > .tcw-modal-body {
20
+ display: grid;
21
+ grid-template-rows: auto minmax(0, 1fr) auto;
22
+ gap: 8px;
23
+ min-height: 0;
19
24
  position: relative;
20
25
  padding: 0 14px 0;
21
- overflow: auto;
26
+ overflow: hidden;
22
27
  }
23
28
 
24
29
  .tcw-modal:has(.tcw-contract-review-summary) .tcw-contract-review-summary {
25
- height: 48px;
30
+ min-height: 48px;
31
+ height: auto;
32
+ min-width: 0;
26
33
  box-sizing: border-box;
34
+ overflow: hidden;
35
+ }
36
+
37
+ .tcw-modal:has(.tcw-contract-review-summary) .tcw-contract-review-summary .tcw-field-grid {
38
+ min-width: 0;
39
+ }
40
+
41
+ .tcw-modal:has(.tcw-contract-review-summary) .tcw-contract-summary-customer strong {
42
+ display: -webkit-box;
43
+ overflow: hidden;
44
+ overflow-wrap: anywhere;
45
+ text-overflow: ellipsis;
46
+ white-space: normal;
47
+ -webkit-box-orient: vertical;
48
+ -webkit-line-clamp: 2;
27
49
  }
28
50
 
29
51
  .tcw-modal:has(.tcw-contract-review-summary) .tcw-contract-review-toolbar {
@@ -39,12 +61,12 @@
39
61
  .tcw-modal:has(.tcw-contract-review-summary) .tcw-contract-review-layout {
40
62
  grid-template-columns: 118px 336px minmax(0, 1fr);
41
63
  grid-template-rows: 32px minmax(0, 1fr);
42
- height: 352px;
64
+ height: auto;
43
65
  min-height: 0;
44
66
  column-gap: 8px;
45
67
  row-gap: 0;
46
- margin-top: 4px;
47
- overflow: visible;
68
+ margin-top: 0;
69
+ overflow: hidden;
48
70
  border: 0;
49
71
  }
50
72
 
@@ -60,6 +82,8 @@
60
82
  grid-column: 1;
61
83
  grid-row: 1 / 3;
62
84
  padding: 8px 0 0;
85
+ min-height: 0;
86
+ overflow: auto;
63
87
  }
64
88
 
65
89
  .tcw-modal:has(.tcw-contract-review-summary) .tcw-contract-clause-tabs button {
@@ -70,6 +94,7 @@
70
94
  .tcw-modal:has(.tcw-contract-review-summary) .tcw-contract-clause-editor,
71
95
  .tcw-modal:has(.tcw-contract-review-summary) .tcw-contract-source-preview {
72
96
  grid-row: 2;
97
+ min-height: 0;
73
98
  padding: 10px;
74
99
  overflow: auto;
75
100
  }
@@ -96,6 +121,17 @@
96
121
  height: 34px;
97
122
  }
98
123
 
124
+ .tcw-modal:has(.tcw-contract-review-summary) .tcw-contract-customer-master {
125
+ max-height: 48px;
126
+ margin-top: 0;
127
+ overflow: hidden;
128
+ }
129
+
130
+ .tcw-modal:has(.tcw-contract-review-summary) .tcw-contract-customer-master[open] {
131
+ max-height: min(220px, 25vh);
132
+ overflow: auto;
133
+ }
134
+
99
135
  .tcw-modal:has(.tcw-contract-review-summary) > footer {
100
136
  min-height: 46px;
101
137
  padding: 6px 14px 8px;
@@ -1,4 +1,5 @@
1
- import type { FilterDefinition, FilterState, PaginationPageSize } from '../contracts.js';
1
+ import type { FilterDefinition, FilterState, PaginationPageSize, WorkbenchRoute } from '../contracts.js';
2
+ import type { ActionExecutionOptions } from '../action-execution.js';
2
3
  import type { Notify } from '../toast-queue.js';
3
4
  import './procurement-contracts.css';
4
5
  import './contracts.css';
@@ -17,7 +18,7 @@ export declare function createCustomerContractActionApi({ onAction, onFileAction
17
18
  saveReview: (payload: CustomerContractReviewPayload) => Promise<CustomerContractActionResult<unknown> & {
18
19
  success?: true;
19
20
  }>;
20
- deleteContract: (customerContractId: string) => Promise<CustomerContractActionResult<unknown> & {
21
+ deleteContract: (customerContractId: string, options?: ActionExecutionOptions) => Promise<CustomerContractActionResult<unknown> & {
21
22
  success?: true;
22
23
  }>;
23
24
  };
@@ -27,13 +28,70 @@ export declare function adaptCustomerContractReview(value: unknown, options?: {
27
28
  sourceFileId?: string;
28
29
  sourceFileName?: string;
29
30
  }): CustomerContractReview;
30
- export declare function createCustomerContractQueryScheduler(query: (query: CustomerContractCollectionQuery) => Promise<void> | void, debounceMs?: number): {
31
+ export declare function createCustomerContractQueryScheduler(query: (query: CustomerContractCollectionQuery) => Promise<boolean | void> | boolean | void, debounceMs?: number): {
31
32
  schedule: (type: FilterDefinition["type"], filters: FilterState, pageSize?: PaginationPageSize) => void;
32
33
  flush: (filters: FilterState, pageSize?: PaginationPageSize) => void;
33
34
  page: (page: number, filters: FilterState, pageSize?: PaginationPageSize) => void;
34
35
  pageSize: (pageSize: PaginationPageSize, filters: FilterState) => void;
35
36
  cancel: () => void;
36
37
  };
38
+ export declare function createCustomerContractQueryCancelCoordinator(): {
39
+ register(cancel: () => void): () => void;
40
+ cancelCurrent(): void;
41
+ };
42
+ type CustomerContractBulkFailure = {
43
+ key: string;
44
+ label: string;
45
+ message: string;
46
+ };
47
+ export declare function createCustomerContractSelectionQuery(query: CustomerContractPageProps['onQuery'], clearSelection: () => void): (request: CustomerContractCollectionQuery) => Promise<boolean | void>;
48
+ export declare function createCustomerContractBulkConfirmGuard(): {
49
+ run<T>(operation: () => Promise<T>): Promise<T> | null;
50
+ };
51
+ type CustomerContractBulkGeneration = {
52
+ revision: number;
53
+ };
54
+ export declare function createCustomerContractBulkGenerationGuard(): {
55
+ invalidate(): void;
56
+ capture(): CustomerContractBulkGeneration;
57
+ isCurrent: (token: CustomerContractBulkGeneration) => boolean;
58
+ commit(token: CustomerContractBulkGeneration, apply: () => void): boolean;
59
+ };
60
+ export declare function customerContractInteractionBusy(pageBusy: boolean, bulkBusy: boolean): boolean;
61
+ export declare function customerContractRouteEntryQuery(route: WorkbenchRoute): CustomerContractCollectionQuery | null;
62
+ export declare function reconcileLatestCustomerContractQuery({ isActive, latestQuery, query }: {
63
+ isActive: () => boolean;
64
+ latestQuery: () => CustomerContractCollectionQuery;
65
+ query: CustomerContractPageProps['onQuery'];
66
+ }): Promise<boolean>;
67
+ export declare function runCustomerContractBulkGeneration<T>({ confirmGuard, generationGuard, operation, onCurrentResult, onStaleReconcile, onBusyChange }: {
68
+ confirmGuard: ReturnType<typeof createCustomerContractBulkConfirmGuard>;
69
+ generationGuard: ReturnType<typeof createCustomerContractBulkGenerationGuard>;
70
+ operation: (isCurrent: () => boolean) => Promise<T>;
71
+ onCurrentResult: (result: T) => void;
72
+ onStaleReconcile?: (result: T) => Promise<void>;
73
+ onBusyChange: (busy: boolean) => void;
74
+ }): Promise<T> | null;
75
+ export declare function reconcileCustomerContractBulkState<T extends {
76
+ id: string;
77
+ }>(selectedIds: Set<string>, failures: CustomerContractBulkFailure[], rows: readonly T[]): {
78
+ selectedIds: Set<string>;
79
+ failures: CustomerContractBulkFailure[];
80
+ };
81
+ export declare function executeCustomerContractBulkDelete({ selectedIds, rows, deleteContract, refresh, query, notify, isCurrent }: {
82
+ selectedIds: ReadonlySet<string>;
83
+ rows: readonly CustomerContractRow[];
84
+ deleteContract: (id: string, options?: ActionExecutionOptions) => Promise<unknown>;
85
+ refresh: CustomerContractPageProps['onQuery'];
86
+ query: CustomerContractCollectionQuery;
87
+ notify?: Notify;
88
+ isCurrent?: () => boolean;
89
+ }): Promise<{
90
+ selectedIds: Set<string>;
91
+ failures: CustomerContractBulkFailure[];
92
+ refreshFailed: boolean;
93
+ stale: boolean;
94
+ }>;
37
95
  export declare function buildCustomerContractReviewPayload(review: CustomerContractReview, customerTransferConfirmation?: CustomerTransferConfirmation): CustomerContractReviewPayload;
38
96
  export declare function customerTransferConfirmationFor(original: CustomerContractReview, current: CustomerContractReview): CustomerTransferConfirmation | null;
39
97
  type PendingCustomerTransfer = {
@@ -1 +1 @@
1
- {"version":3,"file":"contracts.d.ts","sourceRoot":"","sources":["../../../../src/lib/remote-ui/pages/contracts.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"contracts.d.ts","sourceRoot":"","sources":["../../../../src/lib/remote-ui/pages/contracts.tsx"],"names":[],"mappings":"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"}
@@ -2,7 +2,9 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import React from 'react';
3
3
  import { normalizeExactName } from '../../domain/normalization.js';
4
4
  import { DataGrid, FilterPanel, InfoNotice, RiskNotice } from '../components/data-grid.js';
5
+ import { BulkDeleteDialog } from '../components/bulk-delete.js';
5
6
  import { ConfirmDialog, FieldGrid, FormField, Modal, UploadZone } from '../components/forms.js';
7
+ import { pageAfterBulkDelete, retainFailedSelection, runBulkDelete, toggleBulkKey, toggleBulkPage } from '../bulk-delete.js';
6
8
  import { formatBusinessDate } from '../date-time.js';
7
9
  import { runNotifiedMutationWithRefresh } from '../toast-queue.js';
8
10
  import './procurement-contracts.css';
@@ -61,8 +63,10 @@ export function createCustomerContractActionApi({ onAction, onFileAction }) {
61
63
  assertSucceeded(result, '客户合同审核保存失败');
62
64
  return result;
63
65
  },
64
- deleteContract: async (customerContractId) => {
65
- const result = await onAction('delete_entity', { entityType: 'customerContract', id: customerContractId });
66
+ deleteContract: async (customerContractId, options) => {
67
+ const result = options
68
+ ? await onAction('delete_entity', { entityType: 'customerContract', id: customerContractId }, options)
69
+ : await onAction('delete_entity', { entityType: 'customerContract', id: customerContractId });
66
70
  assertSucceeded(result, '客户合同删除失败');
67
71
  return result;
68
72
  }
@@ -139,6 +143,122 @@ export function createCustomerContractQueryScheduler(query, debounceMs = 300) {
139
143
  cancel
140
144
  };
141
145
  }
146
+ export function createCustomerContractQueryCancelCoordinator() {
147
+ let current = null;
148
+ return {
149
+ register(cancel) {
150
+ current = cancel;
151
+ return () => { if (current === cancel)
152
+ current = null; };
153
+ },
154
+ cancelCurrent() { current?.(); }
155
+ };
156
+ }
157
+ export function createCustomerContractSelectionQuery(query, clearSelection) {
158
+ return async (request) => {
159
+ clearSelection();
160
+ return await query(request);
161
+ };
162
+ }
163
+ export function createCustomerContractBulkConfirmGuard() {
164
+ let running = false;
165
+ return {
166
+ run(operation) {
167
+ if (running)
168
+ return null;
169
+ running = true;
170
+ return operation().finally(() => { running = false; });
171
+ }
172
+ };
173
+ }
174
+ export function createCustomerContractBulkGenerationGuard() {
175
+ let revision = 0;
176
+ const isCurrent = (token) => token.revision === revision;
177
+ return {
178
+ invalidate() { revision += 1; },
179
+ capture() { return { revision }; },
180
+ isCurrent,
181
+ commit(token, apply) {
182
+ if (!isCurrent(token))
183
+ return false;
184
+ apply();
185
+ return true;
186
+ }
187
+ };
188
+ }
189
+ export function customerContractInteractionBusy(pageBusy, bulkBusy) {
190
+ return pageBusy || bulkBusy;
191
+ }
192
+ export function customerContractRouteEntryQuery(route) {
193
+ return route.type === 'page' && route.page === 'customer-contract-review'
194
+ ? { page: 1, pageSize: PAGE_SIZE, filters: {} }
195
+ : null;
196
+ }
197
+ export async function reconcileLatestCustomerContractQuery({ isActive, latestQuery, query }) {
198
+ if (!isActive())
199
+ return false;
200
+ try {
201
+ return await query(latestQuery()) !== false;
202
+ }
203
+ catch {
204
+ return false;
205
+ }
206
+ }
207
+ export function runCustomerContractBulkGeneration({ confirmGuard, generationGuard, operation, onCurrentResult, onStaleReconcile, onBusyChange }) {
208
+ return confirmGuard.run(async () => {
209
+ const generation = generationGuard.capture();
210
+ onBusyChange(true);
211
+ try {
212
+ const result = await operation(() => generationGuard.isCurrent(generation));
213
+ if (!generationGuard.commit(generation, () => onCurrentResult(result))) {
214
+ try {
215
+ await onStaleReconcile?.(result);
216
+ }
217
+ catch { /* query owner already reports current refresh failures */ }
218
+ }
219
+ return result;
220
+ }
221
+ finally {
222
+ onBusyChange(false);
223
+ }
224
+ });
225
+ }
226
+ export function reconcileCustomerContractBulkState(selectedIds, failures, rows) {
227
+ const currentIds = rows.map(row => row.id);
228
+ const retainedSelection = retainFailedSelection([...selectedIds].map(key => ({ key, message: '' })), currentIds);
229
+ const retainedFailures = failures.filter(failure => retainedSelection.has(failure.key));
230
+ return {
231
+ selectedIds: retainedSelection.size === selectedIds.size ? selectedIds : retainedSelection,
232
+ failures: retainedFailures.length === failures.length ? failures : retainedFailures
233
+ };
234
+ }
235
+ export async function executeCustomerContractBulkDelete({ selectedIds, rows, deleteContract, refresh, query, notify, isCurrent = () => true }) {
236
+ const keys = [...selectedIds];
237
+ const labels = new Map(rows.map(contract => [contract.id, contract.contractNo || contract.customerName || contract.id]));
238
+ const result = await runBulkDelete(keys, id => deleteContract(id, { suppressRefresh: true }));
239
+ const remainingRows = rows.filter(contract => !result.succeeded.includes(contract.id));
240
+ const nextPage = pageAfterBulkDelete(query.page, remainingRows.length);
241
+ const failures = result.failed.map(failure => ({ ...failure, label: labels.get(failure.key) ?? failure.key }));
242
+ if (!isCurrent())
243
+ return { selectedIds: new Set(keys), failures, refreshFailed: false, stale: true };
244
+ try {
245
+ const refreshed = await refresh({ ...query, page: nextPage });
246
+ if (!isCurrent())
247
+ return { selectedIds: new Set(keys), failures, refreshFailed: false, stale: true };
248
+ if (refreshed === false)
249
+ return { selectedIds: new Set(keys), failures, refreshFailed: true, stale: false };
250
+ }
251
+ catch {
252
+ if (!isCurrent())
253
+ return { selectedIds: new Set(keys), failures, refreshFailed: false, stale: true };
254
+ return { selectedIds: new Set(keys), failures, refreshFailed: true, stale: false };
255
+ }
256
+ if (result.failed.length === 0)
257
+ notify?.({ message: `已删除 ${result.succeeded.length} 条`, tone: 'success' });
258
+ else
259
+ notify?.({ message: `成功 ${result.succeeded.length}、失败 ${result.failed.length}`, tone: result.succeeded.length > 0 ? 'warning' : 'error' });
260
+ return { selectedIds: new Set(result.failed.map(failure => failure.key)), failures, refreshFailed: false, stale: false };
261
+ }
142
262
  export function buildCustomerContractReviewPayload(review, customerTransferConfirmation) {
143
263
  return {
144
264
  reviewSource: review.reviewSource,
@@ -197,23 +317,61 @@ export function validateCustomerContractClauseReviews(clauses) {
197
317
  }
198
318
  export function CustomerContractsPage(props) {
199
319
  const [filters, setFilters] = React.useState({});
320
+ const [selectedIds, setSelectedIds] = React.useState(new Set());
321
+ const [bulkOpen, setBulkOpen] = React.useState(false);
322
+ const [bulkBusy, setBulkBusy] = React.useState(false);
323
+ const [bulkFailures, setBulkFailures] = React.useState([]);
324
+ const bulkConfirmGuardRef = React.useRef();
325
+ if (!bulkConfirmGuardRef.current)
326
+ bulkConfirmGuardRef.current = createCustomerContractBulkConfirmGuard();
327
+ const bulkGenerationGuardRef = React.useRef();
328
+ if (!bulkGenerationGuardRef.current)
329
+ bulkGenerationGuardRef.current = createCustomerContractBulkGenerationGuard();
200
330
  const [review, setReview] = React.useState(null);
201
331
  const [reviewMode, setReviewMode] = React.useState('review');
202
332
  const [deleteContract, setDeleteContract] = React.useState(null);
203
333
  const api = React.useMemo(() => createCustomerContractActionApi(props), [props.onAction, props.onFileAction]);
204
334
  const closeReview = React.useCallback(() => { setReview(null); props.onReviewClose?.(); }, [props.onReviewClose]);
205
- const scheduler = React.useMemo(() => createCustomerContractQueryScheduler(props.onQuery), [props.onQuery]);
206
- React.useEffect(() => () => scheduler.cancel(), [scheduler]);
335
+ const clearBulkState = React.useCallback(() => {
336
+ bulkGenerationGuardRef.current.invalidate();
337
+ setSelectedIds(new Set());
338
+ setBulkFailures([]);
339
+ setBulkOpen(false);
340
+ }, []);
341
+ const selectionQuery = React.useMemo(() => createCustomerContractSelectionQuery(props.onQuery, clearBulkState), [props.onQuery, clearBulkState]);
342
+ const query = React.useCallback((request) => {
343
+ props.onQueryRequested?.(request);
344
+ return selectionQuery(request);
345
+ }, [props.onQueryRequested, selectionQuery]);
346
+ const scheduler = React.useMemo(() => createCustomerContractQueryScheduler(query), [query]);
347
+ React.useEffect(() => {
348
+ const unregisterQueryCancel = props.onQueryCancelRegistration?.(scheduler.cancel);
349
+ return () => {
350
+ unregisterQueryCancel?.();
351
+ scheduler.cancel();
352
+ bulkGenerationGuardRef.current.invalidate();
353
+ };
354
+ }, [props.onQueryCancelRegistration, scheduler]);
207
355
  React.useEffect(() => {
208
356
  if (props.importReviewState.status === 'AWAITING_REVIEW') {
209
357
  setReviewMode('review');
210
358
  setReview(normalizeReview(props.importReviewState.review));
211
359
  }
212
360
  }, [props.importReviewState]);
361
+ React.useEffect(() => {
362
+ const reconciled = reconcileCustomerContractBulkState(selectedIds, bulkFailures, props.collection.items);
363
+ if (reconciled.selectedIds !== selectedIds)
364
+ setSelectedIds(reconciled.selectedIds);
365
+ if (reconciled.failures !== bulkFailures)
366
+ setBulkFailures(reconciled.failures);
367
+ }, [props.collection.items, selectedIds, bulkFailures]);
213
368
  const changeFilter = (key, value) => {
214
369
  const next = { ...filters, [key]: value };
215
370
  setFilters(next);
216
- scheduler.schedule(customerContractFilters.find((definition) => definition.key === key)?.type ?? 'text', next, collectionPagination(props.collection).pageSize);
371
+ clearBulkState();
372
+ const pageSize = collectionPagination(props.collection).pageSize;
373
+ props.onQueryRequested?.({ page: 1, pageSize, filters: next });
374
+ scheduler.schedule(customerContractFilters.find((definition) => definition.key === key)?.type ?? 'text', next, pageSize);
217
375
  };
218
376
  const openExistingReview = async (contractId, mode) => {
219
377
  try {
@@ -225,6 +383,7 @@ export function CustomerContractsPage(props) {
225
383
  }
226
384
  };
227
385
  const pagination = collectionPagination(props.collection);
386
+ const interactionBusy = customerContractInteractionBusy(props.busy, bulkBusy);
228
387
  const columns = [
229
388
  { type: 'text', key: 'customerName', header: '客户名称', width: 150 },
230
389
  { type: 'text', key: 'customerCode', header: '客户代码', width: 90, format: displayValue },
@@ -236,10 +395,45 @@ export function CustomerContractsPage(props) {
236
395
  { type: 'text', key: 'latestAnalysisAt', header: '最新分析时间', width: 170, truncate: false, format: displayValue },
237
396
  { type: 'ellipsis', key: 'sourceFileName', header: '来源文件', width: 160, maxWidth: 160 }
238
397
  ];
239
- return _jsxs("section", { className: "tcw-contract-page tcw-contract-prototype-surface", "aria-label": "\u5BA2\u6237\u5408\u540C\u5BA1\u6838", children: [_jsx("h2", { children: "\u5BA2\u6237\u5408\u540C\u5217\u8868" }), _jsx(FilterPanel, { definitions: customerContractFilters, values: filters, onChange: changeFilter, onQuery: () => scheduler.flush(filters, pagination.pageSize), onReset: () => { setFilters({}); scheduler.flush({}, pagination.pageSize); } }), _jsx(InfoNotice, { children: "\u652F\u6301 PDF\u3001DOC\u3001DOCX\u3001XLS\u3001XLSX\u3001PNG\u3001JPEG\u3001WEBP\u3002\u89E3\u6790\u6210\u529F\u540E\u81EA\u52A8\u6253\u5F00\u5BA1\u6838\u3002" }), props.importReviewState.status === 'PROCESSING' && _jsxs("p", { className: "tcw-contract-import-status", role: "status", children: ["\u6B63\u5728\u89E3\u6790 ", props.importReviewState.sourceFileName, "..."] }), props.importReviewState.status === 'FAILED' && _jsxs("div", { className: "tcw-contract-page-error", role: "alert", children: [props.importReviewState.message, props.importReviewState.importTaskId && props.onRetryImport && _jsxs("button", { type: "button", className: "tcw-button tcw-button--secondary", onClick: () => void Promise.resolve(props.onRetryImport?.(props.importReviewState.status === 'FAILED' ? props.importReviewState.importTaskId : '')).catch(cause => props.onNotify?.({ message: cause instanceof Error ? cause.message : '重新解析失败', tone: 'error' })), children: [_jsx("i", { className: "ri-restart-line", "aria-hidden": "true" }), "\u91CD\u65B0\u89E3\u6790"] })] }), _jsx("div", { className: "tcw-contract-action-table", children: _jsx(DataGrid, { columns: columns, rows: props.collection.items, rowKey: "id", actionWidth: 190, actions: (item) => _jsxs(_Fragment, { children: [_jsxs("button", { type: "button", disabled: props.busy, onClick: () => void openExistingReview(item.id, 'detail'), children: [_jsx("i", { className: "ri-eye-line", "aria-hidden": "true" }), "\u8BE6\u60C5"] }), _jsxs("button", { type: "button", disabled: props.busy, onClick: () => void openExistingReview(item.id, 'review'), children: [_jsx("i", { className: "ri-edit-line", "aria-hidden": "true" }), "\u5BA1\u6838"] }), _jsxs("button", { type: "button", className: "is-danger", disabled: props.busy, onClick: () => setDeleteContract(item), children: [_jsx("i", { className: "ri-delete-bin-line", "aria-hidden": "true" }), "\u5220\u9664"] })] }), pagination: pagination, onPageChange: (page) => scheduler.page(page, filters, pagination.pageSize), onPageSizeChange: (pageSize) => scheduler.pageSize(pageSize, filters), emptyText: "\u6682\u65E0\u5BA2\u6237\u5408\u540C\u6570\u636E" }) }), _jsx(ContractReviewDialog, { open: Boolean(review), mode: reviewMode, review: review, busy: props.busy, reanalysisState: props.reanalysisState, onReanalyze: props.onReanalyze, onClose: closeReview, onSave: async (nextReview, confirmation) => { const saved = await runNotifiedMutationWithRefresh(() => api.saveReview(buildCustomerContractReviewPayload(nextReview, confirmation)).then(() => undefined), () => props.onQuery({ page: pagination.page, pageSize: pagination.pageSize, filters }), props.onNotify, { success: '客户合同审核保存成功', error: '客户合同审核保存失败' }); if (saved)
240
- closeReview(); } }), _jsx(ContractDeleteDialog, { open: Boolean(deleteContract), contract: deleteContract, busy: props.busy, onCancel: () => setDeleteContract(null), onConfirm: async () => { if (!deleteContract)
241
- return; const deleted = await runNotifiedMutationWithRefresh(() => api.deleteContract(deleteContract.id).then(() => undefined), () => props.onQuery({ page: pagination.page, pageSize: pagination.pageSize, filters }), props.onNotify, { success: '客户合同删除成功', error: '客户合同删除失败' }); if (deleted)
242
- setDeleteContract(null); } })] });
398
+ return _jsxs("section", { className: "tcw-contract-page tcw-contract-prototype-surface", "aria-label": "\u5BA2\u6237\u5408\u540C\u5BA1\u6838", children: [_jsx("h2", { children: "\u5BA2\u6237\u5408\u540C\u5217\u8868" }), _jsx(FilterPanel, { definitions: customerContractFilters, values: filters, onChange: changeFilter, onQuery: () => scheduler.flush(filters, pagination.pageSize), onReset: () => { setFilters({}); scheduler.flush({}, pagination.pageSize); } }), _jsx(InfoNotice, { children: "\u652F\u6301 PDF\u3001DOC\u3001DOCX\u3001XLS\u3001XLSX\u3001PNG\u3001JPEG\u3001WEBP\u3002\u89E3\u6790\u6210\u529F\u540E\u81EA\u52A8\u6253\u5F00\u5BA1\u6838\u3002" }), props.importReviewState.status === 'PROCESSING' && _jsxs("p", { className: "tcw-contract-import-status", role: "status", children: ["\u6B63\u5728\u89E3\u6790 ", props.importReviewState.sourceFileName, "..."] }), props.importReviewState.status === 'FAILED' && _jsxs("div", { className: "tcw-contract-page-error", role: "alert", children: [props.importReviewState.message, props.importReviewState.importTaskId && props.onRetryImport && _jsxs("button", { type: "button", className: "tcw-button tcw-button--secondary", disabled: interactionBusy, onClick: () => { if (!interactionBusy)
399
+ void Promise.resolve(props.onRetryImport?.(props.importReviewState.status === 'FAILED' ? props.importReviewState.importTaskId : '')).catch(cause => props.onNotify?.({ message: cause instanceof Error ? cause.message : '重新解析失败', tone: 'error' })); }, children: [_jsx("i", { className: "ri-restart-line", "aria-hidden": "true" }), "\u91CD\u65B0\u89E3\u6790"] })] }), _jsx("div", { className: "tcw-contract-action-table", children: _jsx(DataGrid, { columns: columns, rows: props.collection.items, rowKey: "id", actionWidth: 190, selection: { selectedKeys: selectedIds, onToggle: id => { if (!interactionBusy)
400
+ setSelectedIds(current => toggleBulkKey(current, id)); }, onTogglePage: () => { if (!interactionBusy)
401
+ setSelectedIds(current => toggleBulkPage(current, props.collection.items.map(item => item.id))); }, onClear: () => { if (!interactionBusy)
402
+ setSelectedIds(new Set()); }, onDelete: () => { if (!interactionBusy) {
403
+ setBulkFailures([]);
404
+ setBulkOpen(true);
405
+ } }, labelFor: item => `选择客户合同:${item.contractNo || item.customerName || item.id}`, disabled: interactionBusy }, actions: (item) => _jsxs(_Fragment, { children: [_jsxs("button", { type: "button", disabled: interactionBusy, onClick: () => void openExistingReview(item.id, 'detail'), children: [_jsx("i", { className: "ri-eye-line", "aria-hidden": "true" }), "\u8BE6\u60C5"] }), _jsxs("button", { type: "button", disabled: interactionBusy, onClick: () => void openExistingReview(item.id, 'review'), children: [_jsx("i", { className: "ri-edit-line", "aria-hidden": "true" }), "\u5BA1\u6838"] }), _jsxs("button", { type: "button", className: "is-danger", disabled: interactionBusy, onClick: () => setDeleteContract(item), children: [_jsx("i", { className: "ri-delete-bin-line", "aria-hidden": "true" }), "\u5220\u9664"] })] }), pagination: pagination, onPageChange: (page) => scheduler.page(page, filters, pagination.pageSize), onPageSizeChange: (pageSize) => scheduler.pageSize(pageSize, filters), emptyText: "\u6682\u65E0\u5BA2\u6237\u5408\u540C\u6570\u636E" }) }), _jsx(ContractReviewDialog, { open: Boolean(review), mode: reviewMode, review: review, busy: interactionBusy, reanalysisState: props.reanalysisState, onReanalyze: props.onReanalyze, onClose: closeReview, onSave: async (nextReview, confirmation) => { const saved = await runNotifiedMutationWithRefresh(() => api.saveReview(buildCustomerContractReviewPayload(nextReview, confirmation)).then(() => undefined), async () => { await props.onQuery({ page: pagination.page, pageSize: pagination.pageSize, filters }); }, props.onNotify, { success: '客户合同审核保存成功', error: '客户合同审核保存失败' }); if (saved)
406
+ closeReview(); } }), _jsx(ContractDeleteDialog, { open: Boolean(deleteContract), contract: deleteContract, busy: interactionBusy, onCancel: () => setDeleteContract(null), onConfirm: async () => { if (interactionBusy || !deleteContract)
407
+ return; const deleted = await runNotifiedMutationWithRefresh(() => api.deleteContract(deleteContract.id).then(() => undefined), async () => { await props.onQuery({ page: pagination.page, pageSize: pagination.pageSize, filters }); }, props.onNotify, { success: '客户合同删除成功', error: '客户合同删除失败' }); if (deleted)
408
+ setDeleteContract(null); } }), _jsx(BulkDeleteDialog, { open: bulkOpen, title: "\u6279\u91CF\u5220\u9664\u5BA2\u6237\u5408\u540C", count: selectedIds.size, message: "\u5C06\u903B\u8F91\u5220\u9664\u6240\u9009\u5BA2\u6237\u5408\u540C\u53CA\u5176\u4E03\u7C7B\u5173\u952E\u6761\u6B3E\u5BA1\u6838\u6570\u636E\uFF1B\u5220\u9664\u540E\u4E0D\u53EF\u6062\u590D\uFF1B\u5DF2\u751F\u6210\u9500\u552E\u6587\u4EF6\u5386\u53F2\u4FDD\u6301\u4E0D\u53D8\u3002", busy: interactionBusy, failures: bulkFailures, onCancel: () => { if (!bulkBusy)
409
+ setBulkOpen(false); }, onConfirm: () => {
410
+ if (interactionBusy || selectedIds.size === 0)
411
+ return;
412
+ void runCustomerContractBulkGeneration({
413
+ confirmGuard: bulkConfirmGuardRef.current,
414
+ generationGuard: bulkGenerationGuardRef.current,
415
+ operation: isCurrent => executeCustomerContractBulkDelete({
416
+ selectedIds,
417
+ rows: props.collection.items,
418
+ deleteContract: api.deleteContract,
419
+ refresh: props.onQuery,
420
+ query: { page: pagination.page, pageSize: pagination.pageSize, filters },
421
+ notify: props.onNotify,
422
+ isCurrent
423
+ }),
424
+ onCurrentResult: result => {
425
+ setSelectedIds(result.selectedIds);
426
+ setBulkFailures(result.failures);
427
+ if (!result.refreshFailed && result.failures.length === 0)
428
+ setBulkOpen(false);
429
+ },
430
+ onStaleReconcile: async () => {
431
+ scheduler.cancel();
432
+ await props.onBulkReconcile?.();
433
+ },
434
+ onBusyChange: busy => { setBulkBusy(busy); props.onBulkBusyChange?.(busy); }
435
+ });
436
+ } })] });
243
437
  }
244
438
  export function ContractUploadDialog({ open, busy, onClose, onUpload, onNotify }) {
245
439
  const [file, setFile] = React.useState(null);
@@ -274,12 +468,14 @@ export function ContractReviewDialog({ open, mode = 'review', review, busy, rean
274
468
  const [form, setForm] = React.useState(() => review ? normalizeReview(review) : null);
275
469
  const [activeType, setActiveType] = React.useState('PAYMENT_TERMS');
276
470
  const [error, setError] = React.useState('');
471
+ const [sourcePanelOpen, setSourcePanelOpen] = React.useState(false);
277
472
  const [transferState, dispatchTransfer] = React.useReducer(customerTransferStateReducer, null);
278
473
  React.useEffect(() => {
279
474
  dispatchTransfer({ type: 'sync-review', open });
280
475
  if (open && review) {
281
476
  setForm(normalizeReview(review));
282
477
  setActiveType('PAYMENT_TERMS');
478
+ setSourcePanelOpen(false);
283
479
  setError('');
284
480
  }
285
481
  }, [open, review]);
@@ -334,7 +530,7 @@ export function ContractReviewDialog({ open, mode = 'review', review, busy, rean
334
530
  const reanalyzing = reanalysisState?.status === 'RUNNING';
335
531
  const readOnly = mode === 'detail';
336
532
  const footer = readOnly ? _jsxs("button", { type: "button", className: "tcw-button tcw-button--secondary", onClick: onClose, children: [_jsx("i", { className: "ri-close-line", "aria-hidden": "true" }), "\u5173\u95ED"] }) : _jsxs(_Fragment, { children: [_jsxs("button", { type: "button", className: "tcw-button tcw-button--secondary", disabled: busy, onClick: onClose, children: [_jsx("i", { className: "ri-close-line", "aria-hidden": "true" }), "\u53D6\u6D88"] }), _jsxs("button", { type: "button", className: "tcw-button tcw-button--primary", disabled: busy, onClick: () => void save(), children: [_jsx("i", { className: "ri-save-line", "aria-hidden": "true" }), "\u4FDD\u5B58\u5BA1\u6838"] })] });
337
- return _jsxs(_Fragment, { children: [_jsxs(Modal, { open: open, title: readOnly ? '客户合同详情' : '审核客户合同', size: "wide", onClose: onClose, footer: footer, children: [_jsxs("div", { className: "tcw-contract-review-header tcw-contract-review-summary", children: [_jsxs("div", { className: "tcw-contract-summary-customer", children: [_jsx("span", { children: "\u5BA2\u6237\u540D\u79F0" }), _jsx("strong", { children: form.customer.name }), _jsx("small", { children: form.customer.code || '未设置客户代码' })] }), _jsx(ContractFields, { review: form, readOnly: readOnly, onChange: setForm }), _jsx(RiskBadge, { level: form.overallRiskLevel }), _jsx(SanctionBadge, { status: form.sanctionStatus })] }), reanalysisState?.status === 'FAILED' && _jsx("div", { className: "tcw-contract-dialog-error", role: "alert", children: reanalysisState.message }), error && _jsx("div", { className: "tcw-contract-dialog-error", role: "alert", children: error }), _jsxs("div", { className: "tcw-contract-review-layout", children: [_jsxs("div", { className: "tcw-contract-review-toolbar", children: [_jsxs("strong", { children: ["\u5F53\u524D", readOnly ? '查看' : '审核', "\u6761\u6B3E\uFF1A", clauseLabel(activeType)] }), !readOnly && onReanalyze && _jsxs("button", { type: "button", className: "tcw-button tcw-button--secondary", disabled: busy || reanalyzing, title: "\u91CD\u65B0\u5206\u6790\u4E03\u7C7B\u5173\u952E\u6761\u6B3E", onClick: () => void reanalyze(), children: [_jsx("i", { className: "ri-refresh-line", "aria-hidden": "true" }), reanalyzing ? 'AI 重新分析中' : 'AI 重新分析'] })] }), _jsx("nav", { className: "tcw-contract-clause-tabs", "aria-label": "\u6761\u6B3E\u7C7B\u578B", children: CLAUSE_TYPES.map((item) => _jsxs("button", { type: "button", className: item.type === activeType ? 'is-active' : '', onClick: () => setActiveType(item.type), children: [_jsx("i", { className: "ri-file-search-line", "aria-hidden": "true" }), item.label, _jsx("small", { children: form.clauses.find((value) => value.clauseType === item.type)?.extractionStatus === 'FOUND' ? '已找到' : '未找到' })] }, item.type)) }), _jsxs("section", { className: "tcw-contract-clause-editor", children: [_jsxs("h3", { children: ["\u5F53\u524D\u5BA1\u6838\u6761\u6B3E\uFF1A", clauseLabel(activeType)] }), clause.extractionStatus === 'NOT_FOUND' && _jsx(RiskNotice, { children: "\u5408\u540C\u7F3A\u5C11\u8BE5\u5173\u952E\u6761\u6B3E\u3002" }), _jsx(FormField, { label: "\u63D0\u53D6\u72B6\u6001", children: _jsxs("select", { disabled: readOnly, className: "tcw-contract-clause-status", value: clause.extractionStatus, onChange: (event) => setClause(updateCustomerContractClauseReview(clause, { extractionStatus: event.target.value })), children: [_jsx("option", { value: "FOUND", children: "\u5DF2\u627E\u5230" }), _jsx("option", { value: "NOT_FOUND", children: "\u672A\u627E\u5230" })] }) }), _jsx(FormField, { label: "\u6765\u6E90\u6761\u6B3E\u539F\u6587", children: _jsx("textarea", { disabled: true, rows: 6, value: clause.sourceText ?? '未找到来源条款' }) }), _jsx(FormField, { label: "\u4EBA\u5DE5\u5BA1\u6838\u5185\u5BB9", children: _jsx("textarea", { disabled: readOnly, rows: 7, value: clause.reviewedText, onChange: (event) => setClause(updateCustomerContractClauseReview(clause, { reviewedText: event.target.value })) }) }), _jsx(FormField, { label: "\u6765\u6E90\u4F4D\u7F6E", children: _jsx("input", { disabled: true, value: clause.sourceLocation ?? '—' }) })] }), _jsxs("section", { className: "tcw-contract-source-preview", children: [_jsx("h3", { children: "AI \u5206\u6790\u7ED3\u679C" }), _jsx(ClauseAnalysis, { clause: clause }), _jsxs("details", { children: [_jsx("summary", { children: "\u6E90\u6587\u4EF6\u9884\u89C8" }), _jsx(SourcePreview, { review: form, clause: clause })] })] })] }), _jsxs("details", { className: "tcw-contract-customer-master", children: [_jsx("summary", { children: readOnly ? '查看客户完整主档' : '查看并编辑客户完整主档' }), _jsx(CustomerFields, { value: form.customer, readOnly: readOnly, onChange: (customer) => setForm({ ...form, customer }) })] })] }), _jsx(ConfirmDialog, { open: Boolean(transferState), title: "\u786E\u8BA4\u8F6C\u79FB\u5BA2\u6237\u5408\u540C", confirmText: "\u786E\u8BA4\u8F6C\u79FB\u5E76\u4FDD\u5B58", confirmDisabled: busy || Boolean(transferState?.confirming), onCancel: () => dispatchTransfer({ type: 'cancel' }), onConfirm: async () => {
533
+ return _jsxs(_Fragment, { children: [_jsxs(Modal, { open: open, title: readOnly ? '客户合同详情' : '审核客户合同', size: "wide", onClose: onClose, footer: footer, children: [_jsxs("div", { className: "tcw-contract-review-header tcw-contract-review-summary", children: [_jsxs("div", { className: "tcw-contract-summary-customer", children: [_jsx("span", { children: "\u5BA2\u6237\u540D\u79F0" }), _jsx("strong", { children: form.customer.name }), _jsx("small", { children: form.customer.code || '未设置客户代码' })] }), _jsx(ContractFields, { review: form, readOnly: readOnly, onChange: setForm }), _jsx(RiskBadge, { level: form.overallRiskLevel }), _jsx(SanctionBadge, { status: form.sanctionStatus })] }), reanalysisState?.status === 'FAILED' && _jsx("div", { className: "tcw-contract-dialog-error", role: "alert", children: reanalysisState.message }), error && _jsx("div", { className: "tcw-contract-dialog-error", role: "alert", children: error }), _jsxs("div", { className: "tcw-contract-review-layout", children: [_jsxs("div", { className: "tcw-contract-review-toolbar", children: [_jsxs("strong", { children: ["\u5F53\u524D", readOnly ? '查看' : '审核', "\u6761\u6B3E\uFF1A", clauseLabel(activeType)] }), !readOnly && onReanalyze && _jsxs("button", { type: "button", className: "tcw-button tcw-button--secondary", disabled: busy || reanalyzing, title: "\u91CD\u65B0\u5206\u6790\u4E03\u7C7B\u5173\u952E\u6761\u6B3E", onClick: () => void reanalyze(), children: [_jsx("i", { className: "ri-refresh-line", "aria-hidden": "true" }), reanalyzing ? 'AI 重新分析中' : 'AI 重新分析'] })] }), _jsx("nav", { className: "tcw-contract-clause-tabs", "aria-label": "\u6761\u6B3E\u7C7B\u578B", children: CLAUSE_TYPES.map((item) => _jsxs("button", { type: "button", className: item.type === activeType ? 'is-active' : '', onClick: () => setActiveType(item.type), children: [_jsx("i", { className: "ri-file-search-line", "aria-hidden": "true" }), item.label, _jsx("small", { children: form.clauses.find((value) => value.clauseType === item.type)?.extractionStatus === 'FOUND' ? '已找到' : '未找到' })] }, item.type)) }), _jsxs("section", { className: "tcw-contract-clause-editor", children: [_jsxs("h3", { children: ["\u5F53\u524D\u5BA1\u6838\u6761\u6B3E\uFF1A", clauseLabel(activeType)] }), clause.extractionStatus === 'NOT_FOUND' && _jsx(RiskNotice, { children: "\u5408\u540C\u7F3A\u5C11\u8BE5\u5173\u952E\u6761\u6B3E\u3002" }), _jsx(FormField, { label: "\u63D0\u53D6\u72B6\u6001", children: _jsxs("select", { disabled: readOnly, className: "tcw-contract-clause-status", value: clause.extractionStatus, onChange: (event) => setClause(updateCustomerContractClauseReview(clause, { extractionStatus: event.target.value })), children: [_jsx("option", { value: "FOUND", children: "\u5DF2\u627E\u5230" }), _jsx("option", { value: "NOT_FOUND", children: "\u672A\u627E\u5230" })] }) }), _jsx(FormField, { label: "\u6765\u6E90\u6761\u6B3E\u539F\u6587", children: _jsx("textarea", { disabled: true, rows: 6, value: clause.sourceText ?? '未找到来源条款' }) }), _jsx(FormField, { label: "\u4EBA\u5DE5\u5BA1\u6838\u5185\u5BB9", children: _jsx("textarea", { disabled: readOnly, rows: 7, value: clause.reviewedText, onChange: (event) => setClause(updateCustomerContractClauseReview(clause, { reviewedText: event.target.value })) }) }), _jsx(FormField, { label: "\u6765\u6E90\u4F4D\u7F6E", children: _jsx("input", { disabled: true, value: clause.sourceLocation ?? '—' }) })] }), _jsxs("section", { className: "tcw-contract-source-preview", children: [_jsxs("div", { className: "tcw-contract-source-heading", children: [_jsx("h3", { children: sourcePanelOpen ? '源文件内容' : 'AI 分析结果' }), _jsxs("button", { type: "button", className: "tcw-button tcw-button--secondary", onClick: () => setSourcePanelOpen((current) => !current), children: [_jsx("i", { className: sourcePanelOpen ? 'ri-arrow-left-line' : 'ri-file-search-line', "aria-hidden": "true" }), sourcePanelOpen ? '返回分析' : '打开源文件'] })] }), _jsx("div", { className: "tcw-contract-source-main", children: sourcePanelOpen ? _jsx(SourcePreview, { review: form, clause: clause }) : _jsx(ClauseAnalysis, { clause: clause }) })] })] }), _jsxs("details", { className: "tcw-contract-customer-master", children: [_jsx("summary", { children: readOnly ? '查看客户完整主档' : '查看并编辑客户完整主档' }), _jsx(CustomerFields, { value: form.customer, readOnly: readOnly, onChange: (customer) => setForm({ ...form, customer }) })] })] }), _jsx(ConfirmDialog, { open: Boolean(transferState), title: "\u786E\u8BA4\u8F6C\u79FB\u5BA2\u6237\u5408\u540C", confirmText: "\u786E\u8BA4\u8F6C\u79FB\u5E76\u4FDD\u5B58", confirmDisabled: busy || Boolean(transferState?.confirming), onCancel: () => dispatchTransfer({ type: 'cancel' }), onConfirm: async () => {
338
534
  if (!transferState || transferState.confirming)
339
535
  return;
340
536
  dispatchTransfer({ type: 'begin-confirm' });
@@ -369,13 +565,19 @@ function ClauseAnalysis({ clause }) {
369
565
  }
370
566
  function SourcePreview({ review, clause }) {
371
567
  if (review.sourcePreview.kind === 'URL')
372
- return _jsx("iframe", { title: `源文件预览:${review.sourceFileName}`, src: review.sourcePreview.url });
568
+ return _jsx(SourceDocument, { review: review, activeClause: clause });
373
569
  if (clause.sourceContent)
374
570
  return _jsxs("div", { children: [_jsx("small", { children: clause.sourceLocation ?? '来源位置未标注' }), _jsx("pre", { children: clause.sourceContent })] });
375
571
  if (review.sourcePreview.kind === 'TEXT')
376
572
  return _jsx("pre", { children: review.sourcePreview.content });
377
573
  return _jsxs("div", { className: "tcw-contract-preview-unavailable", children: [_jsx("i", { className: "ri-file-warning-line", "aria-hidden": "true" }), _jsx("p", { children: review.sourcePreview.message ?? '源文件暂无法在线预览' }), _jsx("small", { children: clause.sourceContent ?? review.sourceFileName })] });
378
574
  }
575
+ function SourceDocument({ review, activeClause }) {
576
+ const excerpts = review.clauses.filter((clause) => Boolean(clause.sourceContent?.trim()));
577
+ if (!excerpts.length)
578
+ return _jsxs("div", { className: "tcw-contract-preview-unavailable", children: [_jsx("i", { className: "ri-file-warning-line", "aria-hidden": "true" }), _jsx("p", { children: "\u6E90\u6587\u4EF6\u5DF2\u52A0\u8F7D\uFF0C\u4F46\u5F53\u524D\u6D4F\u89C8\u5668\u6C99\u7BB1\u65E0\u6CD5\u76F4\u63A5\u6E32\u67D3\u539F\u6587\u4EF6\u3002" }), _jsx("small", { children: review.sourceFileName })] });
579
+ return _jsxs("section", { className: "tcw-contract-source-document", "aria-label": `源文件内容:${review.sourceFileName}`, children: [_jsxs("header", { children: [_jsx("i", { className: "ri-file-text-line", "aria-hidden": "true" }), _jsxs("div", { children: [_jsx("strong", { children: review.sourceFileName }), _jsx("small", { children: "\u5DF2\u5728\u5BA1\u6838\u9875\u9762\u5185\u5C55\u793A AI \u63D0\u53D6\u5230\u7684\u6E90\u6587\u4EF6\u4E0A\u4E0B\u6587\uFF0C\u4E0D\u518D\u8DF3\u8F6C\u5916\u90E8\u7A97\u53E3\u3002" })] })] }), excerpts.map((clause) => _jsxs("article", { className: clause.clauseType === activeClause.clauseType ? 'is-active' : '', children: [_jsxs("h4", { children: [clauseLabel(clause.clauseType), _jsx("small", { children: clause.sourceLocation ?? '来源位置未标注' })] }), _jsx("pre", { children: clause.sourceContent })] }, clause.clauseType))] });
580
+ }
379
581
  function RiskBadge({ level }) { return _jsx("span", { className: `tcw-contract-risk tcw-contract-risk--${level.toLowerCase()}`, children: riskLabels[level] }); }
380
582
  function SanctionBadge({ status }) { return _jsx("span", { className: `tcw-contract-sanction tcw-contract-sanction--${status.toLowerCase()}`, children: sanctionLabels[status] }); }
381
583
  function ContractRiskSummary({ item }) { return _jsxs("span", { className: "tcw-contract-risk-summary", children: [_jsx(RiskBadge, { level: item.overallRiskLevel }), _jsxs("span", { className: `tcw-contract-sanction-text tcw-contract-sanction-text--${item.sanctionStatus.toLowerCase()}`, children: ["\u5236\u88C1", sanctionLabels[item.sanctionStatus]] })] }); }