手把手教學(xué)~基于element封裝tree樹(shù)狀下拉框
在日常項(xiàng)目開(kāi)發(fā)中,樹(shù)狀下拉框的需求還是比較常見(jiàn)的,但是element并沒(méi)有這種組件以供使用。在這里,小編就基于element如何封裝一個(gè)樹(shù)狀下拉框做個(gè)詳細(xì)的介紹。
通過(guò)這篇文章,你可以了解學(xué)習(xí)到一個(gè)樹(shù)狀下拉框組件是如何一步一步封裝成功的。
話不多說(shuō),先看效果圖:

封裝組件
該組件主要基于element的select組件、tree組件及input組件進(jìn)行二次封裝的。
 組件布局
首先我們需要基于這幾個(gè)組件對(duì)我們的組件進(jìn)行布局,話不多說(shuō)直接上代碼:
<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)完成了布局,接下來(lái)就是為其豐富數(shù)據(jù)了,因?yàn)槲覀冞@個(gè)組件肯定是復(fù)用的,因此我們?cè)O(shè)計(jì)數(shù)據(jù)的時(shí)候,需要把常用的數(shù)據(jù)屬性提取出來(lái)通過(guò)props傳遞接收。我提取的主要有幾下幾個(gè)數(shù)據(jù):
props:{
     /* 配置項(xiàng) */
     props:{
       type: Object,
       default:()=>{
         return {
           value:'id',             // ID字段名
           label: 'title',         // 顯示名稱
           children: 'children'    // 子級(jí)字段名
         }
       }
     },
     /* 選項(xiàng)列表數(shù)據(jù)(樹(shù)形結(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沒(méi)有,這是因?yàn)樵趯?shí)際使用中下拉框的數(shù)據(jù)value值可能是字符串(String)也可能是數(shù)字(Number),為了項(xiàng)目開(kāi)發(fā)中控制臺(tái)不報(bào)太多無(wú)意義的錯(cuò),此處就沒(méi)有規(guī)定其type。
接收到prop之后,我們就開(kāi)始對(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ù)過(guò)多的時(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ù)也渲染顯示出來(lái)了,這個(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è)問(wèn)題,點(diǎn)擊之后,下拉框選項(xiàng)沒(méi)有隱藏,我們只需要再調(diào)用一下select組件的blur方法即可實(shí)現(xiàn)隱藏
數(shù)據(jù)初始化
細(xì)心的小伙伴肯定已經(jīng)發(fā)現(xiàn)了,上面有一個(gè)初始值,并且在選擇器中,初始數(shù)據(jù)也是必不可少的。實(shí)現(xiàn)思路如下:
    watch監(jiān)聽(tīng)prop中value數(shù)據(jù)變化
     將初始值做對(duì)應(yīng)賦值
     獲取初始值對(duì)應(yīng)的label并做對(duì)應(yīng)賦值
     設(shè)置tree組件的默認(rèn)選中狀態(tài)
     設(shè)置tree組件的默認(rèn)展開(kāi)節(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)展開(kāi)
   }
 },
在mounted中調(diào)用執(zhí)行既可
清除選中
一般輸入框或者選擇器都有清除功能,我們的組件自然也少不了清除功能,實(shí)現(xiàn)思路如下:
    給select組件設(shè)置clearable屬性
     給select組件添加清除監(jiān)聽(tīng)事件
     在監(jiān)聽(tīng)事件中清除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ù)量過(guò)大時(shí),我們需要篩選數(shù)據(jù),實(shí)現(xiàn)思路如下:
    給tree組件添加filter-node-method方法
     添加一個(gè)輸入框,輸入篩選的內(nèi)容
     監(jiān)聽(tīng)輸入內(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)單的樹(shù)狀下拉框組件就封裝好了。
 使用組件
下面給個(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):猴哥說(shuō)前端


                   
 個(gè)人中心
 退出


 
  分類導(dǎo)航