HomeLease/components/pagination/pagination.vue

313 lines
5.7 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>
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
<view v-else-if="noMore && list.length > 0" class="no-more-state">
<text class="no-more-text">{{ noMoreText }}</text>
</view>
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
<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>
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
<view class="pager-controls">
2025-08-26 09:25:40 +08:00
<button
class="pager-btn prev-btn"
2025-08-25 18:06:33 +08:00
:disabled="currentPage <= 1"
@click="handlePageChange(currentPage - 1)"
>
上一页
</button>
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
<view class="page-numbers">
2025-08-26 09:25:40 +08:00
<button
v-for="page in visiblePages"
2025-08-25 18:06:33 +08:00
:key="page"
:class="['page-btn', { active: page === currentPage }]"
@click="handlePageChange(page)"
>
{{ page }}
</button>
</view>
2025-08-26 09:25:40 +08:00
<button
class="pager-btn next-btn"
2025-08-25 18:06:33 +08:00
: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',
2025-08-26 09:25:40 +08:00
validator: value => ['loadMore', 'pager'].includes(value),
2025-08-25 18:06:33 +08:00
},
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
// 数据列表
list: {
type: Array,
2025-08-26 09:25:40 +08:00
default: () => [],
2025-08-25 18:06:33 +08:00
},
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
// 总数据量
total: {
type: Number,
2025-08-26 09:25:40 +08:00
default: 0,
2025-08-25 18:06:33 +08:00
},
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
// 当前页码
currentPage: {
type: Number,
2025-08-26 09:25:40 +08:00
default: 1,
2025-08-25 18:06:33 +08:00
},
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
// 每页数量
pageSize: {
type: Number,
2025-08-26 09:25:40 +08:00
default: 10,
2025-08-25 18:06:33 +08:00
},
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
// 是否正在加载
loading: {
type: Boolean,
2025-08-26 09:25:40 +08:00
default: false,
2025-08-25 18:06:33 +08:00
},
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
// 是否没有更多数据
noMore: {
type: Boolean,
2025-08-26 09:25:40 +08:00
default: false,
2025-08-25 18:06:33 +08:00
},
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
// 自定义文本
loadingText: {
type: String,
2025-08-26 09:25:40 +08:00
default: '正在加载...',
2025-08-25 18:06:33 +08:00
},
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
noMoreText: {
type: String,
2025-08-26 09:25:40 +08:00
default: '没有更多数据了',
2025-08-25 18:06:33 +08:00
},
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
emptyText: {
type: String,
2025-08-26 09:25:40 +08:00
default: '暂无数据',
2025-08-25 18:06:33 +08:00
},
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
emptyIcon: {
type: String,
2025-08-26 09:25:40 +08:00
default: '📋',
2025-08-25 18:06:33 +08:00
},
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
// 分页器显示的页码数量
visiblePageCount: {
type: Number,
2025-08-26 09:25:40 +08:00
default: 5,
},
2025-08-25 18:06:33 +08:00
},
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
computed: {
// 总页数
totalPages() {
return Math.ceil(this.total / this.pageSize)
},
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
// 可见的页码
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)
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
// 调整起始位置
if (end - start + 1 < this.visiblePageCount) {
start = Math.max(1, end - this.visiblePageCount + 1)
}
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
for (let i = start; i <= end; i++) {
pages.push(i)
}
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
return pages
2025-08-26 09:25:40 +08:00
},
2025-08-25 18:06:33 +08:00
},
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
methods: {
// 处理页码变化
handlePageChange(page) {
if (page < 1 || page > this.totalPages || page === this.currentPage) {
return
}
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
this.$emit('page-change', page)
},
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
// 重置分页状态
reset() {
this.$emit('reset')
2025-08-26 09:25:40 +08:00
},
},
2025-08-25 18:06:33 +08:00
}
</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;
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
.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;
}
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
.loading-text {
font-size: 28rpx;
color: #666;
}
}
.no-more-state {
padding: 20rpx 0;
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
.no-more-text {
font-size: 24rpx;
color: #999;
}
}
.empty-state {
padding: 40rpx 0;
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
.empty-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
}
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
.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;
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
.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;
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
&:disabled {
color: #ccc;
background: #f5f5f5;
border-color: #d9d9d9;
}
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
&: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;
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
&.active {
background: #1890ff;
color: #fff;
border-color: #1890ff;
}
2025-08-26 09:25:40 +08:00
2025-08-25 18:06:33 +08:00
&:not(.active):active {
background: #f0f0f0;
}
}
@keyframes spin {
2025-08-26 09:25:40 +08:00
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
2025-08-25 18:06:33 +08:00
}
2025-08-26 09:25:40 +08:00
</style>