在Vue项目中使用xlsx导出表格和下载文件
对于页面上的表格,经常需要导出。一般有两种场景,一种没有分页的表格前端自己可以直接导出,另一种需要请求后端去下载文件。本文介绍基于在Vue项目中实现这两种场景。
1、前端导出表格
这种场景我们需要借助xlsx插件库。首先安装一下:
npm install xlsx -D
然后直接在vue文件中使用,代码如下:
<template>
<!-- 这里注意用div包裹 -->
<div ref="exportTableRef">
<el-table :data="tableData">
<el-table-column prop="date" label="Date" />
<el-table-column prop="name" label="Name" />
</el-table>
</div>
</template>
<script setup>
import { utils, writeFileXLSX } from 'xlsx'
// 直接导出页面展示的Table
const exportTableRef = ref()
const exportTable = () => {
//这里传递导出Table的DOM
const wb = utils.table_to_book(exportTableRef.value.getElementsByTagName("TABLE")[0])
writeFileXLSX(wb, '导出文件名.xlsx')
}
exportTable()
</script>
1.1、注意传递的Dom
上述代码中,我们先看看table_to_book
传递的DOM是什么样子的,如下图:
哦!是一个table的DOM,这样传递没问题。但如果遇到ElementUI的表格是分header和body的情况,如下图:
这也就是为什么需要div包裹table,如果出现header和body分开的情况,直接传递tableau.value
这个外层div即可。我们再来看看这个tableau.value
是啥,如下图:
1.2、额外的逻辑处理
除了直接导出页面上展示的表格,我们通常还需要在此基础上处理表格数据,比如导出的excel内容里小数改为百分比、去掉某一列等等。
这种情况,首先复制一份表格,把它隐藏,导出时操作这个隐藏的表格即可。代码如下:
<template>
<!-- 页面上展示的表格 -->
<el-table :data="tableData">
<el-table-column prop="date" label="Date" />
<el-table-column prop="name" label="Name" />
</el-table>
<!-- 需要导出的Table,隐藏起来。注意如果需要操作数据,则复制一份 -->
<div ref="exportTableRef" v-show="false">
<el-table ref="table" :data="tableDataCopy">
<el-table-column prop="date" label="Date" />
<el-table-column prop="name" label="Name" />
</el-table>
</div>
</template>
<script setup>
import { utils, writeFileXLSX } from 'xlsx'
// 根据展示的表格数据,同步导出的表格数据tableDataCopy
watch(
() => tableData,
(val) => {
tableDataCopy = val
}
)
// 经过处理导出文件
const exportTableRef = ref(), table = ref()
const exportTable = async () => {
// 处理逻辑,得到tableDataCopy
// ......
// 如果你修改了表格,需要等待页面渲染喔~
await nextTick()
const wb = utils.table_to_book(exportTableRef.value.getElementsByTagName("TABLE")[0])
writeFileXLSX(wb, '导出文件名.xlsx')
}
//调用导出表格功能
exportTable()
</script>
1.3、百分比变小数?年月份输出需要格式化?
如果我们的表格里有百分比,但是通过上述代码导出后,发现变成了小数,如下图:
有两种解决方式:
1、使用el-table的话,在table_to_book
参数增加一项raw,如下:
const wb = utils.table_to_book(tableau.value, { raw: true })
2、如果使用自定义表格的话,在td
增加data-t="s"
。当指定了raw:true选项时,xlsx解析器将生成文本单元格。当未指定该选项或将其设置为false时,xlsx解析器将尝试解释每个TD元素的文本。
要覆盖特定单元格的转换,可以将以下数据属性添加到各个TD元素:
我们来实际使用下,看看效果:
<table border id="abc">
<tr>
<th>普通</th>
<th>data-t=s</th>
<th>data-v</th>
<th>data-z</th>
<th>data-z2</th>
</tr>
<tr>
<td>2012-12-03</td>
<td data-t="s">2012-12-03</td>
<td data-t="n" data-v="41246">2012-12-03</td>
<td data-t="n" data-v="41246" data-z="yyyy-mm-dd">2012-12-03</td>
<td data-t="n" data-v="41246" data-z="yyyy年mm月dd日">2012-12-03</td>
</tr>
<tr>
<td>100%</td>
<td data-t="s">100%</td>
<td data-t="n" data-v="100">100%</td>
<td data-t="n" data-v="0.25" data-z="0.00%">25.00%</td>
<td data-t="n" data-v="1000" data-z="#,##0">1000</td>
</tr>
</table>
/* find the table element in the page */
var tbl = document.getElementById('abc');
/* create a workbook */
var wb = XLSX.utils.table_to_book(tbl);
/* export to file */
XLSX.writeFile(wb, "SheetJSTable.xlsx");
页面上的表格:
导出的excel:
2、后端传递,前端下载
后端一般写的都是get请求,前端有两种方式下载:
1、创建a标签模拟点击下载;
2、获取blob文件流。
2.1、创建a标签点击下载
这种方式最简单,不需要前端处理。
const link = document.createElement('a')
link.style.display = 'none'
link.href = '后端的url'
// 不要加下面这句,会导致页面闪烁,用户体验不好
// link.setAttribute('target', '_blank')
document.body.appendChild(link)
link.click()
2.2、获取blob文件流
如果需要前端获取文件名、分段下载,则适用这种方法。这个一般都是写成工具类。
首先看看它的响应头:
其中Content-Disposition
携带了filname,这样我们可以拿到后端的文件名。(如果没有,让后端在请求头里带一个fileName就好啦)
接着,展示:
第一步,api请求写一个get blob(api/index.js)
export function getExportFile(url) {
return request({
url: url, // 后端提供的url
method: 'get',
responseType: 'blob', // 使用Blob格式接收
})
}
第二步,axios设置拦截(utils/request.js)
/**
* respone拦截器
*/
service.interceptors.response.use(
(response) => {
const res = response.data
// 拦截blob文件流,取下文件名
if (response.config.responseType === 'blob') {
if (res.type && res.type == 'application/force-download') {
// 看下响应头里的Content-Disposition,里面会有文件名
// 也可以让后端把文件名放在响应头里,像这里,后端给我放在了filename里
res.fileName = response.headers['filename'] || ''
}
return res
}
return res
},
(error) => {}
)
第三步,写download工具类(utils/FileUtils.js)
/**
* 文件导出函数,支持多种文件类型和浏览器
* @param {Blob} res - 下载流,应为Blob对象
* @param {string} fileName - 导出文件名
*/
import { ElMessage } from 'element-plus'
export function download(res, fileName) {
// 如果下载流不存在,直接返回
if (!res) {
return
}
// 判断下载流的类型
if (
res.type === 'application/octet-stream' ||
res.type === 'application/force-download' ||
res.type === 'text/xml' ||
res.type === 'application/zip' ||
res.type === 'application/csv' ||
res.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
) {
// 如果是IE或IE内核的浏览器
if (window.navigator.msSaveBlob) {
// IE以及IE内核的浏览器
try {
// 使用msSaveBlob方法保存文件
window.navigator.msSaveBlob(res, fileName)
} catch (e) {
console.log(e)
}
} else {
// 如果是其他浏览器,创建一个链接并模拟点击来下载文件
const url = window.URL.createObjectURL(new Blob([res]))
const link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', fileName) // 文件名
document.body.appendChild(link)
link.click()
// 下载完成后移除链接元素并释放Blob对象
document.body.removeChild(link)
// 释放掉blob对象
window.URL.revokeObjectURL(url)
}
} else {
// 如果下载流的类型不是文件,尝试解析为JSON
var reader = new FileReader()
reader.onload = function(event) {
const result = JSON.parse(reader.result)
// 如果解析结果中包含错误信息,显示警告
if (result.code !== undefined && result.code !== '1') {
warn(result.desc)
} else if (result.errorInfo && result.errorInfo.length > 0) {
warn(result.errorInfo[0].errorMessage)
}
}
reader.readAsText(new Blob([res]))
}
}
/**
* 显示警告消息
* @param {string} message - 消息内容
*/
export const warn = message => {
ElMessage({
message: message != null ? message : '操作失败', // 如果消息内容不为空,则显示消息内容,否则显示默认消息"操作失败"
type: 'warning', // 消息类型为警告
showClose: true, // 显示关闭按钮
center: true, // 消息居中显示
duration: 1000 // 消息显示持续时间为1秒
})
}
第四步,使用(index.vue)
import { download } from '@/utils/FileUtils'
API.getExportFile(res.data)
.then((res) => {
if (res.size > 0) {
//res.fileName是axios拦截请求时获取的,decodeURIComponent解码一下
let filename = decodeURIComponent(res.fileName)
filename = '文件名'+ '.' + filename.split('.')[1]
// 工具类download
download(res, filename)
}
})
.catch(() => {
exportLoading.value = false
})
以上就是本文的所有内容啦,遇到了就记下来。主要介绍了前端使用xlsx下载表格和格式化表格内容、接收后端文件流下载文件。献丑了,欢迎大神批评指教~~~
如果对你有帮助的话,不妨帮我评论点赞啦!!!
转载自:https://juejin.cn/post/7359157380046143515