element table manually implements user-defined filtering (Unofficial)

Posted by jwcsk8r on Thu, 27 Jan 2022 12:31:21 +0100

Look at the renderings first

1, Foreword

Party A has to export well and use excel for advanced screening. It has to implement one in the web page....

The initial attempt to use the official screening of element is very different from that of excel after showing it to Party A.. Fortunately, the official provides a custom header, so you can manually implement one. Finally, there is the complete code

In fact, this function is quite simple, but a little troublesome. I divide the screening into four types

  1. txt text type (i.e. input input box)
  2. scope type (query amount range)
  3. Date time type (query date)
  4. select drop-down box

The overall logic is: the child component gives the filtered data to the parent component, which filters it and then displays it to the table

2, Parent component

It is divided into two parts

1.tag section, which displays the filter conditions used

2.table part

html

   <!-- condition tag -->
    <div style="margin-bottom: 10px" v-if="conditionList.length != 0">
      <span>Conditions:</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>
    <!-- form -->
    <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 is a sub component, which contains four types of code

Data in parent data

  data() {
    return {
      customFlag: false, // Show custom filter
      customParams: {}, //Custom filter parameters
      conditionList: [], //Custom filter criteria
      labelColorList: [], //Screening conditions already in use for dyeing
      // table data
      tableData: [],
      // table data copy, we do not operate on the original data
      tableDataCopy: [],
      // table configuration
      tableConfig: [
        {
          label: "full name",
          prop: "name",
          width: "150px",
          conditionType: "txt", // Condition type
        },
        {
          label: "Worth",
          prop: "amount",
          width: "150px",
          conditionType: "scope", // Condition type
        },
        {
          label: "birthday",
          prop: "date",
          width: "150px",
          conditionType: "date", // Condition type
        },
        {
          label: "City",
          prop: "city",
          conditionType: "select", // Condition type
          conditionListName: "cityList", //Condition type drop-down box data
          fuzzyQuery: false, //Fuzzy query
        },
        {
          label: "City (fuzzy query)",
          prop: "city2",
          conditionType: "select", // Condition type
          conditionListName: "cityList", //Condition type drop-down box data
          fuzzyQuery: true, //Fuzzy query
        },
      ],
    };
  },

The data in tableConfig will be traversed by El table

Of which:

conditionType: the type of condition. As mentioned in the preface, it is the four types (txt,scope,date,select)

conditionListName: if it is a select type, there must be drop-down box data, right? This is the List name of the drop-down box data, but the name is not data (if you don't understand, you can understand it from the sub component)

fuzzyQuery: fuzzy query

Several key methods of parent methods

  methods: {
    // Request dropdown data
    getCustomData() {},
    //Color the headings that use the filter criteria
    setlabelColor() {},
    //Custom retrieval of emitted events
    tableUpdate() {},
    //Filter data
    customSearch() {},
  }

getCustomData (): summarize the data of the drop-down box

customSearch (): filtering logic processing. You can change the filtering logic later. The complete code posted later is also very clear. If you don't understand, you can ask me

3, Subcomponents

props received

Column: current column data, used to display header

tableConfig: process type judgment

customParams: select drop-down data

labelColorList: the filter condition being used, for dyeing

computed

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

According to the drop-down box name, find the corresponding drop-down box List data

There's nothing else to say. The sub components are relatively simple. You can see it at a glance from the complete code

4, Complete code

Parent component

<template>
  <div class="app">
    <!-- condition tag -->
    <div style="margin-bottom: 10px" v-if="conditionList.length != 0">
      <span>Conditions:</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>
    <!-- form -->
    <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, // Show custom filter
      customParams: {}, //Custom filter parameters
      conditionList: [], //Custom filter criteria
      labelColorList: [], //Screening conditions already in use for dyeing
      // table data
      tableData: [],
      // table data copy, we do not operate the original data
      tableDataCopy: [],
      // table configuration
      tableConfig: [
        {
          label: "full name",
          prop: "name",
          width: "150px",
          conditionType: "txt", // Condition type
        },
        {
          label: "Worth",
          prop: "amount",
          width: "150px",
          conditionType: "scope", // Condition type
        },
        {
          label: "birthday",
          prop: "date",
          width: "150px",
          conditionType: "date", // Condition type
        },
        {
          label: "City",
          prop: "city",
          conditionType: "select", // Condition type
          conditionListName: "cityList", //Condition type drop-down box data
          fuzzyQuery: false, //Fuzzy query
        },
        {
          label: "City (fuzzy query)",
          prop: "city2",
          conditionType: "select", // Condition type
          conditionListName: "cityList", //Condition type drop-down box data
          fuzzyQuery: true, //Fuzzy query
        },
      ],
    };
  },
  methods: {
    getCustomData() {
      /*
        It is necessary to pay attention to the following data:
          1.The data format is processed here and kept consistent, so that the customHeader does not need to be processed any more
          3.Because we are looking for text when filtering later, the value here is always consistent with the value displayed in the list, which is also text.
          3.You can write promise All, request all the data required by the drop-down box, and then open customFlag
        */
      this.customParams = {
        //City list
        cityList: [
          { value: "Beijing" },
          { value: "Nanjing City" },
          { value: "Shanghai" },
          { value: "Guangzhou City" },
          { value: "Shenzhen City" },
          { value: "Hangzhou" },
          { value: "Chengdu" },
        ],
        // ...
      };
      this.customFlag = true;
    },
    // Color the headings that use the filter criteria
    setlabelColor() {
      this.labelColorList = [];
      this.conditionList.forEach((_item) => {
        this.labelColorList.push(_item.prop);
      });
    },
    // Custom retrieval of emitted events
    tableUpdate(data) {
      console.log(data, "condition");
      let flag = true;
      // Update the filter criteria if they already exist
      this.conditionList.forEach((item, index) => {
        if (item.prop == data.prop) {
          item.value = data.value;
          flag = false;
        }
      });
      // If not, add
      if (flag) {
        this.conditionList.push(data);
      }
      this.customSearch(); //Filter data
    },
    // Filter data
    customSearch() {
      /*
        This can be said to be the core part of filtering. The custom filtering rules are here.
        Come here if you want to change any screening rules in the future
      */
      console.log(this.conditionList, "this.conditionList");
      this.setlabelColor(); //Sets the header color for custom retrieval
      // If the custom search is empty, the query will be called again
      if (this.conditionList.length == 0) {
        this.search();
        return false;
      }
      const result = [];
      // Traverse list data
      for (let i = 0; i < this.tableDataCopy.length; i++) {
        const dataItem = this.tableDataCopy[i];
        // Traverse the user-defined filter conditions and push them if they meet the rules
        let flag = true;
        for (let l = 0; l < this.conditionList.length; l++) {
          const item = this.conditionList[l];
          // Fuzzy query of attribute name and attribute value type
          const { prop, value, conditionType, fuzzyQuery } = item;
          // txt type
          if (conditionType == "txt") {
            if (dataItem[prop].indexOf(value.value1) != -1) {
              flag = true;
            } else {
              flag = false;
            }

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

            // Time type
          } else if (conditionType == "date") {
            // Convert to timestamp and judge
            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;
            }
          }
          // Drop down box type
          else if (conditionType == "select") {
            // If fuzzyQuery is true, it means fuzzy query, otherwise it means exact query
            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: "Wang Xiaohu",
          amount: 100,
          date: "2018-05-02",
          city: "Beijing",
          city2: "Beijing",
        },
        {
          name: "Zhang Erbao",
          amount: 200,
          date: "2019-05-04",
          city: "Shanghai",
          city2: "Shanghai",
        },
        {
          name: "Wang Erya",
          amount: 500,
          date: "2020-05-01",
          city: "Shenzhen City",
          city2: "Shenzhen City",
        },
        {
          name: "Hu Tutu",
          amount: 1000,
          date: "2021-05-03",
          city: "Guangzhou City",
          city2: "Guangzhou City, Guangdong Province",
        },
        {
          name: "Zhang Xiaolong",
          amount: 2000,
          date: "2022-05-03",
          city: "Hangzhou",
          city2: "Hangzhou City, Zhejiang Province",
        },
      ];
      // Copy a copy of the data
      this.tableDataCopy = JSON.parse(JSON.stringify(this.tableData));
    },
    // Closing condition tag
    conditionClose(index) {
      this.conditionList.splice(index, 1);
      this.customSearch(); //Filter data
    },
  },
  mounted() {
    // Request custom filter drop-down box data
    this.getCustomData();
    // Request table data
    this.search();
  },
  components: {
    customHeader,
  },
};
</script>
<style scoped lang='scss'>
// Occupy space, solve the problem that clicking the user-defined filter written by yourself will bubble to sorting
/deep/ .el-table__column-filter-trigger {
  display: none !important;
}
</style>

Subcomponents

<template>
  <!-- Note: the logic part should not be written into this component as far as possible, because this component is based on the outside table The loop is created. Writing logic here will greatly affect the performance -->
  <div class="customHeader" @click.stop style="display: inline-block">
    <el-popover
      placement="bottom"
      title="query criteria"
      width="300"
      trigger="click"
      ref="popover"
    >
      <!-- txt text -->
      <div v-if="item.conditionType == 'txt'">
        <el-input
          v-model.trim="conditions.value1"
          placeholder="Please enter the query content"
          @keyup.native.enter="confirm()"
        ></el-input>
      </div>
      <!-- scope Range-->
      <div v-else-if="item.conditionType == 'scope'">
        <el-input
          style="width: 120px"
          v-model.trim="conditions.value1"
          placeholder="Please enter condition 1"
        ></el-input>
        -
        <el-input
          style="width: 120px"
          v-model.trim="conditions.value2"
          placeholder="Please enter condition 2"
        ></el-input>
      </div>
      <!-- date date-->
      <div v-else-if="item.conditionType == 'date'">
        <el-date-picker
          v-model="conditions.value1"
          type="date"
          clearable
          placeholder="start time"
          value-format="yyyy-MM-dd"
        ></el-date-picker>
        <el-date-picker
          style="margin-top: 10px"
          v-model="conditions.value2"
          type="date"
          clearable
          placeholder="End time"
          value-format="yyyy-MM-dd"
        ></el-date-picker>
      </div>
      <!-- select Selection box-->
      <div v-else-if="item.conditionType == 'select'">
        <el-select
          v-model="conditions.value1"
          placeholder="Please select"
          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 OK box-->
      <div style="text-align: center">
        <el-button @click="confirm" type="primary" size="mini" class="confirm"
          >determine</el-button
        >
      </div>
      <!-- label Title Display-->
      <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 current column data, data in tableConfig, customParams drop-down box data, and the filter criteria being used in labelColorList
  props: ["column", "item", "customParams", "labelColorList"],
  data() {
    return {
      conditions: {
        value1: "",
        value2: "",
      },
    };
  },
  methods: {
    confirm() {
      if (!this.conditions.value1 && !this.conditions.value2) {
        return this.$message.warning("Please select filter criteria");
      }
      // Close popover
      this.$refs.popover.doClose();
      this.$emit("tableUpdate", {
        value: this.conditions, //Filtered data
        ...this.item, //table configuration
      });
    },
  },
  computed: {
    selectList() {
      return function (data) {
        return this.customParams[data.conditionListName];
      };
    },
  },
};
</script>
<style scoped>
.confirm {
  margin-top: 10px;
}
/* Do not double-click the selected text */
.label {
  -moz-user-select: none; /*Firefox*/
  -webkit-user-select: none !important; /*webkit browser*/
  -ms-user-select: none; /*IE10*/
  -khtml-user-select: none; /*Early browser*/
  user-select: none;
}
.labelColor {
  color: #409eff;
}
</style>

There must be some imperfections in the writing. Please advise

Topics: Javascript Front-end Vue elementUI