Vue3 + ts+ elementUi 实现后台数据渲染到下拉框选项中,滑动加载更多数据效果

前言

功能需求:下拉框中分页加载后端接口返回的人员数据,实现滑动加载更多数据效果,并且可以手动搜索定位数据,此项目使用Vue3 + ts+ elementUi 实现

实现

把此分页滑动加载数据功能封装成vue中的hooks,文件命名为useMoreUser.ts

bash 复制代码
import {ref,reactive,nextTick} from 'vue'

export  const useMoreUser = () => {
  const selectMoreData = reactive({
    page: 0, //当前页
    loading: false, //loading
    hasMore: true, //判断是否还有更多数据
    selectValue: '', //下拉框选中数据
    selectOptions: [] //下拉框选项
  })
  // 人员列表加载数据列表【newPage: 页数,name: 搜索条件】
  const loadDataList = async (newPage: number, name?: string = '' ) => {
    try {
      selectMoreData.loading = true;
      //后端接口,入参为搜索条件人员姓名,页数
      let res = await getUserList(name, newPage); 
      if (newPage === 1) { //初始化
        selectMoreData.selectOptions = [];
      }
      //存储后端接口返回数据
      selectMoreData.selectOptions.push(...res.rows);
      //判断是否还有更多数据
      selectMoreData.hasMore = selectMoreData.selectOptions.length < res.total;
      selectMoreData.page = newPage;
    } catch (err) {
      console.error(err);
    } finally {
      selectMoreData.loading = false;
    }
  };

  //加载更多数据
  const handleLoadMore = async (newPage: number,  name?: string = '' ) => {
    await loadDataList(newPage,name);
  };

  //返回下拉框选项
  const getList = ()=>{
    return selectMoreData.selectOptions;
  }
  //导出数据方法等
  return {selectMoreData,getList, loadDataList, handleLoadMore}
}

再继续封装下拉框选项组件 option.vue

bash 复制代码
<!-- 监听 el-select 的滚动,并提供触底加载数据的回调 -->
<template>
    <el-option ref="el" class="el-select-loading" value="">
        <template v-if="hasMore">
            <el-icon class="el-select-loading__icon"><Loading /></el-icon>
            <span class="el-select-loading__tips">{{ loadingText || "正在加载" }}</span>
        </template>
        <template v-else>{{ noMoreText || "到底了~" }}</template>
    </el-option>
</template>

<script setup lang="ts">
  import { onMounted, onUnmounted, ref } from "vue";
  import { ElOption } from "element-plus";

  interface Props {
    // 当前页码
    page: number;
    // 是否加载中,用来过滤重复的加载
    loading: boolean;
    // 加载中的提示文案
    loadingText?: string;
    // 是否有更多数据可加载
    hasMore: boolean;
    // 没有更多数据的提示文案
    noMoreText?: string;
  }

  const props = defineProps<Props>();

  interface Emits {
    (event: "loadMore", data: number): any;
  }

  const emit = defineEmits<Emits>();

  const el = ref<typeof ElOption>();
  const observer = ref<IntersectionObserver>();

  // 组件加载成功,监听滚动
  onMounted(() => {
    if (!el.value) {
      return;
    }
    const callback: IntersectionObserverCallback = (entries) => {
      if (props.loading || !props.hasMore || !entries[0].isIntersecting) {
        return;
      }
      emit("loadMore", props.page + 1);
    };
    const options: IntersectionObserverInit = {
      root: el.value.$el.parentElement?.parentElement,
      rootMargin: "0px 0px 0px 0px",
    };
    observer.value = new IntersectionObserver(callback, options);
    observer.value.observe(el.value.$el);
  });

  // 组件卸载成功,取消滚动监听
  onUnmounted(() => {
    if (!el.value) {
      return;
    }
    observer.value?.unobserve(el.value.$el);
  });
</script>

<style lang="scss" scoped>

    .el-select-loading {
        display: flex;
        align-items: center;
        justify-content: center;
        cursor: initial;
        pointer-events: none;
        color: var(--el-color-info);
        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>

最后到咱们真正使用页面index.vue,封装时候有些费事,但是使用起来就简单了

template部分

bash 复制代码
 <el-select class="customSelect" filterable remote :remote-method="remoteMethod"
                         v-model="user" placeholder="请选择人员"
                         style="width: 100%">
                <el-option
                    v-for="item in selectMoreData.selectOptions"
                    :key="item.userId"
                    :label="`${item.userName}`"
                    :value="item.userId"
                >
                  <span style="float: left">{{ `${item.userName}` }</span>
                </el-option>
                <ElSelectLoading
                    :page="selectMoreData.page"
                    :loading="selectMoreData.loading"
                    :hasMore="selectMoreData.hasMore"
                    @loadMore="handleLoadMore"
                />
              </el-select>

script部分

bash 复制代码
<script lang="ts" setup>
import {ref, reactive, onUnmounted, onMounted, nextTick, computed, watch} from 'vue'
import ElSelectLoading from "@/components/Option/option.vue";
import {useMoreUser} from '@/hooks/useMoreUser.ts'
const {selectMoreData, loadDataList, handleLoadMore} = useMoreUser();

const user = ref('');
const remoteMethod = (query: string) => {
  loadDataList(1, query);
}
</script>
相关推荐
Tiffany_Ho1 分钟前
【TypeScript】知识点梳理(三)
前端·typescript
安冬的码畜日常1 小时前
【D3.js in Action 3 精译_029】3.5 给 D3 条形图加注图表标签(上)
开发语言·前端·javascript·信息可视化·数据可视化·d3.js
小白学习日记2 小时前
【复习】HTML常用标签<table>
前端·html
程序员大金2 小时前
基于SpringBoot+Vue+MySQL的装修公司管理系统
vue.js·spring boot·mysql
丁总学Java2 小时前
微信小程序-npm支持-如何使用npm包
前端·微信小程序·npm·node.js
yanlele2 小时前
前瞻 - 盘点 ES2025 已经定稿的语法规范
前端·javascript·代码规范
懒羊羊大王呀3 小时前
CSS——属性值计算
前端·css
DOKE3 小时前
VSCode终端:提升命令行使用体验
前端
xgq3 小时前
使用File System Access API 直接读写本地文件
前端·javascript·面试