@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
|
-
},
|
|
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
|
-
|
|
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(
|
|
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('
|
|
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
|
|
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.
|
|
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",
|