前端vue前端导出excel,导出xlsx表格。xlsx,xlsx-style给标题内容添加样式_导出excel

1,081 阅读3分钟

纯前端导出xlsx表格,使用xlsx.js可以直接导出xlsx表格,但是不能给表格添加样式,如果需要添加样式我们可以使用xlsx-style来给表格添加。

先来看一下简单的导出效果:

1683165561103.jpg

不加样式我们可以获取table的dom直接导出;

安装

npm install file-saver --save
npm install xlsx --save
// 如不需要给表格添加样式,则不引入以下
npm install xlsx-style --save 

npm i xlsx-style安装完在使用的时候会报错

解决方法:
方法1:
找到\node_modules\xlsx-style\dist\cpexcel.js
把var cpt = require(‘./cpt’ + ‘able’); 改为 var cpt = cptable;

方法2(推荐):
chainWebpack: config => {
config.externals({ ‘./cptable’: ‘var cptable’});
}

代码

一,不加样式导出

1.创建 xlsx.js

import FileSaver from 'file-saver';
import * as XLSX from 'xlsx'

function downloadExcel(id, fileName) {
  /* 从表生成工作簿对象 */
  var wb = XLSX.utils.table_to_book(document.querySelector(`#${id}`));

  /* 获取二进制字符串作为输出 */
  var wbout = XLSX.write(wb, {
    bookType: 'xlsx',
    bookSST: true,
    type: 'array',
  });

  try {
    FileSaver.saveAs(
      //Blob 对象表示一个不可变、原始数据的类文件对象。
      //Blob 表示的不一定是JavaScript原生格式的数据。
      //File 接口基于Blob,继承了 blob 的功能并将其扩展使其支持用户系统上的文件。
      //返回一个新创建的 Blob 对象,其内容由参数中给定的数组串联组成。
      new Blob([wbout], {
        type: 'application/octet-stream',
      }),
      //设置导出文件名称
      fileName
    );
  } catch (e) {
    if (typeof console !== 'undefined') console.log(e, wbout);
  }
  return wbout;
}

export default downloadExcel;

2.使用


<template>
    <div>
        <el-button  @click="exportFile">默认按钮</el-button>
        <el-table id="tableId" :data="tableData" style="width: 100%">
            <el-table-column prop="date" label="日期" width="180" />
            <el-table-column prop="name" label="姓名" width="180" />
            <el-table-column prop="address" label="地址" />
        </el-table>
    </div>
</template>
<script>
import downloadExcel from "@/plugins/xlsx.js"; // 第一步创建的xlsx.js 引入
export default {
    data() {
        return {
            tableData: [
                { date: '2016-05-02', name: '王小虎', address: '上海市普陀区金沙江路 1518 弄' }, 
                { date: '2016-05-04', name: '王小虎', address: '上海市普陀区金沙江路 1517 弄' }, 
                { date: '2016-05-01', name: '王小虎', address: '上海市普陀区金沙江路 1519 弄' }, 
                { date: '2016-05-03', name: '王小虎', address: '上海市普陀区金沙江路 1516 弄' }
            ]
        }
    },
    methods: {
        exportFile() {
            downloadExcel("tableId", "名称.xls");
        }
    }
}
</script>

二,加样式导出

1.创建 xlsxStyle.js

// import XLSX from 'xlsx';
import XLSX from "xlsx-style";

/**
 * 定制化导出excel(定制化:附加标题&&样式)
 * @param { 表头 } headers
 * @param { 数据源  } datasource
 * @param { 表格副标题 } options
 * @param { 配置文件类型 } type
 * @param { 导出的文件名 } fileName
 */
function exportExcel(headers, datasource, options, type, fileName = "未命名") {
  // 处理列宽
  const cloWidth = headers.map((item) => ({ wpx: item.width || 60 }));

  // 处理附加表头 这里处理副标题样式 s
  const _options = options
    .map((item, i) =>
      Object.assign(
        {},
        {
          s: item.s,
          title: item.title,
          position: String.fromCharCode(65) + (i + 1),
        }
      )
    )
    .reduce(
      (prev, next) =>
        Object.assign({}, prev, {
          [next.position]: { v: next.title, s: next.s },
        }),
      {}
    );

  // 处理表头 这里修改表头样式 s
  const _headers = headers
    .map((item, i) =>
      Object.assign(
        {},
        {
          s: item.s,
          ss: item.ss,
          key: item.dataIndex,
          title: item.title,
          position: String.fromCharCode(65 + i) + (options.length + 1),
        }
      )
    )
    .reduce(
      (prev, next) =>
        Object.assign({}, prev, {
          [next.position]: {
            v: next.title,
            key: next.key,
            s: next.s,
            ss: next.ss,
          },
        }),
      {}
    );

  // 处理数据源 这里修改内容样式 ss
  const _data = datasource
    .map((item, i) =>
      headers.map((col, j) =>
        Object.assign(
          {},
          {
            ss: col.ss,
            content: item[col.dataIndex],
            position: String.fromCharCode(65 + j) + (options.length + i + 2),
          }
        )
      )
    )
    .reduce((prev, next) => prev.concat(next))
    .reduce(
      (prev, next) =>
        Object.assign({}, prev, {
          [next.position]: { v: next.content, s: next.ss },
        }),
      {}
    );

  const output = Object.assign({}, _options, _headers, _data);
  const outputPos = Object.keys(output); // 设置表格渲染区域,如从A1到C8

  // 合并单元格
  const merges = options.map((item, i) => ({
    s: { c: 0, r: i },
    e: { c: headers.length - 1, r: i },
  }));

  const wb = {
    SheetNames: ["mySheet"], // 保存的表标题
    Sheets: {
      mySheet: Object.assign(
        {},
        output, // 导出的内容
        {
          "!ref": `${outputPos[0]}:${outputPos[outputPos.length - 1]}`, // 设置填充区域(表格渲染区域)
          "!cols": [...cloWidth],
          "!merges": [...merges],
        }
      ),
    },
  };

  // 这种导出方法只适用于js-xlsx,且设置的单元格样式不生效,
  // 直接打开下面这两行就行了,后面的可以省略
  // XLSX.writeFile(wb,`${fileName}.xlsx`);
  // return;

  /**
   * 以下这种导出方法对于js-xlsx/xlsx-style都适用
   * 区别在于import XLSX from 'xlsx-style';可以设置单元格样式
   * import XLSX from 'xlsx';不支持设置单元格样式
   *
   * new Blob转换成二进制类型的对象
   */
  const tmpDown = new Blob(
    [
      s2ab(
        XLSX.write(
          wb,
          {
            bookType: type == undefined ? "xlsx" : type.bookType,
            bookSST: false,
            type: "binary",
          } // 这里的数据是用来定义导出的格式类型
        )
      ),
    ],
    {
      type: "",
    }
  );
  // 数据都准备完成,可以开始下载excel了
  downExcel(
    tmpDown,
    `${fileName + "."}${type.bookType == "biff2" ? "xls" : type.bookType}`
  );
}

/**
 * <a>标签下载excel
 * @param { Blob对象:二进制的数据 } obj
 * @param { 文件名+文件类型后缀 } fileName
 */
function downExcel(obj, fileName) {
  const a_node = document.createElement("a");
  a_node.download = fileName;

  // 兼容ie
  if ("msSaveOrOpenBlob" in navigator) {
    window.navigator.msSaveOrOpenBlob(obj, fileName);
  } else {
    // URL.createObjectURL根据传入的参数创建一个指向该参数对象的URL. 这个URL的生命仅存在于它被创建的这个文档里.
    // 新的对象URL指向执行的File对象或者是Blob对象.
    a_node.href = URL.createObjectURL(obj);
  }
  a_node.click();

  // 每次调用createObjectURL的时候,一个新的URL对象就被创建了.即使你已经为同一个文件创建过一个URL.
  // 如果你不再需要这个对象,要释放它,需要使用URL.revokeObjectURL()方法.
  //  当页面被关闭,浏览器会自动释放它,但是为了最佳性能和内存使用,当确保不再用得到它的时候,就应该释放它.
  setTimeout(() => {
    URL.revokeObjectURL(obj);
  }, 100);
}

// 字符串转字符流---转化为二进制的数据流
function s2ab(s) {
  if (typeof ArrayBuffer !== "undefined") {
    const buf = new ArrayBuffer(s.length);
    const view = new Uint8Array(buf);
    for (let i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xff;
    return buf;
  } else {
    const buf = new Array(s.length);
    for (let i = 0; i != s.length; ++i) buf[i] = s.charCodeAt(i) & 0xff;
    return buf;
  }
}

export default exportExcel;

2.使用


<script>
export default {
    methods: {
        exportList() {
          // table 标题
          const headers = [
            {
              title: "学员名字",
              dataIndex: "name",
              width: 140,
              // 标题样式
              s: {
                font: { sz: 14, bold: true, vertAlign: true },
                alignment: { vertical: "center", horizontal: "center" },
                fill: { bgColor: { rgb: "E8E8E8" }, fgColor: { rgb: "E8E8E8" } },
              },
              // 标题对应列样式
              ss: {
                font: { sz: 12,  vertAlign: true },
                alignment: { vertical: "center", horizontal: "center" },
              },
            },
            { title: "联系方式", dataIndex: "phone", width: 140 },
            { title: "状态", dataIndex: "status", width: 140 },
            { title: "扣除课时", dataIndex: "deduct", width: 100 },
            { title: "已完成/总课时", dataIndex: "number", width: 100 },
          ];
          // table List
          const datasource = [
            {
              name: "张三",
              phone: "12345678909",
              status: "已签到",
              deduct: 1,
              number: "2/10",
            },
            {
              name: "19876543210",
              phone: "12345678909",
              status: "未签到",
              deduct: 1,
              number: "1/10",
            },
          ];
          // table 副标题标题
          const options = [
            {
              title: "一班s样式",
              s: {
                font: { sz: 14, bold: true, vertAlign: true },
                alignment: { vertical: "center", horizontal: "center" },
                fill: { bgColor: { rgb: "E8E8E8" }, fgColor: { rgb: "E8E8E8" } },
              },
            },
            { title: "班级:二班" },
            { title: "上课时间: 2020-11-11 14:30~16:30" },
            { title: "上课老师:胡sir" },
          ];
          // type 可参考xlsx-style文档
          const type = {
            bookType: "xlsx",
            bookSST: true,
            type: "binary",
            cellStyles: true,
          };
          exportExcel(headers, datasource, options, type, "考勤表");
        }
    };
};
</script>

s 属性可参考 xlsx-style.js 文档

三,使用 xlsx-js-style 导出

sheetjs

但是这个库基础版本无法实现带样式导出,可以使用基于sheetjs扩展的开源项目xlsx-js-style实现。

在导出文件的使用上与sheetjs无异,数据数组和数据表的生成都是沿用SheetJs的核心API,在导出文件前可以对数据表worksheet进行单元格级别的设置,以下提供一个简单的使用示例,具体可设置的样式可点击上线的xlsx-js-style官网查看。

安装

使用 xlsx-js-style 导出
npm install xlsx-js-style --save 

全局引入,main.js加入如下代码:

import XLSX from 'xlsx-js-style'
Vue.prototype.$xlsx = XLSX

这样后面就可以直接this.$xlsx使用

示例文件

<template>
  <div>
    <input type="button" value="点击生成excel" @click="test">
  </div>
</template>

<script>
export default {
  data() {
    return {};
  },
  methods: {
    test() {
      // excel的表数据,根据实际情况生成
      let excelData = [["指标", "2021年", "2020年", "2019年", "2018年", "2017年", "2016年", "2015年", "2014年", "2013年", "2012年"], ["国民总收入(亿元)", "1133239.8", "1005451.3", "983751.2", "915243.5", "830945.7", "742694.1", "685571.2", "644380.2", "588141.2", "537329.0"], ["国内生产总值(亿元)", "1143669.7", "1013567.0", "986515.2", "919281.1", "832035.9", "746395.1", "688858.2", "643563.1", "592963.2", "538580.0"], ["第一产业增加值(亿元)", "83085.5", "78030.9", "70473.6", "64745.2", "62099.5", "60139.2", "57774.6", "55626.3", "53028.1", "49084.6"], ["第二产业增加值(亿元)", "450904.5", "383562.4", "380670.6", "364835.2", "331580.5", "295427.8", "281338.9", "277282.8", "261951.6", "244639.1"], ["第三产业增加值(亿元)", "609679.7", "551973.7", "535371.0", "489700.8", "438355.9", "390828.1", "349744.7", "310654.0", "277983.5", "244856.2"], ["人均国内生产总值(元)", "80976", "71828", "70078", "65534", "59592", "53783", "49922", "46912", "43497", "39771"], ["指标", "2021年", "2020年", "2019年", "2018年", "2017年", "2016年", "2015年", "2014年", "2013年", "2012年"]]; // excel表数据

      let workbook = this.$xlsx.utils.book_new(); // 工作簿
      let worksheet = this.$xlsx.utils.aoa_to_sheet(excelData); // 数据表

      let cols = []; // 设置每列的宽度
      // wpx 字段表示以像素为单位,wch 字段表示以字符为单位
      for (let i = 0; i <= excelData[0].length; i++) {
        let col = {};
        if (i == 0) {
          col.wch = 30;
        } else {
          col.wch = 18;
        }
        cols.push(col)
      }
      worksheet['!cols'] = cols; // 设置列宽信息到工作表

      //以下是样式设置,样式设置放在组织完数据之后,xlsx-js-style的核心API就是SheetJS的
      Object.keys(worksheet).forEach(key => {
        // 非!开头的属性都是单元格
        if (!key.startsWith("!")) {
          worksheet[key].s = {
            font: {
              sz: "12"
            },
            alignment: {
              horizontal: "left",
              vertical: "center",
              wrapText: true
            },
            border: {
              top: { style: 'thin' },
              right: { style: 'thin' },
              bottom: { style: 'thin' },
              left: { style: 'thin' }
            }
          };
        }
      })
      this.$xlsx.utils.book_append_sheet(workbook, worksheet, "Sheet1");
      this.$xlsx.writeFile(workbook, "国内生产总值.xlsx");
    },
  },
};
</script>

导出效果:

1683170065076.jpg

参考文献:
js-xlsx/xlsx-style 纯前端数据导出Excel且支持自定义样式
使用xlsx-js-style实现sheetjs的导出样式设置