实现Excel导入和导出
今天记录一个Excel导入导出用户信息的实现方式。
- 导入:点击选择、拖拽 两种方式,用XLSX来解析文件内容
- 导出:点击导出excel
导入
点击选择、拖拽上传 两种方式
src/components/UploadExcel/index.vue
定义了上传组件的内容
- 点击上传部分:按钮 & 一个隐藏的元素
- 给
<input>
绑定ref="excelUploadInput"
- 点击按钮:
@click="handleUpload"
- 点击之后,触发
handleUpload()
,根据excelUploadInput
这个引用,触发excelUploadInput.value.click()
,模拟点击<input>
,弹出文件选择框
- 给
- 拖拽上传部分:一个拖拽区域div
<template><div class="upload-excel"><div class="btn-upload"><el-button :loading="loading" type="primary" @click="handleUpload">{{ $t('msg.uploadExcel.upload') }}</el-button></div><inputref="excelUploadInput"class="excel-upload-input"type="file"accept=".xlsx, .xls"@change="handleChange"/><!-- https://developer.mozilla.org/zh-CN/docs/Web/API/HTML_Drag_and_Drop_API --><divclass="drop"@drop.stop.prevent="handleDrop"@dragover.stop.prevent="handleDragover"@dragenter.stop.prevent="handleDragover"><i class="el-icon-upload" /><span>{{ $t('msg.uploadExcel.drop') }}</span></div></div>
</template>
点击选择
<input>
组件设置了display:none
,在页面中隐藏,点击上传时需要使用input元素
-
<input>
元素绑定了ref='excelUploadInput'
,所以通过excelUploadInput
变量可以操作<input>
元素点击上传按钮,点击时触发
handleUpload()
方法,模拟点击input元素,从而弹出文件选择框
const handleUpload = () => {excelUploadInput.value.click()
}
<input>
元素绑定@change="handleChange"
,所以在文件选择框选择文件后,<input>
元素会自动执行handleChange()
方法,通过e.target.files
获取excel文件
const handleChange = e => {const files = e.target.filesconst rawFile = files[0] // only use files[0]if (!rawFile) returnupload(rawFile)
}
-
执行
upload(rawFile)
,执行一些回调函数(如果有的话),并执行readerData(rawFile)
,解析数据,并调用generateData({ header, results })
传入解析后的表头数据和数据体,该方法中的onSuccess()
方法是由父组件调用时传入readerData(rawFile)
函数:
/*** 读取数据(异步)*/
const readerData = rawFile => {loading.value = truereturn new Promise((resolve, reject) => {// https://developer.mozilla.org/zh-CN/docs/Web/API/FileReaderconst reader = new FileReader()// 该事件在读取操作完成时触发// https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader/onloadreader.onload = e => {console.log(e)// 1. 获取解析到的数据const data = e.target.result// 2. 利用 XLSX 对数据进行解析const workbook = XLSX.read(data, { type: 'array' })// 3. 获取第一张表格(工作簿)名称const firstSheetName = workbook.SheetNames[0]// 4. 只读取 Sheet1(第一张表格)的数据const worksheet = workbook.Sheets[firstSheetName]// 5. 解析数据表头const header = getHeaderRow(worksheet)// 6. 解析数据体const results = XLSX.utils.sheet_to_json(worksheet)// 7. 传入解析之后的数据generateData({ header, results })// 8. loading 处理loading.value = false// 9. 异步完成resolve()}// 启动读取指定的 Blob 或 File 内容reader.readAsArrayBuffer(rawFile)})
}
`generateData()`函数:
/*** 根据导入内容,生成数据*/
const generateData = excelData => {props.onSuccess && props.onSuccess(excelData)
}
`onSuccess()`函数需要父组件调用`UploadExcel`组件时传入:
const props = defineProps({// 上传前回调beforeUpload: Function,// 成功回调onSuccess: Function
})
-
在
src/views/import/index.vue
中调用了UploadExcel
组件,并定义了onSuccess()
函数给后台发请求,将新增数据存入数据库,弹窗提示操作成功,并跳转到
/user/manage
页面
/*** 数据解析成功之后的回调*/
const onSuccess = async ({ header, results }) => {// 筛选数据const updateData = generateData(results)// 给后台发请求,更新数据await userBatchImport(updateData)// 操作成功时弹出的消息。ElMessage.success({message: results.length + i18n.t('msg.excel.importSuccess'),type: 'success'})// 在导入成功后,用户会被重定向到 /user/manage 页面router.push('/user/manage')
}
拖拽上传
拖拽区域如下:
<divclass="drop"@drop.stop.prevent="handleDrop"@dragover.stop.prevent="handleDragover"@dragenter.stop.prevent="handleDragover"
>
也就是说,“拖进来”以及“撒手之前”都执行handleDragover()
方法,“撒手时”执行handleDrop()
方法
-
handleDragover()
方法:当拖拽元素悬停在目标区域时,浏览器的默认行为是将源元素复制到目标区域,而不是移动它。
/*** 拖拽悬停时触发*/
const handleDragover = e => {// https://developer.mozilla.org/zh-CN/docs/Web/API/DataTransfer/dropEffect// 在新位置生成源项的副本e.dataTransfer.dropEffect = 'copy'
}
-
handleDrop()
方法:判断文件的个数、类型,没有问题就上传
/*** 拖拽文本释放时触发*/
const handleDrop = e => {// 上传中跳过if (loading.value) returnconst files = e.dataTransfer.filesif (files.length !== 1) {ElMessage.error('必须要有一个文件')return}const rawFile = files[0]if (!isExcel(rawFile)) {ElMessage.error('文件必须是 .xlsx, .xls, .csv 格式')return false}// 触发上传事件upload(rawFile)
}
`upload(rawFile)`上面说过了(点击下面跳转)执行upload(rawFile),执行一些回调函数(如果有的话),并执行readerData(rawFile),解析数据,并调用generateData({ header, results })传入解析后的表头数据和数据体,该方法中的onSuccess()方法是由父组件调用时传入
导出
-
点击
excel
导出按钮,弹出弹窗dialog
-
输入导出的
excel
文件名称 -
点击导出按钮:
- 获取 所有用户列表数据
- 将
json
结构数据转化为excel
数据,并下载
src/views/user-manage/components/Export2Excel.vue
组件:
- 一个弹窗
- 一个input元素:设置导出的文件名
- 两个按钮:确定、取消
<template><el-dialog:title="$t('msg.excel.title')":model-value="modelValue"@close="closed"width="30%"><el-inputv-model="excelName":placeholder="$t('msg.excel.placeholder')"></el-input><template #footer><span class="dialog-footer"><el-button @click="closed">{{ $t('msg.excel.close') }}</el-button><el-button type="primary" @click="onConfirm" :loading="loading">{{$t('msg.excel.confirm')}}</el-button></span></template></el-dialog>
</template>
点击确定按钮,执行onConfirm()
- 对后端发起请求获取所有用户数据
- 将json数据转化为excel需要的二维数组形式
- 生成excel文件并下载
/*** 导出按钮点击事件* @returns {Promise<void>}*/
const onConfirm = async () => {loading.value = true// 通过 API 获取所有用户的数据。const allUser = (await getUserManageAllList()).list// 动态导入 Export2Excel 工具,用于将数据导出为 Excel 文件。const excel = await import('@/utils/Export2Excel')// 调用 formatJson 函数,将用户数据格式化为 Excel 导出所需的二维数组。const data = formatJson(USER_RELATIONS, allUser)// 调用导入的 export_json_to_excel 函数,执行 Excel 导出下载。excel.export_json_to_excel({// excel 表头header: Object.keys(USER_RELATIONS),// excel 数据(二维数组结构)data,// 文件名称(用户在input元素中输入)filename: excelName.value || exportDefaultName,// 是否自动列宽autoWidth: true,// 文件类型bookType: 'xlsx'})closed()
}