Vue element-ui el-cascader 隻能末級多選問題

element-ui el-cascader隻能末級多選

像這樣的需求我們直接可以用css完成

/deep/.el-cascader-panel:first-child .el-checkbox{
  display:none;
}

擴展實現element-ui中el-cascader全選功能

重所周知,element-ui中的cascader中,沒有對所有子節點的全選功能。

近期,公司項目有一個功能是,如果點擊瞭全選,則選中所有子節點的功能;如果在全選狀態下,取消瞭任意一個節點,則移除該節點和全部節點;如果在全選狀態下,又點擊瞭全選,則完成全不選的操作,即實現效果如下

圖1.1: 從【選中部分節點】(或【所有節點都沒有選擇】) 到【全選】的實現效果

圖1.2 從【已選中全部節點】狀態到【取消任意一個節點】效果圖

圖1.3 全不選效果圖(在【全選】狀態下再次點擊【全選】按鈕,或挨個取消已選中節點)

實現思路如下

【Ⅰ】返回數據格式

1. 如果是隻有一層字節點,他的數據返回格式應是[節點1, 節點2, …..]

2. 如果含有多層節點,他的數據返回格式應該是[ [父節點,子節點,孫子節點…], [父節點,子節點,孫子節點…] ],這種數據返回格式,可以很清晰的看到各個節點所處的層次結構

【2】全選實現方式

1. 全選狀態:包含瞭所有節點,因為數據結構層次的不確定,可以大致分為兩種:

第一種,單層結構,對於全選實現方式很簡單,通過更改綁定的數據源,加入所有節點即可;

第二種,多層結構,對於這種數據結構,我們采用遞歸的方式(不確定是2層,3層.還是更多),首先我們創建一個臨時數據,用於保存根節點到各個子節點中所有節點的value值,對於如圖所示的11號節點和8號節點,那我們應該保存的值為[ [1, 3, 7, 11], [1, 3, 8] ]

        

 2. 全不選狀態:

  • 對於這個狀態比較簡單,將數組清空即可

3. 從全選狀態到取消任意一個節點狀態:

  • 對於這個狀態,刪除【全選】所對應的節點和刪除本次要移除的節點即可

組件源碼奉上

<template>
  <div>
    <el-cascader
      v-model="select_options"
      ref="cascader"
      :options="options_cascader"
      :props="getProps"
      :filterable="filterable"
      @change="optionsChange"
      collapse-tags
      clearable></el-cascader>
  </div>
</template>
<script>
  import cloneDeep from "lodash/cloneDeep";
 
  export default {
    name: "myCascader",
    props: {
      options: { // 級聯選擇器選項
        type: Array,
        default: []
      },
      is_deep: { // 是否有children,即數據深度是否為多層
        type: Boolean,
        required: true
      },
      has_all_select: { // 是否為帶有全部的選項
        type: Boolean,
        required: true
      },
      all_select_flag: { // '全部'選項的標識
        type: String | Number,
        default: ''
      },
      value: {  // 綁定的value值
        type: String,
        default: 'value'
      },
      label: { // 綁定瞭label值
        type: String,
        default: 'label'
      },
      filterable: { // 是否可以模糊搜索(隻能選中最後一層子節點)
        type: Boolean,
        default: false
      }
    },
    mounted() {
      this.options_cascader = cloneDeep(this.options) // 深拷貝傳過來的數據源,引用瞭lodash包
      if (!this.is_deep && this.has_all_select && !this.all_select_flag) {
        console.warn("當前為單層數據且含有全部選項,請輸入全部選項標識,默認為'',如果全部類型的標識為''請忽略")
      }
    },
    data() {
      return {
        options_cascader: [], // 所有選項
        select_options: [], // 以選擇的節點
        is_select_all: false, // 是否為全選
        deep_option_data: [] // 緩存各個深度的value
      }
    },
    computed: {
      // 獲取配置選項
      getProps() {
        return {
          multiple: true,
          expandTrigger: 'hover',
          emitPath: this.is_deep,
          value: this.value,
          label: this.label
        }
      },
    },
    methods: {
      optionsChange() {
        // 判斷已選中的節點中是否包含全部
        let has_all_option = false
        if (!this.is_deep) {
          has_all_option = this.select_options.includes(this.all_select_flag)
        } else {
          has_all_option = this.select_options.some(res => res.length === 1)
        }
        /**
         * 如果已選擇節點中包含全部,且is_select_all為true時,代表移除瞭選項中除全部外的某個節點
         * 如果已選擇節點中包含全部,且is_select_all為false時,代表選擇瞭全部節點
         * 如果已選擇節點中不包含全部,且is_select_all為true時,代表取消選擇全部節點
         * */
        if (has_all_option && this.is_select_all) {
          this.$refs.cascader.$refs.panel.clearCheckedNodes()
          this.select_options.splice(this.searchSelectAllNodeIndex(), 1)
          this.is_select_all = false
        } else if (has_all_option && !this.is_select_all) {
          this.is_select_all = true
          this.selectAll()
        } else if (!has_all_option && this.is_select_all) {
          this.is_select_all = false
          this.$refs.cascader.$refs.panel.clearCheckedNodes()
          this.select_options = []
        }
        this.$emit('getOptions', this.select_options)
      },
      // 查找全部節點所在的索引
      searchSelectAllNodeIndex() {
        if (!this.is_deep) {
          return this.select_options.indexOf(this.all_select_flag)
        } else {
          let selectAllOptionIndex = -1
          this.select_options.forEach((res, index) => {
            if (res.length === 1) {
              selectAllOptionIndex = index
            }
          })
          return selectAllOptionIndex
        }
      },
      // 選擇全部
      selectAll() {
        this.select_options = []
        if (!this.is_deep) {
          // 為單層數據,即沒有children
          this.options_cascader.forEach(res => {
            this.select_options.push(res[this.value])
          })
        } else {
          // 多層數據, 遞歸
          this.getDeepOptions(this.options_cascader)
        }
      },
      // 遞歸獲取深層數據
      getDeepOptions(value) {
        let arr
        value.forEach(res => {
          if (res.children) {
            // 如果不是最後一層,則緩存當前層次的value
            this.deep_option_data.push(res[this.value])
            this.getDeepOptions(res.children)
          } else {
            // 如果是最後一層,把最後一層的value放入級聯選擇器綁定的數組
            arr = cloneDeep(this.deep_option_data)
            arr.push(res[this.value])
            this.select_options.push(arr)
          }
        })
        // 每一層循環結束後,清空本層的父節點
        this.deep_option_data.pop()
      }
    }
  }
</script>
 
<style scoped>
 
</style>

使用方法

// 多層結構,帶全部
<my-cascader
    :options="options"
    :is_deep="true" 
    :has_all_select="true"
     @getOptions="getOptions">
</my-cascader>
 
// 單層結構,帶全部
<my-cascader
    :options="options"
    :is_deep="false" 
    :has_all_select="true"
     @getOptions="getOptions">
</my-cascader>
 
// 註:如果不需要配置全部節點則直接使用element提供的cascader即可
// 本組件隻考慮第一層帶有全部的情況,且全部節點不帶有children屬性,即隻有一層,若除第一層節點外其他層次節點帶有全部選項,則勾選本層次的上一層節點即可

以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。

推薦閱讀: