ElementUI用法拓展:优雅、简单、暂未有缺点的二次封装-懒加载select选择器

38 阅读2分钟

背景

随着公司业务越来越来,越来越大,前端很多页面都要做懒加载、避免页面卡顿。

直接上代码(重点关注地方会有备注)

.vue文件代码

  • 这里的话,因为要把原有elementUI组件,原有的属性都继承过来,除了几个固定配置属性项。
  • 重点项会有备注
<template>
  <el-select
    ref="el-select"
    class="lazy-select"
    :value="value"
    :placeholder="placeholder"
    :multiple="multiple"
    :clearable="clearable"
    :filterable="filterable"
    :disabled="disabled"
    :filter-method="filterMethod"//查询自定义过滤方法
    :popper-class="popperClass"//下拉选项弹出框类名
    reserve-keyword
    :collapse-tags="collapseTags"
    :autocomplete="autocomplete"
    :automatic-dropdown="automaticDropdown"
    :size="size"
    :allow-create="allowCreate"
    :loading="loading"
    :loading-text="loadingText"
    :no-data-text="noDataText"
    :default-first-option="defaultFirstOption"
    :popper-append-to-body="popperAppendToBody"
    :value-key="valueKey"

    @change="change"
    @visible-change="visibleChange"//下拉选项弹出框的显示与隐藏事件
    @remove-tag="(e)=>$emit('remove-tag',e)"
    @clear="(e)=>$emit('clear',e)"
    @blur="(e)=>$emit('blur',e)"
    @focus="(e)=>$emit('focus',e)"
  >
    <el-option
      v-for="item in newOptions"
      :key="item[props.value]"
      :label="item[props.label]"
      :value="item[props.value]"
    />
  </el-select>
</template>
<script>
import index from './index'
export default index
</script>

<style lang="scss" scope>
@import 'style.scss';
</style>

.js文件代码

重点项会有备注,其余属性与官方文档作用相同,

export default {
  name: 'lazy-select',
  components: {},
  props: {
    value: {
      require: true
    },
    disabled: Boolean,
    clearable: {
      type: Boolean,
      require: false,
      default: true
    },
    filterable: {
      type: Boolean,
      require: false,
      default: true
    },
    placeholder: {
      type: String,
      require: false,
      default: '请选择'
    },
    multiple: Boolean,
    collapseTags: {
      type: Boolean,
      require: false,
      default: true
    },
    autocomplete: {
      type: String,
      default: 'off'
    },
    /** @Deprecated in next major version */
    autoComplete: {
      type: String,
      validator(val) {
        process.env.NODE_ENV !== 'production' &&
          console.warn('[Element Warn][Select]\'auto-complete\' property will be deprecated in next major version. please use \'autocomplete\' instead.')
        return true
      }
    },
    automaticDropdown: Boolean,
    size: String,
    allowCreate: Boolean,
    loading: Boolean,
    remote: Boolean,
    loadingText: String,
    noMatchText: String,
    noDataText: String,
    defaultFirstOption: Boolean,
    reserveKeyword: Boolean,
    //弹窗类名,设置默认命名了,同一个路由页面有多个懒加载的下拉,一定要命不同的名字
    popperClass: {
      type: String,
      default: 'lazy-selsect'
    },
    popperAppendToBody: {
      type: Boolean,
      default: true
    },
    valueKey: {
      type: String,
      default: 'value'
    },
    // 下拉数据
    options: {
      type: Array,
      require: false,
      default: () => { return [] }
    },
    // options取值
    props: {
      type: Object,
      require: false,
      default: () => { return { value: 'value', label: 'label' } }
    },
    // 默认显示及加载数量
    defaultNumber: {
      type: Number,
      require: false,
      default: 30
    },
    // 触底距离
    bottomRange: {
      type: Number,
      require: false,
      default: 100
    }
  },
  data() {
    return {
      searchName: '', // 搜索字段
      page: 1, // 当前页数
      filterOptions: [] // 过滤后的下拉数据
    }
  },
  computed: {
  //对下拉数据options进行处理
    newOptions() {
      //这里有两种情况,一种是有筛选条件options,一种是无筛选条件options,然后对它们分页处理,每次滚动到一定距离page数字增大。
      const { page, defaultNumber, searchName } = this
      if (searchName) return this.filterOptions.slice(0, page * defaultNumber)
      else return this.options.slice(0, page * defaultNumber)
    },
    className() {
      return '.' + this.popperClass
    }
  },
  methods: {
    change(e) {
      this.$emit('change', e)
      this.$emit('input', e)
    },
    //在打开下拉弹窗时监听scroll事件
    visibleChange(e) {
      const selectScorll = document.querySelector(this.className).querySelector('.el-select-dropdown__wrap')
      if (selectScorll) {
        if (e) selectScorll.addEventListener('scroll', () => this.lazyLoad(selectScorll))
        else {
          this.searchName = ''
          selectScorll.removeEventListener('scroll', () => this.lazyLoad(selectScorll))
        }
      }
      this.page = 1
      this.$emit('visible-change', e)
    },
    lazyLoad(selectScorll) {
      const scrollTop = selectScorll.scrollTop
      const scrollHeight = selectScorll.scrollHeight
      const clientHeight = selectScorll.clientHeight
      if (scrollTop + clientHeight + this.bottomRange >= scrollHeight) {
        const { page } = this
        this.page = page + 1
      }
    },
    //筛选时过滤的方法
    filterMethod(query) {
      const selectScorll = document.querySelector(this.className).querySelector('.el-select-dropdown__wrap')
      selectScorll.scrollTop = 0
      this.page = 1
      if (query !== '') {
        this.searchName = query
        this.filterOptions = this.options.filter(i => i[this.props.label].includes(query))
      } else {
        this.searchName = ''
      }
    }
  }
}

有看不懂的欢迎留言,有bug欢迎提出来,学无止境,达者为师!大家一起讨论及学习。