前端导出Excel

image.png

关于导出Excel,我想很多同学都有过接触过,比如:在一个页面点击一个导出按钮,然后就会导出一个Excel表,表里面会展示一些我们需要看的列表数据。这种实现方式呢一般前后端都可以做。但是根据我自己的经历我感觉后端做比较好,为什么呢?因为前端做导出Excel是要考虑很多因素的,比如:导出数据量有多大、导出的时间大概是需要多少时间、导出这么大数据花费这么长时间前端页面会不会挂掉,还有就是用户体验等等。
当然也不是说前端就不能做,这需要看我们项目中的需求是什么样的,如果考虑到的问题都不是什么问题,那么也可以纯前端去做。
因为我在项目中遇到好多导出Excel功能,但每次遇到都得去找,所以呢我就总结一下,做个备份。

1、导入

React项目需要去安装xlsx

npm install xlsx --save // 本次安装的版本是"^0.14.1"

组件中需要导入xlsx

import XLSX from 'xlsx';
原生js
<script src="http://oss.sheetjs.com/js-xlsx/xlsx.full.min.js"></script>
2、导出Excel需要容器及按钮
<a href="" download="导出.xlsx" id="hf">
  <span onClick={this.excelClick.bind(this)} ></span>
</a>
3、导出Excel逻辑代码

Object.assign(target, ...sources)
用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。
target 目标对象。
sources 源对象。
返回值:目标对象。
描述:如果目标对象中的属性具有相同的键,则属性将被源中的属性覆盖。后来的源的属性将类似地覆盖早先的属性。
事例:

var obj = { a: 3, b: 1 };
var copy = Object.assign({}, obj);
console.log(copy); // { a: 3, b: 1 }

array1.reduce(callbackfn[, initialValue])
对数组中的所有元素调用指定的回调函数。该回调函数的返回值为累积结果,并且此返回值在下一次调用该回调函数时作为参数提供。
concat() 用于连接两个或多个数组。

import React from 'react';
import XLSX from 'xlsx';

let tmpDown; // 导出的二进制对象

class ExportExcel extends React.Component {
    constructor() {
        super();
        this.state = {
            dataW: [], // 信息表数据
            dataT: []  // 成绩表数据
        }
    }
    componentDidMount() {
        const dataW = [{
            name: '小明',
            age: 20,
            height: 170,
            weight: 120,
            hobby: '篮球'
        }];
        const dataT = [{
            name: '小红',
            cn: 100,
            en: 105,
            sx: 102,
        }]
        this.setState({ dataW, dataT });
    }
    // 将指定的自然数转换为26进制表示。映射关系:[0-25] -> [A-Z]。
    getCharCol = (n) => {
        let s = '';
        let m = 0;
        while (n > 0) {
            m = n % 26 + 1
            s = String.fromCharCode(m + 64) + s
            n = (n - m) / 26
        }
        return s
    }
    // 导出Excel公共部分
    excelCommon = (data) => {
        let tmpObj = data[0]; // 拿到数据的第一个对象
        data.unshift({}); // 在json内添加一个对象存放表格head
        let keyMap = []; // 定义一个数组存放表格的head
        // 循环json中拿到的对象目的是为了构建head
        for (var k in tmpObj) {
            keyMap.push(k); // 将对象中的key值存放到定义的数组中
            data[0][k] = k; // 将表头的内容以key/value的形式存放到json的空对象中
        };
        let tmpdata = []; // 用来保存转换好的json
        data.map((v, i) => keyMap.map((k, j) => Object.assign({}, {
            v: v[k],
            position: (j > 25 ? this.getCharCol(j) : String.fromCharCode(65 + j)) + (i + 1)
        }))).reduce((prev, next) => prev.concat(next)).forEach((v, i) => tmpdata[v.position] = {
            v: v.v,
        });
        let outputPos = Object.keys(tmpdata); // 设置区域,比如表格从A1到D10
        return {
            outputPos,
            tmpdata
        }
    }
    // 字符串转字符流
    s2ab = (s) => {
        let buf = new ArrayBuffer(s.length);
        let view = new Uint8Array(buf);
        for (let i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
        return buf;
    }
    // 导出Excel
    excelClick = () => {
        const type = "xlsx"; // 导出的格式这里写死为xlsx
        let { dataW, dataT } = this.state;

        // 表格中表头需要显示中文,将数据的key值换成中文
        let dataWCN = [];
        let dataTCN = [];
        if (dataW.length > 0) {
            dataW.map((i) => {
                let dataWObj = {
                    "姓名": i.name,
                    "年龄": i.age || 0, // 导出数据中不能有字段值为null
                    "身高": i.height,
                    "体重": i.weight,
                    "爱好": i.hobby,
                };
                dataWCN.push(dataWObj);
            })
        } else {
            let dataWObj = {
                "姓名": '无',
                "年龄": '无',
                "身高": '无',
                "体重": '无',
                "爱好": '无'
            };
            dataWCN.push(dataWObj);
        };
        if (dataT.length > 0) {
            dataT.map((i) => {
                let dataTObj = {
                    "名称": i.name,
                    "语文": i.cn,
                    '外语': i.en,
                    "数学": i.sx
                };
                dataTCN.push(dataTObj);
            })
        } else {
            let dataTObj = {
                "名称": '无',
                "语文": '无',
                '外语': '无',
                "数学": '无'
            };
            dataTCN.push(dataTObj);
        }

        // 调取公共部分构建数据
        let upDataWCN = this.excelCommon(dataWCN);
        let upDataTCN = this.excelCommon(dataTCN);

        // 生成表格
        let tmpWB = {
            SheetNames: ['信息表', '成绩表'], // 保存的表标题
            Sheets: {
                '信息表': Object.assign({},
                    upDataWCN.tmpdata, // 表格内容
                    {
                        '!ref': upDataWCN.outputPos[0] + ':' + upDataWCN.outputPos[upDataWCN.outputPos.length - 1] // 设置填充区域
                    }),
                '成绩表': Object.assign({},
                    upDataTCN.tmpdata, // 表格内容
                    {
                        '!ref': upDataTCN.outputPos[0] + ':' + upDataTCN.outputPos[upDataTCN.outputPos.length - 1] // 设置填充区域
                    })
            }
        };
        // 创建二进制对象写入转换好的字节流
        tmpDown = new Blob([this.s2ab(XLSX.write(tmpWB, 
            {bookType: (type === undefined ? 'xlsx':type),bookSST: false, type: 'binary'}// 这里是用来定义导出的格式类型
            ))], {
            type: ""
        }); 
        var href = URL.createObjectURL(tmpDown); // 创建对象超链接
        document.getElementById("hf").href = href; // 绑定a标签
        // document.getElementById("hf").click(); // 模拟点击实现下载
        setTimeout(function() { // 延时释放
            URL.revokeObjectURL(tmpDown); // 用URL.revokeObjectURL()来释放这个object URL
        }, 100);
    }
    render() {
        return(
            <a href="" download="导出.xlsx" id="hf">
                <span className="expert_modeler_BO_export" onClick={this.excelClick.bind(this)} ></span>
            </a>
        )
    }
}

export default ExportExcel;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容