vxe-table 将行与列反转展示
在日常开发中,我们常遇到这样的需求:将一张常规表格(行=记录,列=字段)转置展示,即把原表的列名变为行数据,原表的每一行数据变为新表的一列。这种“行列反转”在统计报表、数据对比、参数配置等场景中非常实用。
vxe-table 本身不提供内置的转置功能,但借助其灵活的 columns 和 data 配置,我们可以通过简单的数据转换轻松实现。本文将详细拆解实现逻辑,并提供可复用的代码模板。
需求场景与效果预览
假设原表格结构如下(横向展示多条记录的多个字段):
| ID | Name | Role | Sex | Age | Address |
|---|---|---|---|---|---|
| 10001 | Test1 | Develop | Man | 28 | Shenzhen |
| 10002 | Test2 | Test | Women | 22 | Beijing |
| ... | ... | ... | ... | ... | ... |
转置后,我们希望得到这样的表格:
| 字段名 | 记录1 | 记录2 | 记录3 | ... |
|---|---|---|---|---|
| ID | 10001 | 10002 | 10003 | ... |
| Name | Test1 | Test2 | Test3 | ... |
| Role | Develop | Test | PM | ... |
| Sex | Man | Women | Man | ... |
| Age | 28 | 22 | 32 | ... |
| Address | Shenzhen | Beijing | Shanghai | ... |
即原字段作为行,原每条记录作为列,形成类似“数据透视”的展示方式。
核心实现思路
vxe-grid 的数据由 columns(列定义)和 data(数据行)决定。要实现转置,我们需要:
- 新表头(columns):原数据的每一条记录对应新表的一列(col1, col2, ...),并增加一个固定列用于显示原字段名。
- 新数据(data):原表的每个字段(field)对应新表的一行,该行的各列值即为原数据中各记录的对应字段值。
因此,转换逻辑本质上是二维数组的转置操作,只是我们保留原字段名作为第一列的值。
分步实现详解
准备原始数据
定义原始列配置 myColumns(包含所有字段)和原始数据 myData(多条记录):
const myColumns = [
{ field: 'id', title: 'ID' },
{ field: 'name', title: 'Name' },
// ... 更多字段
]
const myData = [
{ id: 10001, name: 'Test1', ... },
{ id: 10002, name: 'Test2', ... },
// ... 更多记录
]
编写转换函数 reverseTable
该函数负责构建新表格的 columns 和 data:
const reverseTable = () => {
// 1. 构建新数据:每个原字段对应一行,行内各列为原各条记录的值
const buildData = myColumns.map((column) => {
const item = { col0: column.title } // 第一列存放字段名(原title)
myData.forEach((row, index) => {
item[`col${index + 1}`] = row[column.field] // 动态列名 col1, col2, ...
})
return item
})
// 2. 构建新列定义:第一列固定显示字段名,其余列对应原各条记录
const buildColumns = [
{ field: 'col0', fixed: 'left', width: 100 } // 固定左侧便于浏览
]
myData.forEach((_, index) => {
buildColumns.push({
field: `col${index + 1}`,
minWidth: 200 // 可根据内容调整
})
})
// 3. 赋值给 gridOptions
gridOptions.data = buildData
gridOptions.columns = buildColumns
}
关键点说明:
- col0 固定列用于展示原字段名,使用 fixed: 'left' 使其在水平滚动时保持可见。
- 动态列名 col1, col2, ... 对应原数据中的第1条、第2条记录,顺序与原数组索引一致。
- minWidth: 200 保证内容较长的字段(如 Address)能完整展示,也可根据需要设为固定 width。
样式微调(可选)
为了让转置后的表格更易读,通常将第一列(字段名列)赋予特殊背景色,突出其“标签”属性
.reverse-table .vxe-body--row .vxe-body--column:first-child {
background-color: #f8f8f9;
}
代码

<template>
<div>
<vxe-grid class="reverse-table" v-bind="gridOptions"></vxe-grid>
</div>
</template>
<script setup>
import { reactive } from 'vue'
const gridOptions = reactive({
border: true,
showOverflow: true,
height: 400,
showHeader: false,
columns: [],
data: []
})
const myColumns = [
{ field: 'id', title: 'ID' },
{ field: 'name', title: 'Name' },
{ field: 'role', title: 'Role' },
{ field: 'sex', title: 'Sex' },
{ field: 'age', title: 'Age' },
{ field: 'address', title: 'Address' },
{ field: 'rate', title: 'Rate' },
{ field: 'date3', title: 'Date' },
{ field: 'updateTime', title: 'UpdateTime' },
{ field: 'createTime', title: 'CreateTime' }
]
const myData = [
{ id: 10001, name: 'Test1', nickname: 'T1', role: 'Develop', sex: 'Man', age: 28, rate: 2, address: 'Shenzhen', date3: '20:30', updateTime: '2020-09-16', createTime: '2020-09-16' },
{ id: 10002, name: 'Test2', nickname: 'T2', role: 'Test', sex: 'Women', age: 22, rate: 2, address: 'Beijin', date3: '20:30', updateTime: '2019-09-16', createTime: '2020-09-16' },
{ id: 10003, name: 'Test3', nickname: 'T3', role: 'PM', sex: 'Man', age: 32, rate: 6, address: 'Shanghai', date3: '20:30', updateTime: '2020-09-16', createTime: '2021-09-16' },
{ id: 10004, name: 'Test4', nickname: 'T4', role: 'Designer', sex: 'Women', age: 23, rate: 2, address: 'Shenzhen', date3: '20:30', updateTime: '2020-09-16', createTime: '2020-09-16' },
{ id: 10005, name: 'Test5', nickname: 'T5', role: 'Develop', sex: 'Women', age: 30, rate: 10, address: 'Shanghai Beijin Shenzhen Shenzhen Guangzhou', date3: '20:30', updateTime: '2020-01-16', createTime: '2020-10-16' },
{ id: 10006, name: 'Test6', nickname: 'T6', role: 'Designer', sex: 'Women', age: 21, rate: 2, address: 'Shenzhen', date3: '20:30', updateTime: '2018-09-16', createTime: '2020-09-16' },
{ id: 10007, name: 'Test7', nickname: 'T7', role: 'Test', sex: 'Man', age: 29, rate: 3, address: 'Shenzhen', date3: '20:30', updateTime: '2020-01-16', createTime: '2020-10-16' },
{ id: 10008, name: 'Test8', nickname: 'T8', role: 'Develop', sex: 'Man', age: 35, rate: 2, address: 'Beijin Beijin Beijin', date3: '20:30', updateTime: '2017-09-16', createTime: '2020-09-16' },
{ id: 10009, name: 'Test9', nickname: 'T9', role: 'Test', sex: 'Man', age: 29, rate: 3, address: 'Shenzhen', date3: '20:30', updateTime: '2020-01-16', createTime: '2020-10-16' },
{ id: 10010, name: 'Test10', nickname: 'T10', role: 'Develop', sex: 'Man', age: 50, rate: 5, address: 'Shenzhen', date3: '20:30', updateTime: '2017-09-16', createTime: '2020-09-16' }
]
const reverseTable = () => {
const buildData = myColumns.map((column) => {
const item = { col0: column.title }
myData.forEach((row, index) => {
item[`col${index + 1}`] = row[column.field]
})
return item
})
const buildColumns = [{ field: 'col0', fixed: 'left', width: 100 }]
myData.forEach((item, index) => {
buildColumns.push({
field: `col${index + 1}`,
minWidth: 200
})
})
gridOptions.data = buildData
gridOptions.columns = buildColumns
}
reverseTable()
</script>
<style lang="scss">
.reverse-table .vxe-body--row .vxe-body--column:first-child {
background-color: #f8f8f9;
}
</style>
注意事项与优化建议
-
数据量限制
- 转置后,列数 = 原记录数,行数 = 原字段数。若原记录过多(如超过 50 条),新表会有大量列,水平滚动体验变差。建议对记录数进行分页或限制展示条数。
-
字段值为对象或复杂类型
- 若原字段值为对象、数组等,直接赋值会显示 [object Object]。请在转换时进行格式化,例如调用 JSON.stringify 或提取特定属性。
-
缺失值处理
- 若原记录中某些字段不存在,row[column.field] 会返回 undefined,单元格将显示空。可根据需要设置默认值(如 '—')。
-
表头与列名
- 示例中设置 showHeader: false,因为第一行(字段名)已承担表头角色。若需显示列标题(如“记录1”、“记录2”),可保留 showHeader: true,并在 buildColumns 中设置 title 属性。
-
固定列与滚动
- 将第一列固定(fixed: 'left')有助于用户在横向滚动时始终看到字段名称,提升可读性。
-
动态更新
- 若原始数据变化,需重新调用 reverseTable 更新 gridOptions。可将其封装为方法,并在数据变更时触发。
通过简单的数据映射,我们利用 vxe-table 的基础能力实现了行列反转功能。代码清晰、无侵入,且保留了组件原有的排序、筛选、导出等扩展能力。
该方法适用于字段数量有限、记录数量适中的场景,是数据透视展示的一种轻量级替代方案。若需更复杂的多维分析,可考虑集成专业的透视表库,但对于大多数管理后台需求,本方案已足够。

浙公网安备 33010602011771号