参考链接:vue插件 vue-virtual-scroll-list解决数据量太大问题 - 知乎
vue-virtual-scroll-list与selectLoadmore对比:
vue-virtual-scroll-list:一次性返回所有数据。即数据量适中,几千到一两万条,可一次性被后台接口返回的情况下,建议使用vue-virtual-scroll-list。
selectLoadmore:可懒加载下拉框,数据量超级大,几万甚至十几万条以上,后台无法一次性返回,只能分页加载的情况下,建议使用selectLoadmore。
一、第一步:安装
npm install vue-virtual-scroll-list@2.3.4 --save
二、第二步:封装最底层组件
/components/virtualScrollList/select.vue
<template>
<div>
<el-select :placeholder="!disabled?placeholder:'——'" :disabled="disabled" :value="defaultValue" popper-class="virtualselect" filterable :filter-method="filterMethod" @visible-change="visibleChange" v-bind="$attrs" v-on="$listeners">
<virtual-list ref="virtualList" class="virtualselect-list"
:data-key="selectData.value"
:data-sources="selectArr"
:data-component="itemComponent"
:keeps="20"
:extra-props="{
label: selectData.label,
value: selectData.value,
isRight: selectData.isRight
}"></virtual-list>
</el-select>
</div>
</template>
<script>
import virtualList from 'vue-virtual-scroll-list'
import ElOptionNode from './el-option-node'
export default {
components:{
'virtual-list': virtualList
},
model: {
prop: 'defaultValue',
event: 'change',
},
props: {
disabled: {
type: Boolean,
default: false
},
selectData: {
type: Object,
default () {
return {}
}
},//父组件传的值
defaultValue: {
type: String,
default: ''
},
placeholder: {
type: String,
default: ''
}
},
mounted() {
this.init();
},
watch: {
'selectData.data'() {
this.init();
}
},
data() {
return {
itemComponent: ElOptionNode,
selectArr:[]
}
},
methods: {
init() {
if(!this.defaultValue) {
this.selectArr = this.selectData.data;
}else {
// 回显问题
// 由于只渲染20条数据,当默认数据处于20条之外,在回显的时候会显示异常
// 解决方法:遍历所有数据,将对应回显的那一条数据放在第一条即可
this.selectArr = JSON.parse(JSON.stringify(this.selectData.data));
let obj = {};
for (let i = 0; i < this.selectArr.length; i++) {
const element = this.selectArr[i];
if(element[this.selectData.value] === this.defaultValue) {
obj = element;
this.selectArr.splice(i,1);
break;
}
}
this.selectArr.unshift(obj);
}
},
// 搜索
filterMethod(query) {
if (query !== '') {
this.$refs.virtualList.scrollToIndex(0);//滚动到顶部
setTimeout(() => {
this.selectArr = this.selectData.data.filter(item => {
return this.selectData.isRight?
(item[this.selectData.label]?.indexOf(query) > -1 || item[this.selectData.value]?.indexOf(query) > -1)
:item[this.selectData.label]?.indexOf(query) > -1;
});
},100)
} else {
this.init();
}
},
visibleChange(bool) {
if(!bool) {
this.$refs.virtualList.reset();
this.init();
}
}
}
}
</script>
<style lang="scss" scoped>
.virtualselect {
// 设置最大高度
&-list {
max-height:245px;
overflow-y:auto;
}
.el-scrollbar .el-scrollbar__bar.is-vertical {
width: 0;
}
}
</style>
/components/virtualScrollList/el-option-node.vue
<template>
<el-option :key="label+value" :label="source[label]" :value="source[value]">
<span>{{source[label]}}</span>
<span v-if="isRight" style="float:right;color:#939393">{{source[value]}}</span>
</el-option>
</template>
<script>
export default {
name: 'item-component',
props: {
index: {
type: Number
},// 每一行的索引
source: {
type: Object,
default () {
return {}
}
},// 每一行的内容
label: {
type: String
},// 需要显示的名称
value: {
type: String
},// 绑定的值
isRight: {
type: Boolean,
default () {
return false
}
}// 右侧是否显示绑定的值
}
}
</script>
三、第三步:封装倒数第二层组件
举例1:classification.vue
<template>
<cw-select :class="classname" :disabled="disabled" :placeholder="placeholder" :selectData="selectData" v-model="value" clearable @change="selectChange"></cw-select>
</template>
<script>
import CwSelect from '@/components/virtualScrollList/select.vue'
export default {
name:'classification',
components: {
CwSelect
},
props:['defaultValue','disabled','classname','placeholder'],
computed: {
dictData() {
return this.$store.state.app.ybDictMap.yb_dept
},
},
data() {
return {
selectData: {
data:[],// 下拉框数据
label: 'name',// 下拉框需要显示的名称
value: 'value',// 下拉框绑定的值
isRight: false,//右侧是否显示
},
value:this.defaultValue,
selectArr:[]
};
},
watch: {
defaultValue(newVal,oldVal) {
this.value = newVal
if(!oldVal&&newVal){
this.huixian()
}
}
},
mounted() {
this.selectData.data = []
this.selectData.data = this.dictData
},
methods: {
selectChange(val) {
this.$emit('change', val)
console.log('下拉框选择的值', val)
},
huixian(){
this.selectArr = JSON.parse(JSON.stringify(this.selectData.data));
let obj = {};
for (let i = 0; i < this.selectArr.length; i++) {
const element = this.selectArr[i];
if(element[this.selectData.value] === this.defaultValue) {
obj = element;
this.selectArr.splice(i,1);
break;
}
}
!!obj && this.selectArr.unshift(obj);
this.selectData.data=[...this.selectArr];
}
}
};
</script>
举例2:staffList.vue
<template>
<cw-select :class="classname" :disabled="disabled" :placeholder="placeholder" :selectData="selectData" v-model="value" clearable @change="selectChange"></cw-select>
</template>
<script>
import CwSelect from '@/components/virtualScrollList/select.vue'
export default {
name:'staffList',
components: {
CwSelect
},
props: {
type: {
type: String,
default:''
},
defaultValue: {
type: String
},
disabled: {
type: Boolean
},
classname: {
type: Object
},
placeholder: {
type: String
},
},
computed: {
staffList() {//科室人员
return this.$store.state.app.busStaffList
},
},
watch: {
defaultValue(newVal,oldVal) {
this.value = newVal
if(!oldVal&&newVal){
this.huixian()
}
}
},
data() {
return {
value:this.defaultValue,
selectData: {
data:[],// 下拉框数据
label: 'name',// 下拉框需要显示的名称
value: 'value',// 下拉框绑定的值
isRight: false,//右侧是否显示
},
selectArr:[]
};
},
mounted() {
if(this.type=='ybList'){
this.selectData.data = JSON.parse(JSON.stringify(this.staffList).replace(/empName|miCode/g, function(matchStr) {
var tokenMap = {
'empName': 'name',
'miCode': 'value',
};
return tokenMap[matchStr];
}))
}else{
this.selectData.data = JSON.parse(JSON.stringify(this.staffList).replace(/empName|empId/g, function(matchStr) {
var tokenMap = {
'empName': 'name',
'empId': 'value',
};
return tokenMap[matchStr];
}))
}
},
methods: {
selectChange(val) {
let item=this.selectData.data.find((i)=>{
return i.value===val
})
this.$emit('change', {value:val,name:item?.name})
console.log('下拉框选择的值', {value:val,name:item?.name})
},
huixian(){
this.selectArr = JSON.parse(JSON.stringify(this.selectData.data));
let obj = {};
for (let i = 0; i < this.selectArr.length; i++) {
const element = this.selectArr[i];
if(element[this.selectData.value] === this.defaultValue) {
obj = element;
this.selectArr.splice(i,1);
break;
}
}
!!obj && this.selectArr.unshift(obj);
this.selectData.data=[...this.selectArr];
}
}
};
</script>
第四步:使用
<classification placeholder="请选择" :disabled="disabled" :defaultValue="base.miDscgCaty" type="miDscgCaty" @change="(val) => changeClass(val, 'miDscgCaty')" :classname="{'warnQc':warnField?.miDscgCaty,'errorQc':errorField?.miDscgCaty}"/>
<staffList type="ybList" placeholder="请选择" :disabled="disabled" :defaultValue="base.chfpdrName" @change="(val) => changeStaff(val,'chfpdrName','chfpdrCode')" :classname="{'warnQc':warnField?.chfpdrName,'errorQc':errorField?.chfpdrName}"/>
待补充:多选等功能 封装el-select,实现虚拟滚动,可单选、多选、搜索查询、创建条目-CSDN博客