lcw
This commit is contained in:
@ -0,0 +1,298 @@
|
||||
<!-- 文件上传导入组件 -->
|
||||
<template>
|
||||
<el-dialog v-model="modelValue" :title="title" :width="width" top="5vh" @close="close" append-to-body>
|
||||
|
||||
<!-- 上传区域 -->
|
||||
<div class="upload-section">
|
||||
<el-upload class="upload-demo" drag :auto-upload="false" :limit="1" :file-list="fileList"
|
||||
:before-upload="beforeUpload" :on-change="handleFileChange" :on-exceed="handleExceed">
|
||||
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||
<div class="el-upload__text">
|
||||
<em>点击或拖拽文件到此处上传</em>
|
||||
</div>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">
|
||||
只能上传 xlsx/xls 文件,且不超过 {{ fileSizeLimit }}MB
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="upload-actions">
|
||||
<el-button type="primary" @click="handleImport" :disabled="!canImport">导入数据</el-button>
|
||||
<el-button @click="handleTemplate">下载模板</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 数据预览区域 -->
|
||||
<div v-if="previewData.length > 0" class="preview-section">
|
||||
<h4 style="margin-bottom: 16px;">数据预览</h4>
|
||||
<el-table :data="previewData" style="width: 100%;" :max-height="300">
|
||||
<el-table-column v-for="col in tableColumns" :key="col.prop" :prop="col.prop" :label="col.label"
|
||||
show-overflow-tooltip></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<!-- <el-dialog v-model="loadingVisible" title="导入中" :show-close="false">
|
||||
<div style="text-align: center; padding: 20px;">
|
||||
<el-loading :fullscreen="false" text="正在处理数据,请稍候..." :visible="true"></el-loading>
|
||||
</div>
|
||||
</el-dialog> -->
|
||||
|
||||
<!-- <template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="close">关闭</el-button>
|
||||
</div>
|
||||
</template> -->
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import * as XLSX from 'xlsx'
|
||||
import { ref, computed, defineProps, defineEmits } from 'vue'
|
||||
import { UploadFilled } from '@element-plus/icons-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { qcckPost } from '@/api/qcckApi'
|
||||
// Props 定义
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '30%'
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: '导入数据'
|
||||
},
|
||||
|
||||
// 文件大小限制(MB)
|
||||
fileSizeLimit: {
|
||||
type: Number,
|
||||
default: 10
|
||||
},
|
||||
// 表格列配置
|
||||
tableColumns: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
// 模板下载地址
|
||||
templateUrl: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 图标地址
|
||||
aiconUrl: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
isUrl: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
// Emits 定义
|
||||
const emit = defineEmits(['update:modelValue', 'import-success', 'import-error', 'vSocial'])
|
||||
|
||||
// 响应式数据
|
||||
const fileList = ref([])
|
||||
const previewData = ref([])
|
||||
const loadingVisible = ref(false)
|
||||
|
||||
// 计算属性:是否可以导入
|
||||
const canImport = computed(() => {
|
||||
return fileList.value.length > 0 && !loadingVisible.value
|
||||
})
|
||||
|
||||
// 关闭对话框
|
||||
const close = () => {
|
||||
resetState()
|
||||
emit('update:modelValue', false)
|
||||
}
|
||||
|
||||
// 重置状态
|
||||
const resetState = () => {
|
||||
fileList.value = []
|
||||
previewData.value = []
|
||||
loadingVisible.value = false
|
||||
}
|
||||
|
||||
// 处理文件选择变化
|
||||
const handleFileChange = (file) => {
|
||||
// 清空之前的预览数据
|
||||
previewData.value = []
|
||||
// 只保留最后一个文件
|
||||
fileList.value = [file]
|
||||
|
||||
// 读取文件内容进行预览
|
||||
const reader = new FileReader()
|
||||
reader.onload = (e) => {
|
||||
try {
|
||||
const data = e.target.result
|
||||
const workbook = XLSX.read(data, { type: 'binary' })
|
||||
const firstSheetName = workbook.SheetNames[0]
|
||||
const worksheet = workbook.Sheets[firstSheetName]
|
||||
// 转换为JSON数据
|
||||
const jsonData = XLSX.utils.sheet_to_json(worksheet)
|
||||
// 如果有表格列配置,过滤和格式化数据
|
||||
if (props.tableColumns && props.tableColumns.length > 0) {
|
||||
previewData.value = jsonData.slice(0, 10).map(row => {
|
||||
const formattedRow = {}
|
||||
props.tableColumns.forEach(col => {
|
||||
formattedRow[col.prop] = row[col.label] !== undefined ? row[col.label] : row[col.prop]
|
||||
})
|
||||
return formattedRow
|
||||
})
|
||||
} else {
|
||||
// 否则直接使用前10条数据作为预览
|
||||
previewData.value = jsonData.slice(0, 10)
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('文件解析失败,请检查文件格式')
|
||||
console.error('文件解析错误:', error)
|
||||
}
|
||||
}
|
||||
reader.readAsBinaryString(file.raw)
|
||||
}
|
||||
|
||||
// 上传前校验
|
||||
const beforeUpload = (file) => {
|
||||
const isExcel = file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ||
|
||||
file.type === 'application/vnd.ms-excel'
|
||||
const isLtLimit = file.size / 1024 / 1024 < props.fileSizeLimit
|
||||
|
||||
if (!isExcel) {
|
||||
ElMessage.error('只能上传 Excel 文件!')
|
||||
return false
|
||||
}
|
||||
if (!isLtLimit) {
|
||||
ElMessage.error(`文件大小不能超过 ${props.fileSizeLimit}MB!`)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 处理文件超出限制
|
||||
const handleExceed = () => {
|
||||
ElMessage.error('只能上传一个文件')
|
||||
}
|
||||
|
||||
// 处理导入
|
||||
const handleImport = async () => {
|
||||
if (!fileList.value[0]) {
|
||||
ElMessage.warning('请先选择文件')
|
||||
return
|
||||
}
|
||||
loadingVisible.value = true
|
||||
try {
|
||||
// 创建FormData对象
|
||||
const formData = new FormData()
|
||||
formData.append('file', fileList.value[0].raw)
|
||||
if (props.isUrl && props.aiconUrl) {
|
||||
// 调用上传接口
|
||||
qcckPost(formData, props.aiconUrl).then(res => {
|
||||
emit('import-success', res)
|
||||
close()
|
||||
}).catch(error => {
|
||||
ElMessage.error(error.message || '文件上传失败')
|
||||
emit('import-success', null)
|
||||
loadingVisible.value = false
|
||||
})
|
||||
|
||||
} else {
|
||||
// 如果没有配置上传接口,只解析文件并返回数据
|
||||
const reader = new FileReader()
|
||||
reader.onload = (e) => {
|
||||
try {
|
||||
const data = e.target.result
|
||||
const workbook = XLSX.read(data, { type: 'binary' })
|
||||
const firstSheetName = workbook.SheetNames[0]
|
||||
const worksheet = workbook.Sheets[firstSheetName]
|
||||
const jsonData = XLSX.utils.sheet_to_json(worksheet)
|
||||
emit('import-success', { data: jsonData })
|
||||
close()
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
reader.readAsBinaryString(fileList.value[0].raw)
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error(error.message || '导入失败')
|
||||
emit('import-error', error)
|
||||
loadingVisible.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 处理模板下载
|
||||
const handleTemplate = () => {
|
||||
if (props.templateUrl) {
|
||||
window.open(props.templateUrl, '_blank')
|
||||
} else {
|
||||
// 如果没有提供模板下载地址,根据列配置生成简单模板
|
||||
try {
|
||||
const wb = XLSX.utils.book_new()
|
||||
|
||||
// 创建表头数据
|
||||
const headerData = []
|
||||
if (props.tableColumns && props.tableColumns.length > 0) {
|
||||
const headerRow = {}
|
||||
props.tableColumns.forEach(col => {
|
||||
headerRow[col.prop] = col.label
|
||||
})
|
||||
headerData.push(headerRow)
|
||||
}
|
||||
|
||||
const ws = XLSX.utils.json_to_sheet(headerData)
|
||||
XLSX.utils.book_append_sheet(wb, ws, '模板')
|
||||
|
||||
// 下载模板
|
||||
XLSX.writeFile(wb, '导入模板.xlsx')
|
||||
} catch (error) {
|
||||
ElMessage.error('模板生成失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.upload-section {
|
||||
padding: 20px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.upload-demo {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.upload-actions {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.upload-actions .el-button {
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.preview-section {
|
||||
margin-top: 20px;
|
||||
border-top: 1px solid #ebeef5;
|
||||
padding-top: 20px;
|
||||
width: 100%;
|
||||
max-width: 1000px;
|
||||
align-self: center;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.el-table {
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user