原生js实现导出功能

1、使用 npm install exceljs ,npm install file-saver获取导出插件,然后找到下图中两个文件放到项目中,使用npm产生的其他无用文件可删除

2、创建【tableExcel.js】文件

   /**
     * 导出数据到Excel方法
     * @param {Array[Object]} config.data 表格数据
     * @param {Array[String]} config.fields 字段列表
     * @param {Array[String]} config.headers excel表头列表[[]],可以是多级表头[['A1','B1'],['A2','B2']]
     * @param {Array[Object]} config.merges 需要合并的单元格,需要考虑表头的行数[{row:1, col:1, rowspan: 1, colspan: 2}]
     * @param {Array[Object]} config.attrs 单元格样式配置
     * @param {Array[Object]} config.views 工作表视图配置
     * @param {Array[Number]} columnsWidth 每个字段列对应的宽度
     * @param {Object} config.protect 工作表保护【此配置会保护全表,一般推荐只针对单元格进行保护配置】
     * @param {String} sheetName 工作表名称,默认从sheet1开始
     * @param {String} fileName excel文件名称
     */
 function exportDataToExcel(config, fileName) {
       
    if (!config) return;
    const options = {
        fileName: fileName || `导出excel文件【${Date.now()}】.xlsx`,
        worksheets: []
    }
    if(!Array.isArray(config)) {
        config = [config]
    }
    config.forEach((item) => {
        // 深拷贝data【JSON.stringify有缺陷,可自行换成_.cloneDeep】
        const data = JSON.parse(JSON.stringify(item.data));
       
        const results = data.map(obj => {
           return item.fields.map(key => {
                return obj[key]
            })
        })
        // 生成完整excel数据
        let excelData = [];
        excelData = excelData.concat(item.headers).concat(results);
        // 单元格合并处理【excel数据的第一行/列是从1开始】
        let excelMerges = [];
        excelMerges = item.merges.map(m => {
            return [m.row + 1, m.col + 1, m.row + m.rowspan, m.col + m.colspan]
        })
        // 单元格配置处理 excel数据的第一行/列是从1开始】
        let excelAttrs = [];
        excelAttrs = item.attrs.map(attr => {
            attr.rowStart += 1;
            attr.rowEnd += 1;
            attr.colStart += 1;
            attr.colEnd += 1;
            return attr
        })
        options.worksheets.push({
            data: excelData,
            merges: excelMerges,
            attrs: excelAttrs,
            views: item.views,
            columnsWidth: item.columnsWidth,
            protect: item.protect,
            sheetName: item.sheetName
        })
    })
    createExcel(options)
  }

   // 创建Excel文件方法
   async  function createExcel(options) {
       if (!options.worksheets.length) return;
        // 创建工作簿
        const workbook = new ExcelJS.Workbook();
        for (let i = 0; i < options.worksheets.length; i++) {
            const sheetOption = options.worksheets[i];
            // 创建工作表
            const sheet = workbook.addWorksheet(sheetOption.sheetName || 'sheet' + (i + 1));
            // 添加数据行
            sheet.addRows(sheetOption.data);
            // 配置视图
            sheet.views = sheetOption.views;
            // 单元格合并处理【开始行,开始列,结束行,结束列】
            if (sheetOption.merges){
            sheetOption.merges.forEach((item) => {
                sheet.mergeCells(item) 
            });
            }
            // 工作表保 
            if (sheetOption.protect) {
            const res = await sheet.protect(sheetOption.protect.password, sheetOption.protect.options);
            }
            // 单元格样式处理
            if (sheetOption.attrs.length) {
            sheetOption.attrs.forEach((item) => {
                const attr = item.attr || {};
                // 获取开始行-结束行; 开始列-结束列
                const rowStart = item.rowStart;
                const rowEnd = item.rowEnd;
                const colStart = item.colStart;
                const colEnd = item.colEnd;
            if (rowStart) { // 设置行
            for (let r = rowStart; r <= rowEnd; r++) {
                    // 获取当前行
                    const row = sheet.getRow(r);
                    if (colStart) { // 列设置
                    for (let c = colStart; c <= colEnd; c++) {
                        // 获取当前单元格
                        const cell = row.getCell(c);
                        Object.keys(attr).forEach((key) => {
                        // 给当前单元格设置定义的样式
                        cell[key] = attr[key];
                        });
                    }
                    } else {
                    // 未设置列,整行设置【大纲级别】
                    Object.keys(attr).forEach((key) => {
                        row[key] = attr[key];
                    });
                    }
                }
                } else if (colStart) { // 未设置行,只设置了列
                for (let c = colStart; c <= colEnd; c++) {
                    // 获取当前列,整列设置【大纲级别】
                    const column = sheet.getColumn(c);
                    Object.keys(attr).forEach((key) => {
                    column[key] = attr[key];
                    });
                }
                } else {
                // 没有设置具体的行列,则为整表设置
                Object.keys(attr).forEach((key) => {
                    sheet[key] = attr[key];
                });
                }
            })
        }
        // 列宽设置
        if (sheetOption.columnsWidth) {
            for (let i = 0; i < sheet.columns.length; i++) {
                sheet.columns[i].width = sheetOption.columnsWidth[i]
            }
        }
    }
    
    // 生成excel文件
    workbook.xlsx.writeBuffer().then(buffer => {
        // application/octet-stream 二进制数据
        saveAs(new Blob([buffer], { type: 'application/octet-stream' }), options.fileName)
    })
} 

3、引用至项目中使用

<!DOCTYPE html>
<html lang="en">
<head>
    //引入我们放入项目的文件的的压缩js文件
    <script src="/js/exceljs/dist/exceljs.min.js"></script>
    <script src="/js/file-saver/dist/FileSaver.min.js"></script>
    //引入封装导出Excel方法
    <script src="/js/tableExcel.js"></script>
</head>
<body>
<script>
//表格数据
 var exportTableData = [
    {name:'张三',sexy:'男',age:22,hobby:'篮球',provices:'河南省',city:'郑州',status:'未婚'},
    {name:'李四',sexy:'女',age:23,hobby:'排球',provices:'北京市',city:'北京市',status:'未婚'},
    {name:'王二',sexy:'男',age:28,hobby:'足球',provices:'山东省',city:'青岛',status:'已婚'},
  ]
  
  //点击导出按钮事件
 function onExport() {
      let config = exportConfig();
      exportDataToExcel(config, "人员信息表.xlsx");
  }
  
  //导出表格配置
 function  exportConfig(){
      //配置表头header1为一级表头,header2为二级表头,被合并的单元格为空写占位符"":
      //这是实现导出多级表头的关键两点的中的第一点,分几层表头写几个header;
      const header1 = ["姓名", "个人信息","","","居住城市","","婚姻状况"];
      const header2 = ["","性别","年龄","爱好","省份","城市",""];
      //表格展示字段,顺序要正确
      const fields =  ["name", "sexy","age","hobby","provices","city","status"]
      //这是实现导出多级表头的关键两点的中的第二点,合并表头单元格;
      //row:代表行,col:代表列,rowspan:代表合并行数,colspan:代表合并列数,
      //也就是表头有几个要合并单元格的属性,merges数组属性就有几个;
      const merges = [
            //导出表格的第一行第一列,行合并2个单元格,列就用自己的一个;
            {row: 0, col: 0, rowspan: 2, colspan: 1},
            //导出表格的第一行第二列,行合并1个单元格,列合并3个单元格;
            {row: 0, col: 1, rowspan: 1, colspan: 3},
            {row: 0, col: 4, rowspan: 1, colspan: 2},
            {row: 0, col: 6, rowspan: 2, colspan: 1},
         ]
        // 如果导出前要处理数据,需要深克隆一份表格数据,然后进行处理
        exportTableData = JSON.parse(JSON.stringify(exportTableData));
        const config = {
                data: exportTableData,//exportTableData为表格数据,为空的话,导出表格只显示表头
                fields:fields,
                headers: [header1, header2],
                merges: merges,
                attrs: [],
                view: [],
                columnsWidth: [20, 20, 20, 20, 20,20, 20,],//每行列的宽
                // protect: {},
                sheetName: "个人信息"
         };
         // 设置全表单元格边框,居中布局
         config.attrs.push({
                rowStart: 0,
                rowEnd: config.data.length + 1,//表格表头多几层,就加几个
                colStart: 0,
                colEnd: config.fields.length - 1,
                attr: {
                    alignment: { vertical: "middle", horizontal: "center" },
                    border: {
                        top: { style: "thin" },
                        left: { style: "thin" },
                        bottom: { style: "thin" },
                        right: { style: "thin" }
                    }
                }
           });
           // 设置表头填充颜色,字体加粗
            config.attrs.push({
                rowStart: 0,
                rowEnd: 1,//表格表头多几层,就写几
                colStart: 0,
                colEnd: config.fields.length - 1,
                attr: {
                    fill: {
                        type: "pattern",
                        pattern: "solid",
                        fgColor: { argb: "c5c8ce" }
                    },
                    font: {
                        bold: true
                    }
                }
            });
           return config;
 }
</script>
</body
</html>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值