纯前端 导出Excel文件 进阶版

        这篇文章是接着纯前端 导出Excel文件的 方法-CSDN博客https://blog.csdn.net/qq_44327851/article/details/134045649),导出excel的基本步骤在这里就不再多叙述,本文主要是介绍导出excel的一些enhancement的实现方法和注意事项。

        问题:即使给每个colunm设置了样式,比如给每个单元格设置了边框,但是实际导出文件的时候却发现仍然有一些单元格样式没生效,再仔细看会发现,样式没生效的单元格都是没有数据的。

        解决:设置excel的样式是跟着数据的,既然是因为没有数据导致的样式没生效,那么我们就在数据生成的时候加多一种可能,让其生成一种不占位置,人为看不见的数据。对!那就是空数据,最常用的就是空字符串了""。(这里也是用数组数据模拟文件数据进行excel文件导出)

//要导出的数组数据

extract = this.row.map((item) => {
    'Name': item.name || "",
    'Sex': item.sex || ""
  }
)

2.Merge 特定columns的单元格

        这里以垂直merge单元格为例,也就是merge rows。需要导出的数组数据格式依然是:

# 创建数组数据 
data = {
    'A': [{'a':1, 'b':2, 'c':3},{'a':4, 'b':5}], 
    'B': [{'a':'d', 'b':'e', 'c':'c3'}, {'a':'d', 'b':'e'}], 
    'C': [{'a':'x', 'b':'y', 'c':'z'}, {'a':'w', 'b':'v'}]
}

        垂直merge单元格的主要代码:


    // 需要垂直合并的列的索引
    const columnsToMerge = [0, 1, 2]; // 假设需要合并第0、1、2列
    
    // 遍历需要合并的列
    columnsToMerge.forEach(colIndex => {
      // 确定要合并的单元格范围
      const mergeRange = {
        s: { r: 1, c: colIndex }, // 起始位置为第2行
        e: { r: 10, c: colIndex } // 结束位置为第11行
      };
      
      // 应用垂直合并
      worksheet['!merges'] = worksheet['!merges'] || [];
      worksheet['!merges'].push(mergeRange);
    });

        实际的实现要复杂的多,需要多加几重处理,下面是完整代码:(只合并A列,也就是第0列)

<button id="exportBtn">Export Excel</button>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.16.9/xlsx.full.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
  <script>
    document.getElementById('exportBtn').addEventListener('click', function () {
      var fileData = {
        2014: [{ name: 'yanfeng', sex: '', height: 188, weigth: 100 }],
        2015: [{ name: '', sex: 'female', height: 188, weigth: 100 }],
        2016: [{ name: 'yanfeng', sex: 'female', height: 188, weigth: 100 }, { name: '小红', sex: 'female', height: 180, weigth: 90 }],
        2017: [{ name: 'yanfeng', sex: 'female', height: 188, weigth: 110 }, { name: '小白', sex: 'female', height: 180, weigth: 91 }],
        2018: [{ name: 'yanfeng', sex: 'female', height: 188, weigth: 120 }, { name: '小丽', sex: 'female', height: 180, weigth: 94 }],
        2019: [
          { name: 'yanfeng', sex: 'female', height: 0, weigth: 0 },
          { name: 'yanfeng', sex: 'female', height: '', weigth: '' },
          { name: 'yanfengdai', sex: 'female', height: 188, weigth: 130 },
          { height: 188, weigth: 120 },
          { height: 188, weigth: 110 },
          { height: 188, weigth: 100 }
        ],
        2020: [
          { name: 'yanfeng', sex: 'female', height: 167, weigth: 90 },
          { height: 188, weigth: 120 },
          { height: 188, weigth: 110 },
          { height: 188, weigth: 1000 }
        ]
      }
      const workbook = XLSX.utils.book_new();
      const columns = [{ wch: 35 }, { wch: 35 }, { wch: 15 }, { wch: 25 }];
      const mergeRange = [0, 1]//定义需要merge的columns 在这里定义的是合并0,1列,也就是A,B两列
      const cellStyle = {
        alignment: {
          wrapText: true,//换行
          horazital: 'center',//水平居中
          vertical: 'center'//垂直居中
        },
        border: {
          top: { style: 'thin' },
          bottom: { style: 'thin' },
          left: { style: 'thin' },
          right: { style: 'thin' }
        }
      }
      for (const key in fileData) {
        if (fileData.hasOwnProperty(key)) {
          const dataArray = fileData[key];
          //首先要将你需要垂直合并单元格那列的数据进行排序,保证空的数据在前面
          dataArray.sort((a, b) => {
            if (!a.height && !a.weigth && b.height && b.weigth) {
              return -1;
            } else if (a.height && a.weigth && !b.height && !b.weigth) {
              return 1;
            } else {
              return 0;
            }
          });
          //然后将表格数据写进worksheet
          const workSheet = XLSX.utils.json_to_sheet(dataArray);
          //接着设置columns的长度,样式等
          if (columns && columns.length) {
            workSheet['!cols'] = columns;
          }
          Object.keys(workSheet).forEach(cell => {
            if (cell !== '!ref') {
              workSheet[cell].s = cellStyle
            }
          })
          //开始垂直merge row的代码
          //首先计算从哪行开始进行merge,即就是计算上面空数据的长度,空数据不merge,需要merge的在最后一行
          let startLength = 1;
          let isMergeAvaliable = false;
          if (dataArray.length > 1) {
            isMergeAvaliable = true;
            let isAllHaveWAndHValue = 0;
            dataArray.forEach(item => {
              if (!item.weigth && !item.height) {
                startLength++;
              } else {
                isAllHaveWAndHValue++;
              }
            })
            //判断是否需要merge,如果该数组中的数据都是完整的,也就是数组中的每个数据都是有数值的,就不需要被merge,比如2016,2017,2018中的数据就不需要被merge
            if (isAllHaveWAndHValue === dataArray.length) {
              isMergeAvaliable = false;
            }
          }
          if (isMergeAvaliable) {
            mergeRange.forEach((mergeIndex) => {
              const mergeConfig = { s: { r: startLength, c: mergeIndex }, e: { r: dataArray.length, c: mergeIndex } };
              for (let R = mergeConfig.s.r; R < mergeConfig.e.r; ++R) {
                for (let C = mergeConfig.s.c; C < mergeConfig.e.c; ++C) {
                  const cellAdress = XLSX.utils.encode_cell({ r: R, c: C });
                  const cell = workSheet[cellAdress];
                  if (cell) {//给有数据的row添加样式
                    if (!cell.s) cell.s = {};
                    cell.s = cellStyle;
                  } else {//保证没有数据的row增加空数据,添加样式
                    workSheet[cellAdress] = { s: cellStyle, v: '' }
                  }
                }
              }
              workSheet['!merges'] = workSheet['!merges'] || [];
              workSheet['!merges'].push(mergeConfig);
            })
          }
          //生成sheet
          XLSX.utils.book_append_sheet(workbook, workSheet, key);
        }
      }
      //最后就是文件导出的代码了
      const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
      const excelBlob = new Blob([excelBuffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
      saveAs(excelBlob, 'filename.xlsx');
    })
  </script>

        注意:如果使用上述代码而未使用UI框架的话(Angular测试已通过,VUE, React待测试),上面代码实际运行的效果并没有样式,如果想直接在HTML中使用并带有样式导出,请将使用ExcelJS(<script src="https://cdnjs.cloudflare.com/ajax/libs/exceljs/4.3.1/exceljs.min.js"></script>)这样的库来处理Excel文件而非XLSX。

 部分代码的解释:

 workSheet['!merges'] = workSheet['!merge'] || [];
 workSheet['!merges'].push(mergeConfig);这两句话是什么意思?

`worksheet['!merges'] = worksheet['!merges'] || [];`:这行代码使用了JavaScript中的逻辑或运算符`||`。它的作用是检查`worksheet['!merges']`是否已经存在,如果不存在(即为假值),则将一个空数组赋给`worksheet['!merges']`,否则保持不变。这样做是为了确保`worksheet['!merges']`属性一定是一个数组,以便我们可以向其中添加合并单元格的范围。
`worksheet['!merges'].push(mergeRange);`:一旦确保了`worksheet['!merges']`是一个数组,我们就可以使用`push`方法向数组中添加新的合并单元格范围`mergeRange`。这样就把新的合并范围添加到了`worksheet['!merges']`数组中。
这两句代码的目的是为了确保我们能够向`worksheet['!merges']`数组中添加新的合并单元格范围而不会出现错误。

后续:增加处理类似于下面2021的数据格式的主要代码

//数据格式  
 var fileData = {
       2021: [
          { name: '小小', sex: 'female', height: 167, weigth: 90 },
          { height: 188, weigth: 120 },
          { height: 188, weigth: 110 },
          { height: 188, weigth: 1000 },
          { name: 'yanfeng', sex: 'female', height: 166, weigth: 80 },
          { height: 188, weigth: 120 },
          { height: 188, weigth: 110 },
          { height: 188, weigth: 1000 }
        ],
      }

//处理方法
if (dataArray.length > 1) {
            isMergeAvaliable = true;
            let isAllHaveWAndHValue = 0;
            dataArray.forEach((item, index) => {
              if (!item.weigth && !item.height) {
                startLength++;
              } else if (item.weigth && item.height && item.name && item.sex) {
                isAllHaveWAndHValue++;
              }
            })
            //判断是否需要merge,如果该数组中的数据都是完整的,也就是数组中的每个数据都是有数值的,就不需要被merge,比如2016,2017,2018中的数据就不需要被merge
            if (isAllHaveWAndHValue === dataArray.length) {
              isMergeAvaliable = false;
            }
            //处理类似于2021的数据
            merge = dataArray.map((element, index) => { if (element.name && element.sex && element.height && element.weigth && index > startLength) { return index; } }).filter(index => index !== undefined);
            merge.push(dataArray.length);
          }
          if (isMergeAvaliable && (merge && merge.length)) {
            let start = 0; end = 0;
            merge.forEach((mergeLen, index) => {
              if (index == 0) {
                start = startLength;
                end = mergeLen;
              } else {
                start = end + 1;
                end = mergeLen;
              }
              mergeRange.forEach((mergeIndex) => {
                let mergeConfig = {};
                mergeConfig = { s: { r: start, c: mergeIndex }, e: { r: end, c: mergeIndex } };
                for (let R = mergeConfig.s.r; R < mergeConfig.e.r; ++R) {
                  for (let C = mergeConfig.s.c; C < mergeConfig.e.c; ++C) {
                    const cellAdress = XLSX.utils.encode_cell({ r: R, c: C });
                    const cell = workSheet[cellAdress];
                    if (cell) {//给有数据的row添加样式
                      if (!cell.s) cell.s = {};
                      cell.s = cellStyle;
                    } else {//保证没有数据的row增加空数据,添加样式
                      workSheet[cellAdress] = { s: cellStyle, v: '' }
                    }
                  }
                }
                workSheet['!merges'] = workSheet['!merges'] || [];
                workSheet['!merges'].push(mergeConfig);
              })
            })
          }

3. sheet_add_aoa可以用来增加空行

  比如XLSX.utils.sheet_add_aoa(ws, [], { origin: 'A2' }),这句话的意思是向工作表workSheet中的'A2'位置插入一个空的二维数组。这个操作实际上相当于在Excel中,在'A2'位置插入了一个空行。

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值