element-ui的el-cascader增加全选按钮实现(附源码)

news2025/5/16 8:24:19

最近遇到了在级联选择器上添加全选框的需求 ,但是项目使用的是Vue2 + Element UI的架构,而我们都知道Element UI提供的级联选择器el-cascader是不支持全选框的,而我又没有在网上找到适合我项目的实现,索性自己实现一个组件(源码附在文末,需要自取)

实现效果:

主要功能实现:

1. 全选按钮可选

通过属性 showAllNode 控制在多选模式下全选按钮的添加与否(注意,单选模式下没有全选按钮):

props: {
    showAllNode: {
      type: Boolean,
      default: false
    }
},
methods: {
async fetchDictTree() {

      let children = this.data.data[0].children || []
      // 添加全选节点
      this.treeData = this.showAllNode ? [this.allNode, ...children] : children

      // 获取所有节点值(一维数组)
      this.allNodeValues = this.getAllNodeValues()

      // 修改初始化逻辑
      if (this.showAllNode) {
        // 选中所有节点(包括全选节点)
        this.selectedValue = ['0', ...this.allNodeValues]
        this.lastSelectedValue = [...this.selectedValue]
      } else {
        this.selectedValue = []
        this.lastSelectedValue = []
      }

      this.$emit('input', this.selectedValue)
      this.$emit('ready', true)
    },
}
2. 全选按钮与其他节点的联动效果:
  • 点击全选按钮所有节点都被选中,非全选状态下全选按钮不被选中
    if (this.showAllNode) {
        // 检查是否包含全选节点
        const hasAll = value.includes('0')
        const prevHasAll = this.lastSelectedValue.includes('0')

        if (hasAll && !prevHasAll) {
          // 选中全选,同时选中所有节点
          const allValues = ['0', ...this.allNodeValues]
          this.selectedValue = allValues
          this.lastSelectedValue = [...allValues]
          // 只返回 ['0'] 表示全选
          this.$emit('input', ['0'])
          this.$emit('change', ['0'])
          return
        } else if (!hasAll && prevHasAll) {
          // 取消全选,清空所有选中
          setTimeout(() => {
            this.selectedValue = []
            this.lastSelectedValue = []
            this.$emit('input', [])
            this.$emit('change', [])
          }, 0)
          return
        }

        // 检查是否所有节点都被选中
        const allSelected = this.allNodeValues.every(nodeValue =>
          value.includes(nodeValue)
        )

        // 如果所有节点都被选中,但全选节点没有被选中,则添加全选节点
        if (allSelected && !hasAll) {
          const newValue = ['0', ...value]
          this.selectedValue = newValue
          this.lastSelectedValue = [...newValue]
          // 只返回 ['0'] 表示全选
          this.$emit('input', ['0'])
          this.$emit('change', ['0'])
          return
        }

        // 如果不是所有节点都被选中,但全选节点被选中了,则移除全选节点
        if (!allSelected && hasAll) {
          const newValue = value.filter(v => v !== '0')
          this.selectedValue = newValue
          this.lastSelectedValue = [...newValue]
          this.$emit('input', newValue)
          this.$emit('change', newValue)
          return
        }
      }
3. 全选按钮选中时传回的节点数据(dictKey)的值为0而非所有节点key值
watch: {
    value: {
      handler(val) {
        // 如果传入的值是 ['0'],表示选中全选节点
        if (Array.isArray(val) && val.length === 1 && val[0] === '0' && this.showAllNode) {
          // 内部选中所有节点
          this.selectedValue = ['0', ...this.allNodeValues]
        } else if (!this.multiple && Array.isArray(val) && val.length === 1) {
          // 单选模式下,如果传入的是数组,取第一个元素
          this.selectedValue = val[0]
        } else {
          this.selectedValue = val
        }
      },
      immediate: true
    },
  },

组件使用:

<template>
  <div>
      <classify-cascader v-model="classify" placeholder="请选择试卷分类" multiple
      checkStrictly show-all-node width="200px" />
  </div>
</template>

<script>
import classifyCascader from '@/components/classify-cascader/index.vue'

export default {
  name: 'indexVue',
  components: {
    classifyCascader
  },
  data() {
    return {
      classify: []
    }
  }
}
</script>
  • 笔者已经分装好了一个组件,可以直接使用,如果下拉框的数据是从后端获取的话,改一下fetchDictTree() 方法中 children 的数据赋值代码就可以,类似于:
const { data } = await APIgetDictTree(params)
let children = data.data[0].children || []

组件实现:

<template>
  <el-cascader v-model="selectedValue" :options="treeData" :props="cascaderProps" :placeholder="placeholder"
    :disabled="disabled" :clearable="clearable" :style="{ width: width }" collapse-tags @change="handleChange" />
</template>

<script>

export default {
  name: 'ClassifyCascader',
  props: {
    value: {
      type: [String, Array],
      default: () => []
    },
    checkStrictly: {
      type: Boolean,
      default: false
    },
    multiple: {
      type: Boolean,
      default: false
    },
    showAllNode: {
      type: Boolean,
      default: false
    },
    disabled: {
      type: Boolean,
      default: false
    },
    placeholder: {
      type: String,
      default: '请选择'
    },
    clearable: {
      type: Boolean,
      default: true
    },
    width: {
      type: String,
      default: '200px'
    }
  },
  data() {
    return {
      treeData: [],
      selectedValue: this.value,
      cascaderProps: {
        value: 'dictKey',
        label: 'dictValue',
        children: 'children',
        checkStrictly: this.checkStrictly,
        multiple: this.multiple,
        emitPath: false
      },
      allNode: {
        dictKey: '0',
        dictValue: '全选'
      },
      lastSelectedValue: [],
      allNodeValues: [], // 存储所有节点的值(一维数组)
      data: {
        data: [
          {
            children: [
              {
                dictKey: '1',
                dictValue: '分类1'
              },
              {
                dictKey: '2',
                dictValue: '分类2',
                children: [
                  {
                    dictKey: '2-1',
                    dictValue: '子分类2-1',
                    children: []
                  },
                  {
                    dictKey: '2-2',
                    dictValue: '子分类2-2',
                    children: []
                  }
                ]
              }
            ]
          }
        ]
      }
    }
  },
  watch: {
    value: {
      handler(val) {
        // 如果传入的值是 ['0'],表示选中全选节点
        if (Array.isArray(val) && val.length === 1 && val[0] === '0' && this.showAllNode) {
          // 内部选中所有节点
          this.selectedValue = ['0', ...this.allNodeValues]
        } else if (!this.multiple && Array.isArray(val) && val.length === 1) {
          // 单选模式下,如果传入的是数组,取第一个元素
          this.selectedValue = val[0]
        } else {
          this.selectedValue = val
        }
      },
      immediate: true
    },
  },
  created() {
    this.fetchDictTree()
  },
  methods: {
    // 刷新数据
    refreshData() {
      return this.fetchDictTree()
    },

    async fetchDictTree() {

      let children = this.data.data[0].children || []
      // 添加全选节点
      this.treeData = this.showAllNode ? [this.allNode, ...children] : children

      // 获取所有节点值(一维数组)
      this.allNodeValues = this.getAllNodeValues()

      // 修改初始化逻辑
      if (this.showAllNode) {
        // 选中所有节点(包括全选节点)
        this.selectedValue = ['0', ...this.allNodeValues]
        this.lastSelectedValue = [...this.selectedValue]
      } else {
        this.selectedValue = []
        this.lastSelectedValue = []
      }

      this.$emit('input', this.selectedValue)
      this.$emit('ready', true)
    },



    handleChange(value) {
      if (this.showAllNode) {
        // 检查是否包含全选节点
        const hasAll = value.includes('0')
        const prevHasAll = this.lastSelectedValue.includes('0')

        if (hasAll && !prevHasAll) {
          // 选中全选,同时选中所有节点
          const allValues = ['0', ...this.allNodeValues]
          this.selectedValue = allValues
          this.lastSelectedValue = [...allValues]
          // 只返回 ['0'] 表示全选
          this.$emit('input', ['0'])
          this.$emit('change', ['0'])
          return
        } else if (!hasAll && prevHasAll) {
          // 取消全选,清空所有选中
          setTimeout(() => {
            this.selectedValue = []
            this.lastSelectedValue = []
            this.$emit('input', [])
            this.$emit('change', [])
          }, 0)
          return
        }

        // 检查是否所有节点都被选中
        const allSelected = this.allNodeValues.every(nodeValue =>
          value.includes(nodeValue)
        )

        // 如果所有节点都被选中,但全选节点没有被选中,则添加全选节点
        if (allSelected && !hasAll) {
          const newValue = ['0', ...value]
          this.selectedValue = newValue
          this.lastSelectedValue = [...newValue]
          // 只返回 ['0'] 表示全选
          this.$emit('input', ['0'])
          this.$emit('change', ['0'])
          return
        }

        // 如果不是所有节点都被选中,但全选节点被选中了,则移除全选节点
        if (!allSelected && hasAll) {
          const newValue = value.filter(v => v !== '0')
          this.selectedValue = newValue
          this.lastSelectedValue = [...newValue]
          this.$emit('input', newValue)
          this.$emit('change', newValue)
          return
        }
      }
      // 正常情况下的处理
      this.lastSelectedValue = [...value]
      const outputValue = Array.isArray(value) ? value : [value]
      this.$emit('input', outputValue)
      this.$emit('change', outputValue)
    },

    // 获取所有节点的值(一维数组)
    getAllNodeValues() {
      const allValues = []
      const traverse = (nodes) => {
        nodes.forEach(node => {
          if (node.dictKey === '0') return
          allValues.push(node.dictKey)
          if (node.children && node.children.length > 0) {
            traverse(node.children)
          }
        })
      }
      traverse(this.treeData)
      return allValues
    }
  }
}
</script>

大家有什么问题可以评论区交流一下,我看到了也会回复的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2376700.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Scratch游戏 | 企鹅大乱斗

有没有过无聊到抓狂的时刻&#xff1f;试试这款 企鹅大乱斗 吧&#xff01;超简单的玩法&#xff0c;让你瞬间告别无聊&#xff01; &#x1f3ae; 玩法超简单 等待屏幕出现 ”Go!” 疯狂点击&#xff0c;疯狂拍打企鹅&#xff01; &#x1f4a5; 游戏特色 解压神器&#x…

Uniapp中小程序调用腾讯地图(获取定位地址)

1、先配置权限&#xff1a; 这是上图的代码&#xff1a; "permission": { "scope.userLocation": { "desc": "你的位置信息将用于小程序位置接口的效果展示" } } 第二步&#xff1a;写代码&#xff1a; //下面是uniapp的模版代码 主…

2025全网首发:ComfyUI整合GPT-Image-1完全指南 - 8步实现AI图像创作革命

ComfyUI整合GPT-Image-1完全指南&#xff1a;8步实现AI图像创作革命【2025最新】 OpenAI最新发布的GPT-Image-1模型&#xff08;也就是ChatGPT-4o背后的图像生成技术&#xff09;已经通过API开放使用&#xff0c;而令人惊喜的是&#xff0c;ComfyUI已经第一时间提供了完整支持&…

工业4.0神经嫁接术:ethernet ip转profinet协议通信步骤图解

在现代工业自动化领域&#xff0c;不同品牌的设备和协议之间的兼容性问题一直是个挑战。我们的包装线项目就遇到了这样的难题&#xff1a;需要将Rockwell Allen-Bradley的EtherNet/IP伺服系统与西门子PLC的PROFINET主站进行无缝对接。为了解决这一问题&#xff0c;我们采用了et…

【Linux】动静态库的使用

&#x1f4dd;前言&#xff1a; 这篇文章我们来讲讲Linux——动静态库的使用 &#x1f3ac;个人简介&#xff1a;努力学习ing &#x1f4cb;个人专栏&#xff1a;Linux &#x1f380;CSDN主页 愚润求学 &#x1f304;其他专栏&#xff1a;C学习笔记&#xff0c;C语言入门基础&…

Java基础(网络编程)

一、概述 目的&#xff1a;网络通信&#xff1a; 1、设备和设备 2、进程和进程 1&#xff09;不同设备之间 2&#xff09;本地设备之间 需要解决的问题&#xff1a; 如何准确地发送到对方的主机 - IP地址 - 唯一的定位网络中的一台主机 如何准确的发送到对方主机的进程 -…

计量——异方差的检验及其修正

目录 1.异方差的检验 1 BP检验 2white检验 2.异方差的修正 1.异方差的检验 1 BP检验 选择检验方法&#xff1a;BP BP检验的实际步骤&#xff08;非机器&#xff09;&#xff1a; 1.y对所有x进行回归&#xff0c;得到残差u。计算残差的平方u^2 2.u^2对所有x进行回归&#…

学习C++的好书:C++编程之禅

历时四个月&#xff0c;把这本书看了一遍&#xff0c;受益匪浅&#xff0c;推荐给大家&#xff0c;系统的学习一遍C。

OpenCV进阶操作:人脸检测、微笑检测

文章目录 前言一、OpenCV如何实现人脸检测1、haar特征2、级联分类器3、级联分类器的使用 二、人脸检测、微笑检测 案例实现1、预处理2、加载分类器3、标注人脸4、运行结果&#xff1a;4、微笑检测 总结 前言 要实现人脸识别首先要判断当前图像中是否出现了人脸&#xff0c;这就…

车载诊断进阶篇 --- 车载诊断概念

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 钝感力的“钝”,不是木讷、迟钝,而是直面困境的韧劲和耐力,是面对外界噪音的通透淡然。 生活中有两种人,一种人格外在意别人的眼光;另一种人无论…

制作一款打飞机游戏49:敌人抖动

蛇形敌人 如果你玩过一些射击游戏&#xff08;shmups&#xff09;&#xff0c;尤其是老式的射击游戏&#xff0c;你可能会遇到一种敌人&#xff0c;它们像蛇一样移动。我想在我们的游戏中实现这种效果。这种动态的感觉非常棒&#xff0c;我们完全有能力通过动画来实现它。 方…

Pycharm IDEA加载大文件时报错:The file size exceeds configured limit

解决方案&#xff1a;配置一下idea.properties文件 文件里面写入代码&#xff1a; idea.max.intellisense.filesize50000重启IDEA即可&#xff1b;

PDA手持终端应用有哪些?

随着技术进步不断拓展&#xff0c;PDA手持终端其便携性与多功能特性使其成为多行业数字化转型的核心工具。主要包括物流与仓储管理、零售行业、医疗行业以及制造业等。 1.物流与仓储管理 在物流与仓储管理中&#xff0c;PDA手持终端主要用于物品的实时跟踪、库存管理和拣货作业…

Python+Selenium爬虫:豆瓣登录反反爬策略解析

1. 引言 在当今互联网时代&#xff0c;数据抓取&#xff08;爬虫&#xff09;技术广泛应用于数据分析、市场调研、自动化测试等领域。然而&#xff0c;许多网站采用动态加载技术&#xff08;如Ajax、React、Vue.js等框架&#xff09;来渲染页面&#xff0c;传统的**<font s…

电总协议调试助手更新-PowerBus-v1.0.5

电总协议调试助手&#xff0c;该工具主要是用于打包电总协议&#xff0c;用于电总协议的设备调试&#xff08;精密空调、UPS、基站电源等等&#xff09;。电总协议校验计算、编码转换比较麻烦&#xff0c;手动组包困难&#xff0c;使用该工具可以大大提高调试效率。 Ver1.0.5版…

技术文档:变频器干扰问题与解决方案

1. 引言 在现代工业自动化系统中&#xff0c;变频器&#xff08;Variable Frequency Drive, VFD&#xff09;因其高效节能和精确调速的特点被广泛应用于电机控制。然而&#xff0c;变频器在运行过程中会产生高频电磁干扰&#xff08;EMI&#xff09;&#xff0c;对周边设备如P…

2025认证杯数学建模C题思路+代码+模型:化工厂生产流程的预测和控制

2025认证杯数学建模C题思路代码模型&#xff0c;详细内容见文末名片 在化工厂的生产流程中&#xff0c;往往涉及到多个反应釜、管道和储罐等设备。在 流水线上也有每个位置的温度、压力、流量等诸多参数。只有参数处于正常范 围时&#xff0c;最终的产物才是合格的。这些参数…

亚马逊,temu测评采购低成本养号策略:如何用一台设备安全批量管理买家账号

只要能够巧妙规避平台的检测和风控措施&#xff0c;测评便可安全进行。 自养号测评&#xff0c;它更便于卖家掌控&#xff0c;且能降低风险。现在很多卖家都是自己养号&#xff0c;自己养号都是精养&#xff0c;不是自动的机刷&#xff0c;买家账号掌握在自己手里&#xff0c;更…

SiFli-SDK 编译

1.编译报错 scons: *** No SConstruct file found. 出现这个错误是没有正确进入到工程目录执行编译命令&#xff0c;例如应该进入project目录中。 2.scons: *** [build_em-lb525_hcpu\src\resource\strings\en_us.c] AttributeError : dict object has no attribute iteritem…

C++多态实现的必要条件剖析

在C中&#xff0c;多态的一个必要条件确实是通过基类的指针或引用调用虚函数。这一要求背后的原因与C如何实现动态绑定&#xff08;运行时多态&#xff09;密切相关。下面详细解释了为什么需要使用基类的指针或引用来实现多态。 动态绑定与静态绑定 静态绑定&#xff08;编译期…