vue项目+xlsx+xlsx-style 实现table导出为excel的功能
最近遇到一个需求,后端提供一组数据,根据这组数据,导出为一个excel表格。
步骤如下:
由于我之前有用过csdn版本的xlsx-style
,因此首先想到的就是使用xlsx
来实现此功能。
下面介绍详细步骤:
xlsx
和xlsx-style
依次安装以下的内容:
npm install xlsx --save
npm install xlsx-style --save
由于我这边是单个页面使用此功能,因此并没有在main.js
中全局注册
局部注册的方法:
import * as XLSX from 'xlsx';
import * as XLSX2 from 'xlsx-style';
注意:一定要重命名其中的方法,就是用as 重命名,因为两个插件导出的名字默认都叫XLSX,如果不进行重命名,则会报错
vue.config.js
中需要添加以下配置
如果不添加下面的代码,则运行时,代码会报错。
config.externals = {
'./cptable': 'var cptable',
};
html
预览用table
组件来实现(VUE)
我对上面的表格进行了拆分,一个table
里面包含了4组。
合并单元格 我使用的是colspan="n"
<table border="1" width="100%" cellspacing="0" cellpadding="0" id="tableId">
<thead>
<tr>
<th colspan="7" class="headerCls">
{{ info.saleOrderCode }}采购需求清单
</th>
</tr>
</thead>
<tbody>
<tr>
<td>计划套数</td>
<td colspan="2">{{ info.producePlansCount }}</td>
<td>付款时间</td>
<td colspan="3">{{ info.lastPaidTime }}</td>
</tr>
<tr>
<td>申请时间</td>
<td colspan="2">{{ info.applicantTime }}</td>
<td>申请人员</td>
<td colspan="3">{{ info.applicantName }}</td>
</tr>
<tr>
<td>计划交期</td>
<td colspan="2">{{ info.deliveryTime }}</td>
<td>打印时间</td>
<td colspan="3">{{ info.printTime }}</td>
</tr>
</tbody>
<thead>
<tr>
<th colspan="5" class="leftTitCls">板材需求明细</th>
<th>板材系数</th>
<th>1.2</th>
</tr>
</thead>
<tbody>
<tr>
<td>序号</td>
<td>物品名称</td>
<td>型号规格</td>
<td>数量</td>
<td>单位</td>
<td>到货时间</td>
<td>备注</td>
</tr>
<tr v-for="(item, index) in list1" :key="item.materialName">
<td>{{ index + 1 }}</td>
<td>{{ item.materialName }}</td>
<td>{{ item.specification }}</td>
<td>{{ item.procurementCount }}</td>
<td>{{ item.unit }}</td>
<td>{{ item.arrivalTime }}</td>
<td>{{ item.remark }}</td>
</tr>
</tbody>
<thead>
<tr>
<th colspan="5" class="leftTitCls">塑粉需求明细</th>
<th>喷涂系数</th>
<th>0.8</th>
</tr>
</thead>
<tbody>
<tr>
<td>序号</td>
<td>物品名称</td>
<td>型号规格</td>
<td>数量</td>
<td>单位</td>
<td>到货时间</td>
<td>备注</td>
</tr>
<tr v-for="(item, index) in list2" :key="item.materialName">
<td>{{ index + 1 }}</td>
<td>{{ item.materialName }}</td>
<td>{{ item.specification }}</td>
<td>{{ item.procurementCount }}</td>
<td>{{ item.unit }}</td>
<td>{{ item.arrivalTime }}</td>
<td>{{ item.remark }}</td>
</tr>
</tbody>
<thead>
<tr>
<th colspan="7" class="leftTitCls">外购件需求明细</th>
</tr>
</thead>
<tbody>
<tr>
<td>序号</td>
<td>物品名称</td>
<td>型号规格</td>
<td>数量</td>
<td>单位</td>
<td>到货时间</td>
<td>备注</td>
</tr>
<tr v-for="(item, index) in list3" :key="item.materialName">
<td>{{ index + 1 }}</td>
<td>{{ item.materialName }}</td>
<td>{{ item.specification }}</td>
<td>{{ item.procurementCount }}</td>
<td>{{ item.unit }}</td>
<td>{{ item.arrivalTime }}</td>
<td>{{ item.remark }}</td>
</tr>
</tbody>
</table>
数据渲染是通过数组遍历的。
//获取当前时间,用于命名excel表格
let time = this.moment(new Date()).format('YYYY-MM-DD');
//获取整个表格的行数。三个数组长度+其他固定行数(10)
let len = this.list1.length + this.list2.length + this.list3.length + 10;
this.tableToExcel(
'tableId',
`${this.info.saleOrderCode}采购清单需求表${time}`,
7,
len,
1
);
tableToExcel
方法——入参(表格id,导出文档名称,列数,行数,表头行数)this.tableToExcel( 'tableId', `采购清单需求表${time}`, 7,len, 1);
tableToExcel(tableId, fileName, headLength, colsLength, headColsLength) {
var sheet = XLSX.utils.table_to_sheet(
document.querySelector('#' + tableId)
);
//根据tableId获取到元素,通过XLSX.utils.table_to_sheet 获取到sheet表格属性
console.log(sheet);
//下面是固定写法,可以直接照搬
var arr = [];
for (let i = 0; i < headLength; i++) {
if (i < 26) {
arr.push(String.fromCharCode(65 + i).toUpperCase());
} else {
arr.push('A' + String.fromCharCode(65 + (i - 26)).toUpperCase());
}
}
for (let i = 0; i < arr.length; i++) {
//如果是多级表头的话,设置单元格宽度
if (headColsLength > 1) {
for (let j = 0; j < headColsLength; j++) {
if (j < headColsLength && sheet[arr[i] + j]) {
if (
!sheet[arr[i] + (j + 1)] ||
!sheet[arr[i] + (j - 1)] ||
(!sheet[arr[i] + (j - 1)] && !sheet[arr[i] + (j + 1)])
) {
sheet['!cols'].push({
wch: sheet[arr[i] + j].v.length * 2 + 3,
});
}
}
}
} else {
//由于我这边是单个表头,因此走这块代码
if (!sheet['!cols']) {
sheet['!cols'] = [];
}
//我这边是对 A D E三列要求宽度窄一些,其他的宽一些,主要是根据内容的多少来,也可以根据内容的长度来处理
if (
(arr[i] + '1').indexOf('A') > -1 ||
(arr[i] + '1').indexOf('D') > -1 ||
(arr[i] + '1').indexOf('E') > -1
) {
sheet['!cols'].push({
wch: 8,
});
} else {
sheet['!cols'].push({
wch: 14,
});
}
}
//由于我这边合并单元格后,导致部分边框不展示了,因此通过这个方法来补充边框。
sheet = this.addRangeBorder(sheet['!merges'], sheet);
//如果仅仅用上面的方法,发现还是有缺失的边框,因此下面又进行了一遍处理。添加边框及其他样式的处理。
for (let key in sheet) {
if (sheet[key] instanceof Object) {
sheet[key].s = {
border: {
top: {
style: 'thin',
},
bottom: {
style: 'thin',
},
left: {
style: 'thin',
},
right: {
style: 'thin',
},
},
alignment: {
horizontal: 'center',
vertical: 'center',
wrap_text: true,
},
font: {
sz: 11,
},
bold: true,
numFmt: 0,
};
}
}
}
this.downloadExcel(this.sheet2blob(sheet), `${fileName}.xlsx`); //下载
},
addRangeBorder
addRangeBorder(range, ws) {
// s:起始位置,e:结束位置
var arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
range.forEach((item) => {
var startRowNumber = Number(item.s.r),
startColumnNumber = Number(item.s.c),
endColumnNumber = Number(item.e.c),
endRowNumber = Number(item.e.r);
console.log(
'startColumnNumber',
startColumnNumber,
endColumnNumber,
startRowNumber,
endRowNumber
);
// 合并单元格时会丢失边框样式,例如A1->A4 此时内容在A1 而range内获取到的是从0开始的,所以开始行数要+2
for (var i = startColumnNumber; i <= endColumnNumber + 2; i++) {
for (var j = startRowNumber; j <= endRowNumber + 2; j++) {
if (ws[arr[i] + j]) {
ws[arr[i] + j].s = {
border: {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' },
},
alignment: {
horizontal: 'center',
vertical: 'center',
wrap_text: true,
},
font: {
sz: 11,
},
bold: true,
numFmt: 0,
};
} else {
ws[arr[i] + j] = {
s: {
border: {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' },
},
alignment: {
horizontal: 'center',
vertical: 'center',
wrap_text: true,
},
font: {
sz: 11,
},
bold: true,
numFmt: 0,
},
t: '',
v: '',
};
}
}
}
});
return ws;
},
sheet2blob(sheet, sheetName) {
sheetName = sheetName || 'sheet1';
var workbook = {
SheetNames: [sheetName],
Sheets: {},
};
workbook.Sheets[sheetName] = sheet;
var wopts = {
bookType: 'xlsx',
bookSST: false,
type: 'binary',
};
var wbout = XLSX2.write(workbook, wopts);
var blob = new Blob([this.s2ab(wbout)], {
type: 'application/octet-stream',
});
return blob;
},
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;
},
excel
文档 downloadExcel(url, saveName) {
var blob = new Blob([url], {
type: 'application/octet-stream',
});
var a = document.createElement('a');
a.id = 'downloadFtsetBtn';
a.style.display = 'none';
a.target = '_blank';
document.body.appendChild(a);
try {
var URL = window.URL || window.webkitURL;
a.href = URL.createObjectURL(blob);
a.download = saveName;
if (typeof navigator.msSaveBlob == 'function') {
//IE
navigator.msSaveBlob(blob, saveName);
}
a.click();
} catch (e) {
console.log(e);
}
},
完成!!!多多积累,多多收获!!!