284 lines
6.6 KiB
Vue
284 lines
6.6 KiB
Vue
|
<template>
|
|||
|
<view class="u-pagination">
|
|||
|
<!-- 上一页按钮 -->
|
|||
|
<view
|
|||
|
:class="[
|
|||
|
'u-pagination-btn',
|
|||
|
{ disabled: currentPage === 1 }
|
|||
|
]"
|
|||
|
:style="{ backgroundColor: buttonBgColor, borderColor: buttonBorderColor }"
|
|||
|
@click="prev"
|
|||
|
>
|
|||
|
<template v-if="prevText">
|
|||
|
{{ prevText }}
|
|||
|
</template>
|
|||
|
<up-icon v-else name="arrow-left"></up-icon>
|
|||
|
</view>
|
|||
|
|
|||
|
<!-- 页码列表 -->
|
|||
|
<block v-for="page in displayedPages" :key="page" v-if="layout.includes('pager')">
|
|||
|
<view
|
|||
|
:class="[
|
|||
|
'u-pagination-item',
|
|||
|
{ active: page === currentPage }
|
|||
|
]"
|
|||
|
@click="goTo(page)"
|
|||
|
>
|
|||
|
{{ page }}
|
|||
|
</view>
|
|||
|
</block>
|
|||
|
|
|||
|
<!-- 总数显示 -->
|
|||
|
<view v-if="total > 0 && layout.includes('total')" class="u-pagination-total">
|
|||
|
{{ currentPage }} / {{ totalPages }}
|
|||
|
</view>
|
|||
|
|
|||
|
<!-- 每页数量选择器 -->
|
|||
|
<!-- <picker
|
|||
|
v-if="layout.includes('sizes')"
|
|||
|
mode="selector"
|
|||
|
:range="pageSizes"
|
|||
|
range-key="label"
|
|||
|
:value="pageSizeIndex"
|
|||
|
@change="handleSizeChange"
|
|||
|
class="u-pagination-sizes"
|
|||
|
>
|
|||
|
<view>{{ pageSizeLabel }}</view>
|
|||
|
</picker> -->
|
|||
|
|
|||
|
<!-- 下一页按钮 -->
|
|||
|
<view
|
|||
|
:class="[
|
|||
|
'u-pagination-btn',
|
|||
|
{ disabled: currentPage === totalPages }
|
|||
|
]"
|
|||
|
:style="{ backgroundColor: buttonBgColor, borderColor: buttonBorderColor }"
|
|||
|
@click="next"
|
|||
|
>
|
|||
|
<template v-if="nextText">
|
|||
|
下一页
|
|||
|
</template>
|
|||
|
<up-icon v-else name="arrow-right"></up-icon>
|
|||
|
</view>
|
|||
|
|
|||
|
<!-- 跳转输入框 -->
|
|||
|
<!-- <view v-if="layout.includes('jumper')">
|
|||
|
<text>前往</text>
|
|||
|
<input
|
|||
|
type="number"
|
|||
|
class="u-pagination-jumper"
|
|||
|
:value="currentPageInput"
|
|||
|
@input="onInputPage"
|
|||
|
@confirm="onConfirmPage"
|
|||
|
/>
|
|||
|
<text>页</text>
|
|||
|
</view> -->
|
|||
|
</view>
|
|||
|
</template>
|
|||
|
|
|||
|
<script>
|
|||
|
export default {
|
|||
|
name: 'u-pagination',
|
|||
|
props: {
|
|||
|
// 当前页码
|
|||
|
currentPage: {
|
|||
|
type: Number,
|
|||
|
default: 1
|
|||
|
},
|
|||
|
// 每页条目数
|
|||
|
pageSize: {
|
|||
|
type: Number,
|
|||
|
default: 10
|
|||
|
},
|
|||
|
// 总数据条目数
|
|||
|
total: {
|
|||
|
type: Number,
|
|||
|
default: 0
|
|||
|
},
|
|||
|
// 上一页按钮文案
|
|||
|
prevText: {
|
|||
|
type: String,
|
|||
|
default: ''
|
|||
|
},
|
|||
|
// 下一页按钮文案
|
|||
|
nextText: {
|
|||
|
type: String,
|
|||
|
default: ''
|
|||
|
},
|
|||
|
buttonBgColor: {
|
|||
|
type: String,
|
|||
|
default: '#f5f7fa'
|
|||
|
},
|
|||
|
buttonBorderColor: {
|
|||
|
type: String,
|
|||
|
default: '#dcdfe6'
|
|||
|
},
|
|||
|
// 可选的每页条目数
|
|||
|
pageSizes: {
|
|||
|
type: Array,
|
|||
|
default: () => [10, 20, 30, 40, 50]
|
|||
|
},
|
|||
|
// 布局方式(类似 el-pagination)
|
|||
|
layout: {
|
|||
|
type: String,
|
|||
|
default: 'prev, pager, next'
|
|||
|
},
|
|||
|
// 是否隐藏只有一个页面时的分页控件
|
|||
|
hideOnSinglePage: {
|
|||
|
type: Boolean,
|
|||
|
default: false
|
|||
|
}
|
|||
|
},
|
|||
|
emits: ['update:currentPage', 'update:pageSize', 'current-change', 'size-change'],
|
|||
|
data() {
|
|||
|
return {
|
|||
|
currentPageInput: this.currentPage + ''
|
|||
|
};
|
|||
|
},
|
|||
|
computed: {
|
|||
|
totalPages() {
|
|||
|
return Math.max(1, Math.ceil(this.total / this.pageSize));
|
|||
|
},
|
|||
|
pageSizeIndex() {
|
|||
|
const index = this.pageSizes.findIndex(size => size.value === this.pageSize);
|
|||
|
return index >= 0 ? index : 0;
|
|||
|
},
|
|||
|
pageSizeLabel() {
|
|||
|
const found = this.pageSizes.find(size => size.value === this.pageSize);
|
|||
|
return found?.label || this.pageSize;
|
|||
|
},
|
|||
|
displayedPages() {
|
|||
|
const total = this.totalPages;
|
|||
|
const current = this.currentPage;
|
|||
|
|
|||
|
if (total <= 4) {
|
|||
|
return Array.from({ length: total }, (_, i) => i + 1);
|
|||
|
}
|
|||
|
|
|||
|
const pages = [];
|
|||
|
|
|||
|
// 当前页靠近头部
|
|||
|
if (current <= 2) {
|
|||
|
for (let i = 1; i <= 4; i++) {
|
|||
|
pages.push(i);
|
|||
|
}
|
|||
|
pages.push('...');
|
|||
|
pages.push(total);
|
|||
|
}
|
|||
|
// 当前页在尾部附近
|
|||
|
else if (current >= total - 1) {
|
|||
|
pages.push(1);
|
|||
|
pages.push('...');
|
|||
|
for (let i = total - 3; i <= total; i++) {
|
|||
|
pages.push(i);
|
|||
|
}
|
|||
|
}
|
|||
|
// 中间情况
|
|||
|
else {
|
|||
|
pages.push(1);
|
|||
|
pages.push('...');
|
|||
|
pages.push(current - 1);
|
|||
|
pages.push(current);
|
|||
|
pages.push(current + 1);
|
|||
|
pages.push('...');
|
|||
|
pages.push(total);
|
|||
|
}
|
|||
|
|
|||
|
return pages;
|
|||
|
}
|
|||
|
// 控制是否隐藏
|
|||
|
},
|
|||
|
watch: {
|
|||
|
currentPage(val) {
|
|||
|
this.currentPageInput = val + '';
|
|||
|
}
|
|||
|
},
|
|||
|
methods: {
|
|||
|
handleSizeChange(e) {
|
|||
|
const selected = e.detail.value;
|
|||
|
const size = this.pageSizes[selected]?.value || this.pageSizes[0].value;
|
|||
|
this.$emit('update:pageSize', size);
|
|||
|
this.$emit('size-change', size);
|
|||
|
},
|
|||
|
prev() {
|
|||
|
if (this.currentPage > 1) {
|
|||
|
this.goTo(this.currentPage - 1);
|
|||
|
}
|
|||
|
},
|
|||
|
next() {
|
|||
|
if (this.currentPage < this.totalPages) {
|
|||
|
this.goTo(this.currentPage + 1);
|
|||
|
}
|
|||
|
},
|
|||
|
goTo(page) {
|
|||
|
if (page === '...' || page === this.currentPage) return;
|
|||
|
this.$emit('update:currentPage', page);
|
|||
|
this.$emit('current-change', page);
|
|||
|
},
|
|||
|
onInputPage(e) {
|
|||
|
this.currentPageInput = e.detail.value;
|
|||
|
},
|
|||
|
onConfirmPage(e) {
|
|||
|
const num = parseInt(e.detail.value);
|
|||
|
if (!isNaN(num) && num >= 1 && num <= this.totalPages) {
|
|||
|
this.goTo(num);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
};
|
|||
|
</script>
|
|||
|
|
|||
|
<style lang="scss" scoped>
|
|||
|
.u-pagination {
|
|||
|
display: flex;
|
|||
|
flex-direction: row;
|
|||
|
align-items: center;
|
|||
|
justify-content: space-between;
|
|||
|
flex-wrap: wrap;
|
|||
|
font-size: 14px;
|
|||
|
color: #606266;
|
|||
|
|
|||
|
.u-pagination-total {
|
|||
|
margin-right: 10px;
|
|||
|
}
|
|||
|
|
|||
|
.u-pagination-sizes {
|
|||
|
margin-right: 10px;
|
|||
|
padding: 4px 4px;
|
|||
|
border: 1rpx solid #dcdfe6;
|
|||
|
border-radius: 4px;
|
|||
|
}
|
|||
|
|
|||
|
.u-pagination-btn {
|
|||
|
margin: 0 3px;
|
|||
|
padding: 4px 4px;
|
|||
|
border: 1rpx solid #dcdfe6;
|
|||
|
border-radius: 4px;
|
|||
|
background-color: #f5f7fa;
|
|||
|
&.disabled {
|
|||
|
opacity: 0.5;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
.u-pagination-item {
|
|||
|
margin: 0 2px;
|
|||
|
padding: 4px 8px;
|
|||
|
border-radius: 4px;
|
|||
|
&.active {
|
|||
|
background-color: #409eff;
|
|||
|
color: white;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
.u-pagination-jumper {
|
|||
|
width: 40px;
|
|||
|
height: 28px;
|
|||
|
margin: 0 5px;
|
|||
|
padding: 0 5px;
|
|||
|
border: 1rpx solid #dcdfe6;
|
|||
|
border-radius: 4px;
|
|||
|
font-size: 14px;
|
|||
|
}
|
|||
|
}
|
|||
|
</style>
|