上周在做后台管理系统时,有用到了级联选择器,这个级联选择器主要用于分组展示的数据,比如省市区三级两级联动等。

element级联 elementui 级联选择_element级联


使用方法也是比较的简单,主要有以下几个参数:

一个最基本的级联选择器代码如下:

<el-cascader v-model="value" :options="options" @change="handleChange"></el-cascader>

参数如下:

  1. v-model:绑定的值,单面板时是个一维数组,多面板时是个多维数组
  2. options:就是选择器下拉面板中需要展示的内容,是个数组,里面的结构是:value/label/children三项,children里面同理
  3. 如果要监听级联选择器中选值的方法,则需要使用change事件。

我需要实现的是:多选并且可以检索筛选下拉面板内容的功能,而且是双面板的结构。

我刚开始是用了级联选择器,但是由于后端给我的数据有2000+数据,导致面板在进行搜索筛选时,页面卡顿。这个效果跟之前的一篇文章《elementUi——select组件渲染数据很多时卡顿问题解决》提到的一样。因为数据是同一个接口返回的。

element级联 elementui 级联选择_数据_02


我考虑过很多方法:

select组件有个远程搜索,cascader是否可以用(失败)

select组件由于数据量过多,导致页面卡顿,最终的解决方案就是:使用了select组件的远程搜索功能,就是刚开始选择组件的时候,下拉面板是没有数据的,监听到输入框中输入内容后,再根据输入值筛选内容,将筛选后的内容展示在面板中。

在查看cascader组件的官网解释后,只发现了可搜索功能,并没有远程搜索功能。

可搜索功能与远程搜索功能是不一样的,可搜索功能是在组件渲染时,就将全部的内容渲染出来。所以并不能完全实现远程搜索的功能。

动态加载数据(失败)

element级联 elementui 级联选择_elementUi_03


动态加载数据的思路是好的,它主要适用于多级面板的情况,而且二级及后面的面板数据量足够大的情况。

因为这里的动态加载,是加载的后面面板的数据,而非一级面板的数据。

而我现在的情况是,一级面板的数据有2000+数据,二级面板只有2项内容,所以并不符合使用动态加载数据的情况,在实验后,也发现对数据卡顿并无帮助。

分页获取数据(失败)

当数据量过大时,通常我们会想到分页获取数据,后端一次性将2000+数据全部返回给我,我分页去获取。百度后发现有大佬已经实现此功能:

分页获取数据主要难点是:** 什么时候分页获取?如何触发分页?以及filter-method方法的使用**

element级联 elementui 级联选择_搜索_04


刚开始拿到数据时,通过数组的slice方法,只截取前几十条数据展示,下拉触底后加载下一个几十条的数据,一次类推。

如果判断是否下拉触底???

大佬的处理方法就是:通过一个vue的自定义指令,判断下拉框的高度与下拉框中滚动条的高度来判断是否滚动到底,滚动到底则加载数据。

我通过此方法,的确能减免数据量大导致的卡顿问题,但是新的问题也出现了:因为数据量大,用户在使用时,更倾向于使用搜索的方式,此时,如果需要检索的数据在第三页,在没有触底加载更多数据的时候,通过输入关键字筛选,是无法检索到对应的数据的。

此方法也是不可行的。

异步加载数据(失败)

element级联 elementui 级联选择_cascader级联选择器_05


看到有个钩子函数,是筛选之前的钩子。想到可以开始给面板的数据options赋值为空,然后通过输入关键字后,筛选到对应的内容后,赋值给options,但是经过实验发现,筛选只能是从已有的options中筛选,通过自定义的数据中筛选是无法实现的。

自己写一个类似的级联选择器(成功)

用到的组件如下:

element级联 elementui 级联选择_elementUi_06

<div><el-tag v-for="(tag,tagIndex) in dailiOptions" :key="tagIndex" :closable="!disabled" color="#f4f4f5" v-on:close="closeDailiTag(tagIndex)">{{tag.label}}/层级{{tag.level}}</el-tag></div>
  <div class="dailiCls" style="display:flex;flex-direction:column;position:relative;margin-top:10px;" v-if="!disabled">
      <el-input v-model="searchWord" placeholder="请输入品牌关键词" clearable size="mini" :disabled="disabled" v-on:input="searchWordChange" v-on:focus="inputFocus" v-on:blur="inputBlur">
          <i slot="suffix" :class="choosePinpai==null&&!dailiFocusFlag?'el-icon-arrow-down':'el-icon-arrow-up'" v-on:click="flagClick"></i>
      </el-input>
      <div class="ppListCls" v-if="choosePinpai!=null||dailiFocusFlag">
          <div class="ppLeft">
              <p v-for="(p,pIndex) in agentBrandOptions" :key="pIndex" style="cursor:pointer;" v-on:click="selectPinpai(pIndex)" :class="choosePinpai==pIndex?'active':''">{{p.label}}</p>
          </div>
          <div class="ppRight" v-show="showGrapeFlag">
              <p v-on:click="selectGrape(2)" :class="chooseGrape==2?'active':''" style="cursor:pointer;">层级2</p>
              <p v-on:click="selectGrape(3)" :class="chooseGrape==3?'active':''" style="cursor:pointer;">层级3</p>
          </div>
      </div>
  </div>
input组件——监听组件内容的输入

通过setTimeout实现防抖,延迟500毫秒后,将根据input组件中输入的内容进行筛选。然后将筛选后的内容赋值给 options

searchWordChange2(val) {
     setTimeout(() => {
         if (val) {
             this.xianhuoFocusFlag = true;
             this.showGrapeFlag2 = false;
             this.choosePinpai2 = null;
             var dailiOptions = this.pinPaiData.filter(a => {
                 return a.label.toLowerCase().indexOf(val.toLowerCase()) > -1
             })
             this.tradeBrandOptions = dailiOptions;
         } else {
             this.tradeBrandOptions = this.pinPaiData;
             this.showGrapeFlag2 = false;
             this.choosePinpai2 = null;
             this.xianhuoFocusFlag = false;
         }
     }, 500)
 },

其他的都是一些小细节:

  1. 下拉框的显示与隐藏
  2. 选中状态后样式的改变,通过动态绑定class的方式,选中状态后面有个是通过伪元素的方式来实现
    ……

关于选中后的√对号样式

p.active {
    color:#ff9900;
    position:relative;
}
p.active::after {
    display:block;
    position:absolute;
    right:4px;
    top:0;
    content:"√";
    color:#ff9900;
}

最终效果完成,终于完成了!!!