效果:
一、组件代码
<template> <!--el-select中v-model/value的值为当前被选中的el-option的value属性值, 为el-select设置disabled属性,则整个选择器不可用, 为el-select设置clearable属性,则可将选择器清空。clear事件:可清空的单选模式下用户点击清空按钮时触发 --> <el-select :value="valueTitle" :clearable="clearable" @clear="clearHandle" ref="treeSelect" :size="size"
:disabled="isDisabled" class="mySelect"> <el-input class="selectInput" :placeholder="placeholder" v-model="filterText"></el-input> <!--label为选项的标签,为要展示的值。若不设置则默认与 value 相同,value为要提交的值--> <el-option :value="valueTitle" :label="valueTitle" class="options"> <!--data为要展示的数据,props中label指定标签,props中children指定子树,node-key是每个树节点用来作为唯一标识的属性, default-expanded-keys为默认展开的节点的 key 的数组。 filter-node-method对树节点进行筛选时执行的方法,返回 true 表示这个节点可以显示,返回 false 则表示这个节点会被隐藏 node-click为节点被点击时的回调,共三个参数,依次为:传递给 data 属性的数组中该节点所对应的对象、节点对应的 Node、节点组件本身。--> <el-tree id="tree-option" ref="selectTree" :accordion="accordion" :data="options" :props="props" :node-key="props.value" :default-expanded-keys="defaultExpandedKey" :filter-node-method="filterNode" @node-click="handleNodeClick"> </el-tree> </el-option> </el-select> </template> <script> export default { name: "el-tree-select", props: { /* 配置项 */ props: { type: Object, default: () => { return { value: 'id', // ID字段名 label: 'title', // 显示名称 children: 'children', // 子级字段名 } } }, /* 选项列表数据(树形结构的对象数组) */ options: { type: Array, default: () => { return [] } }, /* 接收父组件传递v-model中传递过来的值 */ value: { type: String, default: () => { return null } }, /* 可清空选项 */ clearable: { type: Boolean, default: () => { return true } }, /* 自动收起 */ accordion: { type: Boolean, default: () => { return false } }, placeholder: { type: String, default: () => { return "检索关键字" } }, size: { type: String, default: () => { return "small" } }, isDisabled: { type: Boolean, default: () => { return false } } }, data() { return { filterText: '', valueId: this.value, // 初始值 valueTitle: '', defaultExpandedKey: [] } }, watch: { // 当切换时会改变父组件的值,从而触发该监听器绑定的方法 value() { // 每次切换将value的值赋值给valueId this.valueId = this.value this.initHandle() // 每次切换时都会触发该方法 }, filterText(val) { // val为input输入框要输入的值 console.log(val) this.$refs.selectTree.filter(val); } }, mounted() { this.initHandle() }, methods: { // 每次切换时都会触发该方法 initHandle() { // 添加菜单时,value为默认值null;修改菜单时,由于有节点的值,故value值不为null if (this.valueId) { // 如果不为空, const node = this.$refs.selectTree.getNode(this.valueId) // 获取节点 if(node){ // 通过node的data属性获取节点数据,再获取节点的name的值,赋值给valueTitle this.valueTitle = node.data[this.props.label] // 修改组件要展示的值 } this.$refs.selectTree.setCurrentKey(this.valueId) // 设置当前节点为选中状态 this.defaultExpandedKey = [this.valueId] // 设置valueId当前节点展开 } this.initScroll() }, // 初始化el-select的滚动条 initScroll() { this.$nextTick(() => { // 获取class="el-scrollbar"下class=" el-select-dropdown__wrap"的所有元素 let scrollWrap = document.querySelectorAll('.el-scrollbar .el-select-dropdown__wrap')[0] // 获取class="el-scrollbar"下class="el-scrollbar__bar"的所有元素 let scrollBar = document.querySelectorAll('.el-scrollbar .el-scrollbar__bar') // cssText 的本质就是设置 HTML 元素的 style 属性值 scrollWrap.style.cssText = 'margin: 0px; max-height: none; overflow: hidden;' scrollBar.forEach(ele => ele.style.width = 0) }) }, // 每次点击一个新的菜单都会触发这个方法,即切换时会触发,修改父组件的值 // 每次切换,都会修改组件显示的值,即el-select显示的值,海修改parentId的值, handleNodeClick(node) { this.valueTitle = node[this.props.label] // 菜单名称,如:菜单管理,修改显示的值 this.valueId = node[this.props.value] // 菜单id,如1529450670506250249 this.$emit('getValue', this.valueId) // 将id赋值给父组件的form.parentId this.defaultExpandedKey = [] if (!node.children || !node.children.length) { // 当node为最后一级时,让el-select组件失去焦点 this.$refs.treeSelect.blur() } }, // 清除选中 clearHandle() { this.valueTitle = '' this.valueId = 0 this.defaultExpandedKey = [] this.clearSelected() this.$emit('getValue', 0) }, /* 清空选中样式 */ clearSelected() { // 获取id="tree-option"下class="el-tree-node"的所有元素 let allNode = document.querySelectorAll('#tree-option .el-tree-node') // classList 属性返回元素的类名,该属性用于在元素中添加,移除及切换 CSS 类。classList 属性是只读的,但你可以使用 add() 和 remove() 方法修改它。 allNode.forEach((element) => element.classList.remove('is-current')) }, filterNode(value, data) { // value为输入的值,data为每一个节点的值,即将每一个节点的name值拿出来判断是否包含value // 如果返回true,则显示节点,否则不显示 console.log(value) if (!value) return true; return data.name.indexOf(value) !== -1; } }, }; </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> .el-scrollbar .el-scrollbar__view .el-select-dropdown__item { height: auto; max-height: 274px; padding: 0; overflow: hidden; overflow-y: auto; } .el-select-dropdown__item.selected { font-weight: normal; } ul li>>>.el-tree .el-tree-node__content { height: auto; padding: 0 20px; } .el-tree-node__label { font-weight: normal; } .el-tree>>>.is-current .el-tree-node__label { color: #409EFF; font-weight: 700; } .el-tree>>>.is-current .el-tree-node__children .el-tree-node__label { color: #606266; font-weight: normal; } .selectInput { padding: 0 5px; box-sizing: border-box; } .mySelect { width: 100%; } </style>
获取class="el-scrollbar"下class=" el-select-dropdown__wrap"的所有元素
获取class="el-scrollbar"下class="el-scrollbar__bar"的所有元素
二、组件的使用
1、引入
import SelectTree from "components/tree/TreeSelect"
2、注册
components: {
SelectTree
},
3、使用组件
父组件代码如下:
<el-form-item label="上级菜单" prop="parentId" :rules="form.type!=0?rules.parentId:[]"> <select-tree :props="props" :options="list" v-model="form.parentId" :clearable="isClearable" :accordion="isAccordion" @getValue="getValue($event)" :isDisabled="isDisabled" ref="TreeSelect"> </select-tree> </el-form-item>
由v-model语法糖可知:v-model="form.parentId"相当与:value="form.parentId" @input="form.parentId = $event.target.value",所以在子组件中侦听value的值的变化,value值得变化就相当于parentId得值得变话。然后将当前被选中的el-option的value属性值赋值给form.parentId,从而实现双向绑定。
props中label指定节点标签为节点对象的某个属性值,children指定子树为节点对象的某个属性值。
其中父组件的props如下:
// 配置项 props: { value: "id", label: "name", children: "children", },
父组件传递到子组件的list值如下: