后台系统里面表格页面很多,列配置一般还是固定写在页面里,这里主要记录一下 Element Plus 表格高度自适应的处理
后台项目中,列表页是最常见的页面。
我的表格列并不是动态配置出来的,还是正常在页面里写 el-table-column,这样看起来更直观,后期改某一列也方便。
真正需要统一处理的是表格高度。
因为后台页面经常会有搜索区域、分页、顶部标签页、左侧菜单、浏览器窗口变化等情况,如果表格高度不处理好,就很容易出现页面双滚动条或者表格撑出页面的问题。
页面结构
页面结构大概是这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <div class="page-layout table-demo-page"> <div class="search-component"> 搜索区域 </div>
<div ref="table" class="Table-page"> <el-table ref="tableList" v-loading="tableLoading" :data="tableData" stripe border :max-height="tableHeight" size="large" > <el-table-column align="center" prop="id" label="编号" width="90" /> <el-table-column align="center" prop="name" label="名称" min-width="160" /> <el-table-column align="center" prop="createdAt" label="创建时间" :formatter="formatterTime" min-width="180" /> </el-table>
<Pagination v-model="page" :total="total" @handleCurrentChange="changePage" /> </div> </div>
|
这里主要注意两个地方:
ref="table":外层表格区域,用来计算位置和分页高度
:max-height="tableHeight":Element Plus 表格最大高度
列还是按普通写法写,重点是把 tableHeight 算准确。
为什么用max-height
Element Plus 的表格如果不限制高度,数据多的时候会直接把页面撑开。
后台页面一般不希望整页一直往下滚,而是希望:
- 搜索区域固定在上面
- 分页固定在表格下面
- 表格内容区域内部滚动
- 外层布局不要出现多余滚动条
所以这里使用 max-height。
1 2
| <el-table :max-height="tableHeight"> </el-table>
|
当数据多的时候,表格内部会出现滚动条,页面整体高度就比较稳定。
初始字段
我把表格高度相关的字段放在 tableMixin.js 中:
1 2 3 4 5 6 7 8 9
| data() { return { tableHeight: 300, marginHeight: 80, tableResizeObserver: null, tableHeightFrame: null, tableLayoutFrame: null }; }
|
其中:
tableHeight:最终给 el-table 使用的高度
marginHeight:没有分页时预留的底部高度
tableResizeObserver:监听布局尺寸变化
tableHeightFrame:控制高度计算的节流
tableLayoutFrame:控制表格重新布局
获取表格容器
由于有些页面 ref 可能拿到组件实例,有些可能拿到原生 DOM,所以先封装一个方法:
1 2 3 4
| getTableWrapper() { const tableRef = this.$refs.table; return tableRef?.$el || tableRef || null; }
|
这样后面统一拿到真实 DOM。
计算高度
核心方法是 viewHeight。
大概思路是:
- 获取外层滚动容器高度
- 获取表格区域距离容器顶部的距离
- 获取分页高度
- 减去页面底部 padding
- 得到表格可用高度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| viewHeight() { const tableDom = this.getTableWrapper();
if (!tableDom) { return; }
const currentDom = this.$el; const viewScrollDom = currentDom?.closest('.view-scroll'); const pageLayoutDom = currentDom?.closest('.page-layout') || currentDom; const paginationDom = tableDom.querySelector('.el-footer'); const viewScrollRect = viewScrollDom?.getBoundingClientRect(); const tableRect = tableDom.getBoundingClientRect(); const paginationHeight = paginationDom?.offsetHeight || 0; const pagePaddingBottom = pageLayoutDom ? parseFloat(window.getComputedStyle(pageLayoutDom).paddingBottom) || 0 : 0; const viewHeight = viewScrollDom?.clientHeight || pageLayoutDom?.clientHeight || window.innerHeight; const tableOffsetTop = viewScrollRect ? Math.max(tableRect.top - viewScrollRect.top, 0) : 0;
let nextTableHeight = viewHeight - tableOffsetTop - paginationHeight - pagePaddingBottom;
if (!paginationDom) { nextTableHeight -= this.marginHeight; }
nextTableHeight = Math.max(Math.floor(nextTableHeight), 160); this.tableHeight = nextTableHeight; }
|
实际代码里面会再做一些判断,避免高度变化很小时重复赋值。
为什么要减分页高度
表格区域下面一般会有分页组件。
如果不减分页高度,表格就会把分页挤到下面,导致外层页面出现滚动条。
所以这里通过:
1 2
| const paginationDom = tableDom.querySelector('.el-footer'); const paginationHeight = paginationDom?.offsetHeight || 0;
|
拿到分页高度,再从总高度中减掉。
为什么要减tableOffsetTop
搜索区域的高度不是固定的。
有的页面搜索条件少,有的页面搜索条件多,甚至还有展开收起。
所以不能写死一个高度。
这里用表格区域距离外层滚动容器顶部的距离来计算:
1
| const tableOffsetTop = viewScrollRect ? Math.max(tableRect.top - viewScrollRect.top, 0) : 0;
|
这样搜索区域变高或者变低,表格高度都能跟着变化。
requestAnimationFrame
高度计算会因为窗口变化、页面更新、搜索区域变化触发很多次。
如果每次都直接计算,可能会频繁触发页面回流。
所以我这里加了一层:
1 2 3 4 5 6 7 8 9
| scheduleViewHeight() { if (this.tableHeightFrame) { cancelAnimationFrame(this.tableHeightFrame); }
this.tableHeightFrame = requestAnimationFrame(() => { this.viewHeight(); }); }
|
这样同一帧里面多次触发,也只会执行最后一次。
ResizeObserver
只监听窗口 resize 不够。
比如:
- 左侧菜单收起
- 顶部标签页显示隐藏
- 搜索区域换行
- 页面容器尺寸变化
这些不一定都会触发 window.resize。
所以使用 ResizeObserver 监听相关容器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| initTableResizeObserver() { if (typeof ResizeObserver === 'undefined') { return; }
this.destroyTableResizeObserver();
const tableDom = this.getTableWrapper(); const currentDom = this.$el; const pageLayoutDom = currentDom?.closest('.page-layout') || currentDom; const viewScrollDom = currentDom?.closest('.view-scroll');
this.tableResizeObserver = new ResizeObserver(() => { this.scheduleViewHeight(); });
[tableDom, pageLayoutDom, viewScrollDom] .filter(Boolean) .forEach((dom) => this.tableResizeObserver.observe(dom)); }
|
这样容器尺寸变化的时候,表格高度也会重新计算。
重新布局
Element Plus 表格在高度变化或者数据变化后,有时候固定列、滚动条位置会有一点不对。
这时候需要调用 doLayout:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| refreshTableLayout(refName = 'tableList') { if (this.tableLayoutFrame) { cancelAnimationFrame(this.tableLayoutFrame); }
this.$nextTick(() => { this.tableLayoutFrame = requestAnimationFrame(() => { const tableInstance = this.$refs[refName];
tableInstance?.doLayout?.(); this.tableLayoutFrame = null; }); }); }
|
一般在列表数据请求完成后执行:
1 2 3 4 5 6 7 8
| searchStopLoading() { setTimeout(() => { this.searchLoading = false; this.tableLoading = false; this.scheduleViewHeight(); this.refreshTableLayout(); }, 500); }
|
生命周期
页面第一次进入时,需要初始化监听并计算一次高度:
1 2 3 4 5 6 7 8
| mounted() { this.$nextTick(() => { this.initTableResizeObserver(); this.scheduleViewHeight(); });
window.addEventListener('resize', this.scheduleViewHeight); }
|
如果页面使用了 keep-alive,切换回来时也要重新计算:
1 2 3 4 5 6
| activated() { this.$nextTick(() => { this.initTableResizeObserver(); this.scheduleViewHeight(); }); }
|
离开页面时清理监听:
1 2 3 4 5 6 7 8
| deactivated() { this.destroyTableResizeObserver(); }
beforeUnmount() { this.destroyTableResizeObserver(); window.removeEventListener('resize', this.scheduleViewHeight); }
|
滚动重置
切换分页或者重新查询时,表格滚动条最好回到顶部。
1 2 3 4 5 6 7
| resetTableScroll(refName = 'tableList') { this.$nextTick(() => { const tableInstance = this.$refs[refName];
tableInstance?.setScrollTop?.(0); }); }
|
如果不处理,用户在第一页滚动到底部后,再切换到第二页,表格可能还是停在底部。
总结
这篇主要不是讲动态表格,也不是把表格列配置化。
我的表格列还是按页面固定写,真正抽出来的是高度适配这一块。
整体思路就是:
el-table 使用 :max-height="tableHeight"
- 根据外层容器高度计算表格可用高度
- 减掉搜索区域、分页和页面 padding 占用的空间
- 使用
ResizeObserver 监听容器变化
- 数据变化后调用
doLayout
- 查询和分页后重置表格滚动
这样处理以后,不同高度的搜索区域、不同屏幕尺寸、标签页切换,都能让表格保持在比较合适的高度。
以上就是我对 Element Plus 表格高度适配的一些理解,如有错误,欢迎大佬指出。