手把手教學(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):猴哥說前端