feat: Excel导入支持多文件选择(Ctrl/Shift多选)+逐文件结果展示
This commit is contained in:
Binary file not shown.
@@ -6,12 +6,15 @@
|
|||||||
<el-tab-pane label="Excel导入" name="import">
|
<el-tab-pane label="Excel导入" name="import">
|
||||||
<el-card>
|
<el-card>
|
||||||
<template #header>Excel导入</template>
|
<template #header>Excel导入</template>
|
||||||
<el-upload drag :auto-upload="false" :on-change="handleFile" accept=".xlsx,.xls" :limit="1">
|
<el-upload drag :auto-upload="false" :on-change="handleFile" :on-remove="handleRemove" multiple accept=".xlsx,.xls">
|
||||||
<el-icon :size="32"><UploadFilled /></el-icon>
|
<el-icon :size="32"><UploadFilled /></el-icon>
|
||||||
<div>拖拽或点击上传Excel文件</div>
|
<div>拖拽或点击上传Excel文件</div>
|
||||||
<div style="color:#999;font-size:12px;">支持 .xlsx .xls,需包含 kpi_code, period, actual_value 三列</div>
|
<div style="color:#999;font-size:12px;">支持 .xlsx .xls,可一次选择多个文件(按住Ctrl/Shift多选)</div>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
<el-button v-if="file" type="primary" style="margin-top:12px;" :loading="uploading" @click="doImport">导入数据</el-button>
|
<div v-if="files.length > 0" style="margin-top:8px;">
|
||||||
|
<el-tag v-for="(f, i) in files" :key="i" closable @close="removeFile(i)" style="margin:2px;">{{ f.name }}</el-tag>
|
||||||
|
</div>
|
||||||
|
<el-button v-if="files.length > 0" type="primary" style="margin-top:12px;" :loading="uploading" @click="doImport">导入 {{ files.length }} 个文件</el-button>
|
||||||
<div v-if="result" style="margin-top:12px;"><el-alert :title="result" type="success" show-icon /></div>
|
<div v-if="result" style="margin-top:12px;"><el-alert :title="result" type="success" show-icon /></div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
@@ -94,18 +97,39 @@ import MyDialog from '../components/MyDialog.vue'
|
|||||||
const activeTab = ref('import')
|
const activeTab = ref('import')
|
||||||
|
|
||||||
// === Excel导入 ===
|
// === Excel导入 ===
|
||||||
const file = ref<any>(null)
|
const files = ref<any[]>([])
|
||||||
const uploading = ref(false)
|
const uploading = ref(false)
|
||||||
const result = ref('')
|
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() {
|
async function doImport() {
|
||||||
if (!file.value) return
|
if (files.value.length === 0) return
|
||||||
uploading.value = true
|
uploading.value = true
|
||||||
|
result.value = ''
|
||||||
|
const msgs: string[] = []
|
||||||
|
for (const f of files.value) {
|
||||||
try {
|
try {
|
||||||
const r: any = await dataApi.importExcel(file.value)
|
const r: any = await dataApi.importExcel(f)
|
||||||
result.value = r.message || '导入成功'
|
const skipped = r.skipped || 0
|
||||||
file.value = null
|
const total = r.total || 0
|
||||||
} catch (e) { ElMessage.error('导入失败') }
|
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
|
uploading.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user