vxe-table 导出 Excel 进阶教程:自定义样式与高级功能

vxe-table 不仅支持将表格数据导出为 .xlsx 文件,还提供了强大的自定义样式能力。通过 exportConfig.sheetMethod 钩子,你可以直接操作底层的 ExcelJS 工作表对象,对导出的 Excel 进行精细化样式设置,包括边框、字体、背景色、列宽、行高、超链接、图片等。

  • 前置准备:请确保已安装并注册 @vxe-ui/plugin-export-xlsx 和 exceljs 插件。

核心配置:sheetMethod 钩子

在 gridOptions.exportConfig 中定义 sheetMethod 函数,它会在导出过程中被调用,并接收包含 worksheet(工作表对象)、workbook(工作簿对象)等参数。在此函数内,你可以使用 ExcelJS 的 API 修改样式。

javascript 复制代码
exportConfig: {
  sheetMethod(params) {
    const { worksheet, workbook } = params
    // 自定义样式逻辑
  }
}

示例

设置边框

为所有单元格添加红色细边框:

html 复制代码
<template>
  <div>
    <vxe-button @click="exportEvent">点击导出</vxe-button>
    <vxe-grid ref="gridRef" v-bind="gridOptions"></vxe-grid>
  </div>
</template>

<script setup>
import { ref, reactive } from 'vue'

const gridRef = ref()

const gridOptions = reactive({
  border: true,
  showFooter: true,
  exportConfig: {
    sheetMethod(params) {
      const { worksheet } = params
      worksheet.eachRow((excelRow) => {
        excelRow.eachCell((excelCell) => {
          // 设置单元格边框
          excelCell.border = {
            top: {
              style: 'thin',
              color: {
                argb: 'ff0000'
              }
            },
            left: {
              style: 'thin',
              color: {
                argb: 'ff0000'
              }
            },
            bottom: {
              style: 'thin',
              color: {
                argb: 'ff0000'
              }
            },
            right: {
              style: 'thin',
              color: {
                argb: 'ff0000'
              }
            }
          }
        })
      })
    }
  },
  columns: [
    { field: 'seq', type: 'seq', width: 70 },
    {
      title: '分组1',
      children: [
        { field: 'name', title: 'Name' },
        { field: 'role', title: 'Role' }
      ]
    },
    { field: 'sex', title: 'Sex' },
    { field: 'no1', title: 'NO1' },
    { field: 'no2', title: 'NO2 String', cellType: 'string' }
  ],
  data: [
    { id: 10001, name: '张三', role: 'Develop', sex: 'Man', no1: '028', no2: '028' },
    { id: 10002, name: '李四', role: '研发', sex: 'Women', no1: '220', no2: '220' },
    { id: 10003, name: '王五', role: '产品经理', sex: 'Man', no1: '003200', no2: '003200' },
    { id: 10004, name: '老六', role: 'Designer', sex: 'Women', no1: '02040', no2: '02040' }
  ],
  footerData: [{ seq: '合计', name: '12人', no1: '356' }]
})

const exportEvent = () => {
  const $grid = gridRef.value
  if ($grid) {
    $grid.exportData({
      type: 'xlsx'
    })
  }
}
</script>

设置字体

将所有单元格字体设为红色、粗体、16号:

html 复制代码
<template>
  <div>
    <vxe-button @click="exportEvent">点击导出</vxe-button>
    <vxe-grid ref="gridRef" v-bind="gridOptions"></vxe-grid>
  </div>
</template>

<script setup>
import { ref, reactive } from 'vue'

const gridRef = ref()

const gridOptions = reactive({
  border: true,
  showFooter: true,
  exportConfig: {
    sheetMethod(params) {
      const { worksheet } = params
      worksheet.eachRow((excelRow) => {
        excelRow.eachCell((excelCell) => {
          // 设置单元格字体
          excelCell.font = {
            bold: true,
            size: 16,
            color: {
              argb: 'ff0000'
            }
          }
        })
      })
    }
  },
  columns: [
    { field: 'seq', type: 'seq', width: 70 },
    {
      title: '分组1',
      children: [
        { field: 'name', title: 'Name' },
        { field: 'role', title: 'Role' }
      ]
    },
    { field: 'sex', title: 'Sex' },
    { field: 'no1', title: 'NO1' },
    { field: 'no2', title: 'NO2 String', cellType: 'string' }
  ],
  data: [
    { id: 10001, name: '张三', role: 'Develop', sex: 'Man', no1: '028', no2: '028' },
    { id: 10002, name: '李四', role: '研发', sex: 'Women', no1: '220', no2: '220' },
    { id: 10003, name: '王五', role: '产品经理', sex: 'Man', no1: '003200', no2: '003200' },
    { id: 10004, name: '老六', role: 'Designer', sex: 'Women', no1: '02040', no2: '02040' }
  ],
  footerData: [{ seq: '合计', name: '12人', no1: '356' }]
})

const exportEvent = () => {
  const $grid = gridRef.value
  if ($grid) {
    $grid.exportData({
      type: 'xlsx'
    })
  }
}
</script>

设置表头背景

将前 3 行(表头区域)填充为淡蓝色背景

html 复制代码
<template>
  <div>
    <vxe-button @click="exportEvent">点击导出</vxe-button>
    <vxe-grid ref="gridRef" v-bind="gridOptions"></vxe-grid>
  </div>
</template>

<script setup>
import { ref, reactive } from 'vue'

const gridRef = ref()

const gridOptions = reactive({
  border: true,
  showFooter: true,
  exportConfig: {
    sheetMethod(params) {
      const { worksheet } = params
      worksheet.eachRow((excelRow, rowIndex) => {
        if (rowIndex <= 2) {
          excelRow.eachCell((excelCell) => {
            // 填充单元格背景
            excelCell.fill = {
              type: 'pattern',
              pattern: 'solid',
              fgColor: {
                argb: 'c5d9f1'
              }
            }
          })
        }
      })
    }
  },
  columns: [
    { field: 'seq', type: 'seq', width: 70 },
    {
      title: '分组1',
      children: [
        { field: 'name', title: 'Name' },
        { field: 'role', title: 'Role' }
      ]
    },
    { field: 'sex', title: 'Sex' },
    { field: 'no1', title: 'NO1' },
    { field: 'no2', title: 'NO2 String', cellType: 'string' }
  ],
  data: [
    { id: 10001, name: '张三', role: 'Develop', sex: 'Man', no1: '028', no2: '028' },
    { id: 10002, name: '李四', role: '研发', sex: 'Women', no1: '220', no2: '220' },
    { id: 10003, name: '王五', role: '产品经理', sex: 'Man', no1: '003200', no2: '003200' },
    { id: 10004, name: '老六', role: 'Designer', sex: 'Women', no1: '02040', no2: '02040' }
  ],
  footerData: [{ seq: '合计', name: '12人', no1: '356' }]
})

const exportEvent = () => {
  const $grid = gridRef.value
  if ($grid) {
    $grid.exportData({
      type: 'xlsx'
    })
  }
}
</script>

设置列宽

将所有列宽设置为 16:

html 复制代码
<template>
  <div>
    <vxe-button @click="exportEvent">点击导出</vxe-button>
    <vxe-grid ref="gridRef" v-bind="gridOptions"></vxe-grid>
  </div>
</template>

<script setup>
import { ref, reactive } from 'vue'

const gridRef = ref()

const gridOptions = reactive({
  border: true,
  showFooter: true,
  exportConfig: {
    async sheetMethod(params) {
      const { worksheet } = params
      worksheet.columns.forEach((sheetColumn) => {
        // 设置列宽
        sheetColumn.width = 16
      })
    }
  },
  columns: [
    { field: 'seq', type: 'seq', width: 70 },
    {
      title: '分组1',
      children: [
        { field: 'name', title: 'Name' },
        { field: 'role', title: 'Role' }
      ]
    },
    { field: 'sex', title: 'Sex' },
    { field: 'no1', title: 'NO1' },
    { field: 'no2', title: 'NO2 String', cellType: 'string' }
  ],
  data: [
    { id: 10001, name: '张三', role: 'Develop', sex: 'Man', no1: '028', no2: '028' },
    { id: 10002, name: '李四', role: '研发', sex: 'Women', no1: '220', no2: '220' },
    { id: 10003, name: '王五', role: '产品经理', sex: 'Man', no1: '003200', no2: '003200' },
    { id: 10004, name: '老六', role: 'Designer', sex: 'Women', no1: '02040', no2: '02040' }
  ],
  footerData: [{ seq: '合计', name: '12人', no1: '356' }]
})

const exportEvent = () => {
  const $grid = gridRef.value
  if ($grid) {
    $grid.exportData({
      type: 'xlsx'
    })
  }
}
</script>

设置行高

将所有数据行高设置为 60(单位:磅):

html 复制代码
<template>
  <div>
    <vxe-button @click="exportEvent">点击导出</vxe-button>
    <vxe-grid ref="gridRef" v-bind="gridOptions"></vxe-grid>
  </div>
</template>

<script setup>
import { ref, reactive } from 'vue'

const gridRef = ref()

const gridOptions = reactive({
  border: true,
  showFooter: true,
  exportConfig: {
    async sheetMethod(params) {
      const { worksheet } = params
      worksheet.eachRow((excelRow) => {
        // 设置行高
        excelRow.height = 60
      })
    }
  },
  columns: [
    { field: 'seq', type: 'seq', width: 70 },
    {
      title: '分组1',
      children: [
        { field: 'name', title: 'Name' },
        { field: 'role', title: 'Role' }
      ]
    },
    { field: 'sex', title: 'Sex' },
    { field: 'no1', title: 'NO1' },
    { field: 'no2', title: 'NO2 String', cellType: 'string' }
  ],
  data: [
    { id: 10001, name: '张三', role: 'Develop', sex: 'Man', no1: '028', no2: '028' },
    { id: 10002, name: '李四', role: '研发', sex: 'Women', no1: '220', no2: '220' },
    { id: 10003, name: '王五', role: '产品经理', sex: 'Man', no1: '003200', no2: '003200' },
    { id: 10004, name: '老六', role: 'Designer', sex: 'Women', no1: '02040', no2: '02040' }
  ],
  footerData: [{ seq: '合计', name: '12人', no1: '356' }]
})

const exportEvent = () => {
  const $grid = gridRef.value
  if ($grid) {
    $grid.exportData({
      type: 'xlsx'
    })
  }
}
</script>

设置超链接

为第 2 列(Name 列)的每个数据单元格添加超链接:

html 复制代码
<template>
  <div>
    <vxe-button @click="exportEvent">点击导出</vxe-button>
    <vxe-grid ref="gridRef" v-bind="gridOptions"></vxe-grid>
  </div>
</template>

<script setup>
import { ref, reactive } from 'vue'

const gridRef = ref()

const gridOptions = reactive({
  border: true,
  showFooter: true,
  exportConfig: {
    sheetMethod(params) {
      const { worksheet } = params
      worksheet.eachRow((excelRow, rowIndex) => {
        if (rowIndex > 2) {
          excelRow.eachCell((excelCell, columnIndex) => {
            if (columnIndex === 2) {
              // 设置指定单元格为超链接
              excelCell.value = {
                text: `${excelCell.value}`,
                hyperlink: 'https://vxeui.com',
                tooltip: 'vxeui.com'
              }
              // 设置单元格字体
              excelCell.font = {
                color: {
                  argb: '0000ff'
                }
              }
            }
          })
        }
      })
    }
  },
  columns: [
    { field: 'seq', type: 'seq', width: 70 },
    {
      title: '分组1',
      children: [
        { field: 'name', title: 'Name' },
        { field: 'role', title: 'Role' }
      ]
    },
    { field: 'sex', title: 'Sex' },
    { field: 'no1', title: 'NO1' },
    { field: 'no2', title: 'NO2 String', cellType: 'string' }
  ],
  data: [
    { id: 10001, name: '张三', role: 'Develop', sex: 'Man', no1: '028', no2: '028' },
    { id: 10002, name: '李四', role: '研发', sex: 'Women', no1: '220', no2: '220' },
    { id: 10003, name: '王五', role: '产品经理', sex: 'Man', no1: '003200', no2: '003200' },
    { id: 10004, name: '老六', role: 'Designer', sex: 'Women', no1: '02040', no2: '02040' }
  ],
  footerData: [{ seq: '合计', name: '12人', no1: '356' }]
})

const exportEvent = () => {
  const $grid = gridRef.value
  if ($grid) {
    $grid.exportData({
      type: 'xlsx'
    })
  }
}
</script>

添加图片

在数据行的第 2 列单元格位置插入 Logo 图片(需确保图片服务器支持跨域):

html 复制代码
<template>
  <div>
    <vxe-button @click="exportEvent">点击导出</vxe-button>
    <vxe-grid ref="gridRef" v-bind="gridOptions"></vxe-grid>
  </div>
</template>

<script setup>
import { ref, reactive } from 'vue'

const gridRef = ref()

const gridOptions = reactive({
  border: true,
  showFooter: true,
  exportConfig: {
    async sheetMethod(params) {
      const { worksheet, workbook } = params
      // 加载图片
      const buffer1 = await fetch('https://vxeui.com/logo.png').then((res) => res.arrayBuffer())
      const imageId1 = workbook.addImage({
        buffer: buffer1,
        extension: 'png'
      })
      worksheet.eachRow((excelRow, rowIndex) => {
        if (rowIndex > 2) {
          // 设置行高
          excelRow.height = 60
          excelRow.eachCell((excelCell, columnIndex) => {
            if (columnIndex === 2) {
              // 将图片添加到单元格
              worksheet.addImage(imageId1, {
                tl: { col: columnIndex - 1, row: rowIndex - 1 },
                ext: { width: 40, height: 40 }
              })
            }
          })
        }
      })
    }
  },
  columns: [
    { field: 'seq', type: 'seq', width: 70 },
    {
      title: '分组1',
      children: [
        { field: 'name', title: 'Name' },
        { field: 'role', title: 'Role' }
      ]
    },
    { field: 'sex', title: 'Sex' },
    { field: 'no1', title: 'NO1' },
    { field: 'no2', title: 'NO2 String', cellType: 'string' }
  ],
  data: [
    { id: 10001, name: '张三', role: 'Develop', sex: 'Man', no1: '028', no2: '028' },
    { id: 10002, name: '李四', role: '研发', sex: 'Women', no1: '220', no2: '220' },
    { id: 10003, name: '王五', role: '产品经理', sex: 'Man', no1: '003200', no2: '003200' },
    { id: 10004, name: '老六', role: 'Designer', sex: 'Women', no1: '02040', no2: '02040' }
  ],
  footerData: [{ seq: '合计', name: '12人', no1: '356' }]
})

const exportEvent = () => {
  const $grid = gridRef.value
  if ($grid) {
    $grid.exportData({
      type: 'xlsx'
    })
  }
}
</script>

说明

事项 说明
异步处理 如果 sheetMethod 中包含异步操作(如 fetch 图片),务必使用 async 函数,并等待完成。
行/列索引偏移 worksheet.eachRow 回调中 rowIndex 从 1 开始(1 为第一行)。列索引同样从 1 开始。
图片跨域 使用 fetch 加载图片时,需确保图片服务器支持跨域,否则无法获取图片内容。
性能考虑 大表格(如超过 1000 行)时,遍历所有单元格设置样式可能耗时较长,建议按需设置,或仅对特定区域操作。
样式覆盖 某些全局样式(如列宽)如果在 sheetMethod 中设置,会覆盖默认导出样式;多步设置时注意顺序。
ExcelJS 版本兼容 插件依赖 exceljs,建议版本 ^4.3.0,不同版本 API 可能有细微差异,请查阅对应文档。

https://vxetable.cn

相关推荐
JieE2121 小时前
LeetCode 226. 翻转二叉树|JS 递归超详细拆解,二叉树入门经典题
javascript·算法
JieE2121 小时前
LeetCode 104. 二叉树的最大深度|递归思路超详细拆解
javascript·算法
爱勇宝2 小时前
鸿蒙生态的下半场:开发者不只要能开发,还要能赚钱
android·前端·程序员
IT_陈寒5 小时前
SpringBoot这个自动配置坑我跳了三次
前端·人工智能·后端
kyriewen5 小时前
我用 AI 一周写完了整个项目,上线第一天就崩了——这是我踩过最贵的 5 个坑
前端·javascript·ai编程
Larcher6 小时前
AI Loop:让AI像人一样自主完成任务的核心机制
javascript·人工智能·设计模式
默_笙6 小时前
🃏 JS 只有 8 种数据类型,但我花了 2 天才搞懂 null 和 undefined 的区别
javascript
牧艺6 小时前
从零到协同:构建类飞书在线文档系统的五个技术重难点
前端·人工智能
jump_jump6 小时前
流式 HTML:从 htmx 片段装配到浏览器原生增量渲染
javascript·性能优化·前端工程化
红尘散仙6 小时前
想写一个像样的终端 App?试试把 React 的开发体验搬进 Rust TUI
前端·rust