1. 程式人生 > 實用技巧 >手把手教學~基於element封裝tree樹狀下拉框

手把手教學~基於element封裝tree樹狀下拉框

在日常專案開發中,樹狀下拉框的需求還是比較常見的,但是element並沒有這種元件以供使用。在這裡,小編就基於element如何封裝一個樹狀下拉框做個詳細的介紹。

通過這篇文章,你可以瞭解學習到一個樹狀下拉框元件是如何一步一步封裝成功的。

話不多說,先看效果圖:

封裝元件

該元件主要基於element的select元件、tree元件及input元件進行二次封裝的。

元件佈局

首先我們需要基於這幾個元件對我們的元件進行佈局,話不多說直接上程式碼:

<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只在該元件生效,避免樣式汙染

這個時候直接使用肯定是會報錯的,因為我們元件該傳的引數還未傳遞。

元件資料完善

上面我們已經完成了佈局,接下來就是為其豐富資料了,因為我們這個元件肯定是複用的,因此我們設計資料的時候,需要把常用的資料屬性提取出來通過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之後,我們就開始對元件進行資料的處理,直接上程式碼:

<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>

當資料過多的時候,滾動條會出現兩條,如下所示:

處理方法如下:

// 初始化滾動條
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元件以及返回給父元件

程式碼如下:

<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:[]
    }
},
// 切換選項
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元件選中,並清除父元件中的值

程式碼如下:

<el-select :value="valueTitle" :clearable="clearable" @clear="clearHandle" :placeholder="placeholder" ref="select">
</el-select>
// 清除選中
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元件的篩選方法

程式碼如下:

<template>
  <el-select :value="valueTitle" :clearable="clearable" @clear="clearHandle" :placeholder="placeholder" ref="select">
    <el-input
      class="selectInput"
      placeholder="檢索關鍵字"
      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);
    }
},

這樣一個簡單的樹狀下拉框元件就封裝好了。

使用元件

下面給個簡單的使用示例:

<template>
  <basic-container>
    <treeSelect
      :props="defaultProps"
      :options="treeData"
      :value="value"
      :accordion="true"
      @getValue="getValue($event)"
      placeholder="請選擇所屬區域"
    />
    <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:'',//選中的資料
      treeData:[
          {id:1,name:'monkey',children:[{id:2,name:'monkey2'},{id:3,name:'monkey3'},{id:4,name:'monkey4'}]},
          {id:5,name:'小猴子的web成長之路'},
          {id:6,name:'小猴子的web成長之路'},
          {id:7,name:'小猴子的web成長之路'},
          {id:8,name:'小猴子的web成長之路'},
          {id:9,name:'小猴子的web成長之路'},
          {id:10,name:'小猴子的web成長之路'},
          {id:11,name:'小猴子的web成長之路'},
          {id:12,name:'小猴子的web成長之路'},
          {id:13,name:'小猴子的web成長之路'},
          {id:14,name:'小猴子的web成長之路'},
          {id:15,name:'小猴子的web成長之路'},
          {id:16,name:'小猴子的web成長之路'},
          {id:17,name:'小猴子的web成長之路'},
      ]
    };
  },
  methods:{
      // 取值
    getValue(value) {
      this.value = value
    },
  }
};
</script>

原始碼收錄在公眾號【小猴子的web成長之路】,關注公眾號回覆關鍵字【treeSelect】即可獲取