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

前言:

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

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)"clearablefilterable: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, indexInCurrentconst newGroupId = newArr[0] || ''let checkedGroupInParentGroup = []let oldCheckedGroupIdInParentGroup = ''// 找到当前新勾选的分组idif (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)// 将当前选中的新分组id放到第一位,以便重新打开时,可以默认打开面板时打开当前分组const index = valueArr.indexOf(newGroupId)if (index !== -1) {valueArr.unshift(valueArr.splice(index, 1)[0])}this.devGroupIds = valueArr// 由于<el-cascader组件绑定的值修改之后,UI不会更新,这里只能使用动态key的方式,强行让组件再渲染一遍this.cascaderKey ++console.log('cascaderKey', this.cascaderKey)// cascaderKey修改后,组件会重新渲染,这里模拟重新渲染后默认展开数据面板this.$nextTick(() => {const cascader = this.$refs.cascaderRefconst 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.hqwc.cn/news/23093.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot 配置文件:什么是配置文件?配置文件是干什么?

文章目录 &#x1f387;前言1.配置文件的格式2. properties配置文件说明2.1 properties基本语法2.2 读取配置文件 3. yml 配置文件说明3.1 yml 基本语法 4.properties与yml 对比 &#x1f387;前言 学习一个东西&#xff0c;我们先要知道它有什么用处。整个项目中所有重要的数…

java单元测试(调试)

文章目录 测试分类JUnit单元测试介绍引入本地JUnit.jar编写和运行Test单元测试方法设置执行JUnit用例时支持控制台输入10.6.6 定义test测试方法模板 测试分类 **黑盒测试&#xff1a;**不需要写代码&#xff0c;给输入值&#xff0c;看程序是否能够输出期望的值。 **白盒测试…

【解决】Android Studio打包出现not found for signing config ‘externalOverride‘

问题出现场景 之前我的这个项目在另一台电脑上开发&#xff0c;现在迁移到这台计算机上&#xff0c;出现了key报错的问题&#xff0c;网络上有些说需要在XML中进行配置signature相关的内容&#xff0c;这个感觉比较复杂&#xff0c;本文主要介绍一个简单的解决方法&#xff0c;…

VectorCAST单元测试参数配置

一、打开 VectorCAST 通常情况下&#xff0c;技术人员会配置一个脚本文件&#xff08;.bat、.cmd&#xff09;&#xff0c;用户可以通过这个脚本文件来启动 VectorCAST。使用脚本文件启动 VectorCAST&#xff0c;可以在启动时设置好编译器相关的环境变量&#xff0c;方便 Vecto…

el-ment ui 表格组件table实现列的动态插入功能

在实际需求中我们经常遇到各种奇葩的需求&#xff0c;不足为奇。每个项目的需求各不相同&#xff0c;实现功能的思路大致是一样的。 本文来具体介绍怎么实现table表格动态插入几列。 首先实现思路有2种&#xff0c; 1. 插入的位置如果是已知的&#xff0c;我知道在哪个标题的…

redis -速成

目录 &#xff08;一&#xff09;认识 Redis 1.1数据库分类 1.2 什么是Redis 1.2.1 redis简介 1.2.2 谁在用Redis 1.2.3 怎么学redis 1.2.4 Redis的安装 2 数据类型 2.1 概况 2.2 String类型 2.2.1 常用的命令 2.2.2 非常用命令 2.2.3 举例 2.2.4应用场景&#xf…

ELk日志平台搭建

ELk日志平台搭建 一、ELK概述 1.ELK简介 ELK平台是一套完整的日志集中处理解决方案&#xff0c;将 ElasticSearch、Logstash 和 Kiabana 三个开源工具配合使用&#xff0c; 完成更强大的用户对日志的查询、排序、统计需求。2.组件 ●ElasticSearch&#xff1a;是基于Lucene…

qt和vue的交互

1、首先在vue项目中引入qwebchannel /******************************************************************************** Copyright (C) 2016 The Qt Company Ltd.** Copyright (C) 2016 Klarlvdalens Datakonsult AB, a KDAB Group company, infokdab.com, author Milian …

LangChain + ChatGLM2-6B 搭建个人专属知识库

之前教过大家利用 langchain ChatGLM-6B 实现个人专属知识库&#xff0c;非常简单易上手。最近&#xff0c;智谱 AI 研发团队又推出了 ChatGLM 系列的新模型 ChatGLM2-6B&#xff0c;是开源中英双语对话模型 ChatGLM-6B 的第二代版本&#xff0c;性能更强悍。 树先生之所以现…

能“出汗”,会“呼吸”的户外行走机器人

美国亚利桑那州立大学(ASU)科学家研制出了世界上第一个能像人类一样出汗、颤抖和呼吸的户外行走机器人模型。这个机器人名叫ANDI&#xff0c;是一个能模仿人类出汗的热敏“热模型”。 ANDI 身上不仅有可以使它行走的关节&#xff0c;还有其他机器人身上都没有的东西——它浑身…

编译libtiff库给IOS平台用

打开libtiff官方网 : libtiff / libtiff GitLab 克隆: git clone --recursive https://gitlab.com/libtiff/libtiff.git 克隆成功并打开libtiff目录,发现有autogen.sh 与CMakeLists.txt所以可生成Configure程序来配置并编译,也可直接使用CMake-GUI来配置编译,选择其中一种 …

R语言的水文、水环境模型优化技术及快速率定方法与多模型案例实践

在水利、环境、生态、机械以及航天等领域中&#xff0c;数学模型已经成为一种常用的技术手段。同时&#xff0c;为了提高模型的性能&#xff0c;减小模型误用带来的风险&#xff1b;模型的优化技术也被广泛用于模型的使用过程。模型参数的快速优化技术不但涉及到优化本身而且涉…