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属性,即只有一层,若除第一层节点外其他层次节点带有全部选项,则勾选本层次的上一层节点即可

以上为个人经验,希望能给大家一个参考,也希望大家多多支持Devmax。

Vue element-ui el-cascader 只能末级多选问题的更多相关文章

  1. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  2. vue自定义加载指令v-loading占位图指令v-showimg

    这篇文章主要为大家介绍了vue自定义加载指令和v-loading占位图指令v-showimg的示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  3. vue使用动画实现滚动表格效果

    这篇文章主要为大家详细介绍了vue使用动画实现滚动表格效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  4. 关于Vue 监控数组的问题

    这篇文章主要介绍了Vue 监控数组的示例,主要包括Vue 是如何追踪数据发生变化,Vue 如何更新数组以及为什么有些数组的数据变更不能被 Vue 监测到,对vue监控数组知识是面试比较常见的问题,感兴趣的朋友一起看看吧

  5. Vue子组件props从父组件接收数据并存入data

    这篇文章主要介绍了Vue子组件props从父组件接收数据并存入data的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  6. Vue h函数的使用详解

    本文主要介绍了Vue h函数的使用详解,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  7. VUE响应式原理的实现详解

    这篇文章主要为大家详细介绍了VUE响应式原理的实现,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助

  8. vue+Element ui实现照片墙效果

    这篇文章主要为大家详细介绍了vue+Element ui实现照片墙效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  9. vue+elemet实现表格手动合并行列

    这篇文章主要为大家详细介绍了vue+elemet实现表格手动合并行列,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. iview+vue实现导入EXCEL预览功能

    这篇文章主要为大家详细介绍了iview+vue实现导入EXCEL预览功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部