功能说明
本文讲解如何将table数据转为excel文件下载。亲测,当table有合并行/列时,下载的excel也会对应合并。
核心思想:
获取到table元素,根据其html组装为下载excel的文件地址,再下载
核心代码:
handleDownload() {
// 获取table元素
const tableElem = ReactHTMLTableToExcel.getTableElem(this.props.table);
console.log('tableElem', tableElem)
if(!tableElem) return;
// 获取excel下载的url和文件名称
const filename = `${String(this.props.filename)}.xls`;
const tableHtml = tableElem.outerHTML;
const url = ReactHTMLTableToExcel.getExcelUri(tableHtml, this.props.sheet);
// 下载excel文件
ReactHTMLTableToExcel.downloadExcel(tableHtml, filename, url);
}
获取table元素
其中,getTableElem函数中的tableElemId参数 可以为 table元素的id ,也可以是 其父元素或其祖父元素的id,便于兼容不方便给table增加id属性时的情况。
static getTableElem(tableElemId){ // table元素的id 或者 其祖父元素的id
if (!document || !tableElemId) {
return null;
}
let tableElem = document.getElementById(tableElemId);
if(tableElem){
if(tableElem.nodeType !== 1 || tableElem.nodeName !== 'TABLE'){ // 不是元素类型,或者不是table
tableElem = tableElem.getElementsByTagName('table')[0] || null; //尝试寻找内部table元素
}
}
if(!tableElem){
tableElem = document.getElementsByTagName('table')[0];
if(!tableElem){
console.log('未找到table元素!')
return null;
}
}
return tableElem;
}
生成excel文件地址:
static base64(s) {
return window.btoa(unescape(encodeURIComponent(s)));
}
static format(s, c) {
return s.replace(/{(\w+)}/g, (m, p) => c[p]);
}
static getExcelUri(table, sheetname){
const uri = 'data:application/vnd.ms-excel;base64,';
const template =
'<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-mic' +
'rosoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><head><meta cha' +
'rset="UTF-8"><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:Exce' +
'lWorksheet><x:Name>{worksheet}</x:Name><x:WorksheetOptions><x:DisplayGridlines/>' +
'</x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></' +
'xml><![endif]--></head><body>{table}</body></html>';
const context = {
worksheet: String(sheetname) || 'Worksheet',
table,
};
const url = uri +
ReactHTMLTableToExcel.base64(
ReactHTMLTableToExcel.format(template, context),
);
return url;
}
下载excel文件
static downloadExcel(tableHtml, filename, url){
// If IE11
if (window.navigator.msSaveOrOpenBlob) {
const fileData = [
`${'<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-mic' + 'rosoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><head><meta cha' + 'rset="UTF-8"><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:Exce' + 'lWorksheet><x:Name>{worksheet}</x:Name><x:WorksheetOptions><x:DisplayGridlines/>' + '</x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></' + 'xml><![endif]--></head><body>'}${tableHtml}</body></html>`,
];
const blobObject = new Blob(fileData);
document.getElementById('react-html-table-to-excel').click()(() => {
window.navigator.msSaveOrOpenBlob(blobObject, filename);
});
return true;
}
// 下载excel文件
const element = window.document.createElement('a');
element.href = url;
element.download = filename;
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
附赠完整代码:
/* global window, document, Blob */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
const propTypes = {
table: PropTypes.string.isRequired,
filename: PropTypes.string.isRequired,
sheet: PropTypes.string.isRequired,
id: PropTypes.string,
className: PropTypes.string,
buttonText: PropTypes.string,
};
const defaultProps = {
id: 'button-download-as-xls',
className: 'button-download',
buttonText: 'Download',
};
class ReactHTMLTableToExcel extends Component {
constructor(props) {
super(props);
this.handleDownload = this.handleDownload.bind(this);
}
static base64(s) {
return window.btoa(unescape(encodeURIComponent(s)));
}
static format(s, c) {
return s.replace(/{(\w+)}/g, (m, p) => c[p]);
}
static getTableElem(tableElemId){ // table元素的id 或者 其祖父元素的id
if (!document || !tableElemId) {
return null;
}
let tableElem = document.getElementById(tableElemId);
if(tableElem){
if(tableElem.nodeType !== 1 || tableElem.nodeName !== 'TABLE'){ // 不是元素类型,或者不是table
tableElem = tableElem.getElementsByTagName('table')[0] || null; //尝试寻找内部table元素
}
}
if(!tableElem){
tableElem = document.getElementsByTagName('table')[0];
if(!tableElem){
console.log('未找到table元素!')
return null;
}
}
return tableElem;
}
static getExcelUri(table, sheetname){
const uri = 'data:application/vnd.ms-excel;base64,';
const template =
'<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-mic' +
'rosoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><head><meta cha' +
'rset="UTF-8"><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:Exce' +
'lWorksheet><x:Name>{worksheet}</x:Name><x:WorksheetOptions><x:DisplayGridlines/>' +
'</x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></' +
'xml><![endif]--></head><body>{table}</body></html>';
const context = {
worksheet: String(sheetname) || 'Worksheet',
table,
};
const url = uri +
ReactHTMLTableToExcel.base64(
ReactHTMLTableToExcel.format(template, context),
);
return url;
}
static downloadExcel(tableHtml, filename, url){
// If IE11
if (window.navigator.msSaveOrOpenBlob) {
const fileData = [
`${'<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-mic' + 'rosoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><head><meta cha' + 'rset="UTF-8"><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:Exce' + 'lWorksheet><x:Name>{worksheet}</x:Name><x:WorksheetOptions><x:DisplayGridlines/>' + '</x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></' + 'xml><![endif]--></head><body>'}${tableHtml}</body></html>`,
];
const blobObject = new Blob(fileData);
document.getElementById('react-html-table-to-excel').click()(() => {
window.navigator.msSaveOrOpenBlob(blobObject, filename);
});
return true;
}
// 下载excel文件
const element = window.document.createElement('a');
element.href = url;
element.download = filename;
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
handleDownload() {
// 获取table元素
const tableElem = ReactHTMLTableToExcel.getTableElem(this.props.table);
console.log('tableElem', tableElem)
if(!tableElem) return;
// 获取excel下载的url和文件名称
const filename = `${String(this.props.filename)}.xls`;
const tableHtml = tableElem.outerHTML;
const url = ReactHTMLTableToExcel.getExcelUri(tableHtml, this.props.sheet);
// 下载excel文件
ReactHTMLTableToExcel.downloadExcel(tableHtml, filename, url);
}
render() {
return (
<button
id={this.props.id}
className={this.props.className}
type="button"
onClick={this.handleDownload}
>
{this.props.buttonText}
</button>
);
}
}
ReactHTMLTableToExcel.propTypes = propTypes;
ReactHTMLTableToExcel.defaultProps = defaultProps;
export default ReactHTMLTableToExcel;
使用:
兼容不方便给table增加id属性时的情况:
<div id="table-to-xls">
<ReactHTMLTableToExcel
id="test-table-xls-button"
className="download-table-xls-button"
table="table-to-xls"
filename="tablexls"
sheet="tablexls"
buttonText="Download as XLS"/>
<Mytable>//封装好的table,table上没有透传id</Mytable>
</div>
直接在table上增加id的情况:
<div>
<ReactHTMLTableToExcel
id="test-table-xls-button"
className="download-table-xls-button"
table="table-to-xls"
filename="tablexls"
sheet="tablexls"
buttonText="Download as XLS"/>
<table id="table-to-xls">
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Age</th>
</tr>
<tr>
<td>Jill</td>
<td>Smith</td>
<td>50</td>
</tr>
<tr>
<td>Eve</td>
<td>Jackson</td>
<td>94</td>
</tr>
</table>
</div>
参考代码
ReactHTMLTableToExcel:https://www.npmjs.com/package/react-html-table-to-excel
本文在这个基础上,进行了代码抽离和获取table元素功能增强(兼容不方便给table增加id属性时的情况)。目的不是重复造轮子,是能够满足尽量多的业务场景。