@chenchaolong/plugin-trade-compliance-workbench 0.1.21 → 0.1.23

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.
@@ -1,6 +1,7 @@
1
1
  ;(function () {
2
2
  const CHANNEL = 'xpertai.remote_component'
3
3
  const VERSION = 1
4
+ const REQUEST_TIMEOUT_MS = 120000
4
5
  const h = React.createElement
5
6
  let instanceId = null
6
7
  let requestSequence = 0
@@ -22,7 +23,7 @@
22
23
  if (!pending.has(requestId)) return
23
24
  pending.delete(requestId)
24
25
  reject(new Error('请求超时'))
25
- }, 30000)
26
+ }, REQUEST_TIMEOUT_MS)
26
27
  })
27
28
  }
28
29
 
@@ -83,6 +84,7 @@
83
84
  const [editItem, setEditItem] = React.useState(null)
84
85
  const [editText, setEditText] = React.useState('')
85
86
  const pollingRef = React.useRef(null)
87
+ const fileInputsRef = React.useRef({})
86
88
 
87
89
  React.useEffect(() => {
88
90
  window.__tradeComplianceSetContext = (nextContext) => {
@@ -132,24 +134,38 @@
132
134
  }
133
135
 
134
136
  async function handleFile(actionKey, file) {
135
- if (!file || busy) return
137
+ debugUpload('file change received', { actionKey, hasFile: !!file, busy })
138
+ if (!file) {
139
+ debugUpload('file selection was empty', { actionKey })
140
+ return
141
+ }
142
+ if (busy) {
143
+ debugUpload('file upload ignored because workbench is busy', { actionKey, fileName: file.name })
144
+ return
145
+ }
136
146
  const expectedType = reviewTypeForUploadAction(actionKey)
137
147
  const previousCount = countReviewItemsByType(data.reviewItems, expectedType)
138
148
  setBusy(true)
139
149
  try {
150
+ debugUpload('uploading file to assistant storage', { actionKey, fileName: file.name, size: file.size, type: file.type })
140
151
  const uploadResponse = await uploadAgentFile(file)
141
152
  const agentFile = getActionDataPayload(uploadResponse)
153
+ debugUpload('assistant storage upload completed', { actionKey, fileName: file.name, hasAgentFile: !!agentFile })
154
+ debugUpload('executing plugin file action', { actionKey, fileName: file.name })
142
155
  const response = await executeFileAction(actionKey, file, Object.assign(
143
156
  { name: file.name, fileName: file.name },
144
157
  toAgentFileInput(agentFile)
145
158
  ), {})
146
159
  assertActionSuccess(response)
147
160
  const actionData = getActionDataPayload(response)
161
+ debugUpload('dispatching assistant commands', { actionKey, fileName: file.name })
148
162
  const dispatched = await dispatchAssistantCommands(actionData)
163
+ debugUpload('file workflow completed', { actionKey, fileName: file.name, dispatched })
149
164
  showNotice(dispatched ? '文件已发送给智能体解析,识别结果会进入待审核列表。' : '文件已登记,等待识别结果。', 'success')
150
165
  await reload()
151
166
  if (dispatched && expectedType) startRecognitionPolling(expectedType, previousCount, readExpectedCount(actionData))
152
167
  } catch (error) {
168
+ debugUpload('file workflow failed', { actionKey, fileName: file.name, message: getErrorMessage(error) })
153
169
  showNotice(getErrorMessage(error), 'error')
154
170
  } finally {
155
171
  setBusy(false)
@@ -808,20 +824,43 @@
808
824
  }
809
825
 
810
826
  function uploadButton(actionKey, label) {
811
- return h('label', { className: 'tcw-upload' },
827
+ return h(React.Fragment, null,
812
828
  h('input', {
829
+ ref: (node) => {
830
+ if (node) fileInputsRef.current[actionKey] = node
831
+ else delete fileInputsRef.current[actionKey]
832
+ },
833
+ className: 'tcw-upload-input',
813
834
  type: 'file',
814
835
  disabled: busy,
815
836
  onChange: (event) => {
816
837
  const file = event.target.files && event.target.files[0]
817
838
  event.target.value = ''
839
+ debugUpload('file picker returned', { actionKey, hasFile: !!file, fileName: file && file.name, size: file && file.size })
818
840
  handleFile(actionKey, file)
819
841
  }
820
842
  }),
821
- h('span', null, label)
843
+ h('button', {
844
+ type: 'button',
845
+ className: 'tcw-upload',
846
+ disabled: busy,
847
+ onClick: () => openFilePicker(actionKey)
848
+ }, label)
822
849
  )
823
850
  }
824
851
 
852
+ function openFilePicker(actionKey) {
853
+ debugUpload('upload button clicked', { actionKey, busy })
854
+ if (busy) return
855
+ const input = fileInputsRef.current[actionKey]
856
+ if (!input) {
857
+ debugUpload('file input was not registered', { actionKey })
858
+ showNotice('上传控件未准备好,请刷新页面后重试。', 'error')
859
+ return
860
+ }
861
+ input.click()
862
+ }
863
+
825
864
  function updateForm(key) {
826
865
  return (event) => setForm(Object.assign({}, form, { [key]: event.target.value }))
827
866
  }
@@ -1012,6 +1051,12 @@
1012
1051
  return error && error.message ? error.message : String(error || '操作失败')
1013
1052
  }
1014
1053
 
1054
+ function debugUpload(message, details) {
1055
+ if (typeof console !== 'undefined' && typeof console.debug === 'function') {
1056
+ console.debug('[trade-compliance-workbench:upload] ' + message, details || {})
1057
+ }
1058
+ }
1059
+
1015
1060
  function filterItems(items, query, keys) {
1016
1061
  const keyword = String(query || '').trim().toLowerCase()
1017
1062
  if (!keyword) return items
@@ -1166,7 +1211,7 @@ button, input { font: inherit; letter-spacing: 0; }
1166
1211
  .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); }
1167
1212
  .tcw-wide { width: 100%; }
1168
1213
  .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; }
1169
- .tcw-upload input { display: none; }
1214
+ .tcw-upload-input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
1170
1215
  .tcw-tabs { display: flex; gap: 6px; overflow-x: auto; border-bottom: 1px solid var(--tcw-border); padding: 0 2px; }
1171
1216
  .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; }
1172
1217
  .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.21",
3
+ "version": "0.1.23",
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",