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 可能有细微差异,请查阅对应文档。 |