将 Element UI 表格元素导出为 Excel 文件(处理了多级表头和固定列导出的问题)

javascript 复制代码
import { saveAs } from 'file-saver'
import XLSX from 'xlsx'
/**
 * 将 Element UI 表格元素导出为 Excel 文件
 * @param {HTMLElement} el - 要导出的 Element UI 表格的 DOM 元素
 * @param {string} filename - 导出的 Excel 文件的文件名(不包含扩展名)
 */
export function exportElementTable(el, filename) {
  // 深拷贝表格元素避免污染原始DOM
  const clonedEl = el.cloneNode(true)

  // 移除fixed列容器(避免重复内容)
  const fixedElements = clonedEl.querySelectorAll('.el-table__fixed, .el-table__fixed-right, .el-table__fixed-left')
  if (fixedElements && fixedElements.length > 0) {
    for (let i = 0; i < fixedElements.length; i++) {
      const fixedEl = fixedElements[i]
      if (fixedEl.parentNode) {
        fixedEl.parentNode.removeChild(fixedEl)
      }
    }
  }

  // 获取原始表头和表体的核心table元素
  const headerTable = clonedEl.querySelector('.el-table__header-wrapper table')
  const bodyTable = clonedEl.querySelector('.el-table__body-wrapper table')

  // 创建新表格容器(保留原始table的class和样式)
  const mergedTable = document.createElement('table')
  if (headerTable && headerTable.className) {
    mergedTable.setAttribute('class', headerTable.className)
  }

  // 复制表头结构(关键:保留多级表头的tr层级)
  if (headerTable) {
    const thead = document.createElement('thead')
    const headerRows = headerTable.getElementsByTagName('tr')
    if (headerRows && headerRows.length > 0) {
      for (let i = 0; i < headerRows.length; i++) {
        const tr = headerRows[i]
        thead.appendChild(tr.cloneNode(true))
      }
    }
    mergedTable.appendChild(thead)
  }

  // 复制表体结构(保留数据行)
  if (bodyTable) {
    const tbody = document.createElement('tbody')
    const bodyRows = bodyTable.getElementsByTagName('tr')
    if (bodyRows && bodyRows.length > 0) {
      for (let i = 0; i < bodyRows.length; i++) {
        const tr = bodyRows[i]
        tbody.appendChild(tr.cloneNode(true))
      }
    }
    mergedTable.appendChild(tbody)
  }

  // 关键修复:重新计算并保留合并单元格属性
  const allCells = mergedTable.getElementsByTagName('th')
  const dataCells = mergedTable.getElementsByTagName('td')
  const combinedCells = [].concat(Array.from(allCells), Array.from(dataCells))

  for (let i = 0; i < combinedCells.length; i++) {
    const cell = combinedCells[i]
    if (cell) {
      const rowSpan = cell.getAttribute('data-rowspan') || 1
      const colSpan = cell.getAttribute('data-colspan') || 1
      if (rowSpan > 1) {
        cell.setAttribute('rowspan', rowSpan)
      }
      if (colSpan > 1) {
        cell.setAttribute('colspan', colSpan)
      }
    }
  }

  // 转换配置(关键:启用display模式保留表格结构)
  const workbook = XLSX.utils.table_to_book(mergedTable, {
    raw: true,
    display: true, // 启用display模式,正确解析合并单元格和层级
    cellDates: true, // 保留日期格式(可选)
  })

  // 生成并保存文件
  const wbout = XLSX.write(workbook, {
    bookType: 'xlsx',
    bookSST: true,
    type: 'array',
  })

  saveAs(
    new Blob([wbout], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }),
    `${filename}.xlsx`
  )
}
相关推荐
竹林8182 分钟前
用 The Graph 查询链上数据实战:从手搓 RPC 到 Subgraph,我的 NFT 项目数据加载快了 10 倍
前端·javascript
妙码生花18 分钟前
从 PHP 到 AI + Golang,程序员自救转型手记(十九):点选验证码代码逐行目检
前端·后端·go
Awu12271 小时前
⚡从零开发 Agent CLI(五)实现一个可治理、可扩展的工具系统
前端·人工智能·claude
咪库咪库咪1 小时前
Vue3-生命周期
前端
莪_幻尘2 小时前
你的 AI Skill 越多越蠢?Token 上下文爆炸的求生指南
前端·ai编程
lichenyang4532 小时前
从 has.echo 到异步 API 注册表:一次 ASCF API 回调不触发的排查复盘
前端
林瞅瞅2 小时前
Nuxt3 项目部署 Nginx 防盗链后特定 JS 文件 403 问题修复方案
前端
kyriewen3 小时前
别再每次都 Google 了:我整理了前端日常最常踩的 10 个 Git 坑,附速查表
前端·javascript·git
一颗奇趣蛋3 小时前
Web 视频开发完全指南:从入门到精通
前端
非洲农业不发达3 小时前
windows终端体验大升级,让你拥有macos级别的美化
前端·后端