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

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.
@@ -606,41 +606,104 @@
606
606
  }
607
607
 
608
608
  return h('div', { className: 'tcw-shell' },
609
- h('header', { className: 'tcw-header tcw-frame' },
610
- h('div', { className: 'tcw-title-block' },
611
- h('span', { className: 'tcw-kicker' }, '外贸合规工作台'),
612
- h('h1', null, pageTitle(activePage)),
613
- h('p', null, pageSubtitle(activePage))
609
+ renderSidebar(),
610
+ h('section', { className: 'tcw-workspace' },
611
+ h('header', { className: 'tcw-header' },
612
+ h('div', { className: 'tcw-title-block' },
613
+ h('span', { className: 'tcw-kicker' }, pageBreadcrumb(activePage)),
614
+ h('h1', null, pageTitle(activePage)),
615
+ h('p', null, pageSubtitle(activePage))
616
+ ),
617
+ h('div', { className: 'tcw-header-actions' },
618
+ h('input', { className: 'tcw-search', value: query, placeholder: '搜索商品、供应商、海关编码、文件名', onChange: (event) => setQuery(event.target.value) }),
619
+ h('button', { className: 'tcw-btn tcw-btn-soft', disabled: busy, onClick: () => reload() }, busy ? '处理中' : '刷新')
620
+ )
614
621
  ),
615
- h('div', { className: 'tcw-header-actions' },
616
- h('input', { className: 'tcw-search', value: query, placeholder: '搜索商品、供应商、海关编码、文件名', onChange: (event) => setQuery(event.target.value) }),
617
- h('button', { className: 'tcw-btn tcw-btn-soft', disabled: busy, onClick: () => reload() }, busy ? '处理中' : '刷新')
618
- )
619
- ),
620
- h('nav', { className: 'tcw-tabs tcw-frame' }, [
621
- tab('overview-page', '工作台总览'),
622
- tab('controlled-goods-page', '管控商品'),
623
- tab('products-page', '供应商商品'),
624
- tab('workbooks-page', '销售发票'),
625
- tab('hs-code-search-page', '海关编码查询')
626
- ]),
627
- h('main', { className: 'tcw-main tcw-frame' },
628
- h('section', { className: 'tcw-content' },
629
- activePage === 'overview-page' ? renderOverviewPage() : null,
630
- activePage === 'controlled-goods-page' ? renderControlledGoodsPage() : null,
631
- activePage === 'products-page' ? renderProductsPage() : null,
632
- activePage === 'workbooks-page' ? renderWorkbooksPage() : null,
633
- activePage === 'hs-code-search-page' ? renderHsCodeSearchPage() : null
622
+ h('main', { className: 'tcw-main' },
623
+ h('section', { className: 'tcw-content' },
624
+ activePage === 'overview-page' ? renderOverviewPage() : null,
625
+ activePage === 'controlled-goods-page' ? renderControlledGoodsPage() : null,
626
+ activePage === 'products-page' ? renderProductsPage() : null,
627
+ activePage === 'workbooks-page' ? renderWorkbooksPage() : null,
628
+ activePage === 'hs-code-search-page' ? renderHsCodeSearchPage() : null
629
+ )
634
630
  )
635
631
  ),
632
+ renderAssistantPanel(),
636
633
  detailItem ? renderDetailModal() : null,
637
634
  formDialog ? renderFormModal() : null,
638
635
  deleteDialog ? renderDeleteConfirmModal() : null,
639
636
  hsCodeDetailDialog ? renderHsCodeDetailModal() : null
640
637
  )
641
638
 
642
- function tab(key, label) {
643
- return h('button', { key, className: key === activePage ? 'tcw-tab active' : 'tcw-tab', onClick: () => setActivePage(key) }, label)
639
+ function renderSidebar() {
640
+ return h('aside', { className: 'tcw-sidebar' },
641
+ h('div', { className: 'tcw-brand' },
642
+ h('div', { className: 'tcw-brand-mark' }, 'TC'),
643
+ h('div', null,
644
+ h('strong', null, '外贸合规工作台'),
645
+ h('span', null, 'Trade Compliance')
646
+ )
647
+ ),
648
+ 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
+ )
670
+ )
671
+ }
672
+
673
+ function renderAssistantPanel() {
674
+ return h('aside', { className: 'tcw-assistant' },
675
+ h('div', { className: 'tcw-assistant-head' },
676
+ h('span', { className: 'tcw-assistant-mark' }, 'AI'),
677
+ h('div', null,
678
+ h('strong', null, '外贸合规助手'),
679
+ h('p', null, '按流程处理识别结果,减少来回切换。')
680
+ )
681
+ ),
682
+ h('section', { className: 'tcw-assistant-card' },
683
+ h('h3', null, '当前建议'),
684
+ h('p', null, assistantTip(activePage))
685
+ ),
686
+ h('section', { className: 'tcw-assistant-card' },
687
+ h('h3', null, '快捷入口'),
688
+ h('div', { className: 'tcw-assistant-actions' }, [
689
+ h('button', { className: 'tcw-mini-btn', onClick: () => setActivePage('controlled-goods-page') }, '管控规则'),
690
+ h('button', { className: 'tcw-mini-btn', onClick: () => setActivePage('products-page') }, '商品审核'),
691
+ h('button', { className: 'tcw-mini-btn', onClick: () => setActivePage('hs-code-search-page') }, '查 HS 编码')
692
+ ])
693
+ ),
694
+ h('section', { className: 'tcw-assistant-card' },
695
+ h('h3', null, '处理顺序'),
696
+ h('p', null, '先把管控规则和供应商商品确认入库,再审核购销合同并生成销售发票。')
697
+ )
698
+ )
699
+ }
700
+
701
+ function tab(key, label, icon, count, tone) {
702
+ 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),
704
+ h('span', null, label),
705
+ count == null ? null : h('em', null, count)
706
+ )
644
707
  }
645
708
 
646
709
  function renderOverviewPage() {
@@ -671,7 +734,7 @@
671
734
  renderListToolbar('管控商品列表', controlledReviews.length),
672
735
  h('div', { className: 'pending-controlled-goods confirmed-controlled-goods tcw-table-section' },
673
736
  reviewTable(['商品名称', '海关编码', '关键词', '管控说明', '来源'], page.rows, (item) => {
674
- const row = readSupplierEditableMerged(item)
737
+ const row = readMerged(item)
675
738
  return [
676
739
  value(row.productName || item.title),
677
740
  value(row.hsCode),
@@ -1731,21 +1794,37 @@
1731
1794
  }
1732
1795
 
1733
1796
  function pageTitle(key) {
1734
- if (key === 'overview-page') return '工作台总览'
1735
- if (key === 'controlled-goods-page') return '管控商品'
1797
+ if (key === 'overview-page') return '待处理'
1798
+ if (key === 'controlled-goods-page') return '管控规则库'
1736
1799
  if (key === 'products-page') return '供应商商品'
1737
- if (key === 'hs-code-search-page') return '海关编码查询'
1800
+ if (key === 'hs-code-search-page') return '工具'
1738
1801
  return '销售发票'
1739
1802
  }
1740
1803
 
1741
1804
  function pageSubtitle(key) {
1742
- if (key === 'overview-page') return '查看处理进度、待审核数量和最近识别结果。'
1805
+ if (key === 'overview-page') return '按业务顺序聚合待审核事项,先确认基础资料,再生成销售发票。'
1743
1806
  if (key === 'controlled-goods-page') return '上传管控目录、审核识别结果、维护正式管控商品库。'
1744
1807
  if (key === 'products-page') return '上传供应商合同,审核商品、HS Code、退税率和管控状态。'
1745
1808
  if (key === 'hs-code-search-page') return '输入商品名称或海关编码,查询 HS 编码网结果。'
1746
1809
  return '上传购销合同,审核发票字段并生成固定模板 Excel。'
1747
1810
  }
1748
1811
 
1812
+ function pageBreadcrumb(key) {
1813
+ if (key === 'overview-page') return 'Workspace / 待处理'
1814
+ if (key === 'controlled-goods-page') return 'Workspace / 管控规则库'
1815
+ if (key === 'products-page') return 'Workspace / 供应商商品'
1816
+ if (key === 'workbooks-page') return 'Workspace / 销售发票'
1817
+ return 'Workspace / 工具'
1818
+ }
1819
+
1820
+ function assistantTip(key) {
1821
+ if (key === 'overview-page') return '从待处理开始,优先确认管控规则和供应商商品,再推进发票生成。'
1822
+ if (key === 'controlled-goods-page') return '维护管控商品时,建议先核对 HS 编码和关键词,再保存入库。'
1823
+ if (key === 'products-page') return '供应商商品可先选用候选 HS 编码,再检查英文品名和管控状态。'
1824
+ if (key === 'workbooks-page') return '购销合同审核完毕后,再生成销售发票,避免后面重改。'
1825
+ return '海关编码查询页用于人工确认候选结果,必要时可回到商品页继续处理。'
1826
+ }
1827
+
1749
1828
  function isObject(value) {
1750
1829
  return value && typeof value === 'object' && !Array.isArray(value)
1751
1830
  }
@@ -1888,6 +1967,317 @@ button, input, textarea, select { font: inherit; letter-spacing: 0; }
1888
1967
  .tcw-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
1889
1968
  .tcw-drawer { position: static; max-height: none; }
1890
1969
  }
1970
+
1971
+ /* V2 process-oriented shell */
1972
+ :root {
1973
+ --tcw-bg:#f4f7fb;
1974
+ --tcw-card:#ffffff;
1975
+ --tcw-soft:#f8fafc;
1976
+ --tcw-text:#172033;
1977
+ --tcw-muted:#65758c;
1978
+ --tcw-border:#dbe5f0;
1979
+ --tcw-primary:#2563eb;
1980
+ --tcw-primary-dark:#1d4ed8;
1981
+ --tcw-success:#139160;
1982
+ --tcw-warning:#e07818;
1983
+ --tcw-danger:#bd2f2f;
1984
+ --tcw-violet:#7c3aed;
1985
+ --tcw-slate:#526174;
1986
+ --tcw-shadow:0 14px 42px rgba(20, 35, 55, .08);
1987
+ }
1988
+ body { background: var(--tcw-bg); letter-spacing: 0; }
1989
+ .tcw-shell {
1990
+ display: grid;
1991
+ grid-template-columns: 248px minmax(0, 1fr) 300px;
1992
+ grid-template-rows: 1fr;
1993
+ gap: 0;
1994
+ min-height: 100vh;
1995
+ padding: 0;
1996
+ background: var(--tcw-bg);
1997
+ }
1998
+ .tcw-sidebar {
1999
+ display: grid;
2000
+ grid-template-rows: auto auto auto 1fr;
2001
+ align-content: start;
2002
+ gap: 18px;
2003
+ min-width: 0;
2004
+ border-right: 1px solid var(--tcw-border);
2005
+ background: var(--tcw-card);
2006
+ padding: 20px 14px;
2007
+ }
2008
+ .tcw-brand {
2009
+ display: flex;
2010
+ align-items: center;
2011
+ gap: 10px;
2012
+ padding: 0 6px 6px;
2013
+ }
2014
+ .tcw-brand-mark {
2015
+ display: grid;
2016
+ place-items: center;
2017
+ width: 34px;
2018
+ height: 34px;
2019
+ border-radius: 8px;
2020
+ background: #e8f0ff;
2021
+ color: var(--tcw-primary);
2022
+ font-size: 12px;
2023
+ font-weight: 900;
2024
+ }
2025
+ .tcw-brand strong { display: block; font-size: 16px; line-height: 1.2; }
2026
+ .tcw-brand span { display: block; color: var(--tcw-muted); font-size: 12px; }
2027
+ .tcw-nav { display: grid; gap: 6px; }
2028
+ .tcw-tab {
2029
+ display: grid;
2030
+ grid-template-columns: 26px minmax(0, 1fr) auto;
2031
+ align-items: center;
2032
+ gap: 8px;
2033
+ min-height: 38px;
2034
+ border: 1px solid transparent;
2035
+ border-radius: 8px;
2036
+ background: transparent;
2037
+ color: var(--tcw-text);
2038
+ padding: 7px 8px;
2039
+ text-align: left;
2040
+ font-weight: 800;
2041
+ }
2042
+ .tcw-tab.active {
2043
+ border-color: #d8e7ff;
2044
+ background: #eff6ff;
2045
+ color: #174ea6;
2046
+ }
2047
+ .tcw-tab em {
2048
+ min-width: 22px;
2049
+ border-radius: 999px;
2050
+ background: #edf2f7;
2051
+ color: var(--tcw-muted);
2052
+ padding: 2px 6px;
2053
+ font-size: 11px;
2054
+ font-style: normal;
2055
+ text-align: center;
2056
+ }
2057
+ .tcw-nav-icon {
2058
+ display: grid;
2059
+ place-items: center;
2060
+ width: 24px;
2061
+ height: 24px;
2062
+ border-radius: 7px;
2063
+ font-size: 11px;
2064
+ font-weight: 900;
2065
+ }
2066
+ .tcw-nav-icon.blue { background: #e8f0ff; color: var(--tcw-primary); }
2067
+ .tcw-nav-icon.green { background: #e7f8ef; color: var(--tcw-success); }
2068
+ .tcw-nav-icon.orange { background: #fff2df; color: var(--tcw-warning); }
2069
+ .tcw-nav-icon.violet { background: #f1eaff; color: var(--tcw-violet); }
2070
+ .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
+ .tcw-workspace {
2109
+ display: grid;
2110
+ grid-template-rows: auto 1fr;
2111
+ min-width: 0;
2112
+ padding: 18px;
2113
+ }
2114
+ .tcw-header {
2115
+ display: flex;
2116
+ align-items: center;
2117
+ justify-content: space-between;
2118
+ gap: 14px;
2119
+ min-height: 64px;
2120
+ border: 0;
2121
+ border-radius: 0;
2122
+ background: transparent;
2123
+ padding: 0 0 14px;
2124
+ }
2125
+ .tcw-kicker {
2126
+ margin: 0 0 3px;
2127
+ color: var(--tcw-muted);
2128
+ font-size: 12px;
2129
+ font-weight: 700;
2130
+ }
2131
+ .tcw-title-block h1 {
2132
+ margin: 0;
2133
+ font-size: 24px;
2134
+ line-height: 1.2;
2135
+ }
2136
+ .tcw-title-block p {
2137
+ margin: 4px 0 0;
2138
+ color: var(--tcw-muted);
2139
+ font-size: 13px;
2140
+ }
2141
+ .tcw-header-actions {
2142
+ display: grid;
2143
+ grid-template-columns: minmax(260px, 380px) auto;
2144
+ gap: 8px;
2145
+ align-items: center;
2146
+ }
2147
+ .tcw-main { display: grid; min-width: 0; }
2148
+ .tcw-content { min-width: 0; border: 0; background: transparent; padding: 0; }
2149
+ .tcw-business-panel, .tcw-panel {
2150
+ border: 1px solid var(--tcw-border);
2151
+ border-radius: 8px;
2152
+ background: var(--tcw-card);
2153
+ box-shadow: var(--tcw-shadow);
2154
+ }
2155
+ .tcw-business-head {
2156
+ min-height: auto;
2157
+ align-items: center;
2158
+ border-bottom: 1px solid var(--tcw-border);
2159
+ background: linear-gradient(90deg, #ffffff, #f7fbff);
2160
+ padding: 20px 22px;
2161
+ }
2162
+ .tcw-business-head h2 { font-size: 18px; }
2163
+ .tcw-business-body { padding: 18px 22px 22px; }
2164
+ .tcw-metrics {
2165
+ grid-template-columns: repeat(4, minmax(0, 1fr));
2166
+ gap: 10px;
2167
+ }
2168
+ .tcw-metric {
2169
+ border-color: var(--tcw-border);
2170
+ background: var(--tcw-card);
2171
+ box-shadow: var(--tcw-shadow);
2172
+ }
2173
+ .tcw-metric strong { color: var(--tcw-text); font-size: 24px; }
2174
+ .tcw-btn, .tcw-upload, .tcw-mini-btn, .tcw-icon-btn {
2175
+ border-radius: 7px;
2176
+ }
2177
+ .tcw-btn-primary {
2178
+ border-color: var(--tcw-primary);
2179
+ background: var(--tcw-primary);
2180
+ }
2181
+ .tcw-btn-primary:hover:not(:disabled) { background: var(--tcw-primary-dark); }
2182
+ .tcw-upload {
2183
+ border-color: #bcd3ff;
2184
+ background: #eff6ff;
2185
+ color: var(--tcw-primary);
2186
+ }
2187
+ .tcw-table-wrap {
2188
+ border-color: var(--tcw-border);
2189
+ border-radius: 8px;
2190
+ }
2191
+ .tcw-table th {
2192
+ background: var(--tcw-soft);
2193
+ color: var(--tcw-muted);
2194
+ font-size: 11px;
2195
+ }
2196
+ .tcw-table td { background: var(--tcw-card); }
2197
+ .tcw-table tr:hover td { background: #fbfdff; }
2198
+ .tcw-assistant {
2199
+ display: grid;
2200
+ align-content: start;
2201
+ gap: 12px;
2202
+ border-left: 1px solid var(--tcw-border);
2203
+ background: var(--tcw-card);
2204
+ padding: 18px 14px;
2205
+ }
2206
+ .tcw-assistant-head {
2207
+ display: flex;
2208
+ align-items: flex-start;
2209
+ gap: 10px;
2210
+ padding: 2px 4px 8px;
2211
+ }
2212
+ .tcw-assistant-mark {
2213
+ display: grid;
2214
+ place-items: center;
2215
+ width: 32px;
2216
+ height: 32px;
2217
+ border-radius: 8px;
2218
+ background: #e7f8ef;
2219
+ color: var(--tcw-success);
2220
+ font-size: 12px;
2221
+ font-weight: 900;
2222
+ }
2223
+ .tcw-assistant-head strong { display: block; font-size: 15px; }
2224
+ .tcw-assistant-head p {
2225
+ margin: 3px 0 0;
2226
+ color: var(--tcw-muted);
2227
+ font-size: 12px;
2228
+ line-height: 1.5;
2229
+ }
2230
+ .tcw-assistant-card {
2231
+ display: grid;
2232
+ gap: 8px;
2233
+ border: 1px solid var(--tcw-border);
2234
+ border-radius: 8px;
2235
+ background: var(--tcw-soft);
2236
+ padding: 12px;
2237
+ }
2238
+ .tcw-assistant-card h3 {
2239
+ margin: 0;
2240
+ font-size: 13px;
2241
+ }
2242
+ .tcw-assistant-card p {
2243
+ margin: 0;
2244
+ color: var(--tcw-muted);
2245
+ font-size: 12px;
2246
+ line-height: 1.6;
2247
+ }
2248
+ .tcw-assistant-actions {
2249
+ display: flex;
2250
+ flex-wrap: wrap;
2251
+ gap: 8px;
2252
+ }
2253
+ .tcw-status.ok { background: #e7f8ef; color: var(--tcw-success); }
2254
+ .tcw-status.warn { background: #fff2df; color: #b35b00; }
2255
+ .tcw-status.danger { background: #fde8e6; color: var(--tcw-danger); }
2256
+ .tcw-status.muted { background: #eef2f6; color: var(--tcw-muted); }
2257
+ @media (max-width: 1280px) {
2258
+ .tcw-shell { grid-template-columns: 230px minmax(0, 1fr); }
2259
+ .tcw-assistant { display: none; }
2260
+ }
2261
+ @media (max-width: 900px) {
2262
+ .tcw-shell { grid-template-columns: 1fr; }
2263
+ .tcw-sidebar {
2264
+ position: static;
2265
+ border-right: 0;
2266
+ border-bottom: 1px solid var(--tcw-border);
2267
+ }
2268
+ .tcw-nav { grid-template-columns: repeat(2, minmax(0, 1fr)); }
2269
+ .tcw-workspace { padding: 14px; }
2270
+ .tcw-header, .tcw-business-head, .tcw-list-toolbar {
2271
+ align-items: stretch;
2272
+ flex-direction: column;
2273
+ }
2274
+ .tcw-header-actions { grid-template-columns: 1fr; }
2275
+ .tcw-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
2276
+ }
2277
+ @media (max-width: 560px) {
2278
+ .tcw-nav { grid-template-columns: 1fr; }
2279
+ .tcw-metrics, .tcw-edit-grid, .tcw-edit-grid-two { grid-template-columns: 1fr; }
2280
+ }
1891
2281
  `
1892
2282
  document.head.appendChild(style)
1893
2283
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chenchaolong/plugin-trade-compliance-workbench",
3
- "version": "0.1.43",
3
+ "version": "0.1.44",
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",