众所周知列表分页加载 是经常常见的功能,但在uniapp生态中我似乎没有找到合适的UI库和组件,没办法只能自己琢磨去写了,在这里我会将我的一些代码实现分享给大家
话不多说先看实战效果:
相信大家已经看到效果了哈,目前该组件支持下拉刷新、自动检查加载数据、数据为空时自动提示、加载错误重新加载、返回顶部等功能,也可继续拓展其他功能,如果对大家有帮助欢迎大家拿去使用!
如果需要使用请注意以下几点
- 安装以下依赖包
css
// 核心分页逻辑用到
pnpm i @vueuse/core
- 将相关静态资源下载到本地
- 组件内部的 icon 使用的是
nutui-uniapp
的UI资源,大家也可选择使用其他的icon资源哈
组件源代码如下:
ts
<script setup lang="ts">
import { onLoad } from "@dcloudio/uni-app"
import { computed, getCurrentInstance, ref, watch } from "vue"
import { useListPagination } from "@/hooks"
import { debounce } from "@/utils"
import type { IPaginationFetchDataFnParam, TPaginationDataItem, TPaginationFetchDataFnReturn } from "@/hooks"
import type { ScrollViewOnScrollEvent } from "@uni-helper/uni-app-types"
export interface IListProps {
/**
* @description 是否启用数据为空时元素占位
* @default true
*/
emptyPlaceholder?: boolean
/**
* @description 数据为空时的图片
* @default '/static/images/List/empty.png'
*/
emptyImage?: string
/**
* @description 数据为空时的图片尺寸, 为数组时顺序为: [宽, 高]
* @default ['345rpx', '344rpx']
*/
emptyImageSize?: string | [string, string]
/**
* @description 数据为空时的文本描述
* @default '暂无数据'
*/
emptyText?: string
/**
* @description 是否启用下拉刷新功能
* @default true
*/
refresh?: boolean
/**
* @description 下拉刷新背景颜色
* @default '#ffffff'
*/
refreshBackground?: string
/**
* @description 是否自动加载数据
* @default true
*/
autoLoad?: boolean
/**
* @description 滚动条与底部距离小于 offset 时触发 fetchDataFn 函数
* @default 300
*/
offset?: string | number
/**
* @description 加载过程中的提示文案
* @default '加载中...'
*/
loadingText?: string
/**
* @description 加载失败后的提示文案
* @default '加载失败,点击重试'
*/
errorText?: string
/**
* @description 加载完成后的提示文案
* @default '没有更多了'
*/
finishedText?: string
/**
* @description 是否点击 苹果(状态栏) 按钮(标题) 返回顶部
* @default true
*/
clickStatusBarBackTop?: boolean
/**
* @description 是否启用 back-top 按钮
* @default true
*/
backTop?: boolean
/**
* @description 距离页面右侧的距离
* @default '25rpx'
*/
backTopRight?: string
/**
* @description 距离页面底部的距离
* @default '100rpx'
*/
backTopBottom?: string
/**
* @description 滚动高度达到此参数值时才显示组件
* @default 200
*/
backTopOffset?: number
/**
* @description 返回顶部时是否启用过渡动画
* @default false
*/
backTopTransition?: boolean
/**
* @description 每页数据大小
* @default 10
*/
pageSize?: number
/**
* @description 请求数据的方法
* @param param 请求参数
* @returns 数据返回值
*/
fetchDataFn: (param: IPaginationFetchDataFnParam) => TPaginationFetchDataFnReturn<TPaginationDataItem>
}
// DATA: 定义 props & 给定默认值
const props = withDefaults(defineProps<IListProps>(), {
/** 启用数据为空时的占位 */
emptyPlaceholder: true,
/** 数据为空时的默认图片 */
emptyImage: "/static/image/List/empty.png",
// 数据为空时的图片尺寸
emptyImageSize: () => ["345rpx", "344rpx"],
/** 数据为空时的文本描述 */
emptyText: "暂无数据",
/** 是否启用下拉刷新功能 */
refresh: true,
/** 下拉刷新背景颜色 */
refreshBackground: "#ffffff",
/** 是否自动加载数据 */
autoLoad: true,
/** 滚动距离触发加载的默认值 */
offset: 300,
/** 加载中的提示文案 */
loadingText: "加载中...",
/** 加载失败的提示文案 */
errorText: "加载失败,点击重试",
/** 加载完成的提示文案 */
finishedText: "没有更多了",
/** 是否点击 苹果(状态栏) 按钮(标题) 返回顶部 */
clickStatusBarBackTop: true,
/** 启用 back-top 按钮 */
backTop: true,
/** 距离右侧的距离 */
backTopRight: "25rpx",
/** 距离底部的距离 */
backTopBottom: "100rpx",
/** 滚动到此高度时显示 back-top */
backTopOffset: 200,
/** 返回顶部时是否启用过渡动画 */
backTopTransition: true,
/** 每页数据大小 */
pageSize: 10
})
// HOOKS: 使用列表分页器
const listPagination = useListPagination<TPaginationDataItem>({
pageSize: props.pageSize,
fetchDataFn: param => props.fetchDataFn(param)
})
/** 列表分页器返回值 */
const { currentLoadStatus, currentTotalData, currentTotalSize, totalSize, finished, refreshing, next, clearRefresh } =
listPagination
/** STATIC: 当前实例 */
const instance = getCurrentInstance()
/** STATIC: selectorQuery 对象 */
const selectorQuery = uni.createSelectorQuery().in(instance?.proxy)
/** STATIC: scroll-view 类名 */
const scrollViewClassName = "list__scroll-view"
/** STATIC: scroll-view 内容 类名 */
const scrollViewContentClassName = "list__scroll-view__content"
/** STATIC: scroll-view 元素 */
const scrollViewElement = selectorQuery.select(`.${scrollViewClassName}`)
/** STATIC: scroll-view 内容 */
const scrollViewContentElement = selectorQuery.select(`.${scrollViewContentClassName}`)
/** STATIC: scroll-view 高度 */
let scrollViewHeight: number
// FUN: 获取元素高度
const getElementHeight = (element: UniApp.NodesRef): Promise<number> => {
return new Promise(resolve => {
element
.boundingClientRect(rect => {
// @ts-ignore
resolve(rect.height)
})
.exec()
})
}
/** FUN: 自动加载数据 */
const autoLoad = async() => {
// 如果不自动加载数据,直接返回
if (!props.autoLoad) {
return
}
// scrollView 高度为空时,获取 scrollView 高度
if (!scrollViewHeight) {
scrollViewHeight = await getElementHeight(scrollViewElement)
}
// 记录 scroll-view 主体高度
const _scrollViewContentHeight = await getElementHeight(scrollViewContentElement)
// 如果 scroll-view 主体高度小于或等于 scroll-view 高度,执行 next()
_scrollViewContentHeight <= scrollViewHeight && next()
}
// LIFECYCLE: 初次挂载完成后
onLoad(() => {
autoLoad()
})
/** WATCH: 监听数据变化 注: 只有加载数据成功并且有值时才会发生变化 */
watch(currentTotalSize, () => {
autoLoad()
})
/** COMPUTED: 是否显示空元素占位 */
const showEmpty = computed(() => props.emptyPlaceholder && finished.value && totalSize.value <= 0)
/** COMPUTED: 是否显示空元素占位图片样式 */
const emptyImageStyle = computed(() => {
// 默认图片尺寸
let size = ["0rpx", "0rpx"]
if (Array.isArray(props.emptyImageSize) && props.emptyImageSize.length === 2) {
size = props.emptyImageSize
}
else if (typeof props.emptyImageSize === "string") {
size = [props.emptyImageSize, props.emptyImageSize]
}
return {
width: size[0],
height: size[1]
}
})
/** COMPUTED: 底部提示图标 */
const bottomIcon = computed(() => {
// 根据当前加载状态返回对应图标
switch (currentLoadStatus.value) {
case "loading":
return "loading1"
case "fail":
return "refresh2"
default:
return ""
}
})
/** COMPUTED: 底部提示文案 */
const bottomText = computed(() => {
// 如果正在刷新
if (refreshing.value) {
return ""
}
// 如果所有数据已经加载完成 && 总数据量大于 0
if (finished.value && totalSize.value > 0) {
return props.finishedText
}
// 根据当前加载状态返回对应文案
switch (currentLoadStatus.value) {
case "loading":
return props.loadingText
case "fail":
return props.errorText
default:
return ""
}
})
/** COMPUTED: back-top 样式 */
const backTopStyle = computed(() => {
return {
right: props.backTopRight,
bottom: props.backTopBottom
}
})
/** REF: 是否显示 back-top */
const showBackTop = ref(false)
// EVENT: 滚动事件
const onScroll = debounce((e: ScrollViewOnScrollEvent) => {
const {
detail: { scrollTop: _scrollTop }
} = e
// 是否显示 back-top
showBackTop.value = _scrollTop >= props.backTopOffset
}, 100)
// EVENT: 点击底部提示文案
const onClickBottomText = () => {
// 如果当前加载状态是失败
currentLoadStatus.value === "fail" && next()
}
/** REF: 滚动量 */
const scrollTop = ref(1)
// EVENT: 点击 back-top
const onClickBackTop = () => {
scrollTop.value = 0
// 踩坑:uni-app 中 scroll-view 的 scrollTop 属性只有在变化时才会触发滚动
const _delay = setTimeout(() => {
// 重置滚动量
scrollTop.value = 1
clearTimeout(_delay)
}, 10)
}
/** EXPOSE: 导出列表分页器状态信息 */
defineExpose({
listPagination
})
</script>
<script lang="ts">
export default {
options: {
// 虚拟化组件节点,使组件外部样式能够直接作用到组件内部的第一层节点
// eslint-disable-next-line padded-blocks
virtualHost: true,
// 允许父组件样式穿透到子组件
// eslint-disable-next-line padded-blocks
styleIsolation: "shared"
}
}
</script>
<template>
<view class="list">
<scroll-view
:class="scrollViewClassName"
:scroll-y="true"
:enable-flex="true"
:scroll-top="scrollTop"
:refresher-triggered="refreshing"
:lower-threshold="props.offset"
:scroll-with-animation="props.backTopTransition"
:refresher-enabled="props.refresh"
:refresher-background="props.refreshBackground"
:enable-back-to-top="props.clickStatusBarBackTop"
@scroll="onScroll"
@scrolltolower="next"
@refresherrefresh="clearRefresh"
>
<view :class="scrollViewContentClassName">
<!-- 用于存放列表之上的其他内容 -->
<slot name="top"></slot>
<view v-if="showEmpty" class="list__scroll-view__content__empty-box">
<image
class="list__scroll-view__content__empty-box__img"
:style="emptyImageStyle"
:src="props.emptyImage"
mode="scaleToFill"
/>
<view class="list__scroll-view__content__empty-box__text">{{ props.emptyText }}</view>
</view>
<view v-else class="list__scroll-view__content__main">
<view class="list__scroll-view__content__main__item" v-for="(item, index) in currentTotalData" :key="index">
<slot name="default" :item="item" :index="index"></slot>
</view>
</view>
<view v-if="bottomText" class="list__scroll-view__content__bottom-box" @click="onClickBottomText">
<nut-icon v-if="bottomIcon" :name="bottomIcon" custom-color="#808089" size="30rpx" />
<view class="list__scroll-view__content__bottom-box__text">{{ bottomText }}</view>
</view>
</view>
</scroll-view>
<view
v-if="props.backTop"
class="list__back-top"
:class="{ 'list__back-top--activation': showBackTop }"
:style="backTopStyle"
@click="onClickBackTop"
>
<image class="list__back-top__img" src="/static/image/List/back-top.png" mode="scaleToFill" />
</view>
</view>
</template>
<style lang="scss" scoped>
.list {
position: relative;
box-sizing: border-box;
width: 100%;
height: 100%;
overflow: hidden;
&__scroll-view {
position: relative;
box-sizing: border-box;
width: 100%;
height: 100%;
&__content {
position: relative;
box-sizing: border-box;
width: 100%;
&__empty-box {
display: flex;
flex-direction: column;
gap: 20rpx 0;
align-items: center;
justify-content: center;
width: 100%;
height: fit-content;
padding: 30rpx 0;
overflow: hidden;
&__img {
display: block;
}
&__text {
width: 100%;
color: #808089;
font-size: 27rpx;
text-align: center;
@include text-ellipsis-mixin;
}
}
&__main {
position: relative;
box-sizing: border-box;
width: 100%;
}
&__bottom-box {
display: flex;
gap: 0 10rpx;
align-items: center;
justify-content: center;
width: 100%;
height: fit-content;
padding: 30rpx 0;
&__text {
color: #808089;
font-size: 27rpx;
}
}
}
}
&__back-top {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
width: 84rpx;
height: 84rpx;
overflow: hidden;
background: #29d446;
border-radius: 50%;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.12);
transform: scale(0);
transition: transform 0.3s;
&--activation {
transform: scale(1);
}
&__img {
display: block;
width: 25rpx;
height: 32rpx;
}
}
}
</style>
核心分页逻辑源代码如下:
ts
import { useOffsetPagination } from "@vueuse/core"
import { ref, computed } from "vue"
import type { UseOffsetPaginationReturn } from "@vueuse/core"
import type { ComputedRef, Ref } from "vue"
/** 分页数据项 */
type TPaginationDataItem = string | number | Record<string, any> | any
/** 分页数据映射 */
type TPaginationDataMap<T> = Map<number, T[]>
/** 分页请求数据函数参数 */
interface IPaginationFetchDataFnParam {
/** 当前页码 */
currentPage: number
/** 当前页码大小 */
currentPageSize: number
/** 总页数 */
pageCount: number
/** 是否为第一页 */
isFirstPage: boolean
/** 是否为最后一页 */
isLastPage: boolean
}
/** 分页请求数据函数返回结果类型 */
interface IPaginationFetchDataFnResult<T extends TPaginationDataItem> {
/** 当前页码的数据 */
currentPageData: T[]
/** 数据总大小 */
totalSize: number
}
/** 分页请求数据函数返回类型 */
type TPaginationFetchDataFnReturn<T extends TPaginationDataItem> = Promise<IPaginationFetchDataFnResult<T> | void>
/** 分页配置 */
interface IUsePaginationOptions<T extends TPaginationDataItem> {
/**
* @description 页码
* @default 1
*/
page?: number
/**
* @description 每页显示的项目数
* @default 10
*/
pageSize?: number
/**
* @description 是否在加载失败时使用之前加载的数据
* @default false
*/
usePreviousDataOnFail?: boolean
/**
* 请求数据的函数
*
* @param param 请求参数
* @returns 返回 当前页的数据列表 和 数据总数
*/
fetchDataFn: (param: IPaginationFetchDataFnParam) => TPaginationFetchDataFnReturn<T>
}
/** 分页加载状态 */
type TPaginationLoadStatus = "loading" | "success" | "fail"
/** 分页刷新参数 */
type TPaginationRefreshParam = number | `${number}`
/** 分页返回结果 */
interface IUsePaginationReturn<T extends TPaginationDataItem> extends UseOffsetPaginationReturn {
/** 当前加载状态 */
currentLoadStatus: Ref<TPaginationLoadStatus>
/** 是否初始化成功 */
initialized: Ref<boolean>
/** 是否正在刷新 */
refreshing: Ref<boolean>
/** 当前页的数据(计算属性) */
currentPageData: ComputedRef<T[]>
/** 当前已加载的总数据(计算属性) */
currentTotalData: ComputedRef<T[]>
/** 当前已加载的总数据量(计算属性) */
currentTotalSize: ComputedRef<number>
/** 是否所有数据已加载完成(计算属性) */
finished: ComputedRef<boolean>
/** 数据总数量 */
totalSize: Ref<number>
/** 当前所有页的数据映射 */
currentTotalDataMap: Ref<TPaginationDataMap<T>>
/**
* 加载指定页码数据
*
* @param page 目标页码 默认: 当前页码
*/
load: (page?: TPaginationRefreshParam) => Promise<void>
/**
* 刷新单页数据
*
* @param page 目标页码 默认: 当前页码
*/
refresh: (page?: TPaginationRefreshParam) => Promise<void>
/** 上一页 */
prev: () => Promise<void>
/** 下一页 */
next: () => Promise<void>
}
/**
* 分页器
*
* @author dyb
* @date 04/09/2024/ 18:05:57
* @export
* @template T 分页数据类型
* @param {IUsePaginationOptions<T>} options 分页配置
* @returns {*} {IUsePaginationReturn<T>} 分页返回结果
*/
const usePagination = <T extends TPaginationDataItem>(options: IUsePaginationOptions<T>): IUsePaginationReturn<T> => {
const { page = 1, pageSize = 10, usePreviousDataOnFail = false, fetchDataFn } = options
/** REF: 是否初始化成功 */
const initialized = ref<boolean>(false)
/** REF: 是否正在刷新数据 */
const refreshing = ref<boolean>(false)
/** REF: 数据总数 */
const totalSize = ref<number>(0)
/** REF: 加载状态 */
const currentLoadStatus = ref<TPaginationLoadStatus>("success")
/** REF: 当前所有页的数据映射 */
const currentTotalDataMap: Ref<TPaginationDataMap<T>> = ref(new Map())
/** COMPUTED: 当前页的数据 */
const currentPageData = computed<T[]>(() => currentTotalDataMap.value.get(currentPage.value) || [])
/** COMPUTED: 当前所有页的数据 */
const currentTotalData = computed<T[]>(() => Array.from(currentTotalDataMap.value.values()).flat())
/** COMPUTED: 当前已加载的总数据量 */
const currentTotalSize = computed<number>(() => currentTotalData.value.length)
/** COMPUTED: 是否已加载完成 */
const finished = computed<boolean>(() => initialized.value && currentTotalSize.value === totalSize.value)
// 初始化分页器
const {
currentPage,
currentPageSize,
pageCount,
isFirstPage,
isLastPage,
prev: offsetPaginationPrev,
next: offsetPaginationNext
} = useOffsetPagination({
total: totalSize,
page,
pageSize
})
/**
* 检查页码是否有效
*
* @param {TPaginationRefreshParam} page 目标页码
* @returns {*} {boolean} 是否有效
*/
const _isPageValid = (page: TPaginationRefreshParam): boolean => {
// 强制设置数字类型
page = Number(page)
if (isNaN(page)) {
console.error(`_isPageValid() =>> page不是数字 page: ${page}`)
return false
}
if (page < 1 || page > pageCount.value) {
console.error(`_isPageValid() =>> page超出范围 page: ${page} pageCount: ${pageCount.value}`)
return false
}
return true
}
/**
* 加载指定页码数据
*
* @author dyb-dev
* @date 06/09/2024/ 21:42:48
* @param {TPaginationRefreshParam} [page] 目标页码 默认: 当前页码
*/
const load = async(page: TPaginationRefreshParam = currentPage.value) => {
// 页码无效时
if (!_isPageValid(page)) {
return
}
currentPage.value = <number>page
// 取消数据的响应性
// 解决:同时加载第一页和第二页的数据,当前页码为第二页,第二页的数据先返回,第一页的数据后返回,这个时候 currentTotalDataMap.value.set 由于 currentPage 数据的响应性,会将第一页的请求结果数据覆盖第二页的数据,造成数据错乱
const _param: IPaginationFetchDataFnParam = {
currentPage: currentPage.value,
currentPageSize: currentPageSize.value,
pageCount: pageCount.value,
isFirstPage: isFirstPage.value,
isLastPage: isLastPage.value
}
try {
currentLoadStatus.value = "loading"
const _result = await fetchDataFn(_param)
// 当异步同时加载不同页码的数据时,只对当前页码的数据进行处理
// 解决:同时加载第一页和第二页的数据,当前页码为第二页,第二页的数据先返回,第一页的数据后返回,`currentLoadStatus` 状态可能会设置错误
if (currentPage.value !== _param.currentPage) {
console.warn(
`load() =>> 当前页码已变更取消数据更新操作 加载前页码: ${_param.currentPage} 加载后页码: ${currentPage.value}`
)
return
}
/** 当前页码的数据 */
const _currentPageData = _result?.currentPageData
/** 数据总大小 */
const _totalSize = Number(_result?.totalSize)
if (!Array.isArray(_currentPageData)) {
throw `fetchDataFn() 返回的数据不是数组 currentPageData: ${_currentPageData}`
}
if (isNaN(_totalSize)) {
throw `fetchDataFn() 返回的数据总数不是数字 _totalSize: ${_totalSize}`
}
/** 当前页码的数据长度 */
const _currentPageDataLength = _currentPageData.length
if (_totalSize < _currentPageDataLength) {
throw `fetchDataFn() 数据总数小于当前页数据长度 totalSize: ${_totalSize} _currentPageDataLength: ${_currentPageDataLength}`
}
if (_currentPageDataLength <= 0 && _totalSize > 0) {
throw `fetchDataFn() 当前数据为空,但总数据不为空 totalSize: ${_totalSize} _currentPageDataLength: ${_currentPageDataLength}`
}
// 更新数据
currentTotalDataMap.value.set(_param.currentPage, _currentPageData)
totalSize.value = _totalSize
// 设置初始化完成状态
initialized.value = true
currentLoadStatus.value = "success"
}
catch (error) {
console.error(`load() =>> currentPage: ${_param.currentPage} ${error}`)
// 不需要使用之前加载的数据时
if (!usePreviousDataOnFail) {
// 更新数据
currentTotalDataMap.value.set(_param.currentPage, [])
}
currentLoadStatus.value = "fail"
}
}
/**
* 刷新数据
*
* @author dyb-dev
* @date 06/09/2024/ 22:11:55
* @param {TPaginationRefreshParam} [page=currentPage.value] 目标页码 默认: 当前页码
*/
const refresh = async(page: TPaginationRefreshParam = currentPage.value) => {
// 页码无效时
if (!_isPageValid(page)) {
return
}
// 更新刷新状态
refreshing.value = true
await load(page)
// 更新刷新状态
refreshing.value = false
}
/**
* 上一页/下一页
*
* @author dyb-dev
* @date 06/09/2024/ 20:46:51
* @param {()=> void} method 上一页/下一页
*/
const _change = async(method: () => void) => {
try {
// 未初始化成功
if (!initialized.value) {
throw `初始化未完成 initialized: ${initialized.value}`
}
method()
await load()
}
catch (error) {
throw `_change() =>> ${error}`
}
}
/**
* 上一页
*
* @author dyb
* @date 05/09/2024/ 00:58:19
*/
const prev = async() => {
try {
await _change(offsetPaginationPrev)
}
catch (error) {
console.error("prev() =>>", error)
}
}
/**
* 下一页
*
* @author dyb
* @date 05/09/2024/ 00:58:09
*/
const next = async() => {
try {
await _change(offsetPaginationNext)
}
catch (error) {
console.error("next() =>>", error)
}
}
return {
initialized,
refreshing,
finished,
isFirstPage,
isLastPage,
currentLoadStatus,
currentPage,
currentPageSize,
currentPageData,
currentTotalSize,
currentTotalData,
currentTotalDataMap,
pageCount,
totalSize,
load,
refresh,
prev,
next
}
}
/** 列表分页配置,继承自 IUsePaginationOptions */
interface IUseListPaginationOptions<T extends TPaginationDataItem>
extends Omit<IUsePaginationOptions<T>, "usePreviousDataOnFail"> {}
/** 列表分页返回结果,继承自 IUsePaginationReturn */
interface IUseListPaginationReturn<T extends TPaginationDataItem> extends Omit<IUsePaginationReturn<T>, "refresh" | "prev"> {
/** 清空所有数据并刷新首页 */
clearRefresh: () => Promise<void>
/** 初始化分页器 */
initialize: () => Promise<void>
}
/**
* 列表分页器
*
* @author dyb-dev
* @date 05/09/2024/ 14:19:07
* @template T 列表分页数据类型
* @param {IUseListPaginationOptions<T>} options 列表分页配置
* @returns {*} {IUseListPaginationReturn<T>} 列表分页返回结果
*/
const useListPagination = <T extends TPaginationDataItem>(options: IUseListPaginationOptions<T>): IUseListPaginationReturn<T> => {
// 调用 usePagination 并传入参数
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { refresh, prev, ..._pagination } = usePagination(options)
/**
* 重置分页状态
*
* @author dyb-dev
* @date 16/11/2024/ 00:53:21
*/
const _reset = () => {
_pagination.currentTotalDataMap.value.clear()
_pagination.totalSize.value = 0
_pagination.initialized.value = false
}
/**
* 初始化分页器
*
* @author dyb-dev
* @date 16/11/2024/ 00:53:54
*/
const initialize = async() => {
_reset()
await _pagination.load(1)
}
/**
* 清空所有数据并刷新首页
*
* @author dyb-dev
* @date 06/09/2024/ 22:24:56
*/
const clearRefresh = async() => {
_reset()
await refresh(1)
}
/**
* 列表分页的下一页逻辑
*
* @author dyb
* @date 05/09/2024/ 13:16:57
*/
const next = async() => {
try {
const { initialized, currentLoadStatus, currentPage, finished } = _pagination
if (!initialized.value) {
console.warn("next() =>>", `初始化未完成 initialized: ${initialized.value} 取消执行下一页 优先执行初始化`)
await initialize()
return
}
if (finished.value) {
console.warn("next() =>>", `已经加载完所有数据,无法继续加载下一页 currentPage: ${currentPage.value}`)
return
}
if (currentLoadStatus.value === "loading") {
throw `当前页码数据加载中,取消继续加载下一页的数据 currentPage: ${currentPage.value} currentLoadStatus: ${currentLoadStatus.value}`
}
if (currentLoadStatus.value === "fail") {
console.warn(
"next() =>>",
`当前页码数据加载失败,取消继续加载下一页的数据,重新加载当前页数据 currentPage: ${currentPage.value} currentLoadStatus: ${currentLoadStatus.value}`
)
// 加载失败,重新加载当前页码数据
await _pagination.load()
return
}
await _pagination.next()
}
catch (error) {
console.error("next() =>>", error)
}
}
return {
..._pagination,
initialize,
clearRefresh,
next
}
}
export type {
IUseListPaginationOptions,
IUseListPaginationReturn,
TPaginationLoadStatus,
TPaginationDataItem,
TPaginationDataMap,
IPaginationFetchDataFnParam,
IPaginationFetchDataFnResult,
TPaginationFetchDataFnReturn,
TPaginationRefreshParam,
IUsePaginationOptions,
IUsePaginationReturn
}
export { useListPagination, usePagination }
本文到这里就结束啦,希望能够帮助到大家!