组件描述
这是一个支持下拉刷新和上拉加载更多的列表组件。它提供了空数据状态显示、加载动画、以及到达底部提示等功能,适用于需要展示分页数据的场景。
属性说明
属性名 | 类型 | 默认值 | 说明 |
---|---|---|---|
emptyText | String | “暂无相关数据” | 空数据状态时的提示文本 |
refreshText | String | “正在刷新…” | 下拉刷新时的提示文本 |
loadingText | String | “加载中…” | 加载更多数据时的提示文本 |
hasMore | Boolean | true | 是否还有更多数据可加载 |
scrollHeight | String | calc(100vh - 150px) | 列表容器的高度 |
list | Array | [] | 需要展示的数据列表 |
插槽(Slot)
empty
: 自定义空数据状态视图listContent
: 列表内容区域,通过:list="list"
传递当前列表数据
方法(Methods)
handleRefresh()
处理下拉刷新逻辑,重置页码并调用父组件的refresh
方法。
handleLoadMore()
处理上拉加载更多逻辑,递增页码并调用父组件的loadmore
方法。
handleAction(butItem, key, item)
触发列表项操作,更新对应项的状态。
样式说明
.container
: 主容器样式.card-list
: 列表样式.refresh-indicator
: 下拉刷新指示器样式.loading-more
: 加载更多提示样式.empty-container
: 空数据状态容器样式
使用示例
<template>
<list-refresh-load :emptyText="'没有找到匹配的结果'" :hasMore="hasMoreData" :list="dataList">
<template v-slot:listContent="{ list }">
<!-- 自定义列表内容 -->
<view v-for="(item, index) in list" :key="index">{{ item.name }}</view>
</template>
</list-refresh-load>
</template>
<script>
export default {
data() {
return {
dataList: [],
hasMoreData: true,
}
},
methods: {
// 刷新数据的方法
onRefresh(resolve, reject, page) {
// 模拟网络请求
setTimeout(() => {
this.dataList = fetchData(page); // 假设fetchData是一个获取数据的函数
resolve();
}, 1000);
},
// 加载更多数据的方法
onLoadMore(resolve, reject, page) {
// 模拟网络请求
setTimeout(() => {
const newData = fetchData(page);
if (newData.length > 0) {
this.dataList = [...this.dataList, ...newData];
} else {
this.hasMoreData = false;
}
resolve();
}, 1000);
}
}
}
</script>
代码部分
<template>
<view class="container">
<scroll-view scroll-y :style="{height: scrollHeight}" :refresher-enabled="true" :refresher-triggered="isRefreshing"
@refresherrefresh="handleRefresh" @scrolltolower="handleLoadMore">
<!-- 下拉刷新动画 -->
<view v-if="isRefreshing" class="refresh-indicator" :class="{ refreshing: isRefreshing }">
<text>{{ refreshText }}</text>
</view>
<view class="card-list">
<!-- 空数据状态 -->
<view v-if="showEmpty" class="empty-container">
<slot name="empty">
<view class="default-empty">
<!-- <image src="/static/empty.png" class="empty-image" /> -->
<text class="empty-text">{{ emptyText }}</text>
</view>
</slot>
</view>
<!-- 列表插槽 -->
<slot name="listContent" :list="list"></slot>
<!-- 加载更多提示 -->
<view class="loading-more">
<view v-if="loading" class="loading-spinner">
<text class="loading-text">{{ loadingText }}</text>
</view>
<text v-else-if="!hasMore" class="no-more">—— 已经到底啦 ——</text>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
name: 'infiniteScrollList',
components: {},
props: {
emptyText: {
type: String,
default: '暂无相关数据'
},
refreshText: {
type: String,
default: '正在刷新...'
},
loadingText: {
type: String,
default: '加载中...'
},
// 是否还有更多
hasMore: {
type: Boolean,
default: true
},
// 列表高度
scrollHeight: {
type: String,
default: () => {
return `calc(100vh - 150px)`
}
},
// 列表
list: {
type: Array,
default: () => []
},
},
computed: {
// 计算是否显示空状态
showEmpty() {
return !this.loading && !this.isRefreshing && this.list.length === 0
}
},
data() {
return {
startX: 0,
currentIndex: -1,
isRefreshing: false, // 刷新状态
loading: false, // 加载状态
currentPage: 1, // 页码跟踪
showHint: false
}
},
watch: {},
mounted() {},
methods: {
handleAction(butItem, key, item) {
this.$set(item, 'offsetX', 0)
this.$emit('handleAction', butItem, key, item)
},
// 优化后的刷新处理
async handleRefresh() {
if (this.isRefreshing) return
this.isRefreshing = true
try {
this.currentPage = 1 // 重置页码
// 等待父组件返回的 Promise
await new Promise((resolve, reject) => {
this.$emit('refresh', resolve, reject, this.currentPage)
})
} catch (error) {
console.error('刷新失败:', error)
} finally {
this.isRefreshing = false
}
},
// 优化后的加载处理
async handleLoadMore() {
if (this.loading || !this.hasMore) return
this.loading = true
try {
this.currentPage += 1 // 递增页码
// 等待父组件返回的 Promise
await new Promise((resolve, reject) => {
this.$emit('loadmore', resolve, reject, this.currentPage)
})
} catch (error) {
console.error('加载失败:', error)
} finally {
this.loading = false
}
},
}
}
</script>
<style lang="scss" scoped>
.container {
flex: 1;
background: #f5f5f5;
}
.card-list {
padding: 12rpx;
}
.refresh-indicator {
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
color: #666;
}
.loading-more {
padding: 30rpx 0;
text-align: center;
font-size: 28rpx;
color: #666;
}
.empty-container {
padding: 60rpx 30rpx;
display: flex;
justify-content: center;
align-items: center;
min-height: 400rpx;
.default-empty {
display: flex;
flex-direction: column;
align-items: center;
.empty-image {
width: 240rpx;
height: 240rpx;
margin-bottom: 30rpx;
opacity: 0.6;
}
.empty-text {
color: #999;
font-size: 28rpx;
text-align: center;
}
}
}
</style>