前端VUE3项目通过XLSX.JS实现表格文件导入,文件中表头有换行,数据有合并行

167 阅读5分钟

Excel 表格大冒险:合并行与换行表头的奇幻之旅

有一天,小鹿接到了一个艰巨的任务:从一个充满魔法的 Excel 表格中导入数据到神秘的系统中。这个表格不仅藏有宝贵的设备信息,还布满了各种奇奇怪怪的障碍,比如合并行和换行表头。小鹿心中暗自嘀咕:“这表格是要考考我的耐心吗?”

第一步:揭开合并行的秘密

小鹿打开 Excel 表格,眼前一亮——表格的第一行竟然被施了魔法,变成了合并行!小鹿心想:“这看起来像是一个合并单元格的陷阱。” 她决定先找出所有合并单元格,确保它们不会在数据导入的过程中搞鬼。

她开始编写代码,获取合并单元格的信息:

const merges = workSheet['!merges'];
console.log("合并单元格信息------------", merges);

小鹿在控制台中看到了一长串合并单元格的坐标,心想:“这表格的魔术师真能搞事,竟然有这么多合并单元格!” 小鹿决定先把这些合并单元格的值填充到所有被合并的单元格中,以免数据出现混乱。

第二步:处理换行符的魔法

小鹿继续查看表格,发现表头的列名也被施了魔法,竟然有换行符!她心想:“这表格的魔术师真是无所不用其极,连列名都要搞个花活。” 她决定在获取列名时,截取到换行符之前的部分,确保列名的整洁。

const headers = [];
for (let C = range.s.c; C <= range.e.c; C++) {
  const cell = workSheet[XLSX.utils.encode_cell({ r: range.s.r, c: C })];
  if (cell && cell.v !== undefined) {
    headers.push(cell.v.replace(/↵/g, '\n').trim().split('\n')[0]); // 截取到换行符之前的部分
  } else {
    headers.push('');
  }
}

console.log("处理后的列名------------", headers);

小鹿在控制台中看到了处理后的列名,终于舒了一口气:“这下表头的魔法被破除了,看起来正常多了。”

第三步:填充空数据的空想

小鹿接着开始填充初始数据数组。小鹿心想:“如果表格中有空数据,那我可得小心了,不能让空数据搞乱了整个表格。” 她决定在填充数据时,如果单元格为空,则填充为空字符串 '',确保数据结构的一致性。

for (let R = range.s.r + 1; R <= range.e.r; ++R) { // 从第二行开始,跳过标题行
  for (let C = range.s.c; C <= range.e.c; ++C) {
    const cell = workSheet[XLSX.utils.encode_cell({ r: R, c: C })];
    if (cell && cell.v !== undefined) {
      initialData[R][C] = cell.v;
    } else {
      initialData[R][C] = ''; // 如果单元格为空,则填充为空字符串
    }
  }
}

console.log("填充后的数据数组------------", initialData);

小鹿在控制台中看到填充后的数据数组,心想:“这下空数据的空想被粉碎了,表格看起来更整齐了。”

第四步:魔法反击

小鹿决定反击这些合并单元格的魔法。她编写代码,将合并单元格的第一个单元格的值应用到所有被合并的单元格中,确保数据的完整性。

if (merges) {
  merges.forEach(merge => {
    const startRow = merge.s.r;
    const startCol = merge.s.c;
    const endRow = merge.e.r;
    const endCol = merge.e.c;

    // 获取合并单元格的第一个单元格的值
    const firstCellValue = initialData[startRow][startCol];

    // 将这个值应用到所有被合并的单元格中
    for (let r = startRow; r <= endRow; r++) {
      for (let c = startCol; c <= endCol; c++) {
        if (initialData[r][c] === '') {
          initialData[r][c] = firstCellValue;
        }
      }
    }
  });
}

console.log("处理合并单元格后的数据数组------------", initialData);

小鹿在控制台中看到处理合并单元格后的数据数组,心想:“这下这些合并单元格的魔法都失效了,表格终于恢复正常了!”

第五步:数据转换的奇幻变身

小鹿最后将二维数组转换为 JSON 格式的数组,确保数据在后续处理中更加方便。她心想:“这最后一步真是画龙点睛,数据终于变成了我可以理解的格式。”

const data = [];
for (let R = range.s.r + 1; R < numRows; R++) { // 从第二行开始,跳过标题行
  const rowData = {};
  for (let C = range.s.c; C <= range.e.c; C++) {
    const cellValue = initialData[R][C];
    if (cellValue !== '') {
      const header = headers[C];
      if (header) {
        rowData[header] = cellValue;
      }
    }
  }
  // 过滤掉空行
  if (Object.keys(rowData).length > 0) {
    data.push(rowData);
  }
}

console.log("导入数据------------1114----------", data);
return data;

小鹿在控制台中看到最终的 JSON 格式数据,心想:“这表格的魔术师真是白费心机了,我终于成功导入数据了!”


小鹿的代码总结

以下是小鹿最终的 getXlsxData 函数代码,确保在处理列名和合并单元格时正确填充数据:

const getXlsxData = async (file) => {
  const dataBinary = await readFile(file);
  const workBook = XLSX.read(dataBinary, { type: 'array' });
  const workSheet = workBook.Sheets[workBook.SheetNames[0]];

  // 获取合并单元格的信息
  const merges = workSheet['!merges'];
  console.log("合并单元格信息------------", merges);

  // 获取工作表的行数和列数
  const range = XLSX.utils.decode_range(workSheet['!ref']);
  const numRows = range.e.r + 1;
  const numCols = range.e.c + 1;

  // 初始化一个二维数组来存储数据
  const initialData = Array.from({ length: numRows }, () => Array(numCols).fill(''));
  console.log("初始化后的数据数组------------", initialData);

  // 获取列名,并处理换行符
  const headers = [];
  for (let C = range.s.c; C <= range.e.c; C++) {
    const cell = workSheet[XLSX.utils.encode_cell({ r: range.s.r, c: C })];
    if (cell && cell.v !== undefined) {
      headers.push(cell.v.replace(/↵/g, '\n').trim().split('\n')[0]); // 截取到换行符之前的部分
    } else {
      headers.push('');
    }
  }

  console.log("处理后的列名------------", headers);

  // 填充初始数据数组
  for (let R = range.s.r + 1; R <= range.e.r; ++R) { // 从第二行开始,跳过标题行
    for (let C = range.s.c; C <= range.e.c; ++C) {
      const cell = workSheet[XLSX.utils.encode_cell({ r: R, c: C })];
      if (cell && cell.v !== undefined) {
        initialData[R][C] = cell.v;
      } else {
        initialData[R][C] = ''; // 如果单元格为空,则填充为空字符串
      }
    }
  }

  console.log("填充后的数据数组------------", initialData);

  // 处理合并单元格
  if (merges) {
    merges.forEach(merge => {
      const startRow = merge.s.r;
      const startCol = merge.s.c;
      const endRow = merge.e.r;
      const endCol = merge.e.c;

      // 获取合并单元格的第一个单元格的值
      const firstCellValue = initialData[startRow][startCol];

      // 将这个值应用到所有被合并的单元格中
      for (let r = startRow; r <= endRow; r++) {
        for (let c = startCol; c <= endCol; c++) {
          if (initialData[r][c] === '') {
            initialData[r][c] = firstCellValue;
          }
        }
      }
    });
  }

  console.log("处理合并单元格后的数据数组------------", initialData);

  // 将二维数组转换为 JSON 格式的数组
  const data = [];
  for (let R = range.s.r + 1; R < numRows; R++) { // 从第二行开始,跳过标题行
    const rowData = {};
    for (let C = range.s.c; C <= range.e.c; C++) {
      const cellValue = initialData[R][C];
      if (cellValue !== '') {
        const header = headers[C];
        if (header) {
          rowData[header] = cellValue;
        }
      }
    }
    // 过滤掉空行
    if (Object.keys(rowData).length > 0) {
      data.push(rowData);
    }
  }

  console.log("导入数据------------1114----------", data);
  return data;
};