封装el-select滚动底部加载更多
vue2 实现el-select 下拉列表滚动加载更多数据
父组件页面使用
<template>
<LoadMoreSelect
v-model="selectValue"
:fetchOptions="fetchStockLocationData"
keyField="Cid"
labelField="Code"
valueField="Cid"
:initialPage="initialPage"
:pageSize="pageSize"
/>
</template>
<script>
import LoadMoreSelect from "../components/LoadMoreSelect/index.vue";
import { mapActions } from "vuex";
export default {
name: "LoadMore",
components: {
LoadMoreSelect,
},
data() {
return {
selectValue: null,
initialPage: 1,
pageSize: 15,
};
},
methods: {
...mapActions({
getStockLocationDataAction: "stock/getStockLocationDataAction",
}),
async fetchStockLocationData({ page, pageSize }) {
const response = await this.getStockLocationDataAction({
page_no: page,
page_size: pageSize,
all: false,
detail: false,
});
return response;
},
},
};
</script>
子组件主要代码 components/LoadMoreSelect/index.vue
<template>
<el-select
:placeholder="placeholder"
v-model="internalValue"
@change="handleChange"
size="mini"
clearable
>
<el-option
v-for="item in options"
:key="item[keyField]"
:label="item[labelField]"
:value="item[valueField]"
>
<span style="float: left; font-size: 12px">{{ item.Code }}</span>
<span
style="float: right; color: #8492a6; font-size: 12px; margin-left: 5px"
>{{ item.Description }}</span
>
</el-option>
<ElSelectLoading
:page="currentPage"
:loading="loading"
:hasMore="hasMore"
@loadMore="handleLoadMore"
/>
</el-select>
</template>
<script>
import ElSelectLoading from "./ElSelectLoading.vue";
export default {
components: {
ElSelectLoading,
},
props: {
//选中值
value: {
type: [String, Number],
default: null,
},
placeholder: {
type: String,
default: "please select",
},
//异步请求
fetchOptions: {
type: Function,
required: true,
},
keyField: {
type: String,
default: "id",
},
labelField: {
type: String,
default: "name",
},
valueField: {
type: String,
default: "id",
},
initialPage: {
type: Number,
default: 1,
},
pageSize: {
type: Number,
default: 10,
},
},
data() {
return {
internalValue: this.value,
options: [],
loading: false,
hasMore: true,
currentPage: this.initialPage,
};
},
watch: {
value(newVal) {
this.internalValue = newVal;
},
internalValue(newVal) {
this.$emit("input", newVal);
},
},
mounted() {
this.loadOptions(this.currentPage, this.pageSize);
},
methods: {
async loadOptions(page, pageSize) {
if (this.loading) return;
this.loading = true;
try {
const res = await this.fetchOptions({ page, pageSize });
const list = res.data.list || [];
if (page === this.initialPage) {
this.options = [];
}
this.options.push(...list);
this.hasMore = this.options.length < res.data.total;
this.currentPage = page;
} catch (error) {
console.error(error);
} finally {
this.loading = false;
}
},
handleLoadMore() {
this.loadOptions(this.currentPage + 1, this.pageSize);
},
handleChange(value) {
this.internalValue = value;
this.$emit("change", value);
},
},
};
</script>
<style scoped lang="scss"></style>
底部的加载状态 ./ElSelectLoading.vue
<template>
<el-option ref="el" class="el-select-loading" value="">
<template v-if="hasMore">
<el-icon class="el-icon-loading"></el-icon>
<span class="el-select-loading__tips">{{
loadingText || "loading..."
}}</span>
</template>
<template v-else>{{ noMoreText || "noMore~" }}</template>
</el-option>
</template>
<script>
export default {
name: "LoadMoreSelectOption",
props: {
// 当前页码
page: {
type: Number,
required: true,
},
// 是否加载中,用来过滤重复的加载
loading: {
type: Boolean,
required: true,
},
// 加载中的提示文案
loadingText: {
type: String,
default: "",
},
// 是否有更多数据可加载
hasMore: {
type: Boolean,
required: true,
},
// 没有更多数据的提示文案
noMoreText: {
type: String,
default: "",
},
},
data() {
return {
observer: null,
};
},
mounted() {
this.initObserver();
},
beforeDestroy() {
this.destroyObserver();
},
methods: {
initObserver() {
const el = this.$refs.el;
if (!el) return;
const callback = (entries) => {
if (this.loading || !this.hasMore || !entries[0].isIntersecting) return;
this.$emit("loadMore", this.page + 1);
};
const options = {
root: el.$el.parentElement?.parentElement,
rootMargin: "0px 0px 0px 0px",
};
this.observer = new IntersectionObserver(callback, options);
this.observer.observe(el.$el);
},
destroyObserver() {
if (this.observer) {
const el = this.$refs.el;
if (el) {
this.observer.unobserve(el.$el);
}
this.observer.disconnect();
}
},
},
};
</script>
<style scoped lang="scss">
.el-select-loading {
display: flex;
align-items: center;
justify-content: center;
cursor: initial;
pointer-events: none;
color: #7fb8f2;
font-size: 12px;
&__icon {
font-size: 16px;
animation: rotate 1.5s linear infinite;
}
&__tips {
margin-left: 6px;
}
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
}
</style>