前言
Element Plus的表格组件提供了筛选功能
前端项目中,如果表格使用的是后端分页,使用表格插件及分页器插件就可以了。这种情况下,前端的表格筛选被后端的分页条件查询取代了
另一种情况:不分页,直接查询所有数据,在前端做查询、筛选、排序
第一种情况下,数据统计由后端直接返回。这里,讨论的是第二种情况
一、需求
想要实现的是在上述第二种情况下,在进行前端筛选、重新获取数据等改变表格内数据总数的操作后,获取该总数。
然而,无法从 ElTable
组件绑定的数据、公开的属性/方法中直接获知
二、解决方案
示例基于 [email protected]
, [email protected]
,使用 vue组合式API实现
文末有 vue3 两种代码风格的完整代码
1. 基本示例
下面是个人员表格,可筛选性别、省份
<script setup>
import { ref, computed, onMounted } from "vue";
import { ElButton, ElTable, ElTableColumn } from "element-plus";
const provinces = ["北京", "上海", "广东"];
const tableData = ref([]);
const columns = [
{ label: "No", prop: "no", width: 80 },
{ label: "Name", prop: "name", width: 100 },
{ label: "Age", prop: "age", width: 60 },
{
label: "Gender",
prop: "gender",
width: 100,
filterable: true,
filters: [
{ text: "male", value: "male" },
{ text: "female", value: "female" },
],
},
{
label: "Province",
prop: "province",
width: 140,
filterable: true,
filters: [
{ text: "北京", value: "北京" },
{ text: "上海", value: "上海" },
{ text: "广东", value: "广东" },
],
},
{ label: "Tel", prop: "tel", width: 140 },
];
const tableEl = ref();
onMounted(() => {
queryHandler();
});
// 获取列表数据
function getTableData(amount = 200) {
return new Promise((resolve, reject) => {
resolve(
Array.from({ length: amount }).map((_, idx) => ({
no: "No." + (idx + 1),
name: Math.floor(Math.random() * 900000 + 100000).toString(16),
age: Math.ceil(Math.random() * 40 + 20),
gender: Math.random() > 0.5 ? "male" : "female",
province: provinces[Math.floor(Math.random() * 3)],
tel: "13311112222",
}))
);
});
}
async function queryHandler() {
tableEl.value?.clearFilter();
const amount = Math.floor(Math.random() * 900) + 100;
tableData.value = await getTableData(amount);
}
function filterHandler(value, row, column) {
const property = column["property"];
return row[property] === value;
}
</script>
<template>
<el-table ref="tableEl" :data="tableData" max-height="500">
<template v-for="col in columns" :key="col.prop">
<el-table-column
v-if="col.filterable"
:prop="col.prop"
:label="col.label"
:width="col.width"
:filters="col.filters"
:filter-method="filterHandler"
></el-table-column>
<el-table-column
v-else
:prop="col.prop"
:label="col.label"
:width="col.width"
></el-table-column>
</template>
</el-table>
<el-button @click="queryHandler">重新请求列表数据</el-button>
</template>
从表格绑定数据 tableData 中无法获知筛选后的表格数据总量,查阅 Element Plus 官方文档,该组件也没有提供相应接口,那就只能想办法通过某种方案间接实现了
2. 方案
- 方案一:直接将筛选方法作用于 tableData,手动计算筛选后的表格数据总数
这种方案需要监听表格的 filter-change
事件,然后过滤
由于 filter-change
事件返回的当前更改的筛选条件,存在多个筛选时,需存储过滤条件
总结一下:
> 给表项绑定 column-key
属性;创建存储筛选条件的对象
> 监听表格的 filter-change
事件,更新筛选条件,计算筛选后的表格数据总数
> 表格数据变更时,清空筛选条件
上述第二步,手动计算筛选如果过于复杂,也可以考虑在DOM层面上,在表格渲染完毕后,直接查询条数(<tr>)。
- 方案二:监听表格组件内部数据
虽然官方未提供直接的属性与方法,但表格组件内部肯定知道满足筛选条件的数据有哪些,问题在于它是否被暴露出来
通过打印表格对象,发现store中找到了相关数据,ElTable
组件内部的状态存储信息:
更完美的是,它是响应式变量,意味着可以直接监听
上例中,我们只需要中添加一个计算变量即可:
import { ref, computed, onMounted } from "vue";
const tableEl = ref();
const total = computed(
() => tableEl.value?.store?.states?.data?.value?.length ?? 0
);
注意:若使用的是vue选项式API实现,需等待表格组件挂载后,再添加计算变量
三、总结
第一种方案监听筛选事件,再在监听处理事件中手动筛选数据、计算总数,较为繁琐,不太建议使用
方案二依赖于 ElTable
组件内部的状态存储信息,非官网提供的属性/方法,随着版本的更替,指不定什么时候就成潜在的 “Breaking Change” 了。建议核实后锁定项目的 element-plus
的版本
若官方后续提供相应属性/方法,再替换。
四、完整代码
Demo - 基于vue组合式API:
<script setup>
import { ref, computed, onMounted } from "vue";
import { ElButton, ElTable, ElTableColumn } from "element-plus";
const provinces = ["北京", "上海", "广东"];
const tableData = ref([]);
const columns = [
{ label: "No", prop: "no", width: 80 },
{ label: "Name", prop: "name", width: 100 },
{ label: "Age", prop: "age", width: 60 },
{
label: "Gender",
prop: "gender",
width: 100,
filterable: true,
filters: [
{ text: "male", value: "male" },
{ text: "female", value: "female" },
],
},
{
label: "Province",
prop: "province",
width: 140,
filterable: true,
filters: [
{ text: "北京", value: "北京" },
{ text: "上海", value: "上海" },
{ text: "广东", value: "广东" },
],
},
{ label: "Tel", prop: "tel", width: 140 },
];
const tableEl = ref();
const total = computed(
() => tableEl.value?.store?.states?.data?.value?.length ?? 0
);
onMounted(() => {
queryHandler();
});
// 获取列表数据
function getTableData(amount = 200) {
return new Promise((resolve, reject) => {
resolve(
Array.from({ length: amount }).map((_, idx) => ({
no: "No." + (idx + 1),
name: Math.floor(Math.random() * 900000 + 100000).toString(16),
age: Math.ceil(Math.random() * 40 + 20),
gender: Math.random() > 0.5 ? "male" : "female",
province: provinces[Math.floor(Math.random() * 3)],
tel: "13311112222",
}))
);
});
}
async function queryHandler() {
tableEl.value?.clearFilter();
const amount = Math.floor(Math.random() * 900) + 100;
tableData.value = await getTableData(amount);
}
function filterHandler(value, row, column) {
const property = column["property"];
return row[property] === value;
}
</script>
<template>
<h3>TableData length: {{ tableData.length }}</h3>
<h3>Total entries: {{ total }}</h3>
<el-table ref="tableEl" :data="tableData" max-height="500">
<template v-for="col in columns" :key="col.prop">
<el-table-column
v-if="col.filterable"
:prop="col.prop"
:label="col.label"
:width="col.width"
:filters="col.filters"
:filter-method="filterHandler"
></el-table-column>
<el-table-column
v-else
:prop="col.prop"
:label="col.label"
:width="col.width"
></el-table-column>
</template>
</el-table>
<el-button @click="queryHandler">重新请求列表数据</el-button>
</template>