参考链接: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博客