vue3 Select单、多选下拉选择器懒加载组件封装
2023-12-22 12:58:44
当下拉选择器含大量的数据时,影响性能问题,因而以懒加载的方式作为性能优化;
该下拉选择器包含功能:物理搜索、触底懒加载、适用单选和多选;
其中要点:
- 选择器下拉自定义类名,以标识并查找当前下拉
- 选择器下拉需做滚动监听,此处是用指令
- 监听选中值,并回调父组件以及选中时需赋值
- 选择器筛选时,需将筛选值加入展示数据里并去重,以及初始化时,需将选中值加入展示数据中
<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>
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);
};
代码要点解析:
- data-class 需与popper-class 一致;
- select触底时进行翻页和数据更新处理;
- 修改下拉值时回调父组件并传值;
- 搜索关键字时,需赋值给选择器选中值,不然将会得到键盘点下的最后一个字,而不是全部值。其次是,此处赋值关键字后,将会出现一个场景:搜索关键字,但是鼠标聚焦非下拉处,下拉已收起,未能清空选中值,因此,此处在单选时增加了一个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();
}
}
文章来源:https://blog.csdn.net/xiao_LYn/article/details/135148204
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!