利用ExcelJS实现excel的前端导出功能(Vue3+TS)

打印 上一主题 下一主题

主题 877|帖子 877|积分 2631

ExcelJS:读取,操作并写入电子表格数据和样式到 XLSX 和 JSON 文件。一个 Excel 电子表格文件逆向工程项目。
github中文文档:https://github.com/exceljs/exceljs/blob/master/README_zh.md
   封装excel.ts工具文件
  1. import ExcelJS from 'exceljs'
  2. // 导入参数数据类型
  3. export interface importExcelType {
  4.   // 第 i 张工作表
  5.   i?: number;
  6.   // 表格表头字段数组
  7.   header: readonly any[];
  8. }
  9. // 导出参数数据类型
  10. export interface exportExcelType {
  11.   // 数据
  12.   data: { [key: string]: any }[];
  13.   // 文件名
  14.   name: string;
  15.   // 表头字段
  16.   header: string[];
  17.   // 表头字段对应中文
  18.   customHeader: string[];
  19.   // 工作表名
  20.   sheetName?: string;
  21.   // 标题
  22.   title?: string;
  23.   // 小标题
  24.   subTitle?: string;
  25.   // 工作表保护密码
  26.   password?: string;
  27.   // 对齐方式
  28.   alignment?: Partial<ExcelJS.Alignment>;
  29.   // 合并单元格
  30.   mergeList?: mergeListType[];
  31.   // 标题样式
  32.   titleStyle?: Partial<ExcelJS.Row>;
  33.   // 小标题样式
  34.   subTitleStyle?: Partial<ExcelJS.Row>;
  35.   // 表头样式
  36.   headerStyle?: Partial<ExcelJS.Row>;
  37.   // 单元格统一样式
  38.   cellStyle?: Partial<ExcelJS.Row & ExcelJS.Column>;
  39. }
  40. // 合并单元格数据类型
  41. export interface mergeListType {
  42.   startRow: number;
  43.   startColumn: number;
  44.   endRow: number;
  45.   endColumn: number;
  46. }
  47. // exceljs相关方法
  48. export class Excel {
  49.   blob?: Blob; // 导入的blob文件
  50.   worksheet?: ExcelJS.Worksheet;  // 当前工作表
  51.   header: string[]; // 表头字段数组
  52.   constructor(blob?: Blob) {
  53.     this.blob = blob;
  54.     this.worksheet = undefined;
  55.     this.header = [];
  56.   }
  57.    // 导出excel,参数信息参考exceljs
  58.    // data 数据
  59.    // name 文件名
  60.    // header 表头字段
  61.    // customHeader 表头字段对应中文
  62.    // sheetName 工作表名
  63.    // title 标题
  64.    // subTitle 副标题(日期)
  65.    // password 冻结表格密码
  66.    // mergeList 合并单元格数组
  67.    // titleStyle 标题样式(按需补充方法)
  68.    // subTitleStyle 小标题样式(按需补充方法)
  69.    // headerStyle 表头字段样式(按需补充方法)
  70.    // cellStyle 单元格样式(按需补充方法)
  71.   public async exportExcel(options: exportExcelType): Promise<void> {
  72.     const {
  73.         data,
  74.         name,
  75.         header,
  76.         customHeader,
  77.         sheetName = 'sheet1',
  78.         title = '',
  79.         subTitle = '',
  80.         password = '',
  81.         mergeList = [],
  82.         titleStyle,
  83.         subTitleStyle,
  84.         headerStyle,
  85.         cellStyle } = options;
  86.     // 创建工作簿
  87.     const workbook = new ExcelJS.Workbook();
  88.     workbook.creator = '侃侃';
  89.     workbook.created = new Date();
  90.     // 添加sheet
  91.     this.worksheet = workbook.addWorksheet(sheetName, { properties: { tabColor: { argb: 'FF00FF00' } } });
  92.     this.header = header;
  93.     // 表头行序号
  94.     let headerRowId = 1;
  95.     if (!!title) headerRowId++;
  96.     if (!!subTitle) headerRowId++;
  97.     // 插入单元格数据
  98.     this.setCells(data, customHeader, cellStyle);
  99.     // 插入大标题
  100.     this.getTitle(title, titleStyle);
  101.     // 插入小标题
  102.     this.getSubTitle(subTitle, subTitleStyle);
  103.     // 处理表头
  104.     this.setHeaderStyle(headerRowId, data, headerStyle);
  105.     // 更多处理
  106.     this.handleDealExcel(password, mergeList, headerRowId);
  107.     // 导出excel(此处也可用file-saver将blob导出到excel文件  fs.saveAs(blob, name+'.xlsx');)
  108.     workbook.xlsx.writeBuffer().then((data) => {
  109.       let blob = new Blob([data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
  110.       const a = document.createElement('a');
  111.       a.href = URL.createObjectURL(blob);
  112.       a.download = name + '.xlsx';
  113.       document.body.appendChild(a);
  114.       a.click();
  115.       document.body.removeChild(a);
  116.       window.URL.revokeObjectURL(a.href);
  117.     });
  118.   }
  119.    // 合并单元格数组
  120.    // merge1 需要合并的行/列起止对象数组1
  121.    // 例:[{ startRow: 1, endRow: 1},{ startRow: 10, endRow: 10}]
  122.    // merge2 需要合并的列/行起止对象数组2
  123.    // 例:[{ startColumn: 1, endColumn: 2 }]
  124.    // mergeArr 合并后数组
  125.    // 例:[{ startRow: 1, endRow: 1, startColumn: 1, endColumn: 2},{ startRow: 10, endRow: 10, startColumn: 1, endColumn: 2}]
  126.   public merge(merge1: Partial<mergeListType>[], merge2: Partial<mergeListType>[]): mergeListType[] {
  127.     const mergeArr: any[] = [];
  128.     merge1.forEach(item1 => {
  129.       mergeArr.push(...merge2.map(item2 => {
  130.         return { ...item2, ...item1 };
  131.       }))
  132.     })
  133.     return mergeArr;
  134.   }
  135.    // 单元格数据处理
  136.    // data 表格数据
  137.    // customHeader  表头中文字段
  138.   private setCells(data: exportExcelType['data'], customHeader: string[], style?: Partial<ExcelJS.Row & ExcelJS.Column>): void {
  139.     // 设置列,插入中文表头
  140.     const column: Partial<ExcelJS.Column>[] = [];
  141.     this.header.forEach((item, index) => {
  142.       column.push({
  143.         header: customHeader[index],
  144.         key: item,
  145.         width: style?.width || 25,
  146.       })
  147.     })
  148.     this.worksheet!.columns = column;
  149.     // 设置行,添加数据
  150.     this.worksheet?.addRows(data);
  151.     // 设置行高
  152.     this.worksheet?.eachRow({ includeEmpty: true }, (row: any, rowNumber) => {
  153.       let maxHeight = 1
  154.       row.values.forEach((cur: any) => {
  155.         if (cur && typeof cur === 'string') {
  156.           const tmp = cur.split('\r\n') // 多行数据以此来作为分割设置行高
  157.           const tmpLen = tmp.length
  158.           maxHeight = Math.max(tmpLen, maxHeight)
  159.         }
  160.       })
  161.       row.height = style?.height || 20;
  162.     })
  163.     // 获取每一列数据,再依次对齐
  164.     this.worksheet!.columns.forEach(column => {
  165.         column.alignment = style?.alignment || { vertical: 'middle', horizontal: 'center', wrapText: true }
  166.     })
  167.   }
  168.    // 添加大标题
  169.    // title 标题
  170.    // style 标题样式
  171.   private getTitle(title: string, style?: Partial<ExcelJS.Row>): void {
  172.     if (!!title) {
  173.       // 插入标题
  174.       this.worksheet?.spliceRows(1, 0, [title]);
  175.       this.worksheet?.mergeCells(1, 1, 1, this.header.length);
  176.       // 调整标题样式
  177.       const titleRow = this.worksheet!.getRow(1);
  178.       // 高度
  179.       titleRow.height = style?.height || 40;
  180.       // 字体设置
  181.       titleRow.font = style?.font || {
  182.         size: 20,
  183.         bold: true
  184.       }
  185.       // 背景色
  186.       titleRow.fill = style?.fill || {
  187.         bgColor: {
  188.           'argb': 'FFFFFF00'
  189.         },
  190.         type: 'pattern',
  191.         pattern: 'none'
  192.       }
  193.       // 对齐方式
  194.       titleRow.alignment = style?.alignment || {
  195.         horizontal: "center",
  196.         vertical: "middle"
  197.       };
  198.     }
  199.   }
  200.    // 添加小标题
  201.    // subTitle 标题
  202.    // style 小标题样式
  203.   
  204.   private getSubTitle(subTitle: string, style?: Partial<ExcelJS.Row>): void {
  205.     if (!!subTitle) {
  206.       this.worksheet?.spliceRows(2, 0, [subTitle]);
  207.       this.worksheet?.mergeCells(2, 1, 2, this.header.length);
  208.       // 调整标题样式
  209.       const subtitleRow = this.worksheet!.getRow(2);
  210.       // 高度
  211.       subtitleRow.height = style?.height || 20;
  212.       // 字体设置
  213.       subtitleRow.font = style?.font || {
  214.         size: 14,
  215.       }
  216.       // 背景色
  217.       subtitleRow.fill = style?.fill || {
  218.         bgColor: {
  219.           'argb': 'FFFFFF00'
  220.         },
  221.         type: 'pattern',
  222.         pattern: 'none'
  223.       }
  224.       // 对齐方式
  225.       subtitleRow.alignment = style?.alignment || {
  226.         horizontal: "right",
  227.         vertical: "middle"
  228.       };
  229.     }
  230.   }
  231.    // 设置表头样式
  232.    // num 表头在第几行
  233.    // data 总数据
  234.    // style 表头样式
  235.   private setHeaderStyle(num: number, data: any, style?: Partial<ExcelJS.Row>): void {
  236.     // 自动筛选器
  237.     this.worksheet!.autoFilter = {
  238.       from: {
  239.         row: num,
  240.         column: 1
  241.       },
  242.       to: {
  243.         row: data.length,
  244.         column: this.header.length
  245.       }
  246.     }
  247.     // 给表头添加背景色
  248.     let headerRow = this.worksheet!.getRow(num);
  249.     headerRow!.height = style?.height || 30;
  250.     // 通过 cell 设置背景色,更精准
  251.     headerRow?.eachCell((cell) => {
  252.       cell.fill = style?.fill || {
  253.         type: 'pattern',
  254.         pattern: 'solid',
  255.         fgColor: { argb: 'dde0e7' },
  256.       }
  257.       cell.font = style?.font || {
  258.         size: 12
  259.       }
  260.     })
  261.   }
  262.   
  263.    // 其他内容处理
  264.    // password 密码
  265.    // mergeList 合并行列数组
  266.    // headerRowId  表头行序号
  267.   private handleDealExcel(password: string, mergeList: mergeListType[], headerRowId: number) {
  268.     // 添加工作表保护
  269.     if (!!password) {
  270.       this.worksheet?.protect(password, {
  271.         autoFilter: true,
  272.         selectLockedCells: false,
  273.       });
  274.     }
  275.     // 合并单元格
  276.     mergeList.forEach(item => {
  277.       // 行数为表格数据所在行行数+表头行序号headerRowId
  278.       const startRow = item.startRow + headerRowId;
  279.       const endRow = item.endRow + headerRowId;
  280.       this.worksheet?.mergeCells(startRow, item.startColumn, endRow, item.endColumn);
  281.     })
  282.     // 冻结前几行
  283.     this.worksheet!.views = [
  284.       { state: 'frozen', xSplit: 0, ySplit: headerRowId }
  285.     ];
  286.   }
  287. }
复制代码
表格页面调用excel工具文件 
  1. // export.vue
  2. <template>
  3.   <div class="export">
  4.     <button @click="exportExcel">导出</button>
  5.   </div>
  6. </template>
  7. <script setup lang="ts">
  8. import { Excel } from '@/utils/excel';
  9. import { reactive } from 'vue';
  10. const tableData = reactive([
  11.   { id: 1, name: "张三", age: 18 },
  12.   { id: 2, name: "张三", age: 18 }
  13. ])
  14. const header = ['id', 'name', 'age'];
  15. const customHeader = ['序号', '姓名', '年龄'];
  16. const exportExcel = () => {
  17.   const excel = new Excel();
  18.   excel.exportExcel({
  19.     name: '表格文件名称',
  20.     // title: '表格标题',加入这个标题会导致导出的表格再次导入时数据key值获取不正确
  21.     data: tableData,
  22.     header,
  23.     customHeader
  24.   })
  25. }
  26. </script>
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

怀念夏天

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表