上周在做后台管理系统时,有用到了级联选择器,这个级联选择器主要用于分组展示的数据,比如省市区三级两级联动等。
使用方法也是比较的简单,主要有以下几个参数:
一个最基本的级联选择器代码如下:
<el-cascader v-model="value" :options="options" @change="handleChange"></el-cascader>
参数如下:
-
v-model
:绑定的值,单面板时是个一维数组,多面板时是个多维数组 -
options
:就是选择器下拉面板中需要展示的内容,是个数组,里面的结构是:value/label/children
三项,children
里面同理 - 如果要监听级联选择器中选值的方法,则需要使用
change
事件。
我需要实现的是:多选并且可以检索筛选下拉面板内容的功能,而且是双面板的结构。
我刚开始是用了级联选择器,但是由于后端给我的数据有2000+数据,导致面板在进行搜索筛选时,页面卡顿。这个效果跟之前的一篇文章《elementUi——select组件渲染数据很多时卡顿问题解决》提到的一样。因为数据是同一个接口返回的。
我考虑过很多方法:
select组件有个远程搜索,cascader是否可以用(失败)
select组件由于数据量过多,导致页面卡顿,最终的解决方案就是:使用了select组件的远程搜索功能,就是刚开始选择组件的时候,下拉面板是没有数据的,监听到输入框中输入内容后,再根据输入值筛选内容,将筛选后的内容展示在面板中。
在查看cascader组件的官网解释后,只发现了可搜索功能,并没有远程搜索功能。
可搜索功能与远程搜索功能是不一样的,可搜索功能是在组件渲染时,就将全部的内容渲染出来。所以并不能完全实现远程搜索的功能。
动态加载数据(失败)
动态加载数据的思路是好的,它主要适用于多级面板的情况,而且二级及后面的面板数据量足够大的情况。
因为这里的动态加载,是加载的后面面板的数据,而非一级面板的数据。
而我现在的情况是,一级面板的数据有2000+数据,二级面板只有2项内容,所以并不符合使用动态加载数据的情况,在实验后,也发现对数据卡顿并无帮助。
分页获取数据(失败)
当数据量过大时,通常我们会想到分页获取数据,后端一次性将2000+数据全部返回给我,我分页去获取。百度后发现有大佬已经实现此功能:
分页获取数据主要难点是:** 什么时候分页获取?如何触发分页?以及filter-method方法的使用**
刚开始拿到数据时,通过数组的slice方法,只截取前几十条数据展示,下拉触底后加载下一个几十条的数据,一次类推。
如果判断是否下拉触底???
大佬的处理方法就是:通过一个vue的自定义指令,判断下拉框的高度与下拉框中滚动条的高度来判断是否滚动到底,滚动到底则加载数据。
我通过此方法,的确能减免数据量大导致的卡顿问题,但是新的问题也出现了:因为数据量大,用户在使用时,更倾向于使用搜索的方式,此时,如果需要检索的数据在第三页,在没有触底加载更多数据的时候,通过输入关键字筛选,是无法检索到对应的数据的。
此方法也是不可行的。
异步加载数据(失败)
看到有个钩子函数,是筛选之前的钩子。想到可以开始给面板的数据options
赋值为空,然后通过输入关键字后,筛选到对应的内容后,赋值给options
,但是经过实验发现,筛选只能是从已有的options
中筛选,通过自定义的数据中筛选是无法实现的。
自己写一个类似的级联选择器(成功)
用到的组件如下:
<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)
},
其他的都是一些小细节:
- 下拉框的显示与隐藏
- 选中状态后样式的改变,通过动态绑定
class
的方式,选中状态后面有个√
是通过伪元素的方式来实现
……
关于选中后的√对号样式
p.active {
color:#ff9900;
position:relative;
}
p.active::after {
display:block;
position:absolute;
right:4px;
top:0;
content:"√";
color:#ff9900;
}
最终效果完成,终于完成了!!!