龙空技术网

iview+treeSelect组件,我是如何一步步手动实现全选功能的

中二少年学编程 98

前言:

此时姐妹们对“eltreeselect”大体比较珍视,看官们都需要剖析一些“eltreeselect”的相关文章。那么小编同时在网络上汇集了一些关于“eltreeselect””的相关知识,希望各位老铁们能喜欢,咱们一起来学习一下吧!

如果我掏出下图,阁下除了私信我加入学习群,还能如何应对?

编辑

在这里插入图片描述

@TOC

前言

因为种种历史原因,我们一直选择的vue前端框架是iview,而非element,在老版的iview中,treeselect(树选择组件)一直都是半成品,后来团队买了iview pro版,树选择组件虽然能够使用,但功能仍显单一,缺少全选功能。

现在项目要求实现全选,就只能自己动手了。

心情烦躁者,会觉得一多半文章在讲废话,请直接下拉到最后查看实现方式。

一、历史问题

研究早期iview版本的treeselect源码,我们可以将它和数据有关系的部分分为两类: 1. 树形图数据 2. select选项数据

select选项数据其实又可以分为两部分: 3. 下拉框中选项部分; 4. input中的tag标签显示部分。(看着像input,其实是渲染成div了,为了便于理解,就这么称呼了)

如图所示:

编辑

在这里插入图片描述

这几部分数据大致的逻辑如下: 1.渲染树形图数据,在下拉框中形成树形图。 2.点击树形图的数据,会触发select的方法,选中点击的数据 3.select方法被触发后,会触发input方法,修改tag标签的显示。(这个是真的input方法,形如:vm.$emit('input',xxxxx))

逻辑很简单,不简单的是,当数据增加、删除、改变时,这三个数据逻辑之间互相监听,来回改变,最终为了实现三项数据的联动,导致数据变化监听十分复杂。

这就导致我们如果要实现全选功能,最好就不要再去通过监听select事件来实现。

二、通过监听select事件实现全选不靠谱!!!

iview pro版本是采用函数组件的方式,查看它源码后,会发现虽然代码清爽了许多,但是三者之间还是联动。

所以我不想再采用监听select的方式。原因有二:

监听select,触发某个条件后再手动为select赋值,会继续触发监听,形成死循环。上面的问题可以解决,但代码过多,并无必要,而且需要修改源码,不利于后续框架升级。

切记:研究新旧版select代码的过程绝对不舒服,希望大家有所了解就行,不要好奇去看它代码,会被各种监听搞吐的!!!

三、 通过外部事件控制树选择组件

既然不能监听内部的select事件,那我通过和树组件并不想关的一个事件,调用select提供的方法,是不是就能避免修改源码,并且不会形成死循环。

尝试后,发现是可以的。具体操作如下: 1. 创建一个普通的树组件,其中树形图数据为treeData,组件的v-model绑定的数据为:argStrList,代码如下

<TreeSelect ref="treeSelect" :max-tag-count="3" multiple v-model="argStrList" :data="treeData"/>
在组件毫不相关的地方,创建一个button,点击事件中修改argStrList,并做个延迟,方便我们有时间打开下拉框查看变化。代码如下:
setTimeout(()=>{              this.argStrList=allValueArr            },3000)

allValueArr是我创建的一个value值数组,具体是什么,根据各自项目情况而定。

打开下拉框后,发现过了两三秒,下拉框中对应的选项被选择,input中的tag也相应维护,说明外部事件控制select来触发创建,并不会产生额外的副作用。

但是这种方式也实在太不优雅了,所以就想到在下拉框里渲染元素和事件,但是并不和select本身的元素和事件关联。treeselect组件并没有直接提供在树形图里面添加其它元素的方式。但树选择组件是基于tree组件的,tree组件提供了render函数自定义元素内容。

所以我们要做的就是在tree树形图中,创建一个能被我们自由控制的元素和事件。

四、render函数创建组件4.1 不得不说的h函数

不得不说,h函数很多场景很好用,但在反人类的道路上一去不返,我并不支持这种写法,所以只列代码,不讲解:

render: (h, { root, node, data }) => {          return h('span', {            style: {              display: 'inline-block',              width: '100%'            }          }, [            h('span', [              h('span', data.title)            ]),            h('span', {              style: {                display: 'inline-block',                float: 'right',                marginRight: '32px'              }            }, [              h('Checkbox', {                style: {                },                on: {                  'on-change': () => {                    this.argStrList.push('1400344119453511682')                  }                }              }),              h('span','全选')            ])          ]);        },
4.2 如果条件允许,请使用jsx

Babel版本3.4.0开始已经支持jsx,相当老的版本了,如果项目里插件版本过低,请升级。

树形图root节点的全部代码如下:

let root = {        id: '0',        name: '组织架构树',        type: 0,        render:(h,{root,node,data})=>{          const key='value' || "id"          const allValueArr=_.map(_.map(root,'node'),key)          const checkChange=(e)=>{            if(e){              this.argStrList=allValueArr            }else{              this.argStrList=[]            }          }          return(            <div>              <Checkbox vOn:on-change={(e)=>checkChange(e)}>{data.title}</Checkbox>            </div>          )        }      }

这段代码主要就是在root节点上渲染了一个checkbox,并添加事件。如图:

编辑

在这里插入图片描述

注意点: 1. jsx语法请查看github官网文档; 2. _.map是loadash提供的api,作用是把所有树形节点的value取出来组成数组allValueArr;

总结treeSelect组件内部各种互相监听太复杂了,很容易动一处爆两处bug,所以慎动。内部监听事件解决不了,就做个外部事件。然后用黑科技,把外部事件做到tree里,看起来好像是本身的一部分一样。这样做出来的全选功能,就只在treedata数据里增加一个属性,对项目代码和treeselect组件代码影响都是最小的。treeselect这种实现方式应该引以为戒,太难维护了。我虽然没有实操,但是这种很多类数据之间有关联的场景,应该尽可能用单例模式,创建个类或者自执行的闭包,相当于模拟一个三两个组件共享的小型状态管理工具,把数据维护到内存。尾大不掉还是确实没办法这样去实现,不得而知。后续如果想做级联选择,就是改变on-change事件里的算法即可

标签: #eltreeselect #element tree默认选中第一个