xlsx实现excel下载功能——js

js 复制代码
import dayjs from "dayjs";
import * as XLSX from "xlsx";
import XLSXS from "xlsx-style";

export function export2Excel(info: {
  // 文件名(工作簿名)
  title: string;
  // 数据
  data: any[] | Record<string, any>[];
  // 表格列
  header: {
    title: any;
    key: any;
  }[];
}) {
  const { title, data, header } = info;

  // title重命名
  const tableTitle = `${title}_评论数据_${dayjs().format(
    "YYYY-MM-DD_HH_mm_ss"
  )}`;

  // 创建一个工作簿
  const wb = XLSX.utils.book_new();
  // 添加sheet到工作簿中
  addSheet({
    xl: XLSX,
    wb,
    data,
    header,
    tableTitle,
  });

  // 文件存储成blob流数据(二进制流)
  const tmpDown = new Blob([
    s2ab(
      // 为了解决样式丢失问题只能用XLSXD来进行转换
      XLSXS.write(wb, {
        bookType: "xlsx",
        type: "binary",
      })
    ) as any,
  ]);

  // 下载二进制流(title是下载文件的名字)
  downExcel(tmpDown, tableTitle + ".xlsx");
}

/**
 * 添加sheet页
 * @param info
 * wb: 工作簿
 * xl: XSLX库
 * data: 表格数据
 * header: 表格头
 * title: 表格标题
 *
 */
const addSheet = (info: {
  wb: any;
  xl: any;
  data: any[] | Record<string, any>[];
  header: {
    title: any;
    key: any;
  }[];
  tableTitle: string;
}) => {
  const { wb, xl, data, header, tableTitle } = info;
  const keyArr = header.map((item: any) => item.key);
  const nameArr = header.map((item: any) => item.title);

  const excelData = [nameArr];

  data.forEach((item: any) => {
    const dataArr: any = [];
    keyArr.forEach((key: any) => {
      dataArr.push(item[key]);
    });
    excelData.push(dataArr);
  });

  // excelData:第一行是表头其他行是数据

  // xl.utils.aoa_to_sheet: 将js数据转换成excel数据,类型是A1,A2,A3,B1,B2,B3......,并创建sheet
  const tableData = xl.utils.aoa_to_sheet(excelData); // 此处excelData为表格的数据

  // tableData格式:[excel对应的行列:{v:值,s:表格显示配置}]

  // tableData['!cols']:定义导出表格每列的宽度,从左到右依次对应header中从左到右的列
  tableData["!cols"] = [];
  tableData["!rows"] = [{ hpx: 40 }, { hpx: 40 }, { hpx: 40 }];

  // 给导出的excel做样式处理
  header.forEach((col: any, colIndex: any) => {
    // 设置列宽
    switch (col.key) {
      case "commentTime":
        tableData["!cols"].push({ wch: 18 });
        break;
      case "connect":
        tableData["!cols"].push({ wch: 25 });
        break;

      default:
        tableData["!cols"].push({ wch: 15 });
        break;
    }

    // 格式化tableData数据格式,添加样式
    excelData.forEach((row, rowIndex) => {
      // 该数据对应的单元格位置
      const cellPosition = createPosition(colIndex, rowIndex);
      if (tableData[cellPosition]) {
        // 单元格的样式/主题(如果适用)
        tableData[cellPosition].s = {
          // 数据水平垂直居中
          alignment: {
            horizontal: "left",
            vertical: "center",
          },
          fill: {
            // 背景色
            // fgColor: { rgb: "FEE8D9" },
          },
          border: {
            top: { style: "thin", color: { rgb: "ebebeb" } },
            bottom: { style: "thin", color: { rgb: "ebebeb" } },
            left: { style: "thin", color: { rgb: "ebebeb" } },
            right: { style: "thin", color: { rgb: "ebebeb" } },
          },
          // border: {
          //   right: {
          //     style: "dashed",
          //     color: { rgb: "FFFFFF" },
          //   },
          // },
        };
        // 第一行
        if (rowIndex === 0) {
          tableData[cellPosition].s.fill = {
            // 背景色
            // fgColor: { rgb: "fb731a" },
          };
          tableData[cellPosition].s.font = {
            // 字体
            sz: "12",
            // color: { rgb: "FFFFFF" },
            bold: true,
            shadow: true,
          };
        } else if (
          typeof row[colIndex] === "string" &&
          row[colIndex].includes("%")
        ) {
          // 格式化百分号数据为excel中的number类型
          let value = tableData[cellPosition].v;
          value = Number(value.replace("%", ""));
          tableData[cellPosition].v = (value / 100).toFixed(4);
          // 单元格展示类型
          tableData[cellPosition].z = XLSX.SSF._table[10];
          // 单元格数据类型
          tableData[cellPosition].t = "n";
        }
      }
    });
  });

  // 将sheet追加到工作簿, wb:工作簿,tableData:新的sheet,title:sheet名称
  // sheet 名称 不能 超过 31 位,否则 会报错
  xl.utils.book_append_sheet(wb, tableData, tableTitle.substring(0, 31)); // title为自定义的sheet表名
};

// 自定义下载文件方法
/**
 * 使用a标签将文件进行下载
 * @param obj blob流数据
 * @param fileName 文件名
 */
const downExcel = (obj: any, fileName: string) => {
  const a_node = document.createElement("a");
  a_node.download = fileName;
  if ("msSaveOrOpenBlob" in navigator) {
    //  msSaveOrOpenBlob 方法允许用户在客户端上下载文件
    (navigator as any).msSaveOrOpenBlob(obj, fileName);
  } else {
    // createObjectURL根据传入的参数创建一个指向该参数对象的URL.
    a_node.href = URL.createObjectURL(obj);
  }
  a_node.click();
  //
  setTimeout(() => {
    // 释放一个通过URL.createObjectURL()创建的对象URL
    URL.revokeObjectURL(obj);
  }, 2000);
};

/**
 * 文件流转换:将二进制字符串转换成对应的Unicode编码数组
 * @param s 文件的二进制字符串
 * @returns 返回存储文件数据的buffer或数组
 */
const s2ab = (s: any) => {
  if (typeof ArrayBuffer !== "undefined") {
    // buffer存储
    // 在内存中分配s.length个的字节空间
    const buf = new ArrayBuffer(s.length);
    // Uint8Array 数组类型表示一个 8 位无符号整型数组,创建时内容被初始化为 0
    const view = new Uint8Array(buf);
    // 将二进制字符串转换成Unicode编码存储
    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;
  }
};

// 行数 需要 小于 26 * 26
/**
 *
 * @param colIndex 该数据对应列数
 * @param rowIndex 该数据对应的行数
 * @returns 返回的是该数据对应的单单元个位置
 */
const createPosition = (colIndex: any, rowIndex: any) => {
  rowIndex++;
  if (colIndex >= 26) {
    const n1 = Math.floor(colIndex / 26) - 1;
    const n2 = colIndex % 26;
    const s1 = String.fromCharCode(n1 + 65);
    const s2 = String.fromCharCode(n2 + 65);
    return s1 + s2 + rowIndex;
  } else {
    // fromCharCode:将 Unicode 编码转为一个字符 (65对应A)
    return String.fromCharCode(colIndex + 65) + rowIndex;
  }
};
相关推荐
崔庆才丨静觅1 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60612 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了2 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅2 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅3 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅3 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment3 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅3 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊3 小时前
jwt介绍
前端
爱敲代码的小鱼4 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax