封装el-select滚动底部加载更多

封装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>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值