主要实现el-select下使用树结构,支持筛选功能

封装的组件 composeTree.vue
<template>
<div class="vl-tree">
<el-select class="treeScroll" popper-class="treeScrollSep"
v-model="selectedList"
placeholder="请选择"
filterable
:filter-method="handleFilter"
multiple
collapse-tags
size="mini"
@visible-change="handleSelectVisibleChange"
@remove-tag="removeTag"
>
<el-tree :filter-node-method="filterNode" show-checkbox ref="tree"
@check-change="handleCheckChange"
:data="treeList"
node-key="id"
:props=" {
children: 'children',
label: 'name',
value: 'id',
}">
<template slot-scope="{ node, data }">
<slot :node="node" :data="data">
<span class="custom-tree-node">
{{data.name}}
</span>
</slot>
</template>
</el-tree>
<el-option value="" style="display: none;"></el-option>
</el-select>
</div>
</template>
<script>
import { debounce } from '@/utils';
let orgList = [];//列表初始值
export default {
props: {
selectedIdList: {
type: Array,
default: () => []
},
treeList: {
type: Array,
default: () => []
}
},
model: {
prop: 'selectedIdList',//选中的数组
event: 'updateSelectedIdList'
},
watch: {
selectedIdList: {
handler(val) {
debugger;
this.$nextTick(() => {
this.$refs['tree'].setCheckedKeys(val,true);
})
},
immediate: true
},
treeList: {
handler(val) {
if (val) {
orgList=JSON.parse(JSON.stringify(val));
}
},
immediate: true
}
},
data() {
return {
list: [],
searchVal: '',
noFilterTreeNode: false,//是否过滤树节点
selectedList: [],
}
},
created() {
},
methods: {
//筛选
handleFilter(val) {
if (this.noFilterTreeNode) return;
this.searchVal = val;
let allList = JSON.parse(JSON.stringify(orgList));
if (val == '') {
this.searchList = allList;
} else {
this.searchList = this.filterTreeByKeyword(allList, val);
}
let self = this;
debounce(function() {
self.$nextTick(() => {
self.$refs['tree']?.filter(val);
})
},300,false)()
},
filterTreeByKeyword(treeData, keyword) {
let result = [];
function traverse(nodes) {
for (let node of nodes) {
if (node.name.includes(keyword)) {
// 如果当前节点的 bareName 包含关键词,则添加到结果数组中
result.push(node);
}
if (node.children && node.children.length > 0) {
// 如果有子节点,则递归处理子节点列表
traverse(node.children);
}
}
}
traverse(treeData); // 开始遍历整个树结构
return result;
},
filterNode(value, data, node) {
if (!value) return true;
let obj = {
has: false
};
this.searchList.map(item => {
if (item.id == data.id) {
obj.has = true;
} else {
let children = item.children || [];
this.hasFilterNode(children, data.id, obj)
}
})
return obj.has;
},
hasFilterNode(children, id, obj) {
if (obj.has) {
return true;
} else {
for (let i = 0; i < children.length; i++) {
let child = children[i];
if (child.id == id) {
obj.has = true;
return true;
} else {
let children2 = child.children || [];
this.hasFilterNode(children2, id, obj)
}
}
}
},
removeTag(val) {
let obj = this.checkedNodeList.find(item => item.name === val);
this.$refs.tree.setChecked(obj.id, false);
},
// handleSelectChange() {
// let arr = [];
// this.selectedList.map(item => {
// let obj = this.checkedNodeList.find(val => val.name === item);
// if (obj) {
// arr.push(obj);
// }
// })
// this.checkedNodeList = arr;
// this.handleTreeSelect(arr);//变更树的选择
// },
//类别树值变动
handleTreeSelect(row) {
let arr = row.map(item => item.id);
this.$refs['tree'].setCheckedKeys(arr,true);
},
handleSelectVisibleChange(val) {
if (!val) {//select框隐藏时,重置树结构
this.noFilterTreeNode = false;
if (this.searchVal) {
this.handleFilter('')
}
}
},
//树选择变化
handleCheckChange() {
let checkList=this.$refs['tree'].getCheckedNodes(true);
this.selectedList = checkList.map(item => item.name);
this.$emit('updateSelectedList', checkList.map(item => item.id));
this.checkedNodeList = checkList;
this.noFilterTreeNode = true;//避免vl-tree筛选问题
},
}
}
</script>
页面中引用组件
<template>
<div>
<h2>下拉框中树结构及搜索功能</h2>
<div v-for="(v,i) in list" :key="i" class="box">
<composeTree :id="v.id" v-model="v.selectedIdList" :treeList="treeList">
<!-- <template #default="{node,data}">
<div>
{{data.name}}-{{ data.id }}
</div>
</template> -->
</composeTree>
</div>
</div>
</template>
<script>
import composeTree from './composeTree.vue';
export default {
data() {
return {
list: [
{
id: 1,
selectedIdList:['Option001']
},
{
id: 2,
selectedIdList:['Option111']
}
],
treeList:[]
}
},
components: {
composeTree
},
created() {
const initials = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
const options = Array.from({ length: 50 }).map((_, idx) => ({
id: `Option${idx + 1}`,
name: `${initials[idx % 10]}${idx}`,
children: [
{
id: `Option${'0' + idx + 1}`,
name: `${initials[idx % 10]}${'0'+idx}`,
},
{
id: `Option${'1' + idx + 1}`,
name: `${initials[idx % 10]}${'1'+idx}`,
}
]
}));
this.treeList = options;
}
}
</script>
<style lang="less" scoped>
.box {
margin-bottom: 20px;
}
</style>