主要实现el-select下使用树结构,支持筛选功能
封装的组件 composeTree.vue
<template><div class="vl-tree"><el-select class="treeScroll" popper-class="treeScrollSep"v-model="selectedList"placeholder="请选择"filterable:filter-method="handleFilter" multiplecollapse-tagssize="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>