element-plus table筛选功能改为自定义筛选

vue yekong

element-plus table自带的筛选功能不能够满足客户的需要,客户要求筛选过功能可以支持搜索,这里我们实现这个功能。

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>

喜欢