在日常项目开发中,树状下拉框的需求还是比较常见的,但是element并没有这种组件以供使用。在这里,小编就基于element如何封装一个树状下拉框做个详细的介绍。
通过这篇文章,你可以了解学习到一个树状下拉框组件是如何一步一步封装成功的。
话不多说,先看效果图:
该组件主要基于element的select组件、tree组件及input组件进行二次封装的。
首先我们需要基于这几个组件对我们的组件进行布局,话不多说直接上代码:
注:css添加scoped属性,是为了让css只在该组件生效,避免样式污染
这个时候直接使用肯定是会报错的,因为我们组件该传的参数还未传递。
上面我们已经完成了布局,接下来就是为其丰富数据了,因为我们这个组件肯定是复用的,因此我们设计数据的时候,需要把常用的数据属性提取出来通过props传递接收。我提取的主要有几下几个数据:
props:{
/* 配置项 */
props:{
type: Object,
default:()=>{
return {
value:'id', // ID字段名
label: 'title', // 显示名称
children: 'children' // 子级字段名
}
}
},
/* 选项列表数据(树形结构的对象数组) */
options:{
type: Array,
default: ()=>{ return [] }
},
/* 初始值 */
value:{
default: ()=>{ return null }
},
/* 可清空选项 */
clearable:{
type:Boolean,
default:()=>{ return true }
},
/* 自动收起 */
accordion:{
type:Boolean,
default:()=>{ return false }
},
placeholder:{
type:String,
default:()=>{return "请选择"}
}
},
大家可能注意到,我所有prop字段都给了type属性,唯独value没有,这是因为在实际使用中下拉框的数据value值可能是字符串(String)也可能是数字(Number),为了项目开发中控制台不报太多无意义的错,此处就没有规定其type。
接收到prop之后,我们就开始对组件进行数据的处理,直接上代码:
当数据过多的时候,滚动条会出现两条,如下所示:
处理方法如下:
// 初始化滚动条
initScroll(){
this.$nextTick(()=>{
let scrollWrap = document.querySelectorAll('.el-scrollbar .el-select-dropdown__wrap')[0]
let scrollBar = document.querySelectorAll('.el-scrollbar .el-scrollbar__bar')
scrollWrap.style.cssText = 'margin: 0px; max-height: none; overflow: hidden;'
scrollBar.forEach(ele => ele.style.width = 0)
})
},
在mounted
中调用该方法就可以了,效果如下:
数据也渲染显示出来了,这个时候我们需要实现点击数据选中功能。
思路很简单:
select组件绑定value值
tree组件绑定节点点击事件
点击事件中获取value和label
将获取的值赋给select组件以及返回给父组件
代码如下:
data() {
return {
valueId:this.value,// 初始值
valueTitle:'',
defaultExpandedKey:[]
}
},
// 切换选项
handleNodeClick(node){
this.valueTitle = node[this.props.label]//获取label
this.valueId = node[this.props.value]//获取value
this.$emit('getValue',this.valueId)//传值给父组件
},
这样点击选中功能就实现了,但是有个问题,点击之后,下拉框选项没有隐藏,我们只需要再调用一下select组件的blur方法即可实现隐藏
细心的小伙伴肯定已经发现了,上面有一个初始值,并且在选择器中,初始数据也是必不可少的。实现思路如下:
watch监听prop中value数据变化
将初始值做对应赋值
获取初始值对应的label并做对应赋值
设置tree组件的默认选中状态
设置tree组件的默认展开节点
代码如下:
watch: {
value(){
this.valueId = this.value
this.initHandle()
}
},
// 初始化值
initHandle(){
if(this.valueId){
// 初始化显示label
this.valueTitle = this.$refs.selectTree.getNode(this.valueId).data[this.props.label]
this.$refs.selectTree.setCurrentKey(this.valueId)// 设置默认选中
this.defaultExpandedKey = [this.valueId]// 设置默认展开
}
},
在
mounted
中调用执行既可
一般输入框或者选择器都有清除功能,我们的组件自然也少不了清除功能,实现思路如下:
给select组件设置clearable属性
给select组件添加清除监听事件
在监听事件中清除tree组件选中,并清除父组件中的值
代码如下:
// 清除选中
clearHandle(){
this.valueTitle = ''
this.valueId = null
this.defaultExpandedKey = []
this.clearSelected()
this.$emit('getValue',null)
},
/* 清空选中样式 */
clearSelected(){
let allNode = document.querySelectorAll('#tree-option .el-tree-node')
allNode.forEach((element)=>element.classList.remove('is-current'))
},
当tree中数据量过大时,我们需要筛选数据,实现思路如下:
给tree组件添加filter-node-method方法
添加一个输入框,输入筛选的内容
监听输入内容变化,并调用tree组件的筛选方法
代码如下:
.selectInput{
padding: 0 5px;
box-sizing: border-box;
}
filterNode(value, data) {
if (!value) return true;
return data.name.indexOf(value) !== -1;
}
watch: {
filterText(val) {
this.$refs.selectTree.filter(val);
}
},
这样一个简单的树状下拉框组件就封装好了。
下面给个简单的使用示例:
选中的id:{{value}}
历史好文推荐:
1、Vue3之——和Vite不得不说的事
2、大厂面试算法之斐波那契数列
3、2020字节跳动面试题一面解析
关注公众号【前端优选】,助力成为高级前端架构!
添加个人微信,进群与小伙伴一起玩耍~“在看转发”是最大的支持