欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

使用js将表格数据导出成excel文件(xlsx,xlsx-style)

程序员文章站 2024-03-20 21:23:04
...

1.纯文本格式导出,不设置表格样式

直接使用插件xlsx生成表格的数据流,再使用file-saver实现excel文件的导出功能,安装步骤如下:
运行命令:npm install --save xlsx file-saver

import FileSaver from 'file-saver';
import 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.带有样式的excel文件导出

使用xlsx-style生成数据流,执行下载,安装步骤如下:
运行命令:npm install xlsx-style --save
如果引入发生报错:cpexcel.js找不到:
1.在\node_modules\xlsx-style\dist\cpexcel.js 的
2.将 var cpt = require(’./cpt’ + ‘able’);
3.改为 var cpt = cptable;

/* eslint-disable */
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,
  }));

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

  // 处理表头
  const _headers = headers
    .map((item, i) =>
      Object.assign({}, {
        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,
        },
      }), {}
    );

  // 处理数据源
  // 此处如果数据量过大,将可能导致浏览器卡死,所以做了一个分布处理
  // max 设置每次循环的最大次数
  var _data = {};
  var max = 200;
  var count = Math.ceil(datasource.length / max);
  for (var ic = 0; ic < count; ic++) {
    var tmp = datasource
      .slice(ic * max, (ic + 1) * max)
      .map((item, i) =>
        headers.map((col, j) =>
          Object.assign({}, {
            content: item[col.dataIndex] !== undefined ? item[col.dataIndex] : '',
            position: String.fromCharCode(65 + j) +
              String(Number(options.length + i + 2 + ic * max)),
          })
        )
      )
      .reduce((prev, next) => prev.concat(next))
      .reduce(
        (prev, next) =>
        Object.assign({}, prev, {
          [next.position]: {
            v: next.content,
          },
        }), {}
      );
    Object.assign(_data, tmp);
  }

  const output = Object.assign({}, _options, _headers, _data);
  const outputPos = Object.keys(output); // 设置表格渲染区域,如从A1到C8
  // 设置单元格样式,注意,需要循环遍历
  for (let col in output) {
    output[col].s = {
      alignment: {
        wrapText: true,
      },
    };
    if (output[col].key) {
      output[col].s = {
        alignment: {
          wrapText: true,
        },
        fill: {
          fgColor: {
            rgb: '409eff'
          },
        },
        font: {
          color: {
            rgb: 'ffffff'
          },
        },
      };
    }
  }

  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;
相关标签: js excel