奇葩功能实现:级联选择框组件el-cascader实现同一级的二级只能单选,但是一级可以多选

news2024/11/29 22:29:41

前言:

其实也不能说这个功能奇葩,做项目碰到这种需求也算合理正常,只是确实没有能直接实现这一需求的现成组件。

el-cascader作为级联选择组件,并不能同时支持一级多选,二级单选的功能,只能要么是单选或者多选。

不过既然产品提了这个需求,皱着眉头也得上啊。

实现:

先说下思路,由于一般来讲级联选择框都只需要提交子级的选项,所以先明确emitPath需要设置为false。multiple设置为true。

有了这两个前提,那怎么做呢?

逻辑当然就是监听change事件,通过动态修改el-cascader组件绑定的值的数据,强行不让勾选同一父级下的二级选项。(但是我这边思路优化了下,每次勾选了同一个二级选项之后,取消之前已经勾选的二级选项,具体看后面的代码)

一开始以为很简单,但是做下来发现,坑啊,是真的坑。

直接上代码吧,代码里多加点注释就看的懂了:

先上html的代码:

        <el-cascader
            :key="cascaderKey"
            ref="cascaderRef"
            style="width: 100%"
            :options="groupOptions"
            :props="{
              value: 'value',
              label: 'label',
              multiple: true,
              emitPath: false,
              checkStrictly: true,
              children: 'children'
            }"
            class="tree-search"
            v-model="devGroupIds"
            @change="(val) => handleCascaderChange(val)"
            clearable
            filterable
            :show-all-levels="false"
            :collapse-tags="true"
          />

这个代码就不多解释了, 需要注明的是这里加了这个key,具体原因后面解释,因为这是这个组件最大的坑。

 继续上js的代码,主要是handleCascaderChange方法:

    // 切换分组
    handleCascaderChange(valueArr) {
      // 判断当前选中的节点的父级节点是否同时存在其他已经勾选的节点
      if (valueArr.length > 1) {
        const newArr = valueArr.filter(t => !this.preSelectedGroupIds.includes(t))
        let indexInAll, indexInCurrent
        const newGroupId = newArr[0] || ''
        let checkedGroupInParentGroup = []
        let oldCheckedGroupIdInParentGroup = ''
        // 找到当前新勾选的分组id
        if (newGroupId) {
          let parentGroup = []
          parentGroup = this.groupOptions.find(group => group.children.some(t => t.value === newGroupId))
          // 通过当前分组id找到所属一级分组
          for (const group of parentGroup.children) {
            if (valueArr.includes(group.value)) {
              checkedGroupInParentGroup.push(group.value) // 找到该一级分组下勾选的二级分组,最多只有两个
            }
            if (checkedGroupInParentGroup.length > 1) {
              break
            }
          }
          // 当前一级分组如果存在多个已经勾选的二级分组
          if (checkedGroupInParentGroup.length > 1) {
            checkedGroupInParentGroup.forEach(t => {
              if (t !== newGroupId) {
                oldCheckedGroupIdInParentGroup = t // 找到一级分组下不是当前勾选分组的id,也就是之前勾选的二级分组id
              }
            })
            // 找到之前勾选的二级分组id在所有已经勾选的二级分组id中的序号和在当前一级分组下所有二级分组忠的序号
            indexInAll = valueArr.findIndex(t => t === oldCheckedGroupIdInParentGroup)
            // indexInCurrent = parentGroup.children.findIndex(t => t.value === oldCheckedGroupIdInParentGroup)
            // this.$nextTick(() => {
            //   let panelId = this.$refs.cascaderRef.panel.$refs.menu[1].$el.id  //其中menu[1]表示右侧的面板 menu[0]即为左侧的面板
            //   let liId = document.getElementById(panelId + '-' + indexInCurrent)
            //   // 之前勾选的二级分组id对应的元素,删除其被勾选的样式
            //   // liId.children[0].click()
            //   liId.children[0].classList.remove('is-checked')
            //   liId.children[0].children[0].classList.remove('is-checked')
            // })
            // 之前勾选的二级分组id需要从已勾选的分组id中移除
            valueArr.splice(indexInAll, 1)
            this.devGroupIds = valueArr
            // 由于<el-cascader组件绑定的值修改之后,UI不会更新,这里只能使用动态key的方式,强行让组件再渲染一遍
            this.cascaderKey ++
            console.log('cascaderKey', this.cascaderKey)
            // cascaderKey修改后,组件会重新渲染,这里模拟重新渲染后默认展开数据面板
            this.$nextTick(() => {
              const cascader = this.$refs.cascaderRef
              const trigger = cascader.$el.querySelector(".el-cascader__tags")
              trigger.click()
            })
          }
        }
      }
      this.preSelectedGroupIds = JSON.parse(JSON.stringify(valueArr))
    },

这个代码直接拷贝过去就能用了,不谢。

下面来解释下:

定义preSelectedGroupIds这个全局变量,用来存上一次已经勾选够的选项id,所以preSelectedGroupIds的初始值需要获取devGroupIds的初始值,为了回显的时候也能正常使用。

接下来就是每次触发勾选事件后,通过与preSelectedGroupIds比对,就能找到当前勾选的选项是哪个。

 

因为change事件的参数只有当前全部选项的选项,太坑了,正常逻辑肯定是需要再提供一个当前选择的选项数据的。

找到当前的勾选数据之后,再去找这个选项所属的父级分组。

找到父级分组后,再去找这个父级分组下已经勾选的二级分组,注意这个已经勾选的二级分组肯定是不会超过两条的,因为我们每次都会只保留一个二级分组,所以再勾选一个新的,最多就是勾选了两个。

 找到之后,为了实现保留当前勾选的选项,所以要把之前勾选的选项也找到,这样才能取消之前选项的勾选:

这样就找到了,然后就去修改组件绑定的数据,正常来讲就大功告成了。

 万万没想到,el-cascader组件在手动设置了绑定的选项值后,UI是不会更新的。即便使用this.$set也是没没用的。

再解决这个问题之前,我的思路是通过找到需要取消勾选的选项的dom元素,直接移除 is-checked class样式,这样就直接UI上操作dom,ui肯定就改变了。

想法很美好,确实也做到了。额,一点点。。。

 确实有一点点效果,但是还有很多问题,如果重新打开面板,被取消的数据又勾上,原因很简单,之前修改的绑定的值值没有直接更新到UI上的,所以UI上还是认为那个被删除的选项同时被移除 is-checked样式的选择没有被删除。

怎么办呢?

只能上大招了,操作key。没有办法了,只能让组件重新渲染才能让el-cascader组件接受绑定的已经选择的选项已经被修改的事实。动用了key之后,“通过找到需要取消勾选的选项的dom元素,直接移除 is-checked class样式”,这个也就不再需要了(白想了这个一开始认为是天才的方案)。

但是这样,就有个问题,每次选择完之后,面板就会被关掉,因为组件重新渲染了。

 本来到这里,已经算尽力了。

但是程序员的老毛病犯了,总这样不行,所以又加了最后一个逻辑:每次更新了key之后,模拟点击组件的事件,让面板默认打开:

 总算完美了。。。

但是其实还有一个小问题,那就是用户还是能看到面板闪了一下的,因为被关闭了又打开了一次。

但是本人已经不想折腾, 谁有本事谁来解决这个问题。希望有大神看到最后,能伸出援手,给出更完美的方案,谢谢!

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

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

相关文章

从零开始构建一个电影知识图谱,实现KBQA智能问答[下篇]:Apache jena SPARQL endpoint及推理、KBQA问答Demo超详细教学

项目设计集合&#xff08;人工智能方向&#xff09;&#xff1a;助力新人快速实战掌握技能、自主完成项目设计升级&#xff0c;提升自身的硬实力&#xff08;不仅限NLP、知识图谱、计算机视觉等领域&#xff09;&#xff1a;汇总有意义的项目设计集合&#xff0c;助力新人快速实…

Pyhon学习之条件判断和循环语句

1.if flag 1 if 1:print(type(True)) if 0:print(name) if flag:print("flag的类型&#xff1a;"str(type(flag)))根据这个结果&#xff0c;可以看出来&#xff0c;这里没有进行类型转换&#xff0c;bool就是int true 就是1 false 就是0 flag 0 if 1:print(type(…

Fiddler抓取app HTTPS请求

一、电脑和手机连接同一WIFI cmd->ipconfig&#xff0c;查看电脑当前IP地址为192.168.101.48 二、配置Fiddler Options选项勾选Allow remote computers to connect。 安装证书 勾选抓取HTTPS请求 三、手机端配置代理 手机端连接wifi&#xff0c;手动配置代理。 主机名&…

uniapp下上传图片后图片裁剪加图片旋转,支持H5和app

效果图 代码如下 <template><view class"container" v-show"isShow"><view><view class"cropper-content"><view v-if"isShowImg" class"uni-corpper":style"width: cropperInitW px;he…

Java 动态规划 174. 地下城游戏

代码展示&#xff1a; class Solution {public int calculateMinimumHP(int[][] dungeon) {int mdungeon.length;int ndungeon[0].length;//创建dp数组int[][]dpnew int[m1][n1];//初始化for(int i0;i<m;i){dp[i][n]Integer.MAX_VALUE;}for(int j0;j<n;j){dp[m][j]Integ…

Linux - 用户|权限管理 - 查看用户组

当我们需要查看 Linux服务器都有哪些用户组的时候, 可以通过如下命令进行查看: cat /etc/group如下图所示: 同时, 还可以通过groupmod三次tab键 进行查看: 具体信息内容解读:

js- 左右两边子数组的和相等

给你一个整数数组 nums &#xff0c;请计算数组的 中心下标 。 数组 中心下标 是数组的一个下标&#xff0c;其左侧所有元素相加的和等于右侧所有元素相加的和。 如果中心下标位于数组最左端&#xff0c;那么左侧数之和视为 0 &#xff0c;因为在下标的左侧不存在元素。这一点…

网络编程-day5

IO多路复用--服务器 从中获取返回给指定客户端的信息&#xff08;修改版&#xff09; IO多路复用--客户端

【Distributed】分布式ELK日志文件分析系统(二)

文章目录 一、FilebeatELK 部署1. 环境部署2. 在 Filebeat 节点上操作2.1 安装 Filebeat2.2 设置 filebeat 的主配置文件 3. 在 Apache 节点上操作3.1 在 Logstash 组件所在节点上新建一个 Logstash 配置文件 3. 启动3.1 在Logstash 组件所在节点启动3.2 在 Filebeat 节点 启动…

神仙打架——号称是性能最强的中英文百亿参数量开源模型Baichuan-13B他来了!

下午开个会的功夫看到新闻推送一条最新的大模型相关的项目开源发布了&#xff0c;到底是怎么个事我们来一起看下。 官方项目地址在这里&#xff0c;如下所示&#xff1a; 可以看到&#xff1a;才刚刚过去十几分钟的时间就已经有超过500的star量了。 就在不久前的6月15日&…

Java中使用工厂模式和策略模式优雅消除if-else语句(UML类图+案例分析)

前言&#xff1a;在最近的后端开发中&#xff0c;多多少少会发现有很多if-else语句&#xff0c;如果条件过多则会造成整体代码看起来非常臃肿&#xff0c;这边我就举一个我在实际开发中的例子&#xff0c;来进行阐述这两种模式在实际开发中我是如何运用的。 目录 一、工厂模式…

Web APls-day05

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; Window对象 BOM BOM(Browser Object Model ) 是浏览器对象模型 window对象是一个全局对象&#xff0c;也可以说是…

❤️创意网页:猜数字游戏

✨博主&#xff1a;命运之光 &#x1f338;专栏&#xff1a;Python星辰秘典 &#x1f433;专栏&#xff1a;web开发&#xff08;简单好用又好看&#xff09; ❤️专栏&#xff1a;Java经典程序设计 ☀️博主的其他文章&#xff1a;点击进入博主的主页 前言&#xff1a;欢迎踏入…

【架构设计】高并发架构实战:从需求分析到系统设计

写在前面 很多软件工程师的职业规划是成为架构师&#xff0c;但是要成为架构师很多时候要求先有架构设计经验&#xff0c;而不做架构师又怎么会有架构设计经验呢&#xff1f;那么要如何获得架构设计经验呢&#xff1f; 1 高并发是什么 高并发是指系统在同一时间内处理的请求量…

左神算法中级提升(3)

目录 【案例1】 【题目描述】【2018阿里巴巴面试题】 【思路解析】 【代码实现】 【案例2】 【题目描述】 【思路解析1】 【思路解析2】 【代码实现】 【案例3】 【题目描述】 【思路解析】 【代码实现】 【案例4】 【题目描述】 【思路解析】 【代码实现】 【案例5】…

无人机禁飞区地图更新!图新地球全国限飞区自定义地图免费分享!

随着无人机的普及&#xff0c;人们越来越容易拥有一台无人机。但很多用户并不了解无人机的飞行规则和禁飞限制。对于没有严格遵守规定的人来说&#xff0c;无人机飞行往往会构成公共安全和私人财产的潜在危害。 为此&#xff0c;政府和航空管理机构陆续出台了一系列限制无人机…

247个Python练习案例附源码(百看不如一练)

众所周知&#xff0c;我们在学习Pyhont过程中&#xff0c;大都看书枯燥、看视频进度慢&#xff0c;网上查找的学习案例又比较凌乱不成体系。。。 百看不如一练&#xff0c;今天为大家搜集了一份Python从入门到进阶的实战案例合集&#xff0c;共计247个案例&#xff0c;185页内…

【数据仓库】BI看板DataEase入坑指南

开头夸夸国产开源BI软件DataEase&#xff0c;支持常见各种报表&#xff0c;还支持图表联动和上下级钻取&#xff0c;超赞有木有&#xff01;&#xff01;&#xff01; 再来为什么说入坑&#xff0c;源码启动各种不服啊。本地用的maven3.5一直导入不了Java项目backend。后来看了…

【Linux后端服务器开发】缓冲区

目录 一、缓冲区概述 二、语言层面缓冲区 三、C语言模拟实现stdio库 一、缓冲区概述 Linux缓冲区是指在内存中开辟的一块区域&#xff0c;用于存储输入输出数据的临时存储区域。 当应用程序向文件或设备进行读写操作时&#xff0c;数据会先被存储到缓冲区中&#xff0c;然…

MYSQL学习第一天

1.创建数据库&#xff0c;删除数据库&#xff0c;查询创建数据的语句&#xff0c;使用数据库&#xff0c;查询当前默认的数据库以及使用的编码方式校验规则 1.1 创建数据库 create database db_name; 1.2 使用数据库 use db_name; 1.3 查询当前使用的数据库 select datab…