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

公司项目需要导出表格为报表,尝试了xlsx、sheetjs以及js-xlsx各种库,但是总有不满足需求的点(比如设置行高列宽、居中、合并单元格、添加背景色等等)或者API太少写起来太繁琐,最后找到ExcelJS能够实现所有需求。

ExcelJS:读取,操作并写入电子表格数据和样式到 XLSX 和 JSON 文件。一个 Excel 电子表格文件逆向工程项目。
github中文文档:https://github.com/exceljs/exceljs/blob/master/README_zh.md

我把用到的exceljs的导入导出功能按照需求封装成一个类,具体可以根据自己的项目需求去调整。

  • 一、定义Excel类

    export class Excel {
      blob?: Blob;                      // 导入的blob文件
      worksheet?: ExcelJS.Worksheet;  // 当前工作表
      header: string[];             // 表头字段数组
      constructor(blob?: Blob) {
        this.blob = blob;
        this.worksheet = undefined;
        this.header = [];
      }
    }
    
  • 二、导入excel方法

      /**
       * @description: blob转ArrayBuffer(用于后续生成文件数据)
       * @return {Promise<ArrayBuffer>} ArrayBuffer
       */
      private readFile(): Promise<ArrayBuffer> {
        return new Promise<ArrayBuffer>((resolve, reject) => {
          let reader = new FileReader();
          if (!this.blob) {
            reject('上传文件异常!');
          } else {
            reader.readAsArrayBuffer(this.blob);
            reader.onload = (ev) => {
              resolve(ev.target!.result as ArrayBuffer);
            };
          }
    
        });
      }
      
       /**
       * @description: 导入excel文件获取workbook(workbook属性方法参考exceljs文档)
       * @return {Promise<ExcelJS.Workbook>} 
       */
      public async getWorkBook(): Promise<ExcelJS.Workbook> {
        let buffer = await this.readFile();
        const workbook = new ExcelJS.Workbook();
        return await workbook.xlsx.load(buffer);
      }
      
        /**
       * @description: 将 excel 第i张工作表的数据转为对象数据
       * @param {number} i 工作表序号
       * @param {string[]} header 表头字段数组
       * @return {Promise<Record<(typeof header)[number], string>[]>} 
       * 传入表头作为字段的对象数组(每个元素对象对应每一行)
       */
      public async importExcel(options: importExcelType): Promise<Record<(typeof header)[number], string>[]> {
        const { i = 1, header } = options;
        const workbook = await this.getWorkBook();
        const worksheet = workbook.getWorksheet(i);
        // excel导入后返回的数组
        const excelList: Record<(typeof header)[number], string>[] = [];
    
        worksheet.getSheetValues().filter(temp => !!temp?.length).forEach(item => {   
          // 移除空行
          // 移除每行首个空元素
          (item as string[]).shift();
          // 定义临时对象存储每一行内容
          let tempObj: Record<(typeof header)[number], string> = {};
          (item as string[]).forEach((item2, index2) => {
            tempObj[header[index2]] = item2;
          })
          excelList.push(tempObj);
        })
        return excelList
      }
    
  • 三、导出excel方法

    /**
       * @description: 导出excel,参数信息参考exceljs
       * @param {*} data 数据
       * @param {*} name 文件名
       * @param {*} header 表头字段
       * @param {*} customHeader 表头字段对应中文
       * @param {*} sheetName 工作表名
       * @param {*} title 标题
       * @param {*} subTitle 副标题(日期)
       * @param {*} password 冻结表格密码
       * @param {*} mergeList 合并单元格数组
       * @param {*} titleStyle 标题样式(按需补充方法)
       * @param {*} subTitleStyle 小标题样式(按需补充方法)
       * @param {*} headerStyle 表头字段样式(按需补充方法)
       * @param {*} cellStyle 单元格样式(按需补充方法)
       * @return {*}
       */
    
      public async exportExcel(options: exportExcelType): Promise<void> {
        const { 
          data, 
          name, 
          header, 
          customHeader, 
          sheetName = 'sheet1', 
          title = '', 
          subTitle = '', 
          password = '', 
          mergeList = [],
          titleStyle,
          subTitleStyle, 
          headerStyle, 
          cellStyle 
      } = options;
        // 创建工作簿
        const workbook = new ExcelJS.Workbook();
        workbook.creator = '侃侃';
        workbook.created = new Date();
        // 添加sheet
        this.worksheet = workbook.addWorksheet(sheetName, { properties: { tabColor: { argb: 'FF00FF00' } } });
        this.header = header;
        // 表头行序号
        let headerRowId = 1;
        if (!!title) headerRowId++;
        if (!!subTitle) headerRowId++;
        // 插入单元格数据
        this.setCells(data, customHeader, cellStyle);
        // 插入大标题
        this.getTitle(title, titleStyle);
        // 插入小标题
        this.getSubTitle(subTitle, subTitleStyle);
        // 处理表头
        this.setHeaderStyle(headerRowId, data, headerStyle);
        // 更多处理
        this.handleDealExcel(password, mergeList, headerRowId);
        // 导出excel(此处也可用file-saver将blob导出到excel文件  fs.saveAs(blob, name+'.xlsx');)
        workbook.xlsx.writeBuffer().then((data) => {
          let blob = new Blob([data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
          const a = document.createElement('a');
          a.href = URL.createObjectURL(blob);
          a.download = name + '.xlsx';
          document.body.appendChild(a);
          a.click();
          document.body.removeChild(a);
          window.URL.revokeObjectURL(a.href);
        });
      }
    
  • 四、导出数据及格式方法汇总(按需补充)

    • 1.插入单元格数据

       /**
         * @description:单元格数据处理
         * @param {any} data 表格数据
         * @param {string} customHeader  表头中文字段
         * @return {*}
         */
        private setCells(data: exportExcelType['data'], customHeader: string[], style?: Partial<ExcelJS.Row & ExcelJS.Column>): void {
          // 设置列,插入中文表头
          const column: Partial<ExcelJS.Column>[] = [];
          this.header.forEach((item, index) => {
            column.push({
              header: customHeader[index],
              key: item,
              width: style?.width || 25,
            })
          })
          this.worksheet!.columns = column;
          // 设置行,添加数据
          this.worksheet?.addRows(data);
          // 设置行高
          this.worksheet?.eachRow({ includeEmpty: true }, (row, rowNumber) => {
            row.height = style?.height || 20;
          })
          // 获取每一列数据,再依次对齐
          this.worksheet!.columns.forEach(column => column.alignment = style?.alignment || { vertical: 'middle', horizontal: 'center', wrapText: true })
        }
      
    • 2.添加大标题

        /**
         * @description: 添加大标题 
         * @param {string} title 标题
         * @param {any} style 标题样式
         * @return {*}
         */
        private getTitle(title: string, style?: Partial<ExcelJS.Row>): void {
          if (!!title) {
            // 插入标题
            this.worksheet?.spliceRows(1, 0, [title]);
            this.worksheet?.mergeCells(1, 1, 1, this.header.length);
            // 调整标题样式
            const titleRow = this.worksheet!.getRow(1);
            // 高度
            titleRow.height = style?.height || 40;
            // 字体设置
            titleRow.font = style?.font || {
              size: 20,
              bold: true
            }
            // 背景色
            titleRow.fill = style?.fill || {
              bgColor: {
                'argb': 'FFFFFF00'
              },
              type: 'pattern',
              pattern: 'none'
            }
            // 对齐方式
            titleRow.alignment = style?.alignment || {
              horizontal: "center",
              vertical: "middle"
            };
          }
        }
      
    • 3.添加小标题

        /**
         * @description: 添加小标题 
         * @param {string} subTitle 标题
         * @param {Partial} style 小标题样式
         * @return {*}
         */
        private getSubTitle(subTitle: string, style?: Partial<ExcelJS.Row>): void {
          if (!!subTitle) {
            this.worksheet?.spliceRows(2, 0, [subTitle]);
            this.worksheet?.mergeCells(2, 1, 2, this.header.length);
            // 调整标题样式
            const subtitleRow = this.worksheet!.getRow(2);
            // 高度
            subtitleRow.height = style?.height || 20;
            // 字体设置
            subtitleRow.font = style?.font || {
              size: 14,
            }
            // 背景色
            subtitleRow.fill = style?.fill || {
              bgColor: {
                'argb': 'FFFFFF00'
              },
              type: 'pattern',
              pattern: 'none'
            }
            // 对齐方式
            subtitleRow.alignment = style?.alignment || {
              horizontal: "right",
              vertical: "middle"
            };
          }
        }
      
    • 4.设置表头样式

        /**
         * @description: 设置表头样式
         * @param {number} num 表头在第几行
         * @param {number} data 总数据
         * @param {number} style 表头样式
         * @return {*}
         */
        private setHeaderStyle(num: number, data: any, style?: Partial<ExcelJS.Row>): void {
          // 自动筛选器
          this.worksheet!.autoFilter = {
            from: {
              row: num,
              column: 1
            },
            to: {
              row: data.length,
              column: this.header.length
            }
          }
          // 给表头添加背景色
          let headerRow = this.worksheet!.getRow(num);
          headerRow!.height = style?.height || 30;
          // 通过 cell 设置背景色,更精准
          headerRow?.eachCell((cell) => {
            cell.fill = style?.fill || {
              type: 'pattern',
              pattern: 'solid',
              fgColor: { argb: 'dde0e7' },
            }
            cell.font = style?.font || {
              size: 12
            }
          })
        }
      
      1. 其他处理
        /**
         * @description: 其他内容处理
         * @param {string} password 密码
         * @param {mergeListType} mergeList 合并行列数组
         * @param {number} headerRowId  表头行序号
         * @return {*}
         */
        private handleDealExcel(password: string, mergeList: mergeListType[], headerRowId: number) {
          // 添加工作表保护
          if (!!password) {
            this.worksheet?.protect(password, {
              autoFilter: true,
              selectLockedCells: false,
            });
          }
          // 合并单元格
          mergeList.forEach(item => {
            // 行数为表格数据所在行行数+表头行序号headerRowId
            const startRow = item.startRow + headerRowId;
            const endRow = item.endRow + headerRowId;
            this.worksheet?.mergeCells(startRow, item.startColumn, endRow, item.endColumn);
          })
          // 冻结前几行
          this.worksheet!.views = [
            { state: 'frozen', xSplit: 0, ySplit: headerRowId }
          ];
        }
      
    • 6.合并单元格结构处理方法

         /**
         * @description: 合并单元格数组
         * @param {Partial} merge1 需要合并的行/列起止对象数组1
         * 例:[{ startRow: 1, endRow: 1},{ startRow: 10, endRow: 10}]
         * @param {Partial} merge2 需要合并的列/行起止对象数组2
         * 例:[{ startColumn: 1, endColumn: 2 }]
         * @return {*} mergeArr 合并后数组
         * 例:[{ startRow: 1, endRow: 1, startColumn: 1, endColumn: 2},{ startRow: 10, endRow: 10, startColumn: 1, endColumn: 2}]
         */
        public merge(merge1: Partial<mergeListType>[], merge2: Partial<mergeListType>[]): mergeListType[] {
          const mergeArr: any[] = [];
          merge1.forEach(item1 => {
            mergeArr.push(...merge2.map(item2 => {
              return { ...item2, ...item1 };
            }))
          })
          return mergeArr;
        }
      
  • 五、具体使用:

    • 1.导入excel

      // import.vue
      <template>
        <div class="import">
          <el-upload
            ref="uploadExcelRef"
            action=""
            drag
            :auto-upload="false"
            :on-change="uploadChange"
            :limit="1"
          >
            <i class="el-icon-upload"></i>
            <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
          </el-upload>
        </div>
      </template>
      
      <script setup lang="ts">
      import { Excel } from '@/utils/excel';
      import { ElMessage } from 'element-plus';
      import { ref } from 'vue';
      // 上传组件ref
      const uploadExcelRef = ref();
      const uploadChange = async (file: any) => {
        // 表头字段数组
        const header = ['id', 'name', 'age'];
        if (file.raw?.type !== 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' 
            && file.raw?.type !== 'application/vnd.ms-excel') {
          ElMessage.error('文件格式错误,请重新上传!')
          // 移除上传的文件
          uploadExcelRef.value!.handleRemove(file);
          return false
        }
        // 对excel文件进行处理
        try {
          const excel = new Excel(file.raw);
          // 导入文件获取数据
          const res = await excel.importExcel({ header });
          console.log(res);
        } catch (err) {
          console.log(err);
        }
      }
      </script>
      

      excel文件

    Snipaste_2022-11-17_18-34-04.png
    • 最终导入生成数据:

       [
           { id: "id", name: "姓名", age: "年龄" },
           { id: 1, name: "张三", age: 18 },
           { id: 2, name: "李四", age: 20 },
           { id: 3, name: "王五", age: 24 },
           { id: 4, name: "赵六", age: 26 },
           { id: 5, name: "刘七", age: 28 }
       ]
      
    • 2.导出excel

      // export.vue
      <template>
        <div class="export">
          <button @click="exportExcel">导出</button>
        </div>
      </template>
      
      <script setup lang="ts">
      import { Excel, type mergeListType } from '@/utils/excel';
      import { reactive } from 'vue';
      const tableData = reactive([
        { id: 1, name: "张三", age: 18 },
        { id: 2, name: "张三", age: 18 },
        { id: 3, name: "王五", age: 24 },
        { id: 4, name: "赵六", age: 24 },
        { id: 5, name: "刘七", age: 24 }
      ])
      const header = ['id', 'name', 'age'];
      const customHeader = ['id', '姓名', '年龄'];
      const mergeList: mergeListType[] = reactive([
          { startRow: 1, endRow: 2, startColumn: 3, endColumn: 3 }, 
          { startRow: 3, endRow: 5, startColumn: 3, endColumn: 3 }
      ])
      const exportExcel = () => {
        const excel = new Excel();
        excel.exportExcel({
          name: '个人信息',
          title: '标题',
          data: tableData,
          header,
          customHeader,
          mergeList
        })
      }
      
      </script>
      

      最终生成的excel:

    Snipaste_2022-11-17_18-55-21.png
  • 六、excel.ts完整代码

    import ExcelJS from 'exceljs'
    
    // 导入参数数据类型
    export interface importExcelType {
      /**
       * 第 i 张工作表
       */
      i?: number;
      /**
       * 表格表头字段数组
       */
      header: readonly any[];
    }
    
    // 导出参数数据类型
    export interface exportExcelType {
      /**
       * 数据
       */
      data: { [key: string]: any }[];
      /**
       * 文件名
       */
      name: string;
      /**
     * 表头字段
     */
      header: string[];
      /**
       * 表头字段对应中文
       */
      customHeader: string[];
      /**
     * 工作表名
     */
      sheetName?: string;
      /**
       * 标题
       */
      title?: string;
      /**
       * 小标题
       */
      subTitle?: string;
      /**
       * 工作表保护密码
       */
      password?: string;
      /**
       * 对齐方式
       */
      alignment?: Partial<ExcelJS.Alignment>;
      /**
       * 合并单元格
       */
      mergeList?: mergeListType[];
      /**
       * 标题样式
       */
      titleStyle?: Partial<ExcelJS.Row>;
      /**
       * 小标题样式
       */
      subTitleStyle?: Partial<ExcelJS.Row>;
      /**
       *  表头样式
       */
      headerStyle?: Partial<ExcelJS.Row>;
      /**
       * 单元格统一样式
       */
      cellStyle?: Partial<ExcelJS.Row & ExcelJS.Column>;
    }
    
    // 合并单元格数据类型
    export interface mergeListType {
      startRow: number;
      startColumn: number;
      endRow: number;
      endColumn: number;
    }
    
    // exceljs相关方法
    export class Excel {
      blob?: Blob; // 导入的blob文件
      worksheet?: ExcelJS.Worksheet;  // 当前工作表
      header: string[]; // 表头字段数组
      constructor(blob?: Blob) {
        this.blob = blob;
        this.worksheet = undefined;
        this.header = [];
      }
      /**
       * @description: blob转ArrayBuffer(用于后续生成文件数据)
       * @return {Promise<ArrayBuffer>} ArrayBuffer
       */
      private readFile(): Promise<ArrayBuffer> {
        return new Promise<ArrayBuffer>((resolve, reject) => {
          let reader = new FileReader();
          if (!this.blob) {
            reject('上传文件异常!');
          } else {
            reader.readAsArrayBuffer(this.blob);
            reader.onload = (ev) => {
              resolve(ev.target!.result as ArrayBuffer);
            };
          }
    
        });
      }
      /**
       * @description: 导入excel文件获取workbook(workbook属性方法参考exceljs文档)
       * @return {Promise<ExcelJS.Workbook>} 
       */
      public async getWorkBook(): Promise<ExcelJS.Workbook> {
        let buffer = await this.readFile();
        const workbook = new ExcelJS.Workbook();
        return await workbook.xlsx.load(buffer);
      }
    
      /**
       * @description: 获取第i张工作表
       * @param {number} i 工作表序号
       * @return {Promise<ExcelJS.Worksheet>} 返回第 i 个工作表
       */
      public async getWorkSheet(i: number = 0): Promise<ExcelJS.Worksheet> {
        const workbook = await this.getWorkBook();
        return workbook.getWorksheet(i);
      }
    
    
      /**
       * @description: 将 excel 第i张工作表的数据转为对象数据
       * @param {number} i 工作表序号
       * @param {string[]} header 表头字段数组
       * @return {Promise<Record<(typeof header)[number], string>[]>} 传入表头作为字段的对象数组(每个元素对象对应每一行)
       */
      public async importExcel(options: importExcelType): Promise<Record<(typeof header)[number], string>[]> {
        const { i = 1, header } = options;
        const workbook = await this.getWorkBook();
        const worksheet = workbook.getWorksheet(i);
        // excel导入后返回的数组
        const excelList: Record<(typeof header)[number], string>[] = [];
    
        worksheet.getSheetValues().filter(temp => !!temp?.length).forEach(item => {   // 移除空行
          // 移除每行首个空元素
          (item as string[]).shift();
          // 定义临时对象存储每一行内容
          let tempObj: Record<(typeof header)[number], string> = {};
          (item as string[]).forEach((item2, index2) => {
            tempObj[header[index2]] = item2;
          })
          excelList.push(tempObj);
        })
        return excelList
      }
    
      /**
       * @description: 导出excel,参数信息参考exceljs
       * @param {*} data 数据
       * @param {*} name 文件名
       * @param {*} header 表头字段
       * @param {*} customHeader 表头字段对应中文
       * @param {*} sheetName 工作表名
       * @param {*} title 标题
       * @param {*} subTitle 副标题(日期)
       * @param {*} password 冻结表格密码
       * @param {*} mergeList 合并单元格数组
       * @param {*} titleStyle 标题样式(按需补充方法)
       * @param {*} subTitleStyle 小标题样式(按需补充方法)
       * @param {*} headerStyle 表头字段样式(按需补充方法)
       * @param {*} cellStyle 单元格样式(按需补充方法)
       * @return {*}
       */
    
      public async exportExcel(options: exportExcelType): Promise<void> {
        const { 
            data, 
            name, 
            header, 
            customHeader, 
            sheetName = 'sheet1', 
            title = '', 
            subTitle = '', 
            password = '', 
            mergeList = [], 
            titleStyle, 
            subTitleStyle, 
            headerStyle, 
            cellStyle } = options;
        // 创建工作簿
        const workbook = new ExcelJS.Workbook();
        workbook.creator = '侃侃';
        workbook.created = new Date();
        // 添加sheet
        this.worksheet = workbook.addWorksheet(sheetName, { properties: { tabColor: { argb: 'FF00FF00' } } });
        this.header = header;
        // 表头行序号
        let headerRowId = 1;
        if (!!title) headerRowId++;
        if (!!subTitle) headerRowId++;
        // 插入单元格数据
        this.setCells(data, customHeader, cellStyle);
        // 插入大标题
        this.getTitle(title, titleStyle);
        // 插入小标题
        this.getSubTitle(subTitle, subTitleStyle);
        // 处理表头
        this.setHeaderStyle(headerRowId, data, headerStyle);
        // 更多处理
        this.handleDealExcel(password, mergeList, headerRowId);
        // 导出excel(此处也可用file-saver将blob导出到excel文件  fs.saveAs(blob, name+'.xlsx');)
        workbook.xlsx.writeBuffer().then((data) => {
          let blob = new Blob([data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
          const a = document.createElement('a');
          a.href = URL.createObjectURL(blob);
          a.download = name + '.xlsx';
          document.body.appendChild(a);
          a.click();
          document.body.removeChild(a);
          window.URL.revokeObjectURL(a.href);
        });
      }
      /**
       * @description: 合并单元格数组
       * @param {Partial} merge1 需要合并的行/列起止对象数组1
       * 例:[{ startRow: 1, endRow: 1},{ startRow: 10, endRow: 10}]
       * @param {Partial} merge2 需要合并的列/行起止对象数组2
       * 例:[{ startColumn: 1, endColumn: 2 }]
       * @return {*} mergeArr 合并后数组
       * 例:[{ startRow: 1, endRow: 1, startColumn: 1, endColumn: 2},{ startRow: 10, endRow: 10, startColumn: 1, endColumn: 2}]
       */
      public merge(merge1: Partial<mergeListType>[], merge2: Partial<mergeListType>[]): mergeListType[] {
        const mergeArr: any[] = [];
        merge1.forEach(item1 => {
          mergeArr.push(...merge2.map(item2 => {
            return { ...item2, ...item1 };
          }))
        })
        return mergeArr;
      }
    
      /**
       * @description:单元格数据处理 
       * @param {any} data 表格数据
       * @param {string} customHeader  表头中文字段
       * @return {*}
       */
      private setCells(data: exportExcelType['data'], customHeader: string[], style?: Partial<ExcelJS.Row & ExcelJS.Column>): void {
        // 设置列,插入中文表头
        const column: Partial<ExcelJS.Column>[] = [];
        this.header.forEach((item, index) => {
          column.push({
            header: customHeader[index],
            key: item,
            width: style?.width || 25,
          })
        })
        this.worksheet!.columns = column;
        // 设置行,添加数据
        this.worksheet?.addRows(data);
        // 设置行高
        this.worksheet?.eachRow({ includeEmpty: true }, (row, rowNumber) => {
          row.height = style?.height || 20;
        })
        // 获取每一列数据,再依次对齐
        this.worksheet!.columns.forEach(column => {
            column.alignment = style?.alignment || { vertical: 'middle', horizontal: 'center', wrapText: true }
        })
      }
      /**
       * @description: 添加大标题 
       * @param {string} title 标题
       * @param {any} style 标题样式
       * @return {*}
       */
      private getTitle(title: string, style?: Partial<ExcelJS.Row>): void {
        if (!!title) {
          // 插入标题
          this.worksheet?.spliceRows(1, 0, [title]);
          this.worksheet?.mergeCells(1, 1, 1, this.header.length);
          // 调整标题样式
          const titleRow = this.worksheet!.getRow(1);
          // 高度
          titleRow.height = style?.height || 40;
          // 字体设置
          titleRow.font = style?.font || {
            size: 20,
            bold: true
          }
          // 背景色
          titleRow.fill = style?.fill || {
            bgColor: {
              'argb': 'FFFFFF00'
            },
            type: 'pattern',
            pattern: 'none'
          }
          // 对齐方式
          titleRow.alignment = style?.alignment || {
            horizontal: "center",
            vertical: "middle"
          };
        }
      }
      /**
       * @description: 添加小标题 
       * @param {string} subTitle 标题
       * @param {Partial} style 小标题样式
       * @return {*}
       */
      private getSubTitle(subTitle: string, style?: Partial<ExcelJS.Row>): void {
        if (!!subTitle) {
          this.worksheet?.spliceRows(2, 0, [subTitle]);
          this.worksheet?.mergeCells(2, 1, 2, this.header.length);
          // 调整标题样式
          const subtitleRow = this.worksheet!.getRow(2);
          // 高度
          subtitleRow.height = style?.height || 20;
          // 字体设置
          subtitleRow.font = style?.font || {
            size: 14,
          }
          // 背景色
          subtitleRow.fill = style?.fill || {
            bgColor: {
              'argb': 'FFFFFF00'
            },
            type: 'pattern',
            pattern: 'none'
          }
          // 对齐方式
          subtitleRow.alignment = style?.alignment || {
            horizontal: "right",
            vertical: "middle"
          };
        }
      }
      /**
       * @description: 设置表头样式
       * @param {number} num 表头在第几行
       * @param {number} data 总数据
       * @param {number} style 表头样式
       * @return {*}
       */
      private setHeaderStyle(num: number, data: any, style?: Partial<ExcelJS.Row>): void {
        // 自动筛选器
        this.worksheet!.autoFilter = {
          from: {
            row: num,
            column: 1
          },
          to: {
            row: data.length,
            column: this.header.length
          }
        }
        // 给表头添加背景色
        let headerRow = this.worksheet!.getRow(num);
        headerRow!.height = style?.height || 30;
        // 通过 cell 设置背景色,更精准
        headerRow?.eachCell((cell) => {
          cell.fill = style?.fill || {
            type: 'pattern',
            pattern: 'solid',
            fgColor: { argb: 'dde0e7' },
          }
          cell.font = style?.font || {
            size: 12
          }
        })
      }
      /**
       * @description: 其他内容处理
       * @param {string} password 密码
       * @param {mergeListType} mergeList 合并行列数组
       * @param {number} headerRowId  表头行序号
       * @return {*}
       */
      private handleDealExcel(password: string, mergeList: mergeListType[], headerRowId: number) {
        // 添加工作表保护
        if (!!password) {
          this.worksheet?.protect(password, {
            autoFilter: true,
            selectLockedCells: false,
          });
        }
        // 合并单元格
        mergeList.forEach(item => {
          // 行数为表格数据所在行行数+表头行序号headerRowId
          const startRow = item.startRow + headerRowId;
          const endRow = item.endRow + headerRowId;
          this.worksheet?.mergeCells(startRow, item.startColumn, endRow, item.endColumn);
        })
        // 冻结前几行
        this.worksheet!.views = [
          { state: 'frozen', xSplit: 0, ySplit: headerRowId }
        ];
      }
    }
    
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335