@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.
- package/dist/lib/import-jobs/import-job-callback.processor.d.ts.map +1 -1
- package/dist/lib/import-jobs/import-job-callback.processor.js +5 -4
- package/dist/lib/import-jobs/import-job-callback.processor.js.map +1 -1
- package/dist/lib/remote-components/trade-compliance-workbench/app.css +1 -1
- package/dist/lib/remote-components/trade-compliance-workbench/app.js +5 -5
- package/dist/lib/remote-ui/action-execution.d.ts +5 -2
- package/dist/lib/remote-ui/action-execution.d.ts.map +1 -1
- package/dist/lib/remote-ui/action-execution.js +2 -2
- package/dist/lib/remote-ui/action-execution.js.map +1 -1
- package/dist/lib/remote-ui/app-source.d.ts.map +1 -1
- package/dist/lib/remote-ui/app-source.js +134 -35
- package/dist/lib/remote-ui/app-source.js.map +1 -1
- package/dist/lib/remote-ui/bulk-delete.d.ts +56 -0
- package/dist/lib/remote-ui/bulk-delete.d.ts.map +1 -0
- package/dist/lib/remote-ui/bulk-delete.js +138 -0
- package/dist/lib/remote-ui/bulk-delete.js.map +1 -0
- package/dist/lib/remote-ui/components/bulk-delete.d.ts +21 -0
- package/dist/lib/remote-ui/components/bulk-delete.d.ts.map +1 -0
- package/dist/lib/remote-ui/components/bulk-delete.js +9 -0
- package/dist/lib/remote-ui/components/bulk-delete.js.map +1 -0
- package/dist/lib/remote-ui/components/data-grid.d.ts +12 -1
- package/dist/lib/remote-ui/components/data-grid.d.ts.map +1 -1
- package/dist/lib/remote-ui/components/data-grid.js +24 -5
- package/dist/lib/remote-ui/components/data-grid.js.map +1 -1
- package/dist/lib/remote-ui/components/task-status.d.ts +10 -1
- package/dist/lib/remote-ui/components/task-status.d.ts.map +1 -1
- package/dist/lib/remote-ui/components/task-status.js +79 -10
- package/dist/lib/remote-ui/components/task-status.js.map +1 -1
- package/dist/lib/remote-ui/customer-contract-contracts.d.ts +7 -2
- 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/import-task-status.d.ts +1 -1
- package/dist/lib/remote-ui/import-task-status.d.ts.map +1 -1
- package/dist/lib/remote-ui/import-task-status.js +2 -2
- package/dist/lib/remote-ui/import-task-status.js.map +1 -1
- package/dist/lib/remote-ui/model.d.ts +1 -0
- package/dist/lib/remote-ui/model.d.ts.map +1 -1
- package/dist/lib/remote-ui/model.js +3 -0
- package/dist/lib/remote-ui/model.js.map +1 -1
- package/dist/lib/remote-ui/pages/catalogs.css +91 -0
- package/dist/lib/remote-ui/pages/catalogs.d.ts +49 -3
- package/dist/lib/remote-ui/pages/catalogs.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/catalogs.js +123 -12
- package/dist/lib/remote-ui/pages/catalogs.js.map +1 -1
- package/dist/lib/remote-ui/pages/contracts.css +42 -6
- package/dist/lib/remote-ui/pages/contracts.d.ts +61 -3
- package/dist/lib/remote-ui/pages/contracts.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/contracts.js +213 -11
- package/dist/lib/remote-ui/pages/contracts.js.map +1 -1
- package/dist/lib/remote-ui/pages/files.d.ts +52 -10
- package/dist/lib/remote-ui/pages/files.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/files.js +122 -12
- package/dist/lib/remote-ui/pages/files.js.map +1 -1
- package/dist/lib/remote-ui/pages/home.js +4 -4
- package/dist/lib/remote-ui/pages/home.js.map +1 -1
- package/dist/lib/remote-ui/pages/procurement.d.ts +87 -2
- package/dist/lib/remote-ui/pages/procurement.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/procurement.js +225 -21
- package/dist/lib/remote-ui/pages/procurement.js.map +1 -1
- package/dist/lib/remote-ui/pages/settings.d.ts +39 -6
- package/dist/lib/remote-ui/pages/settings.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/settings.js +94 -5
- package/dist/lib/remote-ui/pages/settings.js.map +1 -1
- package/dist/lib/remote-ui/polling.d.ts +1 -0
- package/dist/lib/remote-ui/polling.d.ts.map +1 -1
- package/dist/lib/remote-ui/polling.js +6 -1
- package/dist/lib/remote-ui/polling.js.map +1 -1
- package/dist/lib/remote-ui/procurement-contracts.d.ts +6 -2
- package/dist/lib/remote-ui/procurement-contracts.d.ts.map +1 -1
- package/dist/lib/remote-ui/procurement-contracts.js +11 -1
- package/dist/lib/remote-ui/procurement-contracts.js.map +1 -1
- package/dist/lib/remote-ui/styles.css +55 -19
- package/dist/lib/workbench.service.d.ts.map +1 -1
- package/dist/lib/workbench.service.js +20 -23
- package/dist/lib/workbench.service.js.map +1 -1
- 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(
|
|
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:
|
|
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:
|
|
64
|
+
height: auto;
|
|
43
65
|
min-height: 0;
|
|
44
66
|
column-gap: 8px;
|
|
45
67
|
row-gap: 0;
|
|
46
|
-
margin-top:
|
|
47
|
-
overflow:
|
|
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":"AAEA,OAAO,KAAK,EAAoB,gBAAgB,EAAE,WAAW,EAAe,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;
|
|
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 =
|
|
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
|
|
206
|
-
|
|
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
|
-
|
|
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",
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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: [
|
|
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(
|
|
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]] })] }); }
|