编程语言
首页 > 编程语言> > 前端javascript 实现到出excel 支持分页

前端javascript 实现到出excel 支持分页

作者:互联网

版本

v2.0.0

进步

丢弃了jquery操作dom,由于大量耗费时间,效率严重低下,此版本改为字符替换代替dom操作。优点:

效率大大提高

可跨函数分页,操作便捷,容易上手。

10万数据级别时间控制在9~15秒左右(具体根据数据对象的大小而定)

可自定义导出各个阶段的回调函数

缺点:

但是百万级别数据虽然能导出,但是仍然很吃力,下个版本将进行优化。

依然没有完全摆脱jquery的依赖,后期版本迭代将完全脱离jquery

目录

 

代码

ExcelUtils.js

/**
 * author:gl
 * data:2020/7/2
 *js导出Excel
 */

/**
 * 默认的Point模型,用户遍历属性
 * @constructor
 */
function DefaultPoint() {
    this.x = null;
    this.y = null;
};

/**
 * table模型
 * @constructor
 */
function TableModel() {
    this.colNames = [];
    this.pointList = [];
};

/**
 * table模型所需函数
 * @type {{setPointList: TableModel.setPointList, getPointList: (function(): []), setColNames: TableModel.setColNames, getColNames: (function(): [])}}
 */
TableModel.prototype = {
    setColNames: function (names) {
        this.colNames = names;
    },
    getColNames: function () {
        return this.colNames;
    },
    setPointList: function (pointList) {
        this.pointList = pointList;
    },
    getPointList: function () {
        return this.pointList;
    }
};

/**
 * Excel导出工具类,核心类
 * @constructor
 */
function ExcelUtils() {}

/**
 * cell,row,sheet,workbook的xml
 * @type {{tmplCellXML: string, tmplWorksheetXML: string, tmplWorkbookXML: string, uri: string}}
 * ss:ExpandedColumnCount="256" 每页最多256列
 * ss:ExpandedRowCount="10000000" 每页最多1百万行
 */
ExcelUtils.paramXml = {
    //uri: 'data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,',
    uri: 'data:application/vnd.ms-excel;base64,',
    tmplWorkbookXML: `<?xml version="1.0"?>
  <?mso-application progid="Excel.Sheet"?>
  <Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
   xmlns:o="urn:schemas-microsoft-com:office:office"
   xmlns:x="urn:schemas-microsoft-com:office:excel"
   xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
   xmlns:html="http://www.w3.org/TR/REC-html40">
   <DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">
    <Created>1996-12-17T01:32:42Z</Created>
   <LastSaved>2006-02-17T13:16:06Z</LastSaved>
   <Version>11.5606</Version>
  </DocumentProperties>
  <OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office">
   <RemovePersonalInformation/>
  </OfficeDocumentSettings>
  <ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
  <WindowHeight>4530</WindowHeight>
  <WindowWidth>8505</WindowWidth>
  <WindowTopX>480</WindowTopX>
  <WindowTopY>120</WindowTopY>
  <AcceptLabelsInFormulas/>
  <ProtectStructure>False</ProtectStructure>
  <ProtectWindows>False</ProtectWindows>
 </ExcelWorkbook>
 <Styles>
  <Style ss:ID="Default" ss:Name="Normal">
   <Alignment ss:Vertical="Bottom"/>
   <Borders/>
   <Font ss:FontName="宋体" x:CharSet="134" ss:Size="12"/>
   <Interior/>
   <NumberFormat/>
   <Protection/>
  </Style>
  <Style ss:ID="s32">
        <Alignment ss:Horizontal="Center" ss:Vertical="Center"/>
        <Borders>' +
        <Border ss:Position="Bottom" ss:LineStyle="Dash" ss:Weight="1"/>
        </Borders>' +
        <Font ss:FontName="宋体" x:CharSet="134" ss:Size="12" ss:Bold="1"/>
        <Interior ss:Color="#FFFF00" ss:Pattern="Solid"/>
  </Style>
  <Style ss:ID="s34">
   <Alignment ss:Horizontal="Center" ss:Vertical="Center"/>
   <Borders>
    <Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
    <Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
    <Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
    <Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
   </Borders>
   <Font x:Family="Swiss" ss:Size="11" ss:Bold="1"/>
   <Interior ss:Color="#CCFFCC" ss:Pattern="Solid"/>
  </Style>
  <Style ss:ID="s35">
       <Alignment ss:Horizontal="Center" ss:Vertical="Center"/>
        <Font ss:FontName="宋体" x:CharSet="134" ss:Size="10"/>
  </Style>
 </Styles>
{worksheets}
</Workbook>
`,
    //    tmplWorksheetXML: '<Worksheet ss:Name="{nameWS}"><Table ss:ExpandedColumnCount="2" ss:ExpandedRowCount="22"ss:DefaultColumnWidth="156.75" ss:DefaultRowHeight="14.25">{column}{rows}</Table></Worksheet>',
    tmplWorksheetXML: `<Worksheet ss:Name="{nameWS}"> 
                                <Table ss:ExpandedColumnCount="256" ss:ExpandedRowCount="10000000" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="54" ss:DefaultRowHeight="14.25">
{column}{rows}</Table></Worksheet>`,
    tmplCellXML: `<Cell {attributeStyleID}{mergeAcross}><Data ss:Type="{nameType}">{data}</Data></Cell>`

};

/**用来缓存多个sheet的xml,便于一次导出**/
ExcelUtils.sheetsXml = [];

/**默认的Point**/
ExcelUtils.UserPointModel = DefaultPoint;

/**默认的列名**/
ExcelUtils.colNames = ['col01', 'col02'];

/**头信息格式[{text:xxx,colspan:1-9}]**/
ExcelUtils.addTableHeadJson = [];

/**
 * 添加caption部门,可以添加多个
 * @param headArr
 * @returns {ExcelUtils}
 */
ExcelUtils.addTableHead = function (headArr) {
    ExcelUtils.addTableHeadJson = headArr;

    return ExcelUtils;
};

/**
 * 将数据转为Html的table
 * @param tableModel
 * @returns {*|jQuery.fn.init|jQuery|HTMLElement}
 * version 1.0.0
 * 状态:丢弃
 * 原因:执行太慢
 */
/*ExcelUtils.tableModelConvertToTable = function (tableModel) {
    var starttime = new Date().getTime();
    var tableId = Math.random().toString(36);
    var $table = $('<table id="' + tableId + '"></table>');
    try {
        if (ExcelUtils.addTableHeadJson != null && ExcelUtils.addTableHeadJson != []) {
            $.each(ExcelUtils.addTableHeadJson, function (index, elem) {
                var $tr = $('<tr></tr>');
                var $td = $('<td colspan=' + elem.colspan + '>' + elem.text + '</td>');
                $tr.append($td);
                $table.append($tr);
            })
        }
        var $tr = $('<tr></tr>');
        //添加首行列名字
        $.each(tableModel.getColNames(), function (index, elem) {
            var $td = $('<td>' + elem + '</td>');
            $tr.append($td);
        })
        $table.append($tr);
        //数据填充table
        var stt = new Date().getTime();
        $.each(tableModel.getPointList(), function (index, point) {
            var $tr = $("<tr></tr>");
            $.each(Object.keys(point), function (index, attr) {
                var $td = $('<td>' + point[attr] + '</td>');
                $tr.append($td);
            })
            $table.append($tr);
        })
        console.log("数据填充花费时间:" + (new Date().getTime() - stt))
        // $("#mytable").append($table)
    } catch (e) {
        ExcelUtils.exceptionCall(e);
    }
    console.log("数据转为uHtml的table:" + (new Date().getTime() - starttime));
    return $table;
};*/
/**
 * 将数据转为Html的table
 * @param tableModel
 * @returns {*|jQuery.fn.init|jQuery|HTMLElement}
 * version 1.0.1
 */
ExcelUtils.tableModelConvertToTable = function (tableModel) {
    var tableId = Math.random().toString(36);
    var table = '<table id="' + tableId + '">{tr}</table>';
    try {
        if (!ExcelUtils.isEmpty(ExcelUtils.addTableHeadJson)) {
            $.each(ExcelUtils.addTableHeadJson, function (index, elem) {
                var tr = '<tr>{td}</tr>';
                var td = '<td colspan=' + elem.colspan + '>' + elem.text + '</td>'
                tr = ExcelUtils.formatStr(tr, td);
                table = ExcelUtils.formatStr(table, tr + '{tr}');
            })
        }
        var tr = '<tr>{td}</tr>';
        //添加首行列名字
        $.each(tableModel.getColNames(), function (index, elem) {
            var td = '<td>' + elem + '</td>';
            tr = ExcelUtils.formatStr(tr, td + '{td}');
        })
        tr = ExcelUtils.formatStr(tr, '');
        table = ExcelUtils.formatStr(table, tr + '{tr}');
        //数据填充table
        var strL = '';
        $.each(tableModel.getPointList(), function (index, point) {
            var tr = '<tr>{td}</tr>';
            $.each(Object.keys(point), function (index, attr) {
                var td = '<td>' + point[attr] + '</td>';
                tr = ExcelUtils.formatStr(tr, td + '{td}');
            })
            tr = ExcelUtils.formatStr(tr, '');
            strL += tr;
        })
        table = ExcelUtils.formatStr(table, strL);
    } catch (e) {
        ExcelUtils.exceptionCall(e);
    }
    var $table = $(table);

    return $table;
};
/**
 * 处理数据
 * @param userPointFunction
 * @param dataList
 * @returns {[]}
 */
ExcelUtils.getAxisData = function (userPointFunction, ...dataList) {
    var pointList = [];
    //$.each(dataList,function())
    try {
        if (dataList != null && dataList.length > 0) {
            //初始化模型列表
            $.each(dataList[0], function () {
                pointList.push(new userPointFunction())
            })
            //填充数据
            $.each(Object.keys(new userPointFunction()), function (index, attrName) {
                $.each(dataList[index], function (index, elem) {
                    var point = pointList[index];
                    point[attrName] = elem;
                })
            })
            return pointList;
        } else {
            throw new Error("数据数组不能为空");
        }
    } catch (e) {
        ExcelUtils.exceptionCall(e);
    }
};
/**
 * 将workbookxml转为base64数据
 * @param s
 * @returns {string}
 */
ExcelUtils.base64 = function (s) {
    var str = window.btoa(unescape(encodeURIComponent(s)))

    return str;
};

/**
 * {xxx}数据替换
 * @param s
 * @param c
 * @returns {*|void|string}
 */
ExcelUtils.format = function (s, c) {
    return s.replace(/{(\w+)}/g, function (m, p) {
        return c[p];
    })
};

/**
 * @param sheetName
 * @param table
 * @returns {string}
 */
ExcelUtils.formatStr = function (s, c) {
    return s.replace(/{(\w+)}/g, function (m, p) {
        return c;
    })
};
/**
 *将table对象转为Sheet
 * @param sheetName
 * @param table
 * @returns {string}
 */
ExcelUtils.tableConvertToSheet = function (sheetName, table) {
    var ctx = "";
    var worksheetsXML = "";
    var rowsXML = "";
    try {
        var table = table[0];
        var columnStr = '';
        //设置单元格宽度
        if (table.rows[ExcelUtils.addTableHeadJson.length].cells.length) {
            for (var j = 0; j < table.rows[ExcelUtils.addTableHeadJson.length].cells.length; j++) {
                columnStr += '<Column ss:Index="' + (j + 1) + '" ss:AutoFitWidth="0" ss:Width="156.75"/>'
            }
        }
        //控制要导出的行数
        for (var j = 0; j < table.rows.length; j++) {
            if (!ExcelUtils.isEmpty(ExcelUtils.addTableHeadJson) && j < ExcelUtils.addTableHeadJson.length) {
                rowsXML += '<Row ss:Height="26">';
            } else {
                rowsXML += '<Row ss:Height="20">';
            }
            for (var k = 0; k < table.rows[j].cells.length; k++) {
                var dataValue = table.rows[j].cells[k].innerHTML;
                var colspan = table.rows[j].cells[k].getAttribute("colspan");
                var styleId = 's35';
                if (!ExcelUtils.isEmpty(ExcelUtils.addTableHeadJson) && j < ExcelUtils.addTableHeadJson.length) {
                    styleId = 's32';
                } else if (j == ExcelUtils.addTableHeadJson.length) {
                    styleId = 's34';
                }
                ctx = {
                    attributeStyleID: (styleId != '') ? 'ss:StyleID="' + styleId + '"' : '',
                    nameType: 'String',
                    data: dataValue,
                    mergeAcross: (colspan) ? ' ss:MergeAcross="' + (colspan - 1) + '"' : '' //合并单元格
                };
                styleId = "";
                rowsXML += ExcelUtils.format(ExcelUtils.paramXml.tmplCellXML, ctx);
            }
            rowsXML += '</Row>'
        }

        ctx = {
            rows: rowsXML,
            nameWS: sheetName,
            column: columnStr
        };

        worksheetsXML += ExcelUtils.format(ExcelUtils.paramXml.tmplWorksheetXML, ctx);
        //  rowsXML = "";
    } catch (e) {
        ExcelUtils.exceptionCall(e);
    }

    return worksheetsXML;
};

/**
 * 判断字符和数组
 * @param data
 * @returns {boolean}
 */
ExcelUtils.isEmpty = function (data) {
    if (data === undefined)
        return true;

    var type = typeof data;

    if (type === 'object') {
        if (typeof data.length === 'number')
            type = 'array';
        else
            type = 'object';
    }

    switch (type) {
        case 'array':
            if (data === undefined || data.length <= 0)
                return true;
            else
                return false;
        case 'string':
            if (data === undefined || data.length() <= 0)
                return true;
            else
                return false;
        default:
            throw new Error('Unknown type');
    }
}

/**
 *
 * @param sheetName 单个sheet的名称
 * @param userPointFunction 用户自定义的Point
 * @param colNames 列名数组
 * @param dataList 每列的数据数组
 * @returns {ExcelUtils} 单个sheetXml
 */
ExcelUtils.addSheet = function (sheetName, userPointFunction, colNames, ...dataList) {
    try {
        var pointList = ExcelUtils.getAxisData(userPointFunction, ...dataList);
        var tableModel = new TableModel();
        tableModel.setPointList(pointList);
        tableModel.setColNames(colNames || ExcelUtils.colNames);
        var $table = ExcelUtils.tableModelConvertToTable(tableModel);
        var sheetXml = ExcelUtils.tableConvertToSheet(sheetName, $table);
        ExcelUtils.sheetsXml.push(sheetXml);
    } catch (e) {
        ExcelUtils.exceptionCall(e);
    }

    return ExcelUtils;
};

/**
 * 下载Excel
 * @param fileName Excel名称
 * @param workbookXML 整个ExcelXml
 */
ExcelUtils.downExcel = function (fileName, workbookXML) {
    try {
        var link = document.createElement("A");
        link.href = ExcelUtils.paramXml.uri + ExcelUtils.base64(workbookXML);
        link.download = fileName || 'Workbook.xlsx';
        link.target = '_blank';
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
    } catch (e) {
        ExcelUtils.exceptionCall(e);
    }
};

/**
 * 清除数据,以及恢复默认值
 */
ExcelUtils.clear = function () {
    ExcelUtils.sheetsXml = [];
    ExcelUtils.UserPointModel = DefaultPoint;
    ExcelUtils.colNames = ['x轴', 'y轴'];
    ExcelUtils.executeExceptionCall = ExcelUtils.defaultExceptionCall;
    ExcelUtils.beforeExecute = ExcelUtils.defaultBeforeExecute;
    ExcelUtils.afterExecute = ExcelUtils.defaultAfterExecute;
};

/**
 * 导出函数,执行一些列的导出工作
 * @param fileName
 */
ExcelUtils.export = function (fileName) {
    try {
        var strXml = '';
        $.each(ExcelUtils.sheetsXml, function (index, xml) {
            strXml += xml;
        })
        var ctx = {
            created: (new Date()).getTime(),
            worksheets: strXml
        };
        var workbookXML = ExcelUtils.format(ExcelUtils.paramXml.tmplWorkbookXML, ctx);
        ExcelUtils.downExcel(fileName, workbookXML);
    } catch (e) {
        ExcelUtils.exceptionCall(e);
        ExcelUtils.end();
    }

    return ExcelUtils;
};

/**
 * 默认异常回调执行函数
 */
ExcelUtils.defaultExceptionCall = function (e) {
    console.log("导出Excel出现异常:" + e);
};

/**
 * 默认的异常回调函数
 * @type {ExcelUtils.defaultExceptionCall|(function(*): void)}
 */
ExcelUtils.executeExceptionCall = ExcelUtils.defaultExceptionCall;

/**
 * 异常回调函数
 */
ExcelUtils.exceptionCall = function (e) {
    ExcelUtils.executeExceptionCall(e);
};

/**
 * 设置自定义异常回调函数
 */
ExcelUtils.setExceptionCall = function (fn) {
    ExcelUtils.executeExceptionCall = fn;
    return ExcelUtils;
};

/**
 * 默认的导出前执行的任务
 */
ExcelUtils.defaultBeforeExecute = function () {
    console.log("Excel开始导出......");
};
/**
 * 执行前执行函数
 */
ExcelUtils.beforeExecute = ExcelUtils.defaultBeforeExecute;

/**
 * 默认的导出完成后执行的任务
 */
ExcelUtils.defaultAfterExecute = function () {
    console.log("Excel导出结束......");
};
/**
 * 导出后执行函数
 * @param fn
 */
ExcelUtils.afterExecute = ExcelUtils.defaultAfterExecute;

/**
 * 导出前必须限制性此函数
 * 开始预执行函数
 * @returns {ExcelUtils}
 */
ExcelUtils.start = function () {
    ExcelUtils.beforeExecute();
    return ExcelUtils;
};

/**
 * 最后必须执行此函数
 * 结束预执行函数
 */
ExcelUtils.end = function () {
    ExcelUtils.afterExecute();
    //清除缓存数据
    ExcelUtils.clear();
};

/**
 * 设置导出前需要执行的任务
 * @param fn
 * @returns {ExcelUtils}
 */
ExcelUtils.setBeforeExecute = function (fn) {
    ExcelUtils.beforeExecute = fn;
    return ExcelUtils;
};

/**
 * 设置导出完成后需要执行的任务
 * @param fn
 * @returns {ExcelUtils}
 */
ExcelUtils.setAfterExecute = function (fn) {
    ExcelUtils.afterExecute = fn;
    return ExcelUtils;
};

thread.js

 
var threadCount=0;
function Runnable(task){
    this.run=task;
};
 
function Thread(runnable){
    var sleep=0;
    var name="Thread-"+(threadCount++);
    var target=runnable;
    //私有变量,默认异常处理
    var uncaughtExceptionHanlder=function(e){
        console.log(e.message);
    };
    this.getName=function(){
        return name;
    };
    this.setName=function(threadName){
        name=threadName;
    };
    this.start=function(){
        setTimeout(this.run,sleep);
    };
    this.setSleep=function(value){
        sleep=value;
    };
    this.run = function () {
        if (target != null) {
            try {
                threadSleep(sleep);
                target.run();
            } catch (e) {
                uncaughtExceptionHanlder(e);
            }
            return;
        }
        console.log('Exception:{线程名称:"' + name + '",错误定位:"thread.js",错误信息:"target对象为null,没有可执行的任务."}');
    };
    this.setUncaughtExceptionHanlder=function(exception){
        uncaughtExceptionHanlder=exception;
    }
};
 
//线程暂停
function threadSleep(millions) {
  for (var t = Date.now(); Date.now() - t <= millions;);
};
 
//继承
Thread.prototype=new Runnable();

test.html

<!DOCTYPE html>
<html lang="en">

<head>
       
    <meta charset="UTF-8">
    <title></title>
    <script src="./ExcelUtils.js"></script>
    <script src="./thread.js"></script>
    <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js">
</script>
</head>

<body>
    <label for="count">输入导出的记录数</label>
    <input id="count" />万
    <button id="export">导出</button>
    
<script>
           
  function btnClickHandle(){
        /**
   * addTableHead     text: 单元格内容 colspan: 该单元格合并几列
   * addSheet   第一个参数是sheet名称,
   * 第二个必须传一个与所要列数一直的对象函数,
   * 第三个参数,列名数组,
   * 第四个参数数据数组(数组顺序就是数据展示顺序)
   * 可链式添加
   */
  function UserPoint() {
    this.x = null;
    this.y = null;
    this.z = null;
    this.o = null;
  }
  function UserPoint2() {
    this.x = null;
    this.y = null;
    this.z = null;
  }

  let headArr1 = [{
    text: "设备名称:波音",
    colspan: 4
  }, {
    text: '时间:2222-22-22',
    colspan: 4
  }, {
    text: '数据类型:xxx',
    colspan: 4
  }];
  let headArr2 = [{
    text: "设备名称:宇宙飞船",
    colspan: 3
  }, {
    text: '时间:2222-22-22',
    colspan: 3
  }, {
    text: '数据类型:xxx',
    colspan: 3
  }];

 const count = document.querySelector('#count').value;
 let dataArr=[...new Array(count*10000).keys()]
  let runnable = new Runnable(() => {
    ExcelUtils
    .setBeforeExecute(() => {
      console.log("我开始执行了")
    })
    .setExceptionCall((e) => {
        console.log(e)
    })
    .setAfterExecute(() => {
      console.log("我结束执行了")
    })
    .start()
    .addTableHead(headArr1)
    .addSheet("波音数据", UserPoint, ['舱门老化', '羽翼新旧', '机油', '船重'],
        dataArr, dataArr, dataArr, dataArr)
    .addTableHead(headArr2)
    .addSheet("宇宙飞船数据", UserPoint2, ['出门', '飞行', '速度'],
        dataArr, dataArr, dataArr)
    .export("波音和宇宙飞船数据")
    .end();
  })
  
  let thread = new Thread(runnable);
  thread.start();
  }
     
   //绑定导出按钮     
    function bindEvent(){
    const exportBtn = document.querySelector('#export');
        exportBtn.addEventListener('click', btnClickHandle, false);
    }

    
    const init = () =>{
        bindEvent();
    };
        
    init();
    </script>
</body>       

</html>   

界面

导出 

 内容

 

 

标签:function,分页,ExcelUtils,tr,javascript,excel,var,table,td
来源: https://blog.csdn.net/sunboylife/article/details/120212032