实现Excel导入和导出
实现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>
<input
ref="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 -->
<div
class="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.files
const rawFile = files[0] // only use files[0]
if (!rawFile) return
upload(rawFile)
}
-
执行
upload(rawFile)
,执行一些回调函数(如果有的话),并执行readerData(rawFile)
,解析数据,并调用generateData({ header, results })
传入解析后的表头数据和数据体,该方法中的onSuccess()
方法是由父组件调用时传入readerData(rawFile)
函数:
/**
* 读取数据(异步)
*/
const readerData = rawFile => {
loading.value = true
return new Promise((resolve, reject) => {
// https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader
const reader = new FileReader()
// 该事件在读取操作完成时触发
// https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader/onload
reader.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')
}
拖拽上传
拖拽区域如下:
<div
class="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) return
const files = e.dataTransfer.files
if (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-input
v-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()
}