Element UI / Element Plus 动态表格深度实践 — 动态列、权限控制、列排序、宽度记忆与右键菜单的完整解决方案

在中后台系统中, el-table 动态表格 几乎是标配。但当需求逐步复杂之后,很多问题会集中爆发:
- 列需要动态显示 / 隐藏
- 列顺序可配置并持久化
- 列宽拖拽后需要记忆 / 恢复默认
- 操作列按钮完全配置化(事件名不固定)
- 表头右键菜单 + 阻止浏览器默认菜单
- 动态列顺序更新后,
el-table不刷新
本文基于真实项目踩坑经验,给出一套 稳定、可扩展、可维护 的解决方案。
一、动态列的核心设计思想
1️⃣ 不要依赖 this.$refs.elTable.columns
el-table 内部的 columns :
- 初始化时才注册
- 有缓存(
realWidth、布局信息) - 生命周期和 Vue 不同步
👉 正确做法 : 完全由我们自己维护一份 columns 配置数组,用 v-for 渲染 <el-table-column> 。
二、基础动态列结构设计
tableColumns = [
{
prop: 'userInfo',
label: '用户信息',
width: 240,
show: true, // boolean | function(row, index)
slot: 'table-user-info'
},
{
prop: 'action',
label: '操作',
width: 140,
fixed: 'right',
slot: 'ActionCell',
dropdown: { atLeast: 1 },
custom: [
{ text: '编辑', type: 'primary', emit: 'edit', show: true },
{ text: '删除', type: 'danger', emit: 'delete', show: true }
]
}
]show 支持三种形态
show: true // 始终显示
show: false // 始终隐藏
show: (row, index) => {} // 按行控制显示三、动态渲染 el-table-column(关键)
<el-table :data="tableData" ref="dataTable">
<el-table-column
v-for="col in tableColumns"
v-if="col.show !== false"
:key="(col.prop || col.label) + '-' + col.order"
:prop="col.prop"
:label="col.label"
:width="col.width || undefined"
:fixed="col.fixed"
>
<template #default="scope">
<!-- 操作列 -->
<table-action-cell
v-if="col.slot === 'ActionCell'"
:row="scope.row"
:custom="col.custom"
:dropdown="col.dropdown"
v-on="actionListeners"
/>
<!-- 普通插槽列 -->
<component
v-else-if="col.slot"
:is="col.slot"
:row="scope.row"
:value="scope.row[col.prop]"
/>
<!-- 默认 -->
<span v-else>{{ scope.row[col.prop] }}</span>
</template>
</el-table-column>
</el-table>四、操作列完全配置化(事件名不固定)
1️⃣ 子组件:动态 emit
<el-button
v-for="btn in custom"
:key="btn.emit"
:type="btn.type"
@click="$emit(btn.emit, row)"
>
{{ btn.text }}
</el-button>2️⃣ 父组件:动态事件监听器生成
computed: {
actionListeners() {
const actionCol = this.tableColumns.find(v => v.slot === 'ActionCell');
const ret = {};
actionCol?.custom?.forEach(item => {
ret[item.emit] = row => this.$emit(item.emit, row);
});
return ret;
}
}❗ 注意:
this.$emit(item.emit)不能直接写 ,必须返回函数。
五、列显示隐藏(Checkbox 控制)
列配置对象结构
columns = {
name: { width: '', show: true },
age: { width: '', show: false }
}绑定 checkbox-group
<el-checkbox-group v-model="columnsCheck" @change="syncColumns">
<el-checkbox
v-for="(col, key) in columns"
:key="key"
:label="key"
>
{{ key }}
</el-checkbox>
</el-checkbox-group>mounted() {
this.columnsCheck = Object.keys(this.columns).filter(
k => this.columns[k].show
)
},
methods: {
syncColumns(keys) {
Object.keys(this.columns).forEach(k => {
this.columns[k].show = keys.includes(k)
})
}
}六、列顺序 + 宽度 + 显隐的持久化合并
合并规则
- 用
label关联 - 按
colData顺序排列 - 不存在的列排最后
width !== ''才覆盖show始终覆盖
updateColumnsByColData() {
const map = new Map(this.colData.map((c, i) => [c.label, { ...c, _order: i }]))
const ordered = this.colData.map((cfg, i) => {
const t = this.tableColumns.find(x => x.label === cfg.label)
if (!t) return null
return {
...t,
order: i,
width: cfg.width !== '' ? cfg.width : t.width,
show: cfg.show ?? t.show
}
}).filter(Boolean)
const rest = this.tableColumns
.filter(c => !map.has(c.label))
.map((c, i) => ({ ...c, order: ordered.length + i }))
this.tableColumns = [...ordered, ...rest]
}七、⚠️ 为什么列顺序变了,el-table 不变?
原因(核心坑)
el-table按 key 复用 DOM- 顺序变了,但 key 没变 → 不重建列
✅ 终极解决方案
让顺序参与 key
:key="(col.prop || col.label) + '-' + col.order"这是整个动态列系统里 最关键的一行代码 。
八、恢复默认列宽( width='' 无效)
正确方式
resetColumnWidth() {
this.$refs.dataTable.columns.forEach(col => {
delete col.width
delete col.realWidth
})
this.$refs.dataTable.doLayout()
}九、表头右键菜单 + 阻止浏览器默认菜单
<el-table @header-contextmenu="onHeaderContextMenu" />onHeaderContextMenu(column, event) {
event.preventDefault()
this.showMenu = true
this.menuX = event.pageX
this.menuY = event.pageY
}点击任意位置关闭
mounted() {
document.addEventListener('click', this.closeMenu)
},
beforeUnmount() {
document.removeEventListener('click', this.closeMenu)
},
methods: {
closeMenu() {
this.showMenu = false
}
}十、总结(最佳实践)
✅ 只信自己的 columns,不信 el-table 内部 columns
✅ 动态列必须用 v-for + 正确 key
✅ 顺序变动 → key 必须变
✅ 配置驱动一切(show / width / emit)
✅ 不要直接操作 DOM / 私有 API
发布评论
发布评论前请先 登录。
评论列表 0

暂无评论





