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

相关推荐
我叫张土豆14 小时前
从 0 到 1 搭一个可用的 Vue Flow 工作流编排器(含下载/加载/自动布局)
前端·javascript·vue.js
止水编程 water_proof14 小时前
Spring Web MVC 入门
前端·spring·mvc
Beginner x_u14 小时前
前端八股整理(代码输出 01)|this指针输出题
前端·javascript·this 指针·代码输出
ZC跨境爬虫14 小时前
跟着 MDN 学CSS day_24:(CSS调试完全指南)
前端·css·ui·html·tensorflow
fish_xk14 小时前
c++11(二)
java·前端·c++
Jinuss14 小时前
Ant Design Slider Tooltip 的一个坑:页面抖动问题与自定义 Tooltip 方案
前端·antdesign
智商不够_熬夜来凑14 小时前
【Timeline】
前端·javascript·vue.js
杨运交15 小时前
[024][Web模块]基于 AntiSamy 的 Spring Boot XSS 防护实践:从过滤器到反序列化的多层防御
前端·spring boot·xss
学点程序15 小时前
HyperFrames:用 HTML 生成视频的开源渲染框架
前端·开源·html·音视频