@chenchaolong/plugin-trade-compliance-workbench 0.1.44 → 0.1.46

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,6 +70,7 @@
70
70
  const [activePage, setActivePage] = React.useState('overview-page')
71
71
  const [selectedIds, setSelectedIds] = React.useState([])
72
72
  const [statusFilter, setStatusFilter] = React.useState('all')
73
+ const [overviewTab, setOverviewTab] = React.useState('all')
73
74
  const [busy, setBusy] = React.useState(false)
74
75
  const [query, setQuery] = React.useState('')
75
76
  const [pageByList, setPageByList] = React.useState({})
@@ -103,7 +104,7 @@
103
104
  }
104
105
  }, [])
105
106
 
106
- React.useEffect(reportResize, [data, activePage, selectedIds, statusFilter, busy, formDialog, deleteDialog, hsCodeSearch, hsCodeDetailDialog])
107
+ React.useEffect(reportResize, [data, activePage, selectedIds, statusFilter, overviewTab, busy, formDialog, deleteDialog, hsCodeSearch, hsCodeDetailDialog])
107
108
 
108
109
  const reviewItems = data.reviewItems.filter((item) => !isPlaceholderReviewItem(item))
109
110
  const pendingItems = reviewItems.filter((item) => (item.reviewStatus || 'pending') === 'pending')
@@ -646,27 +647,12 @@
646
647
  )
647
648
  ),
648
649
  h('nav', { className: 'tcw-nav', 'aria-label': '主导航' }, [
649
- tab('overview-page', '待处理', '1', pendingItems.length, 'blue'),
650
- tab('controlled-goods-page', '管控规则库', '2', controlledReviews.length, 'green'),
651
- tab('products-page', '供应商商品', '3', supplierReviews.length, 'orange'),
652
- tab('workbooks-page', '销售发票', '4', salesReviews.length + filteredWorkbooks.length, 'violet'),
653
- tab('hs-code-search-page', '工具', '5', null, 'slate')
654
- ]),
655
- h('section', { className: 'tcw-sidebar-section' },
656
- h('h2', null, '主流程'),
657
- h('ol', { className: 'tcw-flow-list' }, [
658
- '维护管控规则',
659
- '上传供应商合同',
660
- '确认 HS 编码候选',
661
- '审核供应商商品',
662
- '上传购销合同',
663
- '生成销售发票'
664
- ].map((item) => h('li', { key: item }, h('span', null), item)))
665
- ),
666
- h('section', { className: 'tcw-sidebar-section compact' },
667
- h('h2', null, '数据关系'),
668
- h('p', null, '管控规则用于匹配供应商商品,已确认商品进入销售发票生成流程。')
669
- )
650
+ tab('overview-page', '总览', 'dashboard', pendingItems.length, 'blue'),
651
+ tab('controlled-goods-page', '管控商品', 'shield', controlledReviews.length, 'green'),
652
+ tab('products-page', '供应商商品', 'box', supplierReviews.length, 'orange'),
653
+ tab('workbooks-page', '销售发票', 'invoice', salesReviews.length + filteredWorkbooks.length, 'violet'),
654
+ tab('hs-code-search-page', '工具', 'search', null, 'slate')
655
+ ])
670
656
  )
671
657
  }
672
658
 
@@ -686,7 +672,7 @@
686
672
  h('section', { className: 'tcw-assistant-card' },
687
673
  h('h3', null, '快捷入口'),
688
674
  h('div', { className: 'tcw-assistant-actions' }, [
689
- h('button', { className: 'tcw-mini-btn', onClick: () => setActivePage('controlled-goods-page') }, '管控规则'),
675
+ h('button', { className: 'tcw-mini-btn', onClick: () => setActivePage('controlled-goods-page') }, '管控商品'),
690
676
  h('button', { className: 'tcw-mini-btn', onClick: () => setActivePage('products-page') }, '商品审核'),
691
677
  h('button', { className: 'tcw-mini-btn', onClick: () => setActivePage('hs-code-search-page') }, '查 HS 编码')
692
678
  ])
@@ -700,28 +686,50 @@
700
686
 
701
687
  function tab(key, label, icon, count, tone) {
702
688
  return h('button', { key, className: key === activePage ? 'tcw-tab active' : 'tcw-tab', onClick: () => setActivePage(key) },
703
- h('span', { className: 'tcw-nav-icon ' + (tone || 'blue') }, icon),
689
+ h('span', { className: 'tcw-nav-icon ' + (tone || 'blue') }, iconImage(icon, label, tone)),
704
690
  h('span', null, label),
705
691
  count == null ? null : h('em', null, count)
706
692
  )
707
693
  }
708
694
 
709
695
  function renderOverviewPage() {
696
+ const pendingOverviewItems = overviewTab === 'all'
697
+ ? pendingItems.slice(0, 12)
698
+ : pendingItems.filter((item) => item.type === overviewTab).slice(0, 12)
710
699
  return h('div', { className: 'tcw-page overview-page' },
711
700
  h('section', { className: 'tcw-metrics' },
712
- metric('待审核', pendingItems.length),
713
- metric('管控识别待审', pendingControlledReviews.length),
714
- metric('供应商商品待审', pendingSupplierReviews.length),
715
- metric('销售合同待审', pendingSalesReviews.length),
716
- metric('已入库管控商品', data.controlledGoods.length),
717
- metric('已入库供应商商品', data.products.length),
718
- metric('销售发票历史', data.workbookGenerations.length),
719
- metric('全部审核记录', reviewItems.length)
701
+ metric('待审核', pendingItems.length, 'dashboard', 'blue'),
702
+ metric('管控识别待审', pendingControlledReviews.length, 'shield', 'green'),
703
+ metric('供应商商品待审', pendingSupplierReviews.length, 'box', 'orange'),
704
+ metric('销售合同待审', pendingSalesReviews.length, 'invoice', 'violet'),
705
+ metric('已入库管控商品', data.controlledGoods.length, 'archive', 'slate'),
706
+ metric('已入库供应商商品', data.products.length, 'supplier', 'green'),
707
+ metric('销售发票历史', data.workbookGenerations.length, 'document', 'violet'),
708
+ metric('全部审核记录', reviewItems.length, 'records', 'blue')
720
709
  ),
721
- panel('最近待审核', pendingItems.slice(0, 12).length
722
- ? compactReviewList(pendingItems.slice(0, 12))
723
- : empty('暂无待审核记录。智能体识别结果会先出现在这里。'))
710
+ panel('待办事项明细',
711
+ h('div', { className: 'tcw-overview-panel' },
712
+ h('div', { className: 'tcw-overview-tabs' }, [
713
+ overviewTabButton('all', '全部', pendingItems.length),
714
+ overviewTabButton('controlled_goods', '管控商品', pendingItems.filter((item) => item.type === 'controlled_goods').length),
715
+ overviewTabButton('supplier_product', '供应商商品', pendingItems.filter((item) => item.type === 'supplier_product').length),
716
+ overviewTabButton('customs_workbook', '销售发票', pendingItems.filter((item) => item.type === 'customs_workbook').length)
717
+ ]),
718
+ pendingOverviewItems.length
719
+ ? compactReviewList(pendingOverviewItems)
720
+ : empty('暂无待审核记录。智能体识别结果会先出现在这里。')
721
+ )
722
+ )
724
723
  )
724
+
725
+ function overviewTabButton(key, label, count) {
726
+ return h('button', {
727
+ key,
728
+ type: 'button',
729
+ className: overviewTab === key ? 'tcw-overview-tab active' : 'tcw-overview-tab',
730
+ onClick: () => setOverviewTab(key)
731
+ }, `${label}${typeof count === 'number' ? ` ${count}` : ''}`)
732
+ }
725
733
  }
726
734
 
727
735
  function renderControlledGoodsPage() {
@@ -1282,8 +1290,42 @@
1282
1290
 
1283
1291
  }
1284
1292
 
1285
- function metric(label, valueText) {
1286
- return h('div', { className: 'tcw-metric' }, h('span', null, label), h('strong', null, valueText))
1293
+ function metric(label, valueText, icon, tone) {
1294
+ return h('div', { className: 'tcw-metric' },
1295
+ h('span', { className: 'tcw-metric-icon ' + (tone || 'blue') }, iconImage(icon, label, tone)),
1296
+ h('div', { className: 'tcw-metric-body' },
1297
+ h('span', null, label),
1298
+ h('strong', null, valueText)
1299
+ )
1300
+ )
1301
+ }
1302
+
1303
+ function iconImage(name, label, tone) {
1304
+ return h('img', { src: iconDataUri(name, tone), alt: label || '', 'aria-hidden': label ? undefined : true })
1305
+ }
1306
+
1307
+ function iconDataUri(name, tone) {
1308
+ const colors = {
1309
+ blue: '#2563eb',
1310
+ green: '#139160',
1311
+ orange: '#e07818',
1312
+ violet: '#7c3aed',
1313
+ slate: '#526174'
1314
+ }
1315
+ const color = colors[tone] || colors.blue
1316
+ const paths = {
1317
+ dashboard: '<rect x="4" y="4" width="7" height="7" rx="1.5"/><rect x="13" y="4" width="7" height="7" rx="1.5"/><rect x="4" y="13" width="7" height="7" rx="1.5"/><rect x="13" y="13" width="7" height="7" rx="1.5"/>',
1318
+ shield: '<path d="M12 3l7 3v5c0 4.8-2.9 8.2-7 10-4.1-1.8-7-5.2-7-10V6l7-3z"/><path d="M9 12l2 2 4-5"/>',
1319
+ box: '<path d="M4 8l8-4 8 4-8 4-8-4z"/><path d="M4 8v8l8 4 8-4V8"/><path d="M12 12v8"/>',
1320
+ invoice: '<path d="M7 3h8l4 4v14l-3-1.5L13 21l-3-1.5L7 21V3z"/><path d="M15 3v5h4"/><path d="M9 11h6"/><path d="M9 15h6"/>',
1321
+ search: '<circle cx="11" cy="11" r="6"/><path d="M16 16l4 4"/>',
1322
+ archive: '<path d="M4 7h16v13H4V7z"/><path d="M3 4h18v3H3z"/><path d="M9 11h6"/>',
1323
+ supplier: '<path d="M4 20V8l8-4 8 4v12"/><path d="M8 20v-7h8v7"/><path d="M10 9h4"/>',
1324
+ document: '<path d="M7 3h7l5 5v13H7V3z"/><path d="M14 3v6h5"/><path d="M9 13h6"/><path d="M9 17h6"/>',
1325
+ records: '<path d="M6 5h12v16H6V5z"/><path d="M9 3h6v4H9z"/><path d="M9 11h6"/><path d="M9 15h6"/>'
1326
+ }
1327
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="${color}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${paths[name] || paths.dashboard}</svg>`
1328
+ return 'data:image/svg+xml;utf8,' + encodeURIComponent(svg)
1287
1329
  }
1288
1330
 
1289
1331
  function panel(title, content) {
@@ -1794,8 +1836,8 @@
1794
1836
  }
1795
1837
 
1796
1838
  function pageTitle(key) {
1797
- if (key === 'overview-page') return '待处理'
1798
- if (key === 'controlled-goods-page') return '管控规则库'
1839
+ if (key === 'overview-page') return '总览'
1840
+ if (key === 'controlled-goods-page') return '管控商品'
1799
1841
  if (key === 'products-page') return '供应商商品'
1800
1842
  if (key === 'hs-code-search-page') return '工具'
1801
1843
  return '销售发票'
@@ -1810,15 +1852,15 @@
1810
1852
  }
1811
1853
 
1812
1854
  function pageBreadcrumb(key) {
1813
- if (key === 'overview-page') return 'Workspace / 待处理'
1814
- if (key === 'controlled-goods-page') return 'Workspace / 管控规则库'
1855
+ if (key === 'overview-page') return 'Workspace / 总览'
1856
+ if (key === 'controlled-goods-page') return 'Workspace / 管控商品'
1815
1857
  if (key === 'products-page') return 'Workspace / 供应商商品'
1816
1858
  if (key === 'workbooks-page') return 'Workspace / 销售发票'
1817
1859
  return 'Workspace / 工具'
1818
1860
  }
1819
1861
 
1820
1862
  function assistantTip(key) {
1821
- if (key === 'overview-page') return '从待处理开始,优先确认管控规则和供应商商品,再推进发票生成。'
1863
+ if (key === 'overview-page') return '从总览开始,优先确认管控规则和供应商商品,再推进发票生成。'
1822
1864
  if (key === 'controlled-goods-page') return '维护管控商品时,建议先核对 HS 编码和关键词,再保存入库。'
1823
1865
  if (key === 'products-page') return '供应商商品可先选用候选 HS 编码,再检查英文品名和管控状态。'
1824
1866
  if (key === 'workbooks-page') return '购销合同审核完毕后,再生成销售发票,避免后面重改。'
@@ -1878,9 +1920,38 @@ button, input, textarea, select { font: inherit; letter-spacing: 0; }
1878
1920
  .tcw-subsection-head h3, .tcw-list-title h3 { margin: 0; font-size: 15px; line-height: 1.3; }
1879
1921
  .tcw-subsection-head p, .tcw-list-title span { margin: 0; color: var(--tcw-muted); font-size: 12px; }
1880
1922
  .tcw-metrics { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; }
1881
- .tcw-metric { display: grid; gap: 4px; border: 1px solid var(--tcw-border); border-radius: 8px; background: var(--tcw-card); padding: 12px; }
1882
- .tcw-metric span { color: var(--tcw-muted); font-size: 12px; }
1883
- .tcw-metric strong { font-size: 22px; line-height: 1; }
1923
+ .tcw-metric {
1924
+ display: grid;
1925
+ grid-template-columns: 44px minmax(0, 1fr);
1926
+ gap: 10px;
1927
+ align-items: center;
1928
+ border: 1px solid var(--tcw-border);
1929
+ border-radius: 8px;
1930
+ background: var(--tcw-card);
1931
+ padding: 12px;
1932
+ }
1933
+ .tcw-metric-icon {
1934
+ display: grid;
1935
+ place-items: center;
1936
+ width: 44px;
1937
+ height: 44px;
1938
+ border-radius: 10px;
1939
+ font-size: 18px;
1940
+ font-weight: 900;
1941
+ }
1942
+ .tcw-metric-icon img {
1943
+ width: 22px;
1944
+ height: 22px;
1945
+ color: currentColor;
1946
+ }
1947
+ .tcw-metric-icon.blue { background: #e8f0ff; color: var(--tcw-primary); }
1948
+ .tcw-metric-icon.green { background: #e7f8ef; color: var(--tcw-success); }
1949
+ .tcw-metric-icon.orange { background: #fff2df; color: var(--tcw-warning); }
1950
+ .tcw-metric-icon.violet { background: #f1eaff; color: var(--tcw-violet); }
1951
+ .tcw-metric-icon.slate { background: #eef2f6; color: var(--tcw-slate); }
1952
+ .tcw-metric-body { display: grid; gap: 2px; }
1953
+ .tcw-metric-body span { color: var(--tcw-muted); font-size: 12px; }
1954
+ .tcw-metric-body strong { font-size: 22px; line-height: 1; }
1884
1955
  .tcw-field { display: grid; gap: 5px; min-width: 0; }
1885
1956
  .tcw-field span { color: var(--tcw-muted); font-size: 12px; font-weight: 800; }
1886
1957
  .tcw-field-check { align-content: end; }
@@ -2044,6 +2115,34 @@ body { background: var(--tcw-bg); letter-spacing: 0; }
2044
2115
  background: #eff6ff;
2045
2116
  color: #174ea6;
2046
2117
  }
2118
+ .tcw-overview-panel {
2119
+ display: grid;
2120
+ gap: 12px;
2121
+ }
2122
+ .tcw-overview-tabs {
2123
+ display: inline-flex;
2124
+ align-items: center;
2125
+ gap: 0;
2126
+ width: fit-content;
2127
+ border: 1px solid var(--tcw-border);
2128
+ border-radius: 12px;
2129
+ overflow: hidden;
2130
+ background: #fff;
2131
+ }
2132
+ .tcw-overview-tab {
2133
+ min-height: 38px;
2134
+ border: 0;
2135
+ border-right: 1px solid var(--tcw-border);
2136
+ background: #fff;
2137
+ color: var(--tcw-muted);
2138
+ padding: 8px 16px;
2139
+ font-weight: 800;
2140
+ }
2141
+ .tcw-overview-tab:last-child { border-right: 0; }
2142
+ .tcw-overview-tab.active {
2143
+ background: #eff6ff;
2144
+ color: var(--tcw-primary);
2145
+ }
2047
2146
  .tcw-tab em {
2048
2147
  min-width: 22px;
2049
2148
  border-radius: 999px;
@@ -2063,48 +2162,16 @@ body { background: var(--tcw-bg); letter-spacing: 0; }
2063
2162
  font-size: 11px;
2064
2163
  font-weight: 900;
2065
2164
  }
2165
+ .tcw-nav-icon img {
2166
+ width: 16px;
2167
+ height: 16px;
2168
+ color: currentColor;
2169
+ }
2066
2170
  .tcw-nav-icon.blue { background: #e8f0ff; color: var(--tcw-primary); }
2067
2171
  .tcw-nav-icon.green { background: #e7f8ef; color: var(--tcw-success); }
2068
2172
  .tcw-nav-icon.orange { background: #fff2df; color: var(--tcw-warning); }
2069
2173
  .tcw-nav-icon.violet { background: #f1eaff; color: var(--tcw-violet); }
2070
2174
  .tcw-nav-icon.slate { background: #eef2f6; color: var(--tcw-slate); }
2071
- .tcw-sidebar-section {
2072
- display: grid;
2073
- gap: 8px;
2074
- border-top: 1px solid var(--tcw-border);
2075
- padding: 14px 6px 0;
2076
- }
2077
- .tcw-sidebar-section h2 {
2078
- margin: 0;
2079
- color: var(--tcw-muted);
2080
- font-size: 12px;
2081
- }
2082
- .tcw-sidebar-section.compact p {
2083
- margin: 0;
2084
- color: var(--tcw-muted);
2085
- font-size: 12px;
2086
- line-height: 1.6;
2087
- }
2088
- .tcw-flow-list {
2089
- display: grid;
2090
- gap: 8px;
2091
- margin: 0;
2092
- padding: 0;
2093
- list-style: none;
2094
- color: var(--tcw-text);
2095
- font-size: 12px;
2096
- }
2097
- .tcw-flow-list li {
2098
- display: flex;
2099
- gap: 8px;
2100
- align-items: center;
2101
- }
2102
- .tcw-flow-list span {
2103
- width: 7px;
2104
- height: 7px;
2105
- border-radius: 999px;
2106
- background: var(--tcw-primary);
2107
- }
2108
2175
  .tcw-workspace {
2109
2176
  display: grid;
2110
2177
  grid-template-rows: auto 1fr;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chenchaolong/plugin-trade-compliance-workbench",
3
- "version": "0.1.44",
3
+ "version": "0.1.46",
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",