刚接手一个项目,需要做表格导出,之前没用过,特做此记录,以备下次使用。此模板适用于修改表格样式时使用,若只想简单的直接导出,可以参考另一篇:在vue中,使用xlsx配合FileSaver进行导出excel表格_郭小白呀的博客-CSDN博客
看到一篇文章,受益匪浅:如何使用JavaScript实现纯前端读取和导出excel文件-好记的博客
此项目中的表格数据 使用的是element ui 中的table 导入的,所以导出也是直接将已生成的表格导出为excel
表格导出的基本模板过程
1. 先下载插件
npm i xlsx
npm i xlsx-style
2. 在使用的页面中导入
import XLSX2 from 'xlsx'
import XLSX from 'xlsx-style'
3. 绑定要导出的表格,并设置列宽(不同内容的导出方式是不同的,这里直接是将表格导出为excel文件,用的是 utils.table_to_sheet,其他的用到了再补充)
这里对表格有无数据的判断,请看下边遇到的问题第三个来写!!!
var wb = XLSX2.utils.table_to_sheet(document.querySelector('#mytable'))// mytable为表格的id名
if (!wb['!merges']) {
this.$message.warning('无法导出:报表无数据')
return
}
// 设置列宽(这里用到列的数量是用来设置不同列的不同宽度的)
const sum = 1 + this.govMt.length * 7 + 2 // 列的数量,根据自身项目进行数据的更改
for (var i = 0; i < sum; i++) {
if (i === sum - 1) {
wb['!cols'][i] = { wpx: 120 } // 设置列宽,只有最后一列的宽度是不同的
} else {
wb['!cols'][i] = { wpx: 60 }
}
}
4. 设置单元格的样式
for (const key in wb) {
if (key.indexOf('!') === -1) { // 排除带!的字段,只要单元格字段
wb[key].s = {
font: {// 字体设置
sz: 13,
bold: false,
color: {
rgb: '000000'// 十六进制,不带#
}
},
border: { // 设置边框
top: { style: 'thin' },
bottom: { style: 'thin' },
left: { style: 'thin' },
right: { style: 'thin' }
},
alignment: {// 文字居中 //字体水平居中、垂直居中、自动换行
horizontal: 'center',
vertical: 'center',
wrap_text: true
}
}
}
}
基础设置就是这样, 然后调用后边的三个函数即可
var data = this.addRangeBorder(wb['!merges'], wb) // 合并项添加边框
var filedata = this.sheet2blob(data) // 将一个sheet转成最终的excel文件的blob对象
this.openDownloadDialog(filedata, '报表名字.xlsx') // 下载报表
5. 由于有合并的单元格,所以要为 合并项 添加边框,具体解释看文末遇到的问题中的四!!
// 为合并项添加边框
addRangeBorder(range, ws) {
const arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
range.forEach(item => {
const startColNumber = Number(item.s.r); const endColNumber = Number(item.e.r) // 0-0
const startRowNumber = Number(item.s.c); const endRowNumber = Number(item.e.c) // 0-16
const test = ws[arr[startRowNumber] + (startColNumber + 1)] // 合并项第一个单元格中的内容
for (let col = startColNumber; col <= endColNumber; col++) { // 0-0
for (let row = startRowNumber; row <= endRowNumber; row++) { // 0-16
ws[arr[row] + (col + 1)] = test // A1-P1
}
}
})
return ws
},
6. 将一个sheet转成最终的excel文件的blob对象,然后利用URL.createObjectURL下载
// 将一个sheet转成最终的excel文件的blob对象
sheet2blob(sheet, sheetName) {
// console.log(sheet, sheetName, 'sheet, sheetName')
sheetName = sheetName || 'sheet1'
console.log(sheetName, 'sheetName')
var workbook = {
SheetNames: [sheetName],
Sheets: {}
}
workbook.Sheets[sheetName] = sheet // 生成excel的配置项
var wopts = {
bookType: 'xlsx', // 要生成的文件类型
bookSST: false, // 是否生成Shared String Table,官方解释是,如果开启生成速度会下降,但在低版本IOS设备上有更好的兼容性
type: 'binary'
}
var wbout = XLSX.write(workbook, wopts)
var blob = new Blob([s2ab(wbout)], {
type: 'application/octet-stream'
}) // 字符串转ArrayBuffer
function s2ab(s) {
var buf = new ArrayBuffer(s.length)
var view = new Uint8Array(buf)
for (var i = 0; i !== s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF
return buf
}
return blob
},
利用URL.createObjectURL下载 函数
openDownloadDialog(url, saveName) {
if (typeof url === 'object' && url instanceof Blob) {
url = URL.createObjectURL(url) // 创建blob地址
}
var aLink = document.createElement('a')
aLink.href = url
aLink.download = saveName || '' // HTML5新增的属性,指定保存文件名,可以不要后缀,注意,file:///模式下不会生效
var event
if (window.MouseEvent) event = new MouseEvent('click')
else {
event = document.createEvent('MouseEvents')
event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null)
}
aLink.dispatchEvent(event)
}
遇到的问题:
一、在使用xlsx-style导出的时候,可能会报如下错误:
Can‘t resolve ‘./cptable‘ in ‘xxx\node_modules_xlsx\xxx
或者在npm run serve时出现:
This relative module was not found: * ./cptable in ./node_modules/xlsx-style/dist/cpexcel.js
解决方式有两种:
1. 在config.js中加入如下代码:
module.exports = {
externals: {
'./cptable': 'var cptable'
}
}
这段代码要写进config.js中,我发现我并没有这个文件(我用的是vue2.0),所以要在根目录中创建vue.config.js文件,并在内部写如下代码:
module.exports = {
devServer: {
port: 80,
host: '0.0.0.0',
},
};
这时,我将externals: { './cptable': 'var cptable' }直接写进 module.exports 中,
module.exports = {
devServer: {
port: 80,
host: '0.0.0.0',
},
externals: {
'./cptable': 'var cptable'
}
};
出现了如下错误:
ERROR Invalid optio
.0ns in vue.config.js: "externals" is not allowed
意思是vue.config.js里面没有这个字段,所以配置的这个webpack字段vue并没有把它映射到vue.config.js中。所以我们要换一种引用方式:
configureWebpack: { externals: {'./cptable': 'var cptable'} } // 配置webpack的字段。
然后再重新运行就可以了。
2. 第二种方式是修改 node_modules 中
的代码,但是,每次拉取代码 npm i 后,都要去修改,这样是很不方便的。
找到 ./node_modules/xlsx-style/dist/cpexcel.js
文件手动修改
将
var cpt = require('./cpt' + 'able');
替换为
var cpt = cptable;
二、vue xlsx导出表格时间不全问题
可能为:进入时间一列显示年月日,时分秒被忽略掉、有个别显示####
解决方式:
在代码中添加:{ raw: true }
var wb = XLSX2.utils.table_to_sheet(document.querySelector('#exportTab'), { raw: true })// exportTab为表格的id名
效果图:
三、在代码的 3. 中,报表无数据的判断是可以灵活改变的
如下代码段是用来判断有没有合并项的,如果没有单元格合并,是不会有此选项的,所以就算表格中有数据,也会一直走无法导出的代码,所以可以再从wb中换一个字段来判断。例如: !fullref
若有合并项可以写为:
if (!wb['!merges']) {
this.$message.warning('无法导出:报表无数据')
return
}
根据需要可以改写为:
if (!wb['!fullref']) {
this.$message.warning('无法导出:报表无数据')
return
}
四、 对于合并项函数的需求解答
当我们的单元格是合并单元格时,如果不对合并项添加边框,导出的表格效果如图所示:
我们发现合并项的边框是有缺失的,在我们调用为合并项添加边框函数后,就可解决这个问题,效果图如下所示: