本示例基于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>