先看效果图

一、前言

甲方放着好好的导出,好好的excel的高级筛选不用,非要在网页中实现一个。。。。

最开始尝试使用的element官方的筛选,给甲方看后 说和excel的筛选相差很大。。好在官方有提供自定义表头,那自己手动实现一个了。最后有完整代码

其实此功能挺简单的,只不过麻烦一点。我把筛选分为了四个类型

  1. txt 文本型  (就是input输入框)
  2. scope 范围型 (查询金额区间)
  3. date 时间型 (查询日期)
  4. select 下拉框 (就是下拉框呗)

整体逻辑就是:子组件把筛选好的数据,给父组件,父组件进行筛选过滤,再给table展示

二、父组件

分为两个部分

1.tag部分,显示所用到的筛选条件

2.table部分

html

   <!-- 条件tag -->
    <div style="margin-bottom: 10px" v-if="conditionList.length != 0">
      <span>条件:</span>
      <el-tag
        @close="conditionClose(index)"
        style="margin-left: 10px"
        v-for="(tag, index) in conditionList"
        :key="index"
        closable
        :type="tag.prop"
      >
        {{ tag.label }} :<span style="color: red">{{ tag.value.value1 }}</span>
        <span v-if="tag.value.value2" style="color: red"
          >- {{ tag.value.value2 }}</span
        >
      </el-tag>
    </div>
    <!-- 表格 -->
    <el-table :data="tableData" style="width: 100%" border stripe>
      <template v-for="(item, index) in tableConfig">
        <el-table-column
          sortable
          :key="index"
          :label="item.label"
          align="center"
          :prop="item.prop"
          :width="item.width"
          :filters="[]"
        >
          <template slot="header" slot-scope="scope">
            <custom-header
              v-if="customFlag"
              :column="scope.column"
              :item="item"
              :customParams="customParams"
              :labelColorList="labelColorList"
              @tableUpdate="tableUpdate"
            ></custom-header>
          </template>
        </el-table-column>
      </template>
    </el-table>

custom-header 为子组件,里面写着四个类型的代码

父data里的数据

  data() {
    return {
      customFlag: false, // 自定义筛选是否显示
      customParams: {}, //自定义筛选参数
      conditionList: [], //自定义筛选条件
      labelColorList: [], //已经在使用的筛选条件,染色用
      // table数据
      tableData: [],
      // table数据 拷贝,我们不操作原数据
      tableDataCopy: [],
      // table配置
      tableConfig: [
        {
          label: "姓名",
          prop: "name",
          width: "150px",
          conditionType: "txt", // 条件类型
        },
        {
          label: "身价",
          prop: "amount",
          width: "150px",
          conditionType: "scope", // 条件类型
        },
        {
          label: "生日",
          prop: "date",
          width: "150px",
          conditionType: "date", // 条件类型
        },
        {
          label: "所在城市",
          prop: "city",
          conditionType: "select", // 条件类型
          conditionListName: "cityList", //条件类型下拉框数据
          fuzzyQuery: false, //是否模糊查询
        },
        {
          label: "所在城市(模糊查询)",
          prop: "city2",
          conditionType: "select", // 条件类型
          conditionListName: "cityList", //条件类型下拉框数据
          fuzzyQuery: true, //是否模糊查询
        },
      ],
    };
  },

tableConfig里的数据会被el-table遍历

其中:

        conditionType : 条件的类型,前言已经说了就是那四个类型(txt,scope,date,select)

        conditionListName:  如果是select类型的话,肯定有下拉框数据对吧,这个就是下拉框数据的List名,只是名字不是数据哦(不明白的话看到子组件那里就懂了)

        fuzzyQuery:是否模糊查询

父methods几个关键方法

  methods: {
    // 请求下拉框数据
    getCustomData() {},
    //给使用筛选条件的标题加颜色
    setlabelColor() {},
    //自定义检索发射出来的事件
    tableUpdate() {},
    //筛选数据
    customSearch() {},
  }

getCustomData():汇总下拉框的数据

customSearch():筛选逻辑处理,后面想改筛选的逻辑就来这。后面贴出的完整代码里写的也很清楚,不明白可以问我

三、子组件

接收的props

column:当前列数据,用于显示表头

tableConfig :处理类型判断

customParams :select下拉框数据

labelColorList :正在使用的筛选条件,染色用

computed

  computed: {
    selectList() {
      return function (data) {
        return this.customParams[data.conditionListName];
      };
    },
  },

根据下拉框名称,找到对应的下拉框List数据

其他没什么说的,子组件比较简单,看完整代码一眼就明白了

四、完整代码

父组件

<template>
  <div class="app">
    <!-- 条件tag -->
    <div style="margin-bottom: 10px" v-if="conditionList.length != 0">
      <span>条件:</span>
      <el-tag
        @close="conditionClose(index)"
        style="margin-left: 10px"
        v-for="(tag, index) in conditionList"
        :key="index"
        closable
        :type="tag.prop"
      >
        {{ tag.label }} :<span style="color: red">{{ tag.value.value1 }}</span>
        <span v-if="tag.value.value2" style="color: red"
          >- {{ tag.value.value2 }}</span
        >
      </el-tag>
    </div>
    <!-- 表格 -->
    <el-table :data="tableData" style="width: 100%" border stripe>
      <template v-for="(item, index) in tableConfig">
        <el-table-column
          sortable
          :key="index"
          :label="item.label"
          align="center"
          :prop="item.prop"
          :width="item.width"
          :filters="[]"
        >
          <template slot="header" slot-scope="scope">
            <custom-header
              v-if="customFlag"
              :column="scope.column"
              :item="item"
              :customParams="customParams"
              :labelColorList="labelColorList"
              @tableUpdate="tableUpdate"
            ></custom-header>
          </template>
        </el-table-column>
      </template>
    </el-table>
  </div>
</template>
<script>
import customHeader from "./components/customHeader.vue";
export default {
  name: "app",
  data() {
    return {
      customFlag: false, // 自定义筛选是否显示
      customParams: {}, //自定义筛选参数
      conditionList: [], //自定义筛选条件
      labelColorList: [], //已经在使用的筛选条件,染色用
      // table数据
      tableData: [],
      // table数据 拷贝,我们不操作原数据
      tableDataCopy: [],
      // table配置
      tableConfig: [
        {
          label: "姓名",
          prop: "name",
          width: "150px",
          conditionType: "txt", // 条件类型
        },
        {
          label: "身价",
          prop: "amount",
          width: "150px",
          conditionType: "scope", // 条件类型
        },
        {
          label: "生日",
          prop: "date",
          width: "150px",
          conditionType: "date", // 条件类型
        },
        {
          label: "所在城市",
          prop: "city",
          conditionType: "select", // 条件类型
          conditionListName: "cityList", //条件类型下拉框数据
          fuzzyQuery: false, //是否模糊查询
        },
        {
          label: "所在城市(模糊查询)",
          prop: "city2",
          conditionType: "select", // 条件类型
          conditionListName: "cityList", //条件类型下拉框数据
          fuzzyQuery: true, //是否模糊查询
        },
      ],
    };
  },
  methods: {
    getCustomData() {
      /*
        这里的数据有必要注意下:
          1.数据格式这里处理好 全部保持一致,这样customHeader就不用再处理了
          3.因为我们后面筛选的时候查找的是文字,所以这里的value始终和列表展示的值保持一致,也是文字。
          3.可以写个Promise.All,把下拉框所需要的数据都请求到 然后再打开customFlag
        */
      this.customParams = {
        //城市列表
        cityList: [
          { value: "北京市" },
          { value: "南京市" },
          { value: "上海市" },
          { value: "广州市" },
          { value: "深圳市" },
          { value: "杭州市" },
          { value: "成都市" },
        ],
        // ...
      };
      this.customFlag = true;
    },
    // 给使用筛选条件的标题加颜色
    setlabelColor() {
      this.labelColorList = [];
      this.conditionList.forEach((_item) => {
        this.labelColorList.push(_item.prop);
      });
    },
    // 自定义检索发射出来的事件
    tableUpdate(data) {
      console.log(data, "condition");
      let flag = true;
      // 筛选条件如果已经存在,就更新
      this.conditionList.forEach((item, index) => {
        if (item.prop == data.prop) {
          item.value = data.value;
          flag = false;
        }
      });
      // 如果没有就添加
      if (flag) {
        this.conditionList.push(data);
      }
      this.customSearch(); //筛选数据
    },
    // 筛选数据
    customSearch() {
      /*
        这里可以说是筛选的核心部分吧,自定义的筛选规则都在这。
        以后想改什么筛选规则就来这找
      */
      console.log(this.conditionList, "this.conditionList");
      this.setlabelColor(); //设置使用自定义检索的表头颜色
      // 如果自定义检索 为空了,就重新调用查询
      if (this.conditionList.length == 0) {
        this.search();
        return false;
      }
      const result = [];
      // 遍历列表数据
      for (let i = 0; i < this.tableDataCopy.length; i++) {
        const dataItem = this.tableDataCopy[i];
        // 遍历自定义筛选条件,符合规则就push出来
        let flag = true;
        for (let l = 0; l < this.conditionList.length; l++) {
          const item = this.conditionList[l];
          // 属性名 属性值 类型 是否模糊查询
          const { prop, value, conditionType, fuzzyQuery } = item;
          // txt类型
          if (conditionType == "txt") {
            if (dataItem[prop].indexOf(value.value1) != -1) {
              flag = true;
            } else {
              flag = false;
            }

            //范围类型
          } else if (conditionType == "scope") {
            if (
              dataItem[prop] >= value.value1 &&
              dataItem[prop] <= value.value2
            ) {
              flag = true;
            } else {
              flag = false;
            }

            // 时间类型
          } else if (conditionType == "date") {
            // 转换为时间戳然后判断
            let current = new Date(dataItem[prop]).getTime();
            let value1 = new Date(value.value1).getTime();
            let value2 = new Date(value.value2).getTime();
            if (current >= value1 && current <= value2) {
              flag = true;
            } else {
              flag = false;
            }
          }
          // 下拉框类型
          else if (conditionType == "select") {
            // fuzzyQuery 为true代表模糊查询,否则为精确查询
            if (fuzzyQuery) {
              if (dataItem[prop].indexOf(value.value1) != -1) {
                flag = true;
              } else {
                flag = false;
              }
            } else {
              if (dataItem[prop] == value.value1) {
                flag = true;
              } else {
                flag = false;
              }
            }
          }

          if (flag === false) break;
        }
        if (flag) result.push(dataItem);
      }
      console.log(result, "result");
      this.tableData = result;
      // this.totalSize = result.length;

    },
    search() {
      this.tableData = [
        {
          name: "王小虎",
          amount: 100,
          date: "2018-05-02",
          city: "北京市",
          city2: "北京市",
        },
        {
          name: "张二宝",
          amount: 200,
          date: "2019-05-04",
          city: "上海市",
          city2: "上海市",
        },
        {
          name: "王二丫",
          amount: 500,
          date: "2020-05-01",
          city: "深圳市",
          city2: "深圳市",
        },
        {
          name: "胡图图",
          amount: 1000,
          date: "2021-05-03",
          city: "广州市",
          city2: "广东省广州市",
        },
        {
          name: "张小龙",
          amount: 2000,
          date: "2022-05-03",
          city: "杭州市",
          city2: "浙江省杭州市",
        },
      ];
      // copy 一份数据出来
      this.tableDataCopy = JSON.parse(JSON.stringify(this.tableData));
    },
    // 关闭条件tag
    conditionClose(index) {
      this.conditionList.splice(index, 1);
      this.customSearch(); //筛选数据
    },
  },
  mounted() {
    // 请求自定义筛选下拉框数据
    this.getCustomData();
    // 请求table数据
    this.search();
  },
  components: {
    customHeader,
  },
};
</script>
<style scoped lang='scss'>
// 占位,解决点击自己写的自定义筛选 会冒泡到排序
/deep/ .el-table__column-filter-trigger {
  display: none !important;
}
</style>

 子组件

<template>
  <!-- 注意:逻辑部分尽量不好写到这个组件内,因为这个组件是根据外面table循环创建的,在这里写逻辑会非常影响性能 -->
  <div class="customHeader" @click.stop style="display: inline-block">
    <el-popover
      placement="bottom"
      title="查询条件"
      width="300"
      trigger="click"
      ref="popover"
    >
      <!-- txt 文本 -->
      <div v-if="item.conditionType == 'txt'">
        <el-input
          v-model.trim="conditions.value1"
          placeholder="请输入查询内容"
          @keyup.native.enter="confirm()"
        ></el-input>
      </div>
      <!-- scope 范围-->
      <div v-else-if="item.conditionType == 'scope'">
        <el-input
          style="width: 120px"
          v-model.trim="conditions.value1"
          placeholder="请输入条件1"
        ></el-input>
        -
        <el-input
          style="width: 120px"
          v-model.trim="conditions.value2"
          placeholder="请输入条件2"
        ></el-input>
      </div>
      <!-- date 日期-->
      <div v-else-if="item.conditionType == 'date'">
        <el-date-picker
          v-model="conditions.value1"
          type="date"
          clearable
          placeholder="开始时间"
          value-format="yyyy-MM-dd"
        ></el-date-picker>
        <el-date-picker
          style="margin-top: 10px"
          v-model="conditions.value2"
          type="date"
          clearable
          placeholder="结束时间"
          value-format="yyyy-MM-dd"
        ></el-date-picker>
      </div>
      <!-- select 选择框-->
      <div v-else-if="item.conditionType == 'select'">
        <el-select
          v-model="conditions.value1"
          placeholder="请选择"
          style="width: 100%"
          clearable
        >
          <el-option
            v-for="(item, index) in selectList(item)"
            :key="index"
            :label="item.value"
            :value="item.value"
          >
          </el-option>
        </el-select>
      </div>

      <!-- confirm 确定框-->
      <div style="text-align: center">
        <el-button @click="confirm" type="primary" size="mini" class="confirm"
          >确定</el-button
        >
      </div>
      <!-- label 标题显示-->
      <span
        slot="reference"
        onselectstart="return false"
        oncontextmenu="return false"
        class="label"
        :class="{ labelColor: labelColorList.includes(item.prop) }"
        >{{ column.label }} &nbsp;<i class="el-icon-arrow-down"></i>
      </span>
    </el-popover>
  </div>
</template>
<script>
export default {
  name: "customHeader",
  // column 当前列数据,tableConfig 内数据,customParams 下拉框数据, labelColorList 正在使用的筛选条件
  props: ["column", "item", "customParams", "labelColorList"],
  data() {
    return {
      conditions: {
        value1: "",
        value2: "",
      },
    };
  },
  methods: {
    confirm() {
      if (!this.conditions.value1 && !this.conditions.value2) {
        return this.$message.warning("请选择筛选条件");
      }
      // 关闭popover
      this.$refs.popover.doClose();
      this.$emit("tableUpdate", {
        value: this.conditions, //所筛选的数据
        ...this.item, //table 配置
      });
    },
  },
  computed: {
    selectList() {
      return function (data) {
        return this.customParams[data.conditionListName];
      };
    },
  },
};
</script>
<style scoped>
.confirm {
  margin-top: 10px;
}
/* 禁止双击选中文字 */
.label {
  -moz-user-select: none; /*火狐*/
  -webkit-user-select: none !important; /*webkit浏览器*/
  -ms-user-select: none; /*IE10*/
  -khtml-user-select: none; /*早期浏览器*/
  user-select: none;
}
.labelColor {
  color: #409eff;
}
</style>

肯定有写的不完美的地方,请指教

附录:手动实现自定义筛选后,点击下拉框被排序问题

GitHub 加速计划 / eleme / element
10
1
下载
A Vue.js 2.0 UI Toolkit for Web
最近提交(Master分支:4 个月前 )
c345bb45 8 个月前
a07f3a59 * Update transition.md * Update table.md * Update transition.md * Update table.md * Update transition.md * Update table.md * Update table.md * Update transition.md * Update popover.md 8 个月前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐