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

封装的组件 composeTree.vue
<template>
    <el-select :popper-class="popperClass"
        v-model="selectedList"
        placeholder="请选择"
        filterable
        :filter-method="handleFilter" 
        multiple
        :collapse-tags="collapseTags"
        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="nodeKey"
            :props="props">
            <template slot-scope="{ node, data }">
                <slot :node="node" :data="data">
                    <span class="custom-tree-node">
                        {{data[props.label]}}
                    </span>
                </slot>
            </template>
        </el-tree>
        <el-option value="" style="display: none;"></el-option>
    </el-select>
</template>
<script>
export default {
    props: {
        props: {
            type: Object,
            default: () => {
                return {
                    children: 'children',
                    label: 'label',
                    value: 'value',
                }
            }
        },
        nodeKey: {
            type: String,
            default: 'value'
        },
        collapseTags: {
            type: Boolean,
            default: false
        },
        popperClass: {
            type: String,
            default: ''
        },
        selectedIdList: {
            type: Array,
            default: () => []
        },
        treeList: {
            type: Array,
            default: () => []
        }
    },
    model: {
        prop: 'selectedIdList',//选中的数组
        event: 'updateSelectedIdList'
    },
    watch: {
        selectedIdList: {
            handler(val) {
                this.$nextTick(() => {
                    this.$refs['tree'].setCheckedKeys(val, true);
                })
            },
            immediate: true
        },
    },
    data() {
        return {
            list: [],
            searchVal: '',
            noFilterTreeNode: false,//是否过滤树节点
            selectedList: [],
            // checkedNodeList:[],
        }
    },
    created() {
        
    },
    methods: {
        //筛选
        handleFilter(val) {
            if (this.noFilterTreeNode) return;
            this.searchVal = val;
            this.$refs['tree']?.filter(val);
        },
        filterNode(value, data, node) {
            if (!value) return true
            return data[this.props.label].toLowerCase().indexOf(value.toLowerCase()) !== -1
        },
        removeTag(val) {
            this.watchCheckChangeRun = false;
            let obj = this.checkedNodeList.find(item => item[this.props.label] === val);
            this.$refs.tree.setChecked(obj[this.props.value], false);
            setTimeout(() => {//由于首次回显不对tree有任何操作的时候,直接移除标签,不能触发tree的change事件,所以添加个手动调用
                if (!this.watchCheckChangeRun) {
                    this.handleCheckChange();
                }
            },1)
        },
        handleSelectVisibleChange(val) {
            this.noFilterTreeNode = false;
            if (!val) {//select框隐藏时,重置树结构
                if (this.searchVal) {
                    this.handleFilter('')
                }
            }
        },
        //树选择变化
        handleCheckChange() {
            this.watchCheckChangeRun = true;
            let checkList=this.$refs['tree'].getCheckedNodes(true);
            this.selectedList = checkList.map(item => item[this.props.label]);
            this.$emit('updateSelectedIdList', checkList.map(item => item[this.props.value]));
            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  :props="props":nodeKey="'id'"
             v-model="v.selectedIdList" :treeList="treeList" :collapseTags="true">
               <!--  <template #default="{node,data}">
                    <div>
                        {{data.name}}-{{ data.id }}
                    </div>
                </template> -->
            </composeTree>
            <div>
                selectedIdList:{{ v.selectedIdList }}
            </div>
        </div>
    </div>
</template>
<script>
import composeTree from './composeTree.vue';
export default {
    data() {
        return {
            props:{
                children: 'children',
                label: 'name',
                value: 'id',
            },
            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> 
                

















