vue3手机端列表加载组件

手机端列表加载组件

功能描述

  • 适用手机端,实现列表加载功能。

实现方案

基础用法

html 复制代码
<template>
    <PageList :getList="getList" style="height:100%;">
        <template #default="{ item }">
            <!-- 渲染列表项 -->
            <div class="list-item">{{ item.name }}</div>
        </template>
        <template #empty>
            <div>没有数据可显示</div>
        </template>
    </PageList>
</template>

<script setup>
import { ref } from "vue";
import PageList from "@/components/PageList/index.vue"; // 根据实际路径引入组件

const query = ref({
    memberId: 123,
});
const getList = async ({ pageNum, pageSize }) => {
    // 在这里实现你的数据请求逻辑query(其他参数 如 memberId等)
    const res = await queryList({ pageNum, pageSize, ...query.value });
    return res;
};
</script>

2. 自定义加载文字和属性

你可以通过 options 属性来自定义加载文字和其他参数。options 是一个对象,支持以下属性:

  • pageNum: 当前页码,默认值为 1
  • pageSize: 每页显示的数据条数,默认值为 10
  • finishedText: 当数据加载完毕时显示的文本,默认值为 到底了
  • loadingText: 加载中的文本,默认值为 加载中...
2.1 示例
javascript 复制代码
const options = {
    pageNum: 1,
    pageSize: 20, // 自定义每页显示20条数据
    finishedText: "没有更多数据了", // 自定义加载完毕提示
    loadingText: "请稍等,加载中...", // 自定义加载提示
};

4. 搜索功能

如果需要在列表中实现搜索功能,你可以在请求数据时传递搜索参数,并在 getList 方法中处理。

4.1 实现步骤
  1. 添加搜索输入框: 在你的组件中添加一个搜索框,通过输入获取搜索关键词。
  2. 更新请求参数: 将输入的搜索关键词添加到请求参数中。
4.2 示例
javascript 复制代码
<template>
    <input v-model="searchQuery" placeholder="搜索..." @input="handleSearch" />
    <PageList
        :options="options"
        :getList="fetchData"
        >
        <template #default="{ item }">
            <div class="list-item">{{ item.name }}</div>
        </template>
    </PageList>
</template>

<script setup>
import { ref } from 'vue';
import PageList from './PageList.vue';

const searchQuery = ref('');

const handleSearch = () => {
    // 重置搜索 刷新列表数据
    proxy.$refs["pagelistRef"].refresh();
};

const fetchData = async ({ pageNum, pageSize }) => {
    // 在这里实现你的数据请求逻辑query(其他参数 如 memberId等)
    const res = await queryList({ pageNum, pageSize ,...searchQuery.value})
    return res;
};
</script>

设计思路

  1. 借鉴 Element Plus 的自定义指令 : 在组件设计中,主要沿用了 Element Plus 提供的 v-infinite-scroll 自定义指令,这样可以充分利用现有的成熟解决方案,实现无限滚动加载功能,确保在处理大量数据时能够高效且流畅地加载列表内容。
  2. 灵活的公共参数设置 : 设计中为主要公共参数设置了默认值,使得组件在使用时更加灵活和易于配置。用户可以根据具体需求自定义 pageNumpageSizefinishedTextloadingText 等属性,以适应不同场景下的使用,提升了组件的通用性和适应性。
  3. 使用 Vue 3 插槽实现列表内容渲染: 列表内容的渲染采用了 Vue 3 的插槽机制,使得使用者可以方便地自定义每个列表项的显示方式。这种设计不仅提高了组件的可扩展性,还允许开发者根据具体需求自定义列表项的样式和内容,从而提供更好的使用体验。

组件代码

html 复制代码
<template>
    <div style="overflow-y: auto" v-infinite-scroll="getListData" :infinite-scroll-distance="50">
        <slot v-for="(item, index) in listData" :key="index" :item="item"></slot>
        <div class="list-tip">
            <div v-if="count === 0">
                <div v-if="$slots.empty">
                    <slot name="empty"></slot>
                </div>
                <el-empty v-else description="暂无数据" />
            </div>
            <div class="by-divider" v-if="count > 0 && listData.length >= count">{{ props.options.finishedText }}</div>
            <div class="list-loading" v-if="loading" v-loading="loading" :element-loading-text="props.options.loadingText"></div>
        </div>
    </div>
</template>

<script setup name="PageList">
import { ref, toRefs } from "vue";
const props = defineProps({
    //配置参数
    options: {
        type: Object,
        default: () => ({
            pageNum: 1,
            pageSize: 10,
            finishedText: "到底了",
            loadingText: "加载中...",
        }),
    },

    //请求列表数据接口
    getList: {
        type: Function,
        default: () => () => {},
    },
});

const listData = ref([]); //列表数据
const loading = ref(false); //加载状态
const count = ref(-1);
const queryParams = ref({
    pageNum: props.options.pageNum,
    pageSize: props.options.pageSize,
}); //请求参数
const getListData = async () => {
    // 处于加载状态和已经加载完毕,则不再请求数据
    if (loading.value || (listData.value.length >= count.value && count.value != -1)) return;
    loading.value = true;
    try {
        const { pageNum, pageSize } = queryParams.value;
        const res = await props.getList({ pageNum, pageSize });
        if (res.code == "0") {
            listData.value = listData.value.concat(res.data);
            count.value = res.count;
            queryParams.value.pageNum++;
        }

        loading.value = false;
    } catch (error) {
        loading.value = false;
    }
};
const refresh = () => {
    count.value = -1;
    queryParams.value.pageNum = 1;
    listData.value = [];
    getListData();
};
// 定义 loadMore 方法
const loadMore = () => {
    console.log("Pulled to the top, loading more items...");
    // 在这里添加加载数据的逻辑,例如发起 API 请求
    // 示例:items.value.push(...newItems);
};
// 定义内部指令
const pullDown = {
    mounted(el, binding) {
        const callback = binding.value; // 获取传入的回调函数

        if (typeof callback !== "function") {
            throw new Error("v-pull-down binding value must be a function");
        }

        const onScroll = () => {
            const { scrollTop } = el;
            console.log(scrollTop);
            // 判断是否滚动到顶部
            if (scrollTop === 0) {
                callback(); // 调用回调函数
            }
        };

        el.addEventListener("scroll", onScroll);

        // 清理工作
        el._onScroll = onScroll; // 保存引用以便在 unmounted 中使用
    },
    unmounted(el) {
        el.removeEventListener("scroll", el._onScroll); // 移除事件监听
    },
};

defineExpose({
    pullDown,
    listData,
    loading,
    refresh,
});
</script>

<style lang="scss" scoped>
.list-loading {
    --el-loading-spinner-size: 30px;
    --el-color-primary: #969799;
    height: var(--el-loading-spinner-size);
    margin: 15px 0px;
    background: transparent;
    :deep(.el-loading-spinner) {
        display: flex;
        justify-content: center;
        align-items: center;
    }
    :deep(.el-loading-text) {
        margin-left: 10px;
    }
    :deep(.el-loading-mask) {
        background: transparent;
    }
}
.by-divider {
    margin: 16px 0px;
    color: #969799;
    font-size: 14px;
    line-height: 24px;
    border-color: #ebedf0;
    border-style: solid;
    border-width: 0;
    align-items: center;
    display: flex;
    &:before,
    &:after {
        content: "";
        box-sizing: border-box;
        border-color: inherit;
        border-style: inherit;
        border-width: 1px 0 0;
        flex: 1;
        height: 1px;
        display: block;
    }
    &:before {
        margin-right: 16px;
    }
    &:after {
        margin-left: 16px;
    }
}
</style>
相关推荐
岁月宁静2 小时前
LangGraph 技术详解:基于图结构的 AI 工作流与多智能体编排框架
前端·python·langchain
岁月宁静2 小时前
LangChain 技术栈全解析:从模型编排到 RAG 实战
前端·python·langchain
1024肥宅2 小时前
工程化工具类:实现高效的工具函数库
前端·javascript·面试
Nick_zcy2 小时前
基于Vue和Python的羽毛球拍智能推荐系统, 从“不会选羽毛球拍”到“选对拍”的一站式小工具
前端·vue.js·python·算法·推荐算法
invicinble2 小时前
关于对前端项目(架子级别)的理解和认识
前端
Sapphire~2 小时前
【前端基础】02-命令式组件系统 | 声明式组件系统 | 响应式组件系统
前端
骑驴看星星a2 小时前
【回顾React的一些小细节】render里不可包含的东西
前端·javascript·react.js
小白阿龙2 小时前
浮动元素导致父元素高度塌陷
前端
惜.己3 小时前
前端笔记(三)
前端·笔记