xlsx-js-style导出Excel文件

Jiafeng

分类: JavaScript 414 0

安装 xlsx-js-style

导出 excel 较常见的 js 库是之一是 xlsxxlsx 算是基础版本,不能对单元格进行样式对齐方式、文字颜色、背景颜色等)的修饰,如果需要修饰单元格,可使用 xlsx-js-style

  • 下载引入

    npm install xlsx-js-style
    import XLSXS from 'xlsx-js-style';

  • 数据

    // 一般我们拿到的是从接口中请求到的对象数组,在使用是需要转成二维数组,下面有介绍
    const data = [
    { name: '商品01', mb_num: 50, mb_sum: 5000, pc_num: 30, pc_sum: 3000, total_num: 80, total_sum: 8000 },
    { name: '商品02', mb_num: 50, mb_sum: 5000, pc_num: 30, pc_sum: 3000, total_num: 80, total_sum: 8000 },
    { name: '商品03', mb_num: 50, mb_sum: 5000, pc_num: 30, pc_sum: 3000, total_num: 80, total_sum: 8000 },
    ]
  • 数据需要转化成二维数组

    const body = data.map(x => ([x.name, x.mb_num, x.mb_sum, x.pc_num, x.pc_sum, x.total_num, x.total_sum]))
    
    // 转换后的数据为一个二维数组
    [
    ['商品01', 50, 5000, 30, 3000, 80, 8000]
    ['商品02', 50, 5000, 30, 3000, 80, 8000]
    ['商品03', 50, 5000, 30, 3000, 80, 8000]
    ]
  • 定义表头并 将定义好的表头添加到 body 中

    /*
      定义表头,每一行即是一个数组,数组中的元素即是一个个单元格内容。
      如果单元格不需要样式,那么每个元素类型为字符串即可。如:['一月(2022年01月)'], 
      如果单元格需要样式,那么数组中的元素就需要为一个个对象,在此对象中定义单元格的样式等等。
    */
      const header = [
        // 第一行,需要样式,则数组中元素为对象,进行定义样式。
        [
          {
            v: '一月(2023年01月)',
            t: 's',
            s: {
              // font 字体属性
              font: {
                bold: true,
                sz: 14,
                name: '宋体',
              },
              // alignment 对齐方式
              alignment: {
                vertical: 'center', // 垂直居中
                horizontal: 'center', // 水平居中
              },
              // border 边框属性
              border: {
                top: { style: 'thin' },
                bottom: { style: 'thin' },
                left: { style: 'thin' },
                right: { style: 'thin' }
              },
              // fill 颜色填充属性
              fill: {
                fgColor: { rgb: '87CEEB' },
              },
            },
          },
        ],
        ['商品名称', '手机客户端', '', '电脑客户端', '', '总计', ''],
        ['', '销售数量', '销售金额', '销售数量', '销售金额', '销售数量', '销售金额']
      ]
    
    body.unshift(...header);
  • 将二维数组转成 sheet 工作薄

    // 这里我们举例是用 aoa_to_sheet ,所以是需要将数据源转成一个二维数组
    const sheet = XLSXS.utils.aoa_to_sheet(body);
    
    // aoa_to_sheet   是将【一个二维数组】转化成 sheet
    // json_to_sheet  是将【由对象组成的数组】转化成sheet
    // table_to_sheet     是将【table的dom】直接转成sheet
  • !merges 设置单元格合并

    如果需要设置单元格合并,则定义好merges ,添加到 sheet 中。

    merges 为一个对象数组,每个对象设定了单元格合并的规则。

    { s: { r: 0, c: 0 }, e: { r: 0, c: 2 } }, 即为一个规则,s:开始位置, e:结束位置, r:行, c:列

    const merges = [
    { s: { r: 0, c: 0 }, e: { r: 0, c: 6 } },
    { s: { r: 1, c: 1 }, e: { r: 1, c: 2 } },
    { s: { r: 1, c: 3 }, e: { r: 1, c: 4 } },
    { s: { r: 1, c: 5 }, e: { r: 1, c: 6 } },
    { s: { r: 1, c: 0 }, e: { r: 2, c: 0 } },
    ]
    sheet['!merges'] = merges; // 添加到sheet中
    
    // 方法一:通过 decode_range 设置范围合并单元格
      ws['!merges'].push(utils.decode_range('A1:C1'))
    
      // 方法二:手动设置 A1-C1 的单元格合并
      // merges 为一个对象数组,每个对象设定了单元格合并的规则
      // s:开始位置, e:结束位置, r:行, c:列
    
      // ws['!merges'] = [
      //     { s: { r: 0, c: 0 }, e: { r: 0, c: 2 } },
      // ]
    
  • !cols 设置列宽

    cols 为一个对象数组,依次表示每一列的宽度。

    const cols = [
      { wch: 10 },
      { wch: 10 }, 
      { wch: 10 },
      { wch: 10 },
      { wch: 10 },
      { wch: 10 }, 
      { wch: 10 }
    ];
    sheet['!cols'] = cols; // 添加到sheet中
  • !rows 设置行高

    rows 为一个对象数组,依次表示每一行的高度

    const rows = [
      { hpx: 20 }, 
      { hpx: 16 },
      { hpx: 18 }
    ]
    sheet['!rows'] = rows; // 添加到sheet中
  • 创建虚拟的 workbook

    整个表格可称为 workbook。里面的每张表分别是 sheet

    const workbook = xlsx.utils.book_new();
  • 向 workbook 中添加 sheet

    XLSXS.utils.book_append_sheet(workbook, sheet, 'sheet名称');
    
    // 一个 workbook 允许添加多个 sheet,即可以同时创建多个表
    // xlsx.utils.book_append_sheet(workbook, sheet2, 'sheet名称2');
  • 导出

    // 注意:定义导出 excel 的名称时需要加上后缀 .xlsx
    xlsx.writeFile(workbook, 'excel名称.xlsx');
  • 创建虚拟的 workbook将数组转成 sheet向workbook中添加sheet导出workbook,这四个步骤是必要的。

  • 设置合并单元格设置列宽设置行高是可选的,根据需求进行添加。

  • 如果添加样式需要 border 则需要在被合并的单元格位置进行占位。

  • 常用方法封装

    import { isArray } from 'lodash-es';
    import XLSX from 'xlsx-js-style';
    
    // columns:表头数据   data:表格数据    excelName:导出的文件名    autoWidth: 自适应列宽  sheetTitle:sheet名称,excel打开以后,的底部切换名称 wrapText:是否自动换行
    export interface exportExcelTypes {
      columns: any[]
      data: any[]
      excelName?: string
      autoWidth?: boolean
      sheetTitle?: string
      wrapText?: ''
    }
    // 导出表头默认样式
    const THeaderStyle = {
      // font 字体属性
      font: {
          bold: true,
          sz: 12
      },
      // alignment 对齐方式
      alignment: {
          vertical: 'center', // 垂直居中
          horizontal: 'center', // 水平居中
      },
      // fill 颜色填充属性
      fill: {
          fgColor: { rgb: 'e1e2e3' },
      },
      // border style
      border: {
          top: { style: 'thin', color: { rgb: '909399' } },
          right: { style: 'thin', color: { rgb: '909399' } },
          bottom: { style: 'thin', color: { rgb: '909399' } },
          left: { style: 'thin', color: { rgb: '909399' } }
      }
    }
    
    const TBodyStyle = {
      alignment: {// 文字居中 //字体水平居中、垂直居中、自动换行
          font: {// 字体设置
              sz: 12,
              bold: false,
          },
          horizontal: 'left', //居中
          vertical: 'center', //垂直居中
          wrapText: true
      },
      // numFmt:'@'
    }
    /**
    * 自适应列宽
    * @param {*} data
    * @returns {*}  {*}
    */
    const autoWidthFn = (data: any): any => {
      // 设置worksheet每列的最大宽度
      const colWidth = data.map((row: any) => row.map((val: { toString: () => { (): any; new(): any; charCodeAt: { (arg0: number): number; new(): any; }; length: number; }; } | null) => {
          // 先判断是否为null/undefined
          if (val == null) {
              return {
                  'width': 10
              }
          }
          // 再判断是否为中文
          else if (val.toString().charCodeAt(0) > 255) {
              return {
                  'width': val.toString().length * 2 + 6
              }
          } else {
              return {
                  'width': val.toString().length + 2 + 1
              }
          }
      }))
      // 以第一行为初始值
      const result = colWidth[0]
      for (let i = 1; i < colWidth.length; i++) {
          for (let j = 0; j < colWidth[i].length; j++) {
              if (result[j]['width'] < colWidth[i][j]['width']) {
                  result[j]['width'] = colWidth[i][j]['width'] > 480 ? 480 : colWidth[i][j]['width']
              }
          }
      }
      return result
    }
    /**
    * 是否自动换行
    * @param {*} sheet
    * @returns {*}  {*}
    */
    const wrapTextFn = (sheet: any): any => {
      for (const key in sheet) {
          if (key.indexOf('!') === -1) { // 排除带!的字段,只要单元格字段
              if (!sheet[key].s && sheet[key].z != 'm/d/yy') {
                  sheet[key].t = 's'
                  sheet[key].s = TBodyStyle
              }
          }
      }
      return sheet
    }
    
    /**
    * json数据导出excel
    * @param {exportExcelTypes} params
    */
    export const exportExcel = (params: exportExcelTypes) => {
      const { columns, data, excelName, autoWidth = true, sheetTitle = 'sheet名称', wrapText = false } = params
      if (!isArray(columns) || !isArray(data)) {
          return
      }
      const THeader: any = [], filterVal: any = [];
      columns.forEach((item: { label: any; prop: any; }) => {
          THeader.push({
              v: item.label,
              t: 's',
              s: THeaderStyle
          })
          filterVal.push(item.prop)
      })
      const TData = data.map(v => filterVal.map((j: string | number) => v[j]))
      const sheetData = [[...THeader], ...TData]
      const sheet = XLSX.utils.aoa_to_sheet(sheetData);// aoa_to_sheet 将二维数组转成 sheet
      // 自适应列宽
      if (autoWidth) { sheet['!cols'] = autoWidthFn(sheetData) }
      // 自动换行
      if (wrapText) { wrapTextFn(sheet) }
      // 设置表头行高
      const rows = [{ hpx: 20 }]
      sheet['!rows'] = rows; // 将rows添加到sheet中,设置行高
    
      // 创建虚拟的 workbook
      const workbook = XLSX.utils.book_new();
      // 向 workbook 中添加 sheet
      XLSX.utils.book_append_sheet(workbook, sheet, sheetTitle);
      // 导出 workbook
      XLSX.writeFile(workbook, ${excelName ? (excelName.includes('.xlsx') ? excelName : excelName + '.xlsx') : 'excel名称.xlsx'});
    }
    
    /**
    * json数据导出excel 导出多个
    * @param {{ params: exportExcelTypes[], excelName: string }} dataParams
    * @returns 
    */
    export const exportMultipleExcel = (dataParams: { params: exportExcelTypes[], excelName: string }) => {
      const { params, excelName } = dataParams
    
      if (!isArray(params)) {
          return
      }
      // 创建虚拟的 workbook
      const workbook = XLSX.utils.book_new();
      params.forEach((item: any) => {
          const { columns, data, autoWidth = true, sheetTitle = 'sheet名称', wrapText = false } = item
          if (!isArray(columns) || !isArray(data)) {
              return
          }
    
          const THeader: any = [], filterVal: any = [];
          columns.forEach((item: { label: any; prop: any; }) => {
              THeader.push({
                  v: item.label,
                  t: 's',
                  s: THeaderStyle
              })
              filterVal.push(item.prop)
          })
          const TData = data.map(v => filterVal.map((j: string | number) => v[j]))
          const sheetData = [[...THeader], ...TData]
          const sheet = XLSX.utils.aoa_to_sheet(sheetData);// aoa_to_sheet 将二维数组转成 sheet
          // 自适应列宽
          if (autoWidth) { sheet['!cols'] = autoWidthFn(sheetData) }
          // 自动换行
          if (wrapText) { wrapTextFn(sheet) }
    
          // 设置表头行高
          const rows = [{ hpx: 20 }]
          sheet['!rows'] = rows; // 将rows添加到sheet中,设置行高
          // 向 workbook 中添加 sheet
          XLSX.utils.book_append_sheet(workbook, sheet, sheetTitle);
      })
    
      // 导出 workbook
      XLSX.writeFile(workbook, ${excelName ? (excelName.includes('.xlsx') ? excelName : excelName + '.xlsx') : 'excel名称.xlsx'});
    }
    
    /**
    * 使用插件内的方法,自动使用dom转换为数据
    * 通过将一个table dom直接转成sheet,会自动识别colspan和rowspan并将其转成对应的单元格合并
    * @param {{ id: string, excelName: string, autoWidth: boolean, sheetTitle: string, wrapText: boolean}} dataParams
    * @returns 
    */
    export const exportDomExcel = (dataParams: { id: string, excelName: string, autoWidth: boolean, sheetTitle: string, wrapText?: boolean }) => {
      const { id, excelName, autoWidth = true, sheetTitle = 'sheet名称', wrapText = false } = dataParams
      const table = document.querySelector(id);
      // Generate the sheet data and add styles:
      const sheet = XLSX.utils.table_to_sheet(table); //将一个table对象转换成一个sheet对象
      const headerRange = XLSX.utils.decode_range(sheet['!ref']);
      // 把table sheet对象转换成二进制数据
      const jsonData = XLSX.utils.sheet_to_json(sheet, { header: 1 });
      const arrayData: any = [];
      jsonData.forEach((row: any) => {
          const rowData: any = [];
          Object.values(row).forEach((cell) => {
              rowData.push(cell);
          });
          arrayData.push(rowData);
      });
      // 设置表头样式
      for (let col = headerRange.s.c; col <= headerRange.e.c; col++) {
          const cellInfo = XLSX.utils.encode_cell({ r: 0, c: col });
          const cell = sheet[cellInfo];
          cell.s = THeaderStyle;
      }
      // 自适应列宽
      if (autoWidth) {
          sheet['!cols'] = autoWidthFn(arrayData)
      }
      // 自动换行
      if (wrapText) { wrapTextFn(sheet) }
    
      // 设置表头行高
      const rows = [{ hpx: 20 }]
      sheet['!rows'] = rows; // 将rows添加到sheet中,设置行高
      // 创建虚拟的 workbook
      const workbook = XLSX.utils.book_new();
      XLSX.utils.book_append_sheet(workbook, sheet, sheetTitle);
      XLSX.writeFile(workbook, ${excelName ? (excelName.includes('.xlsx') ? excelName : excelName + '.xlsx') : 'excel名称.xlsx'});
      return
    }
    
    /**
    * 手动把table内的值,获取到组装成所需要的数组 如果不需要过滤下载的表格列,用第一个更好,这个主要是为了过滤列,报告里需要过滤掉需要跟操作两列
    * 通过将一个table dom直接转成sheet,会自动识别colspan和rowspan并将其转成对应的单元格合并
    * @param {{ id: string, excelName: string, autoWidth: boolean, sheetTitle: string, wrapText: boolean,filterTHeaderTextArr:string[]}} dataParams
    * @returns 
    */
    export const exportDomExcelFilter = (dataParams: { id: string, excelName: string, autoWidth: boolean, sheetTitle: string, wrapText?: boolean, filterTHeaderTextArr?: string[] }) => {
      const { id, excelName, autoWidth = true, sheetTitle = 'sheet名称', wrapText = false, filterTHeaderTextArr = [] } = dataParams
      const table: any = document.querySelector(id);
      // Generate the sheet data and add styles:
      const rows = Array.from(table.getElementsByTagName('tr'));
      const tableData = rows.map((row: any) => Array.from(row.getElementsByTagName('td')).map((cell: any) => cell.textContent.trim())).filter(item => item.length > 0);
      const THeader = rows.map((row: any) => Array.from(row.getElementsByTagName('th')).map((cell: any) => cell.textContent.trim())).filter(item => item.length > 0);
    
      if (THeader.length) {
          THeader.forEach((item: any) => {
              item.forEach((it: any, ind: number) => {
                  if (filterTHeaderTextArr.includes(it)) {
                      item.splice(ind, 1)
                      tableData.forEach(datas => {
                          datas.splice(ind, 1)
                      })
                  }
              })
    
          })
      }
    
      const sheetData = [...THeader, ...tableData]
      const sheet = XLSX.utils.aoa_to_sheet(sheetData);// aoa_to_sheet 将二维数组转成 sheet
      const headerRange = XLSX.utils.decode_range(sheet['!ref']);
      // 设置表头样式
      for (let col = headerRange.s.c; col <= headerRange.e.c; col++) {
          const cellInfo = XLSX.utils.encode_cell({ r: 0, c: col });
          const cell = sheet[cellInfo];
          cell.s = THeaderStyle;
      }
      // 自适应列宽
      if (autoWidth) { sheet['!cols'] = autoWidthFn(sheetData) }
      // 自动换行
      if (wrapText) { wrapTextFn(sheet) }
      // 设置表头行高
      const rowstyle = [{ hpx: 20 }]
      sheet['!rows'] = rowstyle; // 将rows添加到sheet中,设置行高
    
      // 创建虚拟的 workbook
      const workbook = XLSX.utils.book_new();
      // 向 workbook 中添加 sheet
      XLSX.utils.book_append_sheet(workbook, sheet, sheetTitle);
      // 导出 workbook
      XLSX.writeFile(workbook, ${excelName ? (excelName.includes('.xlsx') ? excelName : excelName + '.xlsx') : 'excel名称.xlsx'});
    }
  • 0人 Love
  • 0人 Haha
  • 0人 Wow
  • 0人 Sad
  • 0人 Angry
Excel导出

作者简介: Jiafeng

共 0 条评论关于 “xlsx-js-style导出Excel文件”

Loading...