@chenchaolong/plugin-trade-compliance-workbench 0.1.12 → 0.1.13
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.
|
@@ -70,7 +70,6 @@
|
|
|
70
70
|
const [selectedIds, setSelectedIds] = React.useState([])
|
|
71
71
|
const [statusFilter, setStatusFilter] = React.useState('all')
|
|
72
72
|
const [busy, setBusy] = React.useState(false)
|
|
73
|
-
const [notice, setNotice] = React.useState('')
|
|
74
73
|
const [query, setQuery] = React.useState('')
|
|
75
74
|
const [showManualForm, setShowManualForm] = React.useState(false)
|
|
76
75
|
const [form, setForm] = React.useState({ productName: '', hsCode: '', keywords: '', controlNote: '', enabled: true })
|
|
@@ -95,7 +94,7 @@
|
|
|
95
94
|
}
|
|
96
95
|
}, [])
|
|
97
96
|
|
|
98
|
-
React.useEffect(reportResize, [data, activePage, selectedIds, statusFilter, busy,
|
|
97
|
+
React.useEffect(reportResize, [data, activePage, selectedIds, statusFilter, busy, showManualForm])
|
|
99
98
|
|
|
100
99
|
const reviewItems = data.reviewItems.filter((item) => !isPlaceholderReviewItem(item))
|
|
101
100
|
const pendingItems = reviewItems.filter((item) => (item.reviewStatus || 'pending') === 'pending')
|
|
@@ -190,10 +189,12 @@
|
|
|
190
189
|
if (!item || !item.id || busy) return
|
|
191
190
|
setBusy(true)
|
|
192
191
|
try {
|
|
193
|
-
const response =
|
|
192
|
+
const response = item.materializedOnly
|
|
193
|
+
? await executeAction(materializedDeleteAction(item), null, { id: item.materializedId || parseMaterializedId(item.id) }, {})
|
|
194
|
+
: await executeAction('reject_review_item', item.id, { itemId: item.id }, {})
|
|
194
195
|
assertActionSuccess(response)
|
|
195
196
|
setSelectedIds(selectedIds.filter((id) => id !== item.id))
|
|
196
|
-
showNotice('审核项已驳回。', 'success')
|
|
197
|
+
showNotice(item.materializedOnly ? '记录已驳回并移出正式列表。' : '审核项已驳回。', 'success')
|
|
197
198
|
await reload()
|
|
198
199
|
} catch (error) {
|
|
199
200
|
showNotice(getErrorMessage(error), 'error')
|
|
@@ -204,12 +205,54 @@
|
|
|
204
205
|
|
|
205
206
|
async function batchApproveSelected() {
|
|
206
207
|
if (!selectedIds.length || busy) return
|
|
208
|
+
const rows = getSelectedRows().filter((row) => !row.materializedOnly && row.reviewStatus !== 'confirmed' && row.reviewStatus !== 'rejected')
|
|
209
|
+
if (!rows.length) return
|
|
207
210
|
setBusy(true)
|
|
208
211
|
try {
|
|
209
|
-
const response = await executeAction('confirm_review_items', null, { itemIds:
|
|
212
|
+
const response = await executeAction('confirm_review_items', null, { itemIds: rows.map((row) => row.id) }, {})
|
|
210
213
|
assertActionSuccess(response)
|
|
211
214
|
setSelectedIds([])
|
|
212
|
-
showNotice(`已批量审核 ${
|
|
215
|
+
showNotice(`已批量审核 ${rows.length} 条记录。`, 'success')
|
|
216
|
+
await reload()
|
|
217
|
+
} catch (error) {
|
|
218
|
+
showNotice(getErrorMessage(error), 'error')
|
|
219
|
+
} finally {
|
|
220
|
+
setBusy(false)
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async function batchRejectSelected() {
|
|
225
|
+
const rows = getSelectedRows().filter((row) => row.reviewStatus !== 'rejected')
|
|
226
|
+
if (!rows.length || busy) return
|
|
227
|
+
setBusy(true)
|
|
228
|
+
try {
|
|
229
|
+
for (const row of rows) {
|
|
230
|
+
if (row.materializedOnly) {
|
|
231
|
+
await executeAction(materializedDeleteAction(row), null, { id: row.materializedId || parseMaterializedId(row.id) }, {})
|
|
232
|
+
} else {
|
|
233
|
+
await executeAction('reject_review_item', row.id, { itemId: row.id }, {})
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
setSelectedIds([])
|
|
237
|
+
showNotice(`已批量驳回 ${rows.length} 条记录。`, 'success')
|
|
238
|
+
await reload()
|
|
239
|
+
} catch (error) {
|
|
240
|
+
showNotice(getErrorMessage(error), 'error')
|
|
241
|
+
} finally {
|
|
242
|
+
setBusy(false)
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async function batchDeleteSelected() {
|
|
247
|
+
const rows = getSelectedRows()
|
|
248
|
+
if (!rows.length || busy) return
|
|
249
|
+
setBusy(true)
|
|
250
|
+
try {
|
|
251
|
+
for (const row of rows) {
|
|
252
|
+
await deleteRowRequest(row)
|
|
253
|
+
}
|
|
254
|
+
setSelectedIds([])
|
|
255
|
+
showNotice(`已批量删除 ${rows.length} 条记录。`, 'success')
|
|
213
256
|
await reload()
|
|
214
257
|
} catch (error) {
|
|
215
258
|
showNotice(getErrorMessage(error), 'error')
|
|
@@ -220,14 +263,9 @@
|
|
|
220
263
|
|
|
221
264
|
async function deleteRow(item) {
|
|
222
265
|
if (!item || !item.id || busy) return
|
|
223
|
-
const actionKey = item.materializedOnly
|
|
224
|
-
? (item.type === 'controlled_goods' ? 'delete_controlled_goods' : 'delete_supplier_product')
|
|
225
|
-
: 'delete_review_item'
|
|
226
|
-
const input = item.materializedOnly ? { id: item.materializedId || parseMaterializedId(item.id) } : { itemId: item.id }
|
|
227
266
|
setBusy(true)
|
|
228
267
|
try {
|
|
229
|
-
|
|
230
|
-
assertActionSuccess(response)
|
|
268
|
+
await deleteRowRequest(item)
|
|
231
269
|
setSelectedIds(selectedIds.filter((id) => id !== item.id))
|
|
232
270
|
showNotice('记录已删除。', 'success')
|
|
233
271
|
await reload()
|
|
@@ -238,6 +276,14 @@
|
|
|
238
276
|
}
|
|
239
277
|
}
|
|
240
278
|
|
|
279
|
+
async function deleteRowRequest(item) {
|
|
280
|
+
const actionKey = item.materializedOnly ? materializedDeleteAction(item) : 'delete_review_item'
|
|
281
|
+
const input = item.materializedOnly ? { id: item.materializedId || parseMaterializedId(item.id) } : { itemId: item.id }
|
|
282
|
+
const response = await executeAction(actionKey, null, input, {})
|
|
283
|
+
assertActionSuccess(response)
|
|
284
|
+
return response
|
|
285
|
+
}
|
|
286
|
+
|
|
241
287
|
function openEdit(item) {
|
|
242
288
|
const data = readMerged(item)
|
|
243
289
|
setEditItem(item)
|
|
@@ -340,9 +386,7 @@
|
|
|
340
386
|
}
|
|
341
387
|
|
|
342
388
|
function showNotice(message, level) {
|
|
343
|
-
setNotice((level === 'error' ? '错误:' : '') + message)
|
|
344
389
|
notify(level || 'info', message)
|
|
345
|
-
setTimeout(() => setNotice(''), 4500)
|
|
346
390
|
}
|
|
347
391
|
|
|
348
392
|
function startRecognitionPolling(expectedType, previousCount) {
|
|
@@ -381,7 +425,6 @@
|
|
|
381
425
|
h('button', { className: 'tcw-btn tcw-btn-soft', disabled: busy, onClick: () => reload() }, busy ? '处理中' : '刷新')
|
|
382
426
|
)
|
|
383
427
|
),
|
|
384
|
-
notice ? h('div', { className: 'tcw-notice' }, notice) : null,
|
|
385
428
|
h('nav', { className: 'tcw-tabs tcw-frame' }, [
|
|
386
429
|
tab('overview-page', '工作台总览'),
|
|
387
430
|
tab('controlled-goods-page', '管控商品'),
|
|
@@ -571,7 +614,9 @@
|
|
|
571
614
|
h('option', { value: 'rejected' }, '已驳回')
|
|
572
615
|
)
|
|
573
616
|
),
|
|
574
|
-
h('button', { className: 'tcw-btn tcw-btn-primary', disabled: busy || selectedIds.length === 0, onClick: batchApproveSelected }, `批量审核${selectedIds.length ? ` (${selectedIds.length})` : ''}`)
|
|
617
|
+
h('button', { className: 'tcw-btn tcw-btn-primary', disabled: busy || selectedIds.length === 0, onClick: batchApproveSelected }, `批量审核${selectedIds.length ? ` (${selectedIds.length})` : ''}`),
|
|
618
|
+
h('button', { className: 'tcw-btn tcw-btn-soft', disabled: busy || selectedIds.length === 0, onClick: batchRejectSelected }, '批量驳回'),
|
|
619
|
+
h('button', { className: 'tcw-btn tcw-btn-danger', disabled: busy || selectedIds.length === 0, onClick: batchDeleteSelected }, '批量删除')
|
|
575
620
|
)
|
|
576
621
|
)
|
|
577
622
|
}
|
|
@@ -619,7 +664,7 @@
|
|
|
619
664
|
className: 'row-selection-checkbox',
|
|
620
665
|
type: 'checkbox',
|
|
621
666
|
checked: selectedIds.includes(row.id),
|
|
622
|
-
disabled: !row.id
|
|
667
|
+
disabled: !row.id,
|
|
623
668
|
onChange: (event) => toggleSelected(row.id, event.target.checked)
|
|
624
669
|
}),
|
|
625
670
|
...mapRow(row),
|
|
@@ -647,10 +692,26 @@
|
|
|
647
692
|
|
|
648
693
|
function getSelectableRowIds(rows) {
|
|
649
694
|
return (rows || [])
|
|
650
|
-
.filter((row) => row.id
|
|
695
|
+
.filter((row) => row.id)
|
|
651
696
|
.map((row) => row.id)
|
|
652
697
|
}
|
|
653
698
|
|
|
699
|
+
function getSelectedRows() {
|
|
700
|
+
const rows = activePage === 'controlled-goods-page'
|
|
701
|
+
? controlledReviews
|
|
702
|
+
: activePage === 'products-page'
|
|
703
|
+
? supplierReviews
|
|
704
|
+
: activePage === 'workbooks-page'
|
|
705
|
+
? salesReviews
|
|
706
|
+
: []
|
|
707
|
+
const selected = new Set(selectedIds)
|
|
708
|
+
return rows.filter((row) => selected.has(row.id))
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
function materializedDeleteAction(item) {
|
|
712
|
+
return item.type === 'controlled_goods' ? 'delete_controlled_goods' : 'delete_supplier_product'
|
|
713
|
+
}
|
|
714
|
+
|
|
654
715
|
function paginateRows(listKey, rows) {
|
|
655
716
|
const pageSize = 10
|
|
656
717
|
const total = rows.length
|
|
@@ -1065,10 +1126,10 @@ button, input { font: inherit; letter-spacing: 0; }
|
|
|
1065
1126
|
.tcw-btn-primary { border-color: var(--tcw-primary); background: var(--tcw-primary); color: #fff; }
|
|
1066
1127
|
.tcw-btn-primary:hover:not(:disabled) { background: var(--tcw-primary-dark); }
|
|
1067
1128
|
.tcw-btn-soft { background: var(--tcw-soft); }
|
|
1129
|
+
.tcw-btn-danger { border-color: color-mix(in srgb, var(--tcw-danger) 35%, var(--tcw-border)); background: color-mix(in srgb, var(--tcw-danger) 6%, var(--tcw-card)); color: var(--tcw-danger); }
|
|
1068
1130
|
.tcw-wide { width: 100%; }
|
|
1069
1131
|
.tcw-upload { border-style: dashed; border-color: color-mix(in srgb, var(--tcw-primary) 42%, var(--tcw-border)); color: var(--tcw-primary); font-weight: 800; }
|
|
1070
1132
|
.tcw-upload input { display: none; }
|
|
1071
|
-
.tcw-notice { border: 1px solid color-mix(in srgb, var(--tcw-primary) 24%, var(--tcw-border)); border-radius: 6px; background: color-mix(in srgb, var(--tcw-primary) 7%, var(--tcw-card)); padding: 10px 12px; color: var(--tcw-primary-dark); font-size: 13px; }
|
|
1072
1133
|
.tcw-tabs { display: flex; gap: 6px; overflow-x: auto; border-bottom: 1px solid var(--tcw-border); padding: 0 2px; }
|
|
1073
1134
|
.tcw-tab { min-height: 34px; border: 0; border-bottom: 3px solid transparent; background: transparent; color: var(--tcw-muted); padding: 7px 12px; cursor: pointer; font-weight: 900; }
|
|
1074
1135
|
.tcw-tab.active { border-color: var(--tcw-primary); color: var(--tcw-primary); }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chenchaolong/plugin-trade-compliance-workbench",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"description": "Trade Compliance Workbench app plugin for controlled goods review, supplier product management, and customs workbook generation.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "XpertAI",
|