当前位置:   article > 正文

vue3 Select单、多选下拉选择器懒加载组件封装_vue3中el-select实现懒加载

vue3中el-select实现懒加载

当下拉选择器含大量的数据时,影响性能问题,因而以懒加载的方式作为性能优化;
该下拉选择器包含功能:物理搜索、触底懒加载、适用单选和多选;

其中要点:

  • 选择器下拉自定义类名,以标识并查找当前下拉
  • 选择器下拉需做滚动监听,此处是用指令
  • 监听选中值,并回调父组件以及选中时需赋值
  • 选择器筛选时,需将筛选值加入展示数据里并去重,以及初始化时,需将选中值加入展示数据中
<template>
  <yh-select v-model="selectVal" placeholder="请选择" filterable clearable :data-class="className" :popper-class="className" v-selectScroll="scrollChange" :filter-method="filtereMethod" :multiple="multiple" collapse-tags collapse-tags-tooltip :reserve-keyword="false" @change="changeVal">
    <yh-option v-for="(item, index) in selectParams.showData" @click="clickOption(item.value)" :key="index" :value="item.value" :label="item.label" :title="item.label" />
  </yh-select>
</template>
  • 1
  • 2
  • 3
  • 4
  • 5
const props = defineProps({
  value: { default: null },
  optionsData: { default: [] },
  className: { default: 'lazyselect' },
  multiple: { default: false },
});

const emit = defineEmits(['returnValue']);

let selectVal = ref();

//默认下拉的分页数
let defaultPageNum = ref(200);

//下拉参数
let selectParams = ref({
  pageSize: 1,//当前页码
  pageNumber: defaultPageNum.value,//默认显示条数
  allData: [],//全部数据
  showData: [],//展示数据
  activeFilter: false,//搜索状态
});

//下拉选中值监听
watch(
  () => props.value,
  (to, from) => {
    selectVal.value = to;
    addValueSelect();
  }
);

onMounted(() => {
  selectParams.value = {
    pageSize: 1,
    pageNumber: defaultPageNum.value,
    total: 0,
    allData: props.optionsData || [],
    showData: props.optionsData?.length ? props.optionsData?.slice(0, props.optionsData.length < defaultPageNum.value ? props.optionsData.length : defaultPageNum.value) : [],
    activeFilter: false,
  };
  addValueSelect();
  selectVal.value = props.multiple ? selectVal.value?.split(',') || [] : selectVal.value || '';
});

//点击选项,非多选时执行
const clickOption = (e) => {
  if (!props.multiple) {
    selectParams.value.activeFilter = false;
    selectVal.value = e;
    emit('returnValue', selectVal.value);
  }
};

//添加已选的下拉选项
const addValueSelect = () => {
  const value = selectVal.value;
  if (![null, undefined].includes(value) && value !== '' && !selectParams.value.showData?.find((i) => i.value == value) && selectParams.value.allData.length) {
    const item = selectParams.value.allData.find((i) => i.value === value);
    if (item) {
      selectParams.value.showData.push(item);
    }
  }
};

//滚动下拉
const scrollChange = () => {
  const { allData, showData, pageNumber } = selectParams.value;
  if (showData.length < allData.length && !selectParams.value.activeFilter) {
    selectParams.value.pageSize += 1;
    const addData = selectParams.value.pageSize * pageNumber;
    selectParams.value.showData = addData > allData.length ? allData : allData.slice(0, addData);
  }
  addValueSelect();
};


//下拉搜索
const filtereMethod = (e) => {
  const { allData, showData } = selectParams.value;
  if (e !== '') {
    if (!props.multiple) {
      selectVal.value = e;
    }
    const filterArr = allData.filter((i) => i.label.toLowerCase().includes(e?.toLowerCase()));
    selectParams.value.showData = filterArr;
    selectParams.value.activeFilter = true;
  } else {
    selectParams.value.showData = reSetOptions(allData.slice(0, defaultPageNum.value).concat(showData), 'value');
    if (selectParams.value.activeFilter) {
      changeVal(e);
    }
    selectParams.value.activeFilter = false;
  }
};

//数组去重
const reSetOptions = (arr, name) => {
  let obj = {};
  return arr.reduce((cur, next) => {
    obj[next[name]] ? '' : (obj[next[name]] = true && cur.push(next));
    return cur;
  }, []);
};

//下拉值修改
const changeVal = (e) => {
  selectParams.value.activeFilter = false;
  selectVal.value = e;
  emit('returnValue', selectVal.value);
};

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112

代码要点解析:

  1. data-class 需与popper-class 一致;
  2. select触底时进行翻页和数据更新处理;
  3. 修改下拉值时回调父组件并传值;
  4. 搜索关键字时,需赋值给选择器选中值,不然将会得到键盘点下的最后一个字,而不是全部值。其次是,此处赋值关键字后,将会出现一个场景:搜索关键字,但是鼠标聚焦非下拉处,下拉已收起,未能清空选中值,因此,此处在单选时增加了一个option的点击事件,用于标识选中的值;

select触底指令:

import type { Directive, DirectiveBinding } from 'vue';

//下拉滚动触底监听指令
const selectScroll: Directive = {
  mounted(el, binding) {
    const className = `.${el.dataset.class}`, element = document.querySelector(className)?.querySelector(".el-select-dropdown .el-select-dropdown__wrap");
    element?.addEventListener("scroll", () => {
      scrollSelect(element, binding);
    })
  },
  beforeUnmount(el, binding) {
    const className = `.${el.dataset.class}`, element = document.querySelector(className)?.querySelector(".el-select-dropdown .el-select-dropdown__wrap");
    element?.removeEventListener("scroll", () => {
      scrollSelect(element, binding);
    })
  }
}

const scrollSelect = (element, binding) => {
  const { scrollTop, scrollHeight, clientHeight } = element,
    scrollDistance = scrollHeight - scrollTop - clientHeight;
  if (scrollDistance <= 0) {
    binding.value();
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/107512
推荐阅读
相关标签
  

闽ICP备14008679号