手把手教學(xué)~基于element封裝tree樹狀下拉框
在日常項(xiàng)目開發(fā)中,樹狀下拉框的需求還是比較常見的,但是element并沒有這種組件以供使用。在這里,小編就基于element如何封裝一個(gè)樹狀下拉框做個(gè)詳細(xì)的介紹。
通過這篇文章,你可以了解學(xué)習(xí)到一個(gè)樹狀下拉框組件是如何一步一步封裝成功的。
話不多說,先看效果圖:
封裝組件
該組件主要基于element的select組件、tree組件及input組件進(jìn)行二次封裝的。
組件布局
首先我們需要基于這幾個(gè)組件對(duì)我們的組件進(jìn)行布局,話不多說直接上代碼:
<template>
<el-select ref="select">
<el-option class="options">
<el-tree id="tree-option"
ref="selectTree"
>
</el-tree>
</el-option>
</el-select>
</template>
<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;
}
</style>
注:css添加scoped屬性,是為了讓css只在該組件生效,避免樣式污染
這個(gè)時(shí)候直接使用肯定是會(huì)報(bào)錯(cuò)的,因?yàn)槲覀兘M件該傳的參數(shù)還未傳遞。
組件數(shù)據(jù)完善
上面我們已經(jīng)完成了布局,接下來就是為其豐富數(shù)據(jù)了,因?yàn)槲覀冞@個(gè)組件肯定是復(fù)用的,因此我們?cè)O(shè)計(jì)數(shù)據(jù)的時(shí)候,需要把常用的數(shù)據(jù)屬性提取出來通過props傳遞接收。我提取的主要有幾下幾個(gè)數(shù)據(jù):
props:{
/* 配置項(xiàng) */
props:{
type: Object,
default:()=>{
return {
value:'id', // ID字段名
label: 'title', // 顯示名稱
children: 'children' // 子級(jí)字段名
}
}
},
/* 選項(xiàng)列表數(shù)據(jù)(樹形結(jié)構(gòu)的對(duì)象數(shù)組) */
options:{
type: Array,
default: ()=>{ return [] }
},
/* 初始值 */
value:{
default: ()=>{ return null }
},
/* 可清空選項(xiàng) */
clearable:{
type:Boolean,
default:()=>{ return true }
},
/* 自動(dòng)收起 */
accordion:{
type:Boolean,
default:()=>{ return false }
},
placeholder:{
type:String,
default:()=>{return "請(qǐng)選擇"}
}
},
大家可能注意到,我所有prop字段都給了type屬性,唯獨(dú)value沒有,這是因?yàn)樵趯?shí)際使用中下拉框的數(shù)據(jù)value值可能是字符串(String)也可能是數(shù)字(Number),為了項(xiàng)目開發(fā)中控制臺(tái)不報(bào)太多無意義的錯(cuò),此處就沒有規(guī)定其type。
接收到prop之后,我們就開始對(duì)組件進(jìn)行數(shù)據(jù)的處理,直接上代碼:
<template>
<el-select :placeholder="placeholder" ref="select">
<el-option class="options">
<el-tree id="tree-option"
ref="selectTree"
:accordion="accordion"
:data="options"
:props="props"
:node-key="props.value"
:default-expanded-keys="[]"
>
</el-tree>
</el-option>
</el-select>
</template>
當(dāng)數(shù)據(jù)過多的時(shí)候,滾動(dòng)條會(huì)出現(xiàn)兩條,如下所示:
處理方法如下:
// 初始化滾動(dòng)條
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中調(diào)用該方法就可以了,效果如下:
點(diǎn)擊選中
數(shù)據(jù)也渲染顯示出來了,這個(gè)時(shí)候我們需要實(shí)現(xiàn)點(diǎn)擊數(shù)據(jù)選中功能。
思路很簡(jiǎn)單:
select組件綁定value值
tree組件綁定節(jié)點(diǎn)點(diǎn)擊事件
點(diǎn)擊事件中獲取value和label
將獲取的值賦給select組件以及返回給父組件
代碼如下:
<template>
<el-select :value="valueTitle" :placeholder="placeholder" ref="select">
<el-option :value="valueTitle" :label="valueTitle" class="options">
<el-tree id="tree-option"
ref="selectTree"
:accordion="accordion"
:data="options"
:props="props"
:node-key="props.value"
:default-expanded-keys="defaultExpandedKey"
@node-click="handleNodeClick"
>
</el-tree>
</el-option>
</el-select>
</template>
data() {
return {
valueId:this.value,// 初始值
valueTitle:'',
defaultExpandedKey:[]
}
},
// 切換選項(xiàng)
handleNodeClick(node){
this.valueTitle = node[this.props.label]//獲取label
this.valueId = node[this.props.value]//獲取value
this.$emit('getValue',this.valueId)//傳值給父組件
},
這樣點(diǎn)擊選中功能就實(shí)現(xiàn)了,但是有個(gè)問題,點(diǎn)擊之后,下拉框選項(xiàng)沒有隱藏,我們只需要再調(diào)用一下select組件的blur方法即可實(shí)現(xiàn)隱藏
數(shù)據(jù)初始化
細(xì)心的小伙伴肯定已經(jīng)發(fā)現(xiàn)了,上面有一個(gè)初始值,并且在選擇器中,初始數(shù)據(jù)也是必不可少的。實(shí)現(xiàn)思路如下:
watch監(jiān)聽prop中value數(shù)據(jù)變化
將初始值做對(duì)應(yīng)賦值
獲取初始值對(duì)應(yīng)的label并做對(duì)應(yīng)賦值
設(shè)置tree組件的默認(rèn)選中狀態(tài)
設(shè)置tree組件的默認(rèn)展開節(jié)點(diǎn)
代碼如下:
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)// 設(shè)置默認(rèn)選中
this.defaultExpandedKey = [this.valueId]// 設(shè)置默認(rèn)展開
}
},
在mounted中調(diào)用執(zhí)行既可
清除選中
一般輸入框或者選擇器都有清除功能,我們的組件自然也少不了清除功能,實(shí)現(xiàn)思路如下:
給select組件設(shè)置clearable屬性
給select組件添加清除監(jiān)聽事件
在監(jiān)聽事件中清除tree組件選中,并清除父組件中的值
代碼如下:
<el-select :value="valueTitle" :clearable="clearable" @clear="clearHandle" :placeholder="placeholder" ref="select">
</el-select>
1
2
// 清除選中
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'))
},
篩選數(shù)據(jù)
當(dāng)tree中數(shù)據(jù)量過大時(shí),我們需要篩選數(shù)據(jù),實(shí)現(xiàn)思路如下:
給tree組件添加filter-node-method方法
添加一個(gè)輸入框,輸入篩選的內(nèi)容
監(jiān)聽輸入內(nèi)容變化,并調(diào)用tree組件的篩選方法
代碼如下:
<template>
<el-select :value="valueTitle" :clearable="clearable" @clear="clearHandle" :placeholder="placeholder" ref="select">
<el-input
class="selectInput"
placeholder="檢索關(guān)鍵字"
v-model="filterText">
</el-input>
<el-option :value="valueTitle" :label="valueTitle" class="options">
<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>
.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);
}
},
這樣一個(gè)簡(jiǎn)單的樹狀下拉框組件就封裝好了。
使用組件
下面給個(gè)簡(jiǎn)單的使用示例:
<template>
<basic-container>
<treeSelect
:props="defaultProps"
:options="treeData"
:value="value"
:accordion="true"
@getValue="getValue($event)"
placeholder="請(qǐng)選擇所屬區(qū)域"
/>
<span>選中的id:{{value}}</span>
</basic-container>
</template>
<script>
import treeSelect from "@/components/treeSelect/treeSelect";
export default {
components: {
treeSelect,
},
data() {
return {
defaultProps: {
label: "name",
value: "id",
children: "children",
},
value:'',//選中的數(shù)據(jù)
treeData:[
{id:1,name:'monkey',children:[{id:2,name:'monkey2'},{id:3,name:'monkey3'},{id:4,name:'monkey4'}]},
{id:5,name:'小猴子的web成長(zhǎng)之路'},
{id:6,name:'小猴子的web成長(zhǎng)之路'},
{id:7,name:'小猴子的web成長(zhǎng)之路'},
{id:8,name:'小猴子的web成長(zhǎng)之路'},
{id:9,name:'小猴子的web成長(zhǎng)之路'},
{id:10,name:'小猴子的web成長(zhǎng)之路'},
{id:11,name:'小猴子的web成長(zhǎng)之路'},
{id:12,name:'小猴子的web成長(zhǎng)之路'},
{id:13,name:'小猴子的web成長(zhǎng)之路'},
{id:14,name:'小猴子的web成長(zhǎng)之路'},
{id:15,name:'小猴子的web成長(zhǎng)之路'},
{id:16,name:'小猴子的web成長(zhǎng)之路'},
{id:17,name:'小猴子的web成長(zhǎng)之路'},
]
};
},
methods:{
// 取值
getValue(value) {
this.value = value
},
}
};
</script>
歡迎關(guān)注微信公眾號(hào):猴哥說前端