element-ui Tree 树形控件 过滤保留子级并获取过滤后的数据

本示例基于vue2 + element-ui

element-ui 的官网demo是只保留到过滤值一级的,并不会保留其子级

目标

1、Tree 树形控件 保留过滤值的子级

2、在第一次过滤数据的基础上进行第二次过滤

先看效果

Tree 树形控件 保留过滤值的子级

复制代码
<el-tree
  class="filter-tree"
  node-key="id"
  :data="treeData"
  :props="defaultProps"
  default-expand-all
  show-checkbox
  @check-change="handleCheckChange"
  :filter-node-method="filterNode"
  ref="tree">
</el-tree>


// 对树节点进行筛选时执行的方法
filterNode (value, data, node) {
  let parentNode = node.parent; // 父级node
  let labels = [node.label]; // 当前node的名字
  let level = 1; // 层级
  while (level < node.level) {
    labels = [...labels, parentNode.label]; // 当前node名字,父级node的名字
    parentNode = parentNode.parent;
    level++;
  }
  return labels.some((d) => d.indexOf(value) !== -1);
},

在第一次过滤数据的基础上进行第二次过滤

1、下载插件库

复制代码
npm install -S circular-json

2、在使用页面引入

复制代码
import CircularJSON from 'circular-json'

3、代码应用

复制代码
<template>
  <div class="about-container">
    <h1>获取过滤后的tree数据</h1>
    <el-input
      clearable
      placeholder="第一次过滤"
      @keyup.enter.native="handleFilter1"
      v-model="firstText">
    </el-input>
    <el-input
      style="margin-top: 10px;"
      clearable
      placeholder="第二次过滤"
      @keyup.enter.native="handleFilter2"
      v-model="secondText">
    </el-input>
    <el-button @click="handleFilter1(),handleFilter2()" type="primary" icon="el-icon-search">搜索</el-button>
    <div>
      <el-tree
        class="filter-tree"
        node-key="id"
        :data="treeData"
        :props="defaultProps"
        default-expand-all
        show-checkbox
        @check-change="handleCheckChange"
        :filter-node-method="filterNode"
        ref="tree">
      </el-tree>
    </div>
  </div>
</template>

<script>
  // 在使用的组件内引入
  import CircularJSON from 'circular-json'

  export default {
    data() {
      return {
        // 第一次过滤
        firstText: '',
        // 第二次过滤
        secondText:'',
        // tree控件的渲染值
        treeData: [{
          id: 1,
          label: '一级1',
          children: [{
            id: 4,
            label: '二级1-1',
            children: [{
              id: 9,
              label: '三级1-1-1',
              children: [{
                id:91,
                label: '四级1'
              },{
                  id:92,
                  label: '四级2'
              }]
            }, {
              id: 10,
              label: '三级1-1-2'
            }]
          }]
        }, {
          id: 2,
          label: '一级2',
          children: [{
            id: 5,
            label: '二级2-1'
          }, {
            id: 6,
            label: '二级2-2'
          }]
        }, {
          id: 3,
          label: '一级',
          children: [{
            id: 7,
            label: '二级3-1'
          }, {
            id: 8,
            label: '二级3-2'
          },{
            id: 82,
            label: '四级3'
          }]
        },{
          id: 31,
          label: '奇迹',
          children: [{
            id: 71,
            label: '奇迹1'
          }, {
            id: 81,
            label: '奇迹2'
          }]
        }],
        defaultProps: {
          children: 'children',
          label: 'label'
        },
        // tree的原版备份数据
        deepCloneTreeData:[],
        // 第一次过滤后tree控件渲染数据
        firstFilterdata:[],
      }
    },
    mounted() {
      this.deepClone(this.treeData).then((res)=>{
        this.deepCloneTreeData = res;
      })
    },
    methods: {
      // 第一次过滤
      handleFilter1() {
        // 第一次过滤时没有输入值,即用tree控件的原始值
        if(!this.firstText){
          this.treeData = this.deepCloneTreeData;
          this.firstFilterdata = this.deepCloneTreeData;
          return
        }
        this.$refs.tree.filter(this.firstText)
        this.firstFilterdata = this.getFilterData();
      },
      // 第二次过滤
      handleFilter2() {
        // 基于第一次过滤出来的数据
        if(this.firstFilterdata.length > 0) this.treeData = this.firstFilterdata;
        this.$nextTick(()=>{
          this.$refs.tree.filter(this.secondText);
        })
      },
      // 对树节点进行筛选时执行的方法
      filterNode (value, data, node) {
        let parentNode = node.parent; // 父级node
        let labels = [node.label]; // 当前node的名字
        let level = 1; // 层级
        while (level < node.level) {
          labels = [...labels, parentNode.label]; // 当前node名字,父级node的名字
          parentNode = parentNode.parent;
          level++;
        }
        return labels.some((d) => d.indexOf(value) !== -1);
      },
      // tree 的选择事件
      handleCheckChange(data, checked, indeterminate) {
        const arr = this.$refs.tree.getCheckedKeys()
      },
      // 需要获取过滤后的 Tree组件数据
      getFilterData() {
          const rootData = this.$refs.tree.root;
          if (rootData.visible) {
            const childNodesStr = CircularJSON.stringify(rootData.childNodes);
            const childNodes = CircularJSON.parse(childNodesStr);
            const filterData = this.recursionNodes(childNodes);
            return filterData;
        }
      },
      /**
       * 递归遍历数据
       * 这里解释一下为什么要用CircularJSON这个插件,因为element tree
       * node数据存在一个对象里的子项存在循环引用,存在循环引用的对象
       */
      recursionNodes(childNodes) {
        const nodes = childNodes;
        const result = [];
        for (const item of nodes) {
          if (item.visible) {
            result.push(item.data);
          }
          if (item.childNodes && item.childNodes.length) {
            const tempResult = this.recursionNodes(item.childNodes);
            item.data.children = tempResult;
          }
        }
        return result;
      },
      /**
       * 深拷贝
       */
      deepClone(obj){
          return new Promise((resolve) => {
              const { port1,port2 } = new MessageChannel();
              port1.postMessage(obj);
              port2.onmessage = (msg) => {
                  resolve(msg.data)
              }
          })
      }
    },
  }
</script>
相关推荐
我有满天星辰13 分钟前
Vue 3 响应式双雄:ref 与 reactive 完全指南
前端·javascript·vue.js
leoZ23111 小时前
Claude 驱动的全栈开发:Spring Boot + Vue 的 BS 架构实践
vue.js·spring boot·架构
荒诞英雄15 小时前
从 17MB Vendor 大包到按需加载:Vite 多入口 Vue 组件库首屏优化实践
vue.js·vite
jsonbro15 小时前
手把手带你实现发布订阅模式
前端·vue.js·设计模式
Cobyte17 小时前
23.Vue Vapor 组件 attrs 的实现
前端·javascript·vue.js
斯特凡今天也很帅18 小时前
xml页面可以打开,不报错,但是显示数据的地方也不显示,我是怎么解决的
xml·vue.js
柯克七七19 小时前
给老项目加了 TypeScript,本来只想自己爽,结果全公司代码审查标准被我抬高了
前端·vue.js·typescript
自然 醒21 小时前
前端如何实现在线预览office常见文件功能?
前端·vue.js
肉肉不吃 肉1 天前
前端调试跨域如何解决
前端·vue.js
卓怡学长1 天前
w268基于springboot + vue 物流系统
java·数据库·vue.js·spring boot·spring·intellij-idea