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

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

在这里插入图片描述

正文开始

  • 前言
  • 一、历史问题
  • 二、通过监听select事件实现全选不靠谱!!!
  • 三、 通过外部事件控制树选择组件
  • 四、render函数创建组件
  • 4.1 不得不说的h函数
  • 4.2 如果条件允许,请使用jsx
  • 总结


前言

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

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

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


一、历史问题

研究早期iview版本的treeselect源码,我们可以将它和数据有关系的部分分为两类:

  1. 树形图数据
  2. select选项数据

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

如图所示:
iview+treeSelect组件,我是如何一步步手动实现全选功能的_第1张图片
这几部分数据大致的逻辑如下:
1.渲染树形图数据,在下拉框中形成树形图。
2.点击树形图的数据,会触发select的方法,选中点击的数据
3.select方法被触发后,会触发input方法,修改tag标签的显示。(这个是真的input方法,形如:vm.$emit(‘input’,xxxxx))

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

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

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

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

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

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

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

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

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

尝试后,发现是可以的。具体操作如下:

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

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

  1. 打开下拉框后,发现过了两三秒,下拉框中对应的选项被选择,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,并添加事件。如图:

iview+treeSelect组件,我是如何一步步手动实现全选功能的_第2张图片

注意点:

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

总结

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

你可能感兴趣的:(vue3实战专栏,view,design,javascript,前端)