需求场景:
- table 的列是由接口动态返回的;
- 列的筛选项就是数据的值,比如【姓名】这个字段总共有三个值,那么姓名这一列的筛选项就是这三个值本身;
- 当有一列筛选后,其他列的筛选项也要动态变化。
vxe-table 版本:4.9.14
完整代码如下:
vue template 部分:
<vxe-tableref="vxeTableRef":data="state.tableData"height="100%"border@filter-change="handleFilterChange"><vxe-columnv-for="colOpt in columnsOptions":key="colOpt.field":field="colOpt.field":title="colOpt.title"align="center"resizablesortable:filters="colOpt.filters":filter-method="colOpt.filterMethod"/></vxe-table>
script 部分:
<script setup lang="ts">
// 其他代码
const vxeTableRef = ref();const handleSearch = () => {// 获取table数据部分的逻辑API(params).then((res: any) => {// ...state.tableData = data || [];if (state.tableData.length > 0) {// 在数据加载后重置所有筛选nextTick(() => {if (vxeTableRef.value) {vxeTableRef.value.clearFilter();}});}});
}// 下面是处理列筛选的逻辑
// 获取筛选选项
const getColumnFilters = (column: string) => {// 使用 Map 来提高性能const uniqueMap = new Map();for (const row of state.tableData) {const value = row[column];if (!uniqueMap.has(value)) {uniqueMap.set(value, {label: value == null || value == "" ? "(空)" : String(value),value: value,checked: false});}}return Array.from(uniqueMap.values());
};// 创建筛选方法
const createFilterMethod = (column: string) => {return ({ value, row }: { value: any; row: any }) => {const cellValue = row[column];// 处理空值的情况if (value === null || value === "") {return cellValue === null || cellValue === "";}// 如果单元格值是数字,进行数字比较if (typeof cellValue === "number") {return cellValue === Number(value);}// 默认进行字符串比较return String(cellValue) === String(value);};
};// 数据是异步加载的,使用计算属性来处理列配置
const columnsOptions = computed(() => {return state.columns.map(col => ({field: col,title: col,filters: getColumnFilters(col),filterMethod: createFilterMethod(col)}));
});// 处理筛选变化
const handleFilterChange = (params: any) => {// 获取当前表格中可见的行数据const { filterList } = params;// 如果没有筛选项,就刷新if (!filterList || filterList.length === 0) {handleSearch();}// 筛选已经应用,获取处理后的可见数据const visibleData = vxeTableRef.value?.getTableData().visibleData || [];// 获取当前被筛选的列,避免修改它们的筛选项const filteredColumns = new Set(filterList.map((item: any) => item.field));// 更新筛选状态nextTick(() => {// 只更新未被筛选的列state.columns.forEach(column => {// 跳过当前正在筛选的列if (filteredColumns.has(column)) return;// 为此列生成新的筛选选项,但仅从可见数据中获取const uniqueMap = new Map();for (const row of visibleData) {const value = row[column];if (!uniqueMap.has(value)) {uniqueMap.set(value, {label: value == null || value === "" ? "(空)" : String(value),value: value,checked: false});}}// 更新此列的筛选项if (vxeTableRef.value) {vxeTableRef.value.setFilter(column, Array.from(uniqueMap.values()));}});});
};
</script>
里面关键的一个点在于,vxe-table 筛选后的展示数据跟我们的源数据是分开的,所以筛选触发的事件中,我们应该拿 visibleData 来做筛选项的动态处理。
由于逻辑可复用,所以记录一下,需要用的时候直接copy就好了。