HomeLease/components/pagination/pagination.vue

308 lines
5.8 KiB
Vue
Raw Normal View History

2025-08-25 18:06:33 +08:00
<template>
<view class="pagination-container">
<!-- 上拉加载更多模式 -->
<view v-if="mode === 'loadMore'" class="load-more-container">
<view v-if="loading" class="loading-state">
<view class="loading-spinner"></view>
<text class="loading-text">{{ loadingText }}</text>
</view>
<view v-else-if="noMore && list.length > 0" class="no-more-state">
<text class="no-more-text">{{ noMoreText }}</text>
</view>
<view v-else-if="list.length === 0 && !loading" class="empty-state">
<view class="empty-icon">{{ emptyIcon }}</view>
<text class="empty-text">{{ emptyText }}</text>
</view>
</view>
<!-- 分页器模式 -->
<view v-else-if="mode === 'pager'" class="pager-container">
<view v-if="total > 0" class="pager-info">
<text class="pager-text">
{{ total }} {{ currentPage }} / {{ totalPages }}
</text>
</view>
<view class="pager-controls">
<button
class="pager-btn prev-btn"
:disabled="currentPage <= 1"
@click="handlePageChange(currentPage - 1)"
>
上一页
</button>
<view class="page-numbers">
<button
v-for="page in visiblePages"
:key="page"
:class="['page-btn', { active: page === currentPage }]"
@click="handlePageChange(page)"
>
{{ page }}
</button>
</view>
<button
class="pager-btn next-btn"
:disabled="currentPage >= totalPages"
@click="handlePageChange(currentPage + 1)"
>
下一页
</button>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'Pagination',
props: {
// 分页模式loadMore(上拉加载) 或 pager(分页器)
mode: {
type: String,
default: 'loadMore',
validator: value => ['loadMore', 'pager'].includes(value)
},
// 数据列表
list: {
type: Array,
default: () => []
},
// 总数据量
total: {
type: Number,
default: 0
},
// 当前页码
currentPage: {
type: Number,
default: 1
},
// 每页数量
pageSize: {
type: Number,
default: 10
},
// 是否正在加载
loading: {
type: Boolean,
default: false
},
// 是否没有更多数据
noMore: {
type: Boolean,
default: false
},
// 自定义文本
loadingText: {
type: String,
default: '正在加载...'
},
noMoreText: {
type: String,
default: '没有更多数据了'
},
emptyText: {
type: String,
default: '暂无数据'
},
emptyIcon: {
type: String,
default: '📋'
},
// 分页器显示的页码数量
visiblePageCount: {
type: Number,
default: 5
}
},
computed: {
// 总页数
totalPages() {
return Math.ceil(this.total / this.pageSize)
},
// 可见的页码
visiblePages() {
const pages = []
const half = Math.floor(this.visiblePageCount / 2)
let start = Math.max(1, this.currentPage - half)
let end = Math.min(this.totalPages, start + this.visiblePageCount - 1)
// 调整起始位置
if (end - start + 1 < this.visiblePageCount) {
start = Math.max(1, end - this.visiblePageCount + 1)
}
for (let i = start; i <= end; i++) {
pages.push(i)
}
return pages
}
},
methods: {
// 处理页码变化
handlePageChange(page) {
if (page < 1 || page > this.totalPages || page === this.currentPage) {
return
}
this.$emit('page-change', page)
},
// 重置分页状态
reset() {
this.$emit('reset')
}
}
}
</script>
<style lang="scss" scoped>
.pagination-container {
width: 100%;
}
// 上拉加载更多样式
.load-more-container {
padding: 20rpx;
text-align: center;
}
.loading-state {
display: flex;
align-items: center;
justify-content: center;
padding: 20rpx 0;
.loading-spinner {
width: 40rpx;
height: 40rpx;
border: 4rpx solid #f3f3f3;
border-top: 4rpx solid #1890ff;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-right: 20rpx;
}
.loading-text {
font-size: 28rpx;
color: #666;
}
}
.no-more-state {
padding: 20rpx 0;
.no-more-text {
font-size: 24rpx;
color: #999;
}
}
.empty-state {
padding: 40rpx 0;
.empty-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
}
.empty-text {
font-size: 28rpx;
color: #999;
}
}
// 分页器样式
.pager-container {
padding: 20rpx;
background: #fff;
border-top: 1rpx solid #f0f0f0;
}
.pager-info {
text-align: center;
margin-bottom: 20rpx;
.pager-text {
font-size: 24rpx;
color: #666;
}
}
.pager-controls {
display: flex;
align-items: center;
justify-content: center;
gap: 20rpx;
}
.pager-btn {
min-width: 120rpx;
height: 60rpx;
border: 1rpx solid #d9d9d9;
border-radius: 8rpx;
background: #fff;
color: #333;
font-size: 26rpx;
&:disabled {
color: #ccc;
background: #f5f5f5;
border-color: #d9d9d9;
}
&:not(:disabled):active {
background: #f0f0f0;
}
}
.page-numbers {
display: flex;
gap: 10rpx;
}
.page-btn {
min-width: 60rpx;
height: 60rpx;
border: 1rpx solid #d9d9d9;
border-radius: 8rpx;
background: #fff;
color: #333;
font-size: 26rpx;
&.active {
background: #1890ff;
color: #fff;
border-color: #1890ff;
}
&:not(.active):active {
background: #f0f0f0;
}
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>