我的项目性能

  • 任意层级合并单元格简单表头导出

  • 表头与数据项间接映射,无需保护 Excel 索引项匹配关系

  • 主动计算、生成表头合并单元格配置信息

在线示例

  • 步骤零:如需疾速测试,可点击顶部的示例按钮,可疾速填充各层级合并单元格 Excel 测试数据

  • 步骤一:输出测试数据源,即从后端获取的数据数组。
  • 步骤二:输出「Excel 表头构造字符串」与「指标数据结构 key」之间的映射关系数组

    • "key" 为 Excel 表头,每一列的所处层级关系。如「根底信息.年龄」对应的就是 Excel 表在第二列中的关系,第一级是「根底信息」,第二级是「年龄」

    • "value" 为数据源数据结构的层级关系。「baseInfo.age」的意思是,将数据源中 baseInfo.age 这个属性的值,设置到 Excel 表的第二列中。

    • 【特地留神一】数组的程序,即为 Excel 表头的程序。第 N - 1 索引项的配置,即为 Excel 中第 N 列的菜单配置

    • 【特地留神二】Excel 表的层级映射关系与指标对象的层级映射关系,没有强制束缚和要求。如Excel 中的 "手机号",只有一层构造,但齐全能够转化为指标对象中的 "contact.phone" 二级构造。反之亦然。

  • 步骤三:点击「导出 Excel」按钮,即可实现数据 Excel 文件导出。

解析后的页面介绍

  • 左侧区域为数据源转化为 Excel 数据后,每个单元格的数据。包含顶部表头数据,及具体数据行。
  • 右侧区域为 Excel 表头数据的合并单元格配置项。利用该配置数据,能够通过「xlsx-style」库实现对表头的单元格合并。

具体函数应用形式

  • 示例遵循起码常识准则,我的项目中的 core.js 文件,即为转换函数所在文件。外面一共不到 200 行代码。能够间接粘贴复制到所需我的项目外面。

  • 示例间接应用的 script 文件整体引入的形式。所以迁徙到基于 npm 的我的项目中时,须要将 core.js 中的 ‘Window.XLS.write’ 全局变量应用形式,改为通过 import { write } from 'xlsx' 的应用形式。
  • 如果本我的项目的确有帮忙到小伙伴,小伙伴有需要的话,能够在 github 中提 issues,有需要的话,将补充基于 npm 的版本,以及带上 ts 类型束缚的版本。

全副外围源码

/** * 将数据源,转化为 Excel 单元格数据,并生成 Excel 表头 * @param dataList 数据源 * @param textKeyMaps // Excel 中文表头层级与数据源英文层级间的映射表 * @param headerFirstRow // 表头首行所在行,为了兼容表格顶部还插入有其余 Excel 行的状况,即表格不在首行 * @returns {    headerMerges, // 表头合并单元格配置项    cells, // 表头及数据项的 Excel 单元格数组  } */function transformDataToSheetCells(dataList, textKeyMaps, headerFirstRow = 0) {  // 获取从 textKeyMaps 解析,拆分后的,中英文 keys 数组  function getKeysList(textKeyMaps) {    const chineseKeysList = []    const englishKeysList = []    textKeyMaps.forEach(textKeyMap => {      const keyStr = Object.values(textKeyMap)[0]      const textStr = Object.keys(textKeyMap)[0]      englishKeysList.push(keyStr.split('.'))      chineseKeysList.push(textStr.split('.'))    })    return {      englishKeysList,      chineseKeysList    }  }  // 获取表头行数  function getHeaderRowNum(chineseKeysList) {    let maxLevel = 1    chineseKeysList.forEach(chineseKeys => {      maxLevel = Math.max(chineseKeys.length, maxLevel)    })    return maxLevel  }  // 获取表头行 cell 数据  function getHeaderRows(headerRowNum, chineseKeysList) {    const headerRows = []    // 初始化,全副设置为 ''    for (let rowIndex = 0; rowIndex < headerRowNum; rowIndex++) {      const row = new Array(chineseKeysList.length).fill('')      headerRows.push(row)    }    // 将表头 cell 设置为对应的中文    chineseKeysList.forEach((chineseKeys, colIndex) => {      for (let rowIndex = 0; rowIndex < chineseKeys.length; rowIndex++) {        headerRows[rowIndex][colIndex] = chineseKeys[rowIndex]      }    })    // 去除须要合并单元格的每一列中。反复的 cell 数据,反复的,则设置为 ''    headerRows.forEach(headerRow => {      let lastColValue = ''      headerRow.forEach((cell, colIndex) => {        if (lastColValue !== cell) {          lastColValue = cell        } else {          headerRow[colIndex] = ''        }      })    })    return headerRows  }  // 获取合并单元格配置  function getMerges(headerRowNum, chineseKeysList) {    const merges = []    // 竖向合并    chineseKeysList.forEach((chineseKeys, colIndex) => {      // 当前列,每一行都有数据,这无须要竖向合并      if (chineseKeys.length === headerRowNum) {        return      }      // 否则。存在数据须要竖向合并,竖向合并的行数,即为比最高行数少的行数      merges.push({        s: {          r: chineseKeys.length - 1 + headerFirstRow,          c: colIndex,        },        e: {          r: headerRowNum - 1 + headerFirstRow,          c: colIndex,        }      })    })    // 横向合并    for (let rowIndex = 0; rowIndex < headerRowNum; rowIndex++) {      const rowCells = chineseKeysList.map(chineseKeys => chineseKeys[rowIndex])      let preCell = '' // 前一个单元格      let merge = null // 以后合并配置项      rowCells.forEach((cell, colIndex) => {        if (preCell === cell) { // 如果二者雷同,则证实须要横向合并单元格          if (!merge) { // merge 不存在,则创立,            merge = {              s: {                r: rowIndex + headerFirstRow,                c: colIndex - 1              },              e: {                r: rowIndex + headerFirstRow,                c: colIndex              }            }            merges.push(merge) // 增加一个合并对象          } else {            merge.e.c = colIndex // 批改其合并完结列          }        } else {          preCell = cell          merge = null        }      })    }    return merges  }  // 获取转化数据结构为 Excel 数据行  function getDataRows(dataList) {    const dataRows = []    dataList.forEach(dataItem => {      const cells = []      englishKeysList.forEach(keyLevel => {        const value = keyLevel.reduce((dataItem, key) => dataItem[key] || '', dataItem).toString()        cells.push(value)      })      dataRows.push(cells)    })    return dataRows  }  const { englishKeysList, chineseKeysList } = getKeysList(textKeyMaps)  const headerRowNum = getHeaderRowNum(chineseKeysList)  const headerMerges = getMerges(headerRowNum, chineseKeysList)  const headerRows = getHeaderRows(headerRowNum, chineseKeysList)  const dataRows = getDataRows(dataList)  return {    headerMerges,    cells: [...headerRows, ...dataRows],  }}/** * 导出为携带款式的 xlsx 文件 * @param {*} param * @param  param.filename 导出的文件名 * @param  param.worksheet 导出的 sheet 数据 */function toStyleXlsx({ filename, worksheet }) {  const workbook = {    SheetNames: [filename],    Sheets: {      [filename]: worksheet,    },  }  // writeFile(workbook, filename, { bookType: 'xlsx' })  let wopts = {    bookType: 'xlsx',    bookSST: false,    type: 'binary'  }  let wbout = window.XLS.write(workbook, wopts) // 应用xlsx-style 写入  function s2ab(s) {    let buf = new ArrayBuffer(s.length)    let view = new Uint8Array(buf)    for (let i = 0; i !== s.length; ++i) {      // eslint-disable-next-line no-bitwise      view[i] = s.charCodeAt(i) & 0xFF    }    return buf  }  saveAs(new Blob([s2ab(wbout)], { type: '' }), filename)}

这里还有 Excel 合并单元格简单表头导入解析示例