element-plus table自带的筛选功能不能够满足客户的需要,客户要求筛选过功能可以支持搜索,这里我们实现这个功能。
筛选组件
<template>
<div class="filter-container">
<el-popover
placement="bottom-start"
trigger="click"
popper-class="filter-popover"
v-model:visible="popoverVisible"
>
<template #reference>
<div class="icons cur mt2">
<el-icon size="12">
<Filter />
</el-icon>
</div>
</template>
<div class="filter-box">
<div class="filter-title">筛选</div>
<div class="search-bar">
<el-input v-model="keyword" placeholder="输入关键词搜索" />
</div>
<div class="select-all">
<el-checkbox v-model="allSelected" @change="selectAll">全选</el-checkbox>
</div>
<div class="checkbox-list">
<el-scrollbar style="height: 100%">
<el-checkbox-group v-model="selectedOptions">
<el-checkbox
v-for="item in filteredOptions"
:key="item.value"
:label="item.value"
>
{{ item.text }}
</el-checkbox>
</el-checkbox-group>
</el-scrollbar>
</div>
<div class="buttons">
<el-button @click="cancel">取消</el-button>
<el-button type="primary" :disabled="selectedOptions.length === 0" @click="confirm">确认</el-button>
</div>
</div>
</el-popover>
</div>
</template>
<script setup>
import { ref, computed, defineProps, defineEmits } from 'vue';
import {
ElInput,
ElButton,
ElCheckbox,
ElScrollbar,
ElIcon,
ElCheckboxGroup,
ElPopover,
} from 'element-plus';
import { Search, Filter } from '@element-plus/icons-vue';
const props = defineProps({
options: {
type: Array,
default: () => [],
},
});
const emit = defineEmits(['filter-changed']);
const keyword = ref('');
const selectedOptions = ref(props.options.map(item => item.value));
const filteredOptions = computed(() => {
return props.options.filter(option => option.text.includes(keyword.value));
});
const popoverVisible = ref(false);
const cancel = () => {
console.log('取消');
popoverVisible.value = false;
selectedOptions.value = props.options.map(item => item.value); // 重置为全选
emitFilterChange(selectedOptions.value);
};
const confirm = () => {
console.log('确认', selectedOptions.value);
popoverVisible.value = false;
emitFilterChange(selectedOptions.value);
};
const allSelected = computed({
get: () => selectedOptions.value.length === props.options.length,
set: (val) => {
selectedOptions.value = val ? props.options.map(item => item.value) : [];
},
});
const selectAll = (val) => {
selectedOptions.value = val ? props.options.map(item => item.value) : [];
};
const emitFilterChange = (selected) => {
emit('filter-changed', selected);
};
</script>
<style scoped>
.filter-container {
position: relative;
display: inline-block;
}
.filter-box {
width: 254px;
display: flex;
flex-direction: column;
box-sizing: border-box; /* 确保内边距不影响宽度 */
}
.filter-title {
font-family: PingFang SC, PingFang SC;
font-weight: 400;
font-size: 14px;
color: #333333;
line-height: 20px;
text-align: left;
font-style: normal;
text-transform: none;
margin-bottom: 10px;
}
.search-bar {
width: 100%;
height: 32px;
display: flex;
margin-bottom: 10px;
background: rgba(255, 255, 255, 1);
}
.select-all {
width: 100%;
height: 38px;
border-bottom: 1px solid #eeeeee;
margin-bottom: 10px;
background: rgba(255, 255, 255, 1);
padding: 0 10px;
box-sizing: border-box;
}
.checkbox-list {
width: 100%;
height: 149px;
border: 1px solid #eeeeee;
margin-bottom: 10px;
overflow: hidden;
background: rgba(255, 255, 255, 1);
padding: 0 0px 0 10px;
box-sizing: border-box;
}
.buttons {
display: flex;
justify-content: space-around;
}
.el-button {
width: 62px;
height: 32px;
border-radius: 4px;
border: 1px solid #d3d8df;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
font-size: 14px;
color: #2f3046;
}
.el-button--primary {
width: 60px;
height: 32px;
background: #3845e0;
border-radius: 4px;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
font-size: 14px;
color: #ffffff;
border: none;
}
/* 调整 checkbox 样式,使其纵向排列 */
.el-checkbox-group {
display: flex;
flex-direction: column;
}
.icons {
width: 16px;
height: 16px;
background: #DEE1EC;
border-radius: 3px 3px 3px 3px;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: nowrap;
flex-direction: row;
align-content: flex-start;
margin-left: 5px;
margin-top: 3px;
}
</style>
<style lang="scss">
/* 将 el-popover 插入到 body 中 */
.filter-popover .el-popover {
position: fixed !important;
top: 0 !important;
left: 0 !important;
z-index: 2000; /* 确保 popover 在其他元素之上 */
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.12); /* 补充阴影 */
border-radius: 4px; /* 补充圆角 */
border: 1px solid #eeeeee; /* 补充边框 */
}
.el-popper.is-light {
width: auto !important;
background: #fafbfb;
}
</style>
使用
<template>
<div>
<el-table
:data="filteredTableData"
height="100%"
style="width: 100%"
border
stripe
>
<el-table-column type="index" label="序号" width="80" align="center" />
<el-table-column prop="role" label="角色" width="100">
<template #header>
<span>角色</span>
<filter-com :options="roleFilters" @filter-changed="handleRoleFilter" />
</template>
</el-table-column>
<el-table-column prop="name" label="姓名" width="80">
<template #header>
<span>姓名</span>
<filter-com :options="nameFilters" @filter-changed="handleNameFilter" />
</template>
<template #default="scope">
<span class="name-style">{{ scope.row.name }}</span>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script setup>
import { ref, onMounted, computed } from 'vue';
import filterCom from '@/components/filter/index.vue';
// 表格数据
const tableData = ref([
{
role: '特派员',
name: '王利',
},
{
role: '工程师',
name: '张三',
},
{
role: '特派员',
name: '李四',
},
{
role: '安全专家',
name: '赵六',
},
{
role: '工程师',
name: '孙七',
},
{
role: '特派员',
name: '周八',
},
{
role: '安全专家',
name: '吴九',
},
{
role: '工程师',
name: '郑十',
},
{
role: '特派员',
name: '刘一',
},
{
role: '安全专家',
name: '陈二',
},
]);
// 筛选相关
const roleFilters = ref([]);
const nameFilters = ref([]);
const initFilters = () => {
roleFilters.value = [...new Set(tableData.value.map(item => item.role))].map(role => ({ text: role, value: role }));
nameFilters.value = [...new Set(tableData.value.map(item => item.name))].map(name => ({ text: name, value: name }));
};
// 新增处理筛选结果的函数
const filteredRole = ref([]);
const filteredName = ref([]);
const handleRoleFilter = (selected) => {
filteredRole.value = selected;
};
const handleNameFilter = (selected) => {
filteredName.value = selected;
};
// 计算属性,用于根据筛选条件过滤表格数据
const filteredTableData = computed(() => {
return tableData.value.filter(row => {
const roleFilter = filteredRole.value.length === 0 || filteredRole.value.includes(row.role);
const nameFilter = filteredName.value.length === 0 || filteredName.value.includes(row.name);
return roleFilter && nameFilter;
});
});
onMounted(() => {
initFilters();
});
</script>
<style scoped>
/* 可以添加一些样式来美化表格和分页 */
</style>