Element Plus表格高度适配


后台系统里面表格页面很多,列配置一般还是固定写在页面里,这里主要记录一下 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>

这里主要注意两个地方:

  1. ref="table":外层表格区域,用来计算位置和分页高度
  2. :max-height="tableHeight":Element Plus 表格最大高度

列还是按普通写法写,重点是把 tableHeight 算准确。

为什么用max-height

Element Plus 的表格如果不限制高度,数据多的时候会直接把页面撑开。

后台页面一般不希望整页一直往下滚,而是希望:

  1. 搜索区域固定在上面
  2. 分页固定在表格下面
  3. 表格内容区域内部滚动
  4. 外层布局不要出现多余滚动条

所以这里使用 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
};
}

其中:

  1. tableHeight:最终给 el-table 使用的高度
  2. marginHeight:没有分页时预留的底部高度
  3. tableResizeObserver:监听布局尺寸变化
  4. tableHeightFrame:控制高度计算的节流
  5. tableLayoutFrame:控制表格重新布局

获取表格容器

由于有些页面 ref 可能拿到组件实例,有些可能拿到原生 DOM,所以先封装一个方法:

1
2
3
4
getTableWrapper() {
const tableRef = this.$refs.table;
return tableRef?.$el || tableRef || null;
}

这样后面统一拿到真实 DOM。

计算高度

核心方法是 viewHeight

大概思路是:

  1. 获取外层滚动容器高度
  2. 获取表格区域距离容器顶部的距离
  3. 获取分页高度
  4. 减去页面底部 padding
  5. 得到表格可用高度
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 不够。

比如:

  1. 左侧菜单收起
  2. 顶部标签页显示隐藏
  3. 搜索区域换行
  4. 页面容器尺寸变化

这些不一定都会触发 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);
});
}

如果不处理,用户在第一页滚动到底部后,再切换到第二页,表格可能还是停在底部。

总结

这篇主要不是讲动态表格,也不是把表格列配置化。

我的表格列还是按页面固定写,真正抽出来的是高度适配这一块。

整体思路就是:

  1. el-table 使用 :max-height="tableHeight"
  2. 根据外层容器高度计算表格可用高度
  3. 减掉搜索区域、分页和页面 padding 占用的空间
  4. 使用 ResizeObserver 监听容器变化
  5. 数据变化后调用 doLayout
  6. 查询和分页后重置表格滚动

这样处理以后,不同高度的搜索区域、不同屏幕尺寸、标签页切换,都能让表格保持在比较合适的高度。

以上就是我对 Element Plus 表格高度适配的一些理解,如有错误,欢迎大佬指出。

-------------本文结束感谢您的阅读-------------