element-ui-解决下拉框数据量过多问题(vue-virtual-scroll-list)

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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值