@chenchaolong/plugin-trade-compliance-workbench 0.1.47 → 0.1.48

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.
@@ -194,15 +194,17 @@
194
194
  }
195
195
  }
196
196
 
197
- async function searchSupplierHsCandidatesForForm() {
197
+ async function searchSupplierHsCandidatesForForm(page) {
198
198
  if (!formDialog || formDialog.type !== 'supplier_product' || busy) return
199
199
  const keyword = String(formDialog.hsCandidateKeyword || buildSupplierHsCandidateKeyword(formDialog.values) || '').trim()
200
200
  if (!keyword) {
201
201
  setFormDialog(Object.assign({}, formDialog, { hsCandidateError: '请输入商品名称、型号或海关编码后再查询。' }))
202
202
  return
203
203
  }
204
+ const nextPage = Math.max(1, Number(page) || 1)
204
205
  setFormDialog(Object.assign({}, formDialog, {
205
206
  hsCandidateKeyword: keyword,
207
+ hsCandidatePage: nextPage,
206
208
  hsCandidateLoading: true,
207
209
  hsCandidateError: '',
208
210
  hsCandidateStatus: ''
@@ -210,20 +212,22 @@
210
212
  try {
211
213
  const response = await executeAction('search_hs_code', null, {
212
214
  keywords: keyword,
213
- page: 1,
215
+ page: nextPage,
214
216
  filterFailureCode: true,
215
217
  displayChapter: false,
216
218
  displayEnName: true
217
219
  }, {})
218
220
  assertActionSuccess(response)
219
221
  const result = getActionDataPayload(response)
220
- const candidates = Array.isArray(result && result.results) ? result.results.slice(0, 10) : []
222
+ const candidates = Array.isArray(result && result.results) ? result.results : []
221
223
  setFormDialog((current) => current ? Object.assign({}, current, {
222
224
  hsCandidateKeyword: keyword,
225
+ hsCandidatePage: nextPage,
223
226
  hsCandidateLoading: false,
224
227
  hsCandidateError: '',
225
228
  hsCandidateStatus: candidates.length ? 'pending_confirmation' : 'not_found',
226
- hsCandidates: candidates
229
+ hsCandidates: candidates,
230
+ hsCandidatePagination: result && result.pagination ? result.pagination : null
227
231
  }) : current)
228
232
  } catch (error) {
229
233
  setFormDialog((current) => current ? Object.assign({}, current, {
@@ -466,6 +470,8 @@
466
470
  values: valuesFromItem(item.type, item),
467
471
  hsCandidates: Array.isArray(merged.hsCodeCandidates) ? merged.hsCodeCandidates : [],
468
472
  hsCandidateKeyword: merged.hsCodeLookupKeyword || buildSupplierHsCandidateKeyword(merged),
473
+ hsCandidatePage: 1,
474
+ hsCandidatePagination: null,
469
475
  hsCandidateStatus: merged.hsCodeLookupStatus || '',
470
476
  hsCandidateError: merged.hsCodeLookupError || '',
471
477
  hsCandidateLoading: false
@@ -480,6 +486,8 @@
480
486
  values: emptyFormValues(type),
481
487
  hsCandidates: [],
482
488
  hsCandidateKeyword: '',
489
+ hsCandidatePage: 1,
490
+ hsCandidatePagination: null,
483
491
  hsCandidateStatus: '',
484
492
  hsCandidateError: '',
485
493
  hsCandidateLoading: false
@@ -1100,7 +1108,7 @@
1100
1108
  const fields = formDialog.type === 'controlled_goods' ? controlledGoodsFormFields : supplierProductFormFields
1101
1109
  const title = `${formDialog.mode === 'create' ? '新增' : '编辑'}${formDialog.type === 'controlled_goods' ? '管控商品' : '供应商商品'}`
1102
1110
  return h('div', { className: 'tcw-modal-backdrop' },
1103
- h('form', { className: 'tcw-modal tcw-form-modal', onSubmit: saveProductForm },
1111
+ h('form', { className: formDialog.type === 'supplier_product' ? 'tcw-modal tcw-form-modal tcw-form-modal-supplier' : 'tcw-modal tcw-form-modal', onSubmit: saveProductForm },
1104
1112
  h('div', { className: 'tcw-modal-head' },
1105
1113
  h('h2', null, title),
1106
1114
  h('button', { type: 'button', className: 'tcw-icon-btn', onClick: () => setFormDialog(null) }, '×')
@@ -1119,6 +1127,7 @@
1119
1127
 
1120
1128
  function renderSupplierHsCandidatePanel() {
1121
1129
  const candidates = Array.isArray(formDialog.hsCandidates) ? formDialog.hsCandidates : []
1130
+ const pagination = formDialog.hsCandidatePagination || null
1122
1131
  return h('section', { className: 'tcw-hs-candidate-panel' },
1123
1132
  h('div', { className: 'tcw-subsection-head' },
1124
1133
  h('div', null,
@@ -1132,23 +1141,46 @@
1132
1141
  placeholder: '商品名称、型号或海关编码',
1133
1142
  onChange: (event) => setFormDialog(Object.assign({}, formDialog, { hsCandidateKeyword: event.target.value }))
1134
1143
  }),
1135
- h('button', { type: 'button', className: 'tcw-mini-btn', disabled: busy || formDialog.hsCandidateLoading, onClick: searchSupplierHsCandidatesForForm }, formDialog.hsCandidateLoading ? '查询中' : '查询')
1144
+ h('button', { type: 'button', className: 'tcw-mini-btn', disabled: busy || formDialog.hsCandidateLoading, onClick: () => searchSupplierHsCandidatesForForm(1) }, formDialog.hsCandidateLoading ? '查询中' : '查询')
1136
1145
  )
1137
1146
  ),
1138
1147
  formDialog.hsCandidateError ? h('div', { className: 'tcw-error' }, formDialog.hsCandidateError) : null,
1139
- candidates.length ? table(['编码', '商品名称', '英文名称', '单位', '退税率', '监管', '检验检疫', '操作'], candidates, (candidate) => [
1140
- h('strong', { className: 'tcw-hs-code-text' }, value(candidate.code)),
1141
- value(candidate.name),
1142
- value(candidate.englishName),
1143
- value(candidate.unit),
1144
- value(candidate.taxRefundRate),
1145
- value(candidate.regulatoryConditions),
1146
- value(candidate.inspectionQuarantine),
1147
- h('div', { className: 'tcw-row-actions' },
1148
- h('button', { type: 'button', className: 'tcw-mini-btn', onClick: () => selectHsCandidate(candidate) }, '选用'),
1149
- h('button', { type: 'button', className: 'tcw-mini-btn', disabled: !candidate.code && !candidate.detailUrl, onClick: () => loadHsCodeDetail(candidate) }, '详情')
1150
- )
1151
- ], '暂无候选编码。') : empty('暂无候选编码,可调整关键词后查询。')
1148
+ candidates.length ? h('div', { className: 'tcw-hs-candidate-result' },
1149
+ table(['编码', '商品名称', '英文名称', '单位', '退税率', '监管', '检验检疫', '操作'], candidates, (candidate) => [
1150
+ h('strong', { className: 'tcw-hs-code-text' }, value(candidate.code)),
1151
+ value(candidate.name),
1152
+ value(candidate.englishName),
1153
+ value(candidate.unit),
1154
+ value(candidate.taxRefundRate),
1155
+ value(candidate.regulatoryConditions),
1156
+ value(candidate.inspectionQuarantine),
1157
+ h('div', { className: 'tcw-row-actions' },
1158
+ h('button', { type: 'button', className: 'tcw-mini-btn', onClick: () => selectHsCandidate(candidate) }, '选用'),
1159
+ h('button', { type: 'button', className: 'tcw-mini-btn', disabled: !candidate.code && !candidate.detailUrl, onClick: () => loadHsCodeDetail(candidate) }, '详情')
1160
+ )
1161
+ ], '暂无候选编码。'),
1162
+ renderSupplierHsCandidatePagination(pagination)
1163
+ ) : empty('暂无候选编码,可调整关键词后查询。')
1164
+ )
1165
+ }
1166
+
1167
+ function renderSupplierHsCandidatePagination(pagination) {
1168
+ if (!pagination || (!pagination.hasPrevious && !pagination.hasNext && (!pagination.pages || pagination.pages.length <= 1))) return null
1169
+ const pages = Array.isArray(pagination.pages) ? pagination.pages.filter((item) => item.page) : []
1170
+ const visiblePages = pages.filter((item, index, arr) => arr.findIndex((candidate) => candidate.page === item.page) === index).slice(0, 10)
1171
+ const currentPage = pagination.currentPage || formDialog.hsCandidatePage || 1
1172
+ return h('div', { className: 'tcw-pagination hs-code-pagination' },
1173
+ h('span', null, `第 ${currentPage} 页`),
1174
+ h('div', { className: 'tcw-page-buttons' },
1175
+ h('button', { className: 'tcw-mini-btn', disabled: formDialog.hsCandidateLoading || !pagination.hasPrevious, onClick: () => searchSupplierHsCandidatesForForm(currentPage - 1) }, '上一页'),
1176
+ visiblePages.map((item) => h('button', {
1177
+ key: item.page,
1178
+ className: item.current ? 'tcw-mini-btn active' : 'tcw-mini-btn',
1179
+ disabled: formDialog.hsCandidateLoading || item.current,
1180
+ onClick: () => searchSupplierHsCandidatesForForm(item.page)
1181
+ }, String(item.page))),
1182
+ h('button', { className: 'tcw-mini-btn', disabled: formDialog.hsCandidateLoading || !pagination.hasNext, onClick: () => searchSupplierHsCandidatesForForm(currentPage + 1) }, '下一页')
1183
+ )
1152
1184
  )
1153
1185
  }
1154
1186
 
@@ -1950,6 +1982,12 @@ button, input, textarea, select { font: inherit; letter-spacing: 0; }
1950
1982
  .tcw-modal-backdrop { position: fixed; inset: 0; z-index: 10; display: grid; place-items: center; background: rgba(15, 23, 42, .32); padding: 20px; }
1951
1983
  .tcw-modal { display: grid; gap: 12px; width: min(900px, 100%); max-height: calc(100vh - 48px); overflow: auto; border: 1px solid var(--tcw-border); border-radius: 8px; background: var(--tcw-card); padding: 16px; box-shadow: 0 18px 48px rgba(15, 23, 42, .18); }
1952
1984
  .tcw-form-modal { width: min(960px, 100%); }
1985
+ .tcw-form-modal-supplier {
1986
+ width: min(1180px, calc(100vw - 96px));
1987
+ max-height: min(760px, calc(100vh - 56px));
1988
+ gap: 10px;
1989
+ padding: 14px;
1990
+ }
1953
1991
  .tcw-confirm-modal { width: min(480px, 100%); }
1954
1992
  .tcw-hs-detail-modal { width: min(980px, 100%); }
1955
1993
  .tcw-modal-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
@@ -1959,6 +1997,61 @@ button, input, textarea, select { font: inherit; letter-spacing: 0; }
1959
1997
  .tcw-edit-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
1960
1998
  .tcw-edit-grid-two { grid-template-columns: repeat(2, minmax(260px, 1fr)); }
1961
1999
  .tcw-field-wide { grid-column: 1 / -1; }
2000
+ .tcw-form-modal-supplier .tcw-modal-head h2 { font-size: 17px; }
2001
+ .tcw-form-modal-supplier .tcw-edit-grid-two {
2002
+ grid-template-columns: repeat(3, minmax(0, 1fr));
2003
+ gap: 8px 12px;
2004
+ }
2005
+ .tcw-form-modal-supplier .tcw-field { gap: 4px; }
2006
+ .tcw-form-modal-supplier .tcw-field span { font-size: 11px; }
2007
+ .tcw-form-modal-supplier .tcw-field input,
2008
+ .tcw-form-modal-supplier .tcw-field select {
2009
+ min-height: 32px;
2010
+ padding: 6px 9px;
2011
+ }
2012
+ .tcw-form-modal-supplier .tcw-field textarea {
2013
+ min-height: 58px;
2014
+ padding: 7px 9px;
2015
+ }
2016
+ .tcw-form-modal-supplier .tcw-field-wide {
2017
+ grid-column: span 3;
2018
+ }
2019
+ .tcw-form-modal-supplier .tcw-hs-candidate-panel {
2020
+ display: grid;
2021
+ grid-template-columns: minmax(0, 1fr) minmax(320px, .55fr);
2022
+ gap: 10px;
2023
+ align-items: start;
2024
+ border-top: 1px solid var(--tcw-border);
2025
+ padding-top: 10px;
2026
+ }
2027
+ .tcw-form-modal-supplier .tcw-hs-candidate-panel > .tcw-subsection-head {
2028
+ align-items: center;
2029
+ }
2030
+ .tcw-form-modal-supplier .tcw-hs-candidate-panel > .tcw-table-wrap,
2031
+ .tcw-form-modal-supplier .tcw-hs-candidate-panel > .tcw-empty,
2032
+ .tcw-form-modal-supplier .tcw-hs-candidate-panel > .tcw-error {
2033
+ grid-column: 1 / -1;
2034
+ max-height: 210px;
2035
+ overflow: auto;
2036
+ }
2037
+ .tcw-form-modal-supplier .tcw-hs-candidate-result {
2038
+ display: grid;
2039
+ gap: 8px;
2040
+ grid-column: 1 / -1;
2041
+ }
2042
+ .tcw-form-modal-supplier .tcw-hs-candidate-result .tcw-table-wrap {
2043
+ max-height: 210px;
2044
+ }
2045
+ .tcw-form-modal-supplier .tcw-hs-candidate-search {
2046
+ grid-template-columns: minmax(0, 1fr) auto;
2047
+ }
2048
+ .tcw-form-modal-supplier .tcw-modal-actions {
2049
+ position: sticky;
2050
+ bottom: -14px;
2051
+ border-top: 1px solid var(--tcw-border);
2052
+ background: var(--tcw-card);
2053
+ padding-top: 10px;
2054
+ }
1962
2055
  .tcw-confirm-text { margin: 0; color: var(--tcw-text); font-size: 14px; line-height: 1.7; }
1963
2056
  .tcw-detail-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
1964
2057
  .tcw-detail-field { display: grid; grid-template-columns: 108px minmax(0, 1fr); gap: 8px; align-items: start; border: 1px solid var(--tcw-border); border-radius: 6px; background: var(--tcw-soft); padding: 8px 10px; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chenchaolong/plugin-trade-compliance-workbench",
3
- "version": "0.1.47",
3
+ "version": "0.1.48",
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",