diff --git a/backend/app/api/__pycache__/data.cpython-312.pyc b/backend/app/api/__pycache__/data.cpython-312.pyc index b531ac6d..a167b66d 100644 Binary files a/backend/app/api/__pycache__/data.cpython-312.pyc and b/backend/app/api/__pycache__/data.cpython-312.pyc differ diff --git a/frontend/src/views/DataManage.vue b/frontend/src/views/DataManage.vue index 73528e4f..5e194908 100644 --- a/frontend/src/views/DataManage.vue +++ b/frontend/src/views/DataManage.vue @@ -6,12 +6,15 @@ Excel导入 - + 拖拽或点击上传Excel文件 - 支持 .xlsx .xls,需包含 kpi_code, period, actual_value 三列 + 支持 .xlsx .xls,可一次选择多个文件(按住Ctrl/Shift多选) - 导入数据 + + {{ f.name }} + + 导入 {{ files.length }} 个文件 @@ -94,18 +97,39 @@ import MyDialog from '../components/MyDialog.vue' const activeTab = ref('import') // === Excel导入 === -const file = ref(null) +const files = ref([]) const uploading = ref(false) const result = ref('') -function handleFile(f: any) { file.value = f.raw } + +function handleFile(f: any) { + // el-upload multiple 模式下每次 onChange 传单个文件 + if (f.raw && !files.value.some(ex => ex.name === f.raw.name && ex.size === f.raw.size)) { + files.value.push(f.raw) + } +} +function handleRemove(f: any) { + files.value = files.value.filter(ex => !(ex.name === f.name && ex.size === f.size)) +} +function removeFile(i: number) { + files.value.splice(i, 1) +} async function doImport() { - if (!file.value) return + if (files.value.length === 0) return uploading.value = true - try { - const r: any = await dataApi.importExcel(file.value) - result.value = r.message || '导入成功' - file.value = null - } catch (e) { ElMessage.error('导入失败') } + result.value = '' + const msgs: string[] = [] + for (const f of files.value) { + try { + const r: any = await dataApi.importExcel(f) + const skipped = r.skipped || 0 + const total = r.total || 0 + msgs.push(`📄 ${f.name}: ${total}条导入${skipped > 0 ? `, ${skipped}条跳过` : ''}`) + } catch (e: any) { + msgs.push(`❌ ${f.name}: ${e?.response?.data?.detail || '导入失败'}`) + } + } + files.value = [] + result.value = msgs.join('\n') uploading.value = false }