HomeLease/components/pagination/pagination.vue
2025-08-26 09:25:40 +08:00

313 lines
5.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>