Element-ui下拉列表二次开发——虚拟下拉列表(一)

190 阅读1分钟

概述

使用过ElementUI 2.0组件库的小伙伴都知道,在下拉列表数据量大的情况会出现页面卡顿、无响应的情况;这时候我们想到通过虚拟下拉列表来解决,而ElementUI 2.0中并没有这种组件,此时就需要我们对下拉列表做二次开发才能实现。

实现效果

GIF.gif

一、下拉列表子组件virtualOption

export default {
    name: 'virtualOption',
    props: {
        index: { // 每一行的索引
            type: Number
        },
        label: String,
        value: [String, Number],
        source: { // 每一行的内容
            type: Object,
            default: () => ({})
        }
    },
    render() {
        let label = this.source[this.label]
        let value = this.source[this.value] || this.source

        return (<el-option label={label} value={value}></el-option>)
    }
}

二、下拉列表组件

<template>
    <el-select v-model="defaultValue" :filter-method="filterMethod" filterable @visible-change="visibleChange" placeholder="请选择">
      <virtual-list
        ref="virtualList"
        :data-sources="options"
        data-key="value"
        :estimateSize="34"
        :keeps="keeps"
        :data-component="virtualOption"
        :extra-props="{ label: 'label', value: 'value' }"
        style="max-height: 245px; overflow-y: auto;">
      </virtual-list>
    </el-select>
</template>

<script>

import VirtualList from 'vue-virtual-scroll-list'
import VirtualOption from './virtualOption'
export default {
    name: 'virtual-select',
    props: {
        list: {
            type: Array,
            default: () => [],
        },
    },
    components: {
        VirtualList,
    },
    data() {
        return {
            options: [],
            keeps: 25,
            defaultValue: '',
            virtualOption,
        }
    },
    watch: {
        list(val) {
           this.options = JSON.parse(JSON.stringify(val))
        }
    },
    methods: {
        // 更新下拉搜索框数据
        filterMethod(keyword) {
            this.$refs.virtualList.reset()
            if (!keyword) {
                this.options = this.list
                this.visibleChange(true)
            } else {
                const data = this.list.filter(v => v.label.toLowerCase().includes(keyword.toLowerCase()))

                this.options = data
            }

        },
        // 显示隐藏更新下拉显示数据范围
        visibleChange(bool) {
            let virtualList = this.$refs.virtualList
                
            if (bool) {
                let index = this.options.findIndex(v => v.value === this.defaultValue)
                if (index > -1) {
                    this.$nextTick(() => {
                        // 滚动到选中的下拉项
                        virtualList.scrollToIndex(index)
                        
                        // 找到最后一组选中下拉项第一条位置
                        let keeps = this.keeps
                        if (index >= (this.options.length - keeps)) {
                            index = this.options.length - keeps
                        }
                        // 更新虚拟列表范围,不更新会出现下拉列表会出现空白
                        virtualList.virtual.updateRange(index, index + keeps)
                    })
                } else {
                    virtualList.reset()
                }
                
            } else {
                // 重置
                virtualList.reset()
                this.options = this.list
            }
        }
    }
}
</script>