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>
相关推荐
kyriewen16 小时前
别再 console.log 了:5 个 Chrome DevTools 调试技巧,用过就回不去了
前端·javascript·面试
IT_陈寒18 小时前
Python搞不定字符串编码?这破玩意坑我两小时!
前端·人工智能·后端
DigitalOcean19 小时前
Laravel 开发者已在 DigitalOcean 上开通超过 10 万台服务器
前端·laravel
星始流年19 小时前
从 Tool 到 Skill——基于 LangChain 的服务端Skill实现
前端·langchain·agent
李惟19 小时前
开源本地通信库,纯客户端 RPC,像聊天一样通信
前端
YAwu1119 小时前
深入解析 React 炫彩鼠标跟随标题组件:从坐标定位到动画性能
前端·react.js
GuWenyue19 小时前
排序效率低?5分钟吃透快速排序,性能飙升至O(nlogn)
前端·javascript·面试
OpenTiny社区19 小时前
🎨 看完 GenUI SDK 源码我悟了!
前端·vue.js·github
叁两19 小时前
前端转型AI Agent该如何学习?(前置篇)
前端·人工智能·node.js
何时梦醒19 小时前
深入理解递归与快速排序 —— 从基础入门到手写实现
前端·javascript