nankai-cms-website-gov/components/common/Pagination.vue

297 lines
8.2 KiB
Vue
Raw Normal View History

2025-03-13 09:52:18 +08:00
<template>
<div v-if="pageCount > 1" class="pagination">
<div class="pagination-content">
<!-- 总数 -->
2025-03-13 10:14:57 +08:00
<span class="pagination-info"> <em>{{ modelValue.total }}</em> </span>
2025-03-13 09:52:18 +08:00
<ul>
<!-- 上一页 -->
<li :class="{ disabled: modelValue.page <= 1 }">
<el-icon v-if="modelValue.page <= 1"><ArrowLeft/></el-icon>
<nuxt-link v-else :to="getPageUrl(modelValue.page - 1)">
<el-icon><ArrowLeft/></el-icon>
</nuxt-link>
</li>
<!-- 前置分页 -->
<li
v-for="pageIndex in prevPages"
:key="pageIndex"
:class="{ selected: pageIndex === modelValue.page }"
>
<nuxt-link :to="getPageUrl(pageIndex)">{{ pageIndex }}</nuxt-link>
</li>
<!-- 前置翻页器存在中间页码 || 完全展示了后置页码 -->
<li v-if="centerPages.length > 0 || afterPages.length === limitPageCount" class="page-control">
<nuxt-link :to="getPageUrl(modelValue.page-limitPageCount < 1 ? 1 : modelValue.page-limitPageCount)">
<span>...</span>
<el-icon><DArrowLeft /></el-icon>
</nuxt-link>
</li>
<!-- 中间分页 -->
<li
v-for="pageIndex in centerPages"
:key="pageIndex"
:class="{ selected: pageIndex === modelValue.page }"
>
<nuxt-link :to="getPageUrl(pageIndex)">{{ pageIndex }}</nuxt-link>
</li>
<!-- 后置翻页器存在中间页码 || 完全展示了前面的页码但总页数要大于前方展示的页码数 -->
<li v-if="centerPages.length > 0 || (prevPages.length === limitPageCount && pageCount > limitPageCount)" class="page-control">
<nuxt-link :to="getPageUrl(modelValue.page+limitPageCount > pageCount ? pageCount : modelValue.page+limitPageCount)">
<span>...</span>
<el-icon><DArrowRight /></el-icon>
</nuxt-link>
</li>
<!-- 后置分页 -->
<li
v-for="pageIndex in afterPages"
:key="pageIndex"
:class="{ selected: pageIndex === modelValue.page }"
>
<nuxt-link :to="getPageUrl(pageIndex)">{{ pageIndex }}</nuxt-link>
</li>
<!-- 下一页 -->
<li :class="{ disabled: modelValue.page >= pageCount }">
<el-icon v-if="modelValue.page >= pageCount"><ArrowRight /></el-icon>
<nuxt-link v-else :to="getPageUrl(modelValue.page + 1)">
<el-icon><ArrowRight/></el-icon>
</nuxt-link>
</li>
</ul>
<!-- 跳转 -->
2025-03-13 10:14:57 +08:00
<div class="pagination-info pagination-jump">
2025-03-13 09:52:18 +08:00
前往
<el-input-number
v-model="targetPage"
:controls="false"
:max="pageCount"
@keydown.enter="jump"
/>
</div>
</div>
</div>
</template>
<script>
import { ArrowLeft, DArrowLeft, ArrowRight, DArrowRight } from '@element-plus/icons-vue'
export default {
name: 'Pagination',
components: { ArrowLeft, DArrowLeft, ArrowRight, DArrowRight },
emits: ['update:modelValue', 'page-change'],
props: {
// 分页值e.g: { page: 1, capacity: 10, total: 100 }
modelValue: {
type: Object,
required: true,
default: function () {
return {}
}
},
// 获取页码链接函数
getPageUrl: {
type: Function,
default: function (page) {
return `?page=${page}`
}
},
// 最多一次性展示多少个页码
2025-03-13 10:14:57 +08:00
limitPageCount: {
2025-03-13 09:52:18 +08:00
type: Number,
default: 5
}
},
data () {
return {
targetPage: 1,
}
},
computed: {
// 最大同时展示的页码数量其中4 = 首页 + 页面控制按钮 + 尾页 + 页面控制按钮
maxPageCount () {
return this.limitPageCount + 4
},
// 总页数
pageCount () {
return Math.ceil(this.modelValue.total / this.modelValue.capacity)
},
// 中间数
limitPageCountHalf () {
return Math.floor(this.limitPageCount / 2)
},
// 前置分页
prevPages () {
// 总页不超过maxPageCount全部展示
if (this.pageCount <= this.maxPageCount) {
return new Array(this.pageCount)
.fill(0)
.map((item, index) => index + 1)
}
// 当前页码在头部(即当前页码 <= limitPageCount
if (this.modelValue.page < this.limitPageCount) {
return new Array(this.limitPageCount)
.fill(0)
.map((item, index) => index + 1)
}
return [1]
},
// 中间分页展示5条
centerPages () {
if (this.pageCount <= this.maxPageCount) {
return []
}
// 当前页码不在头和尾部(即当前页码 > limitPageCount && 当前页码 < pageCount - limitPageCount
if (this.modelValue.page >= this.limitPageCount && this.modelValue.page <= this.pageCount - this.limitPageCount + 1) {
const pages = []
for (let i = 0; i < this.limitPageCount; i++) {
// 中间页面
if (i === this.limitPageCountHalf) {
pages.push(this.modelValue.page)
continue
}
// 左右两侧页码
pages.push(this.modelValue.page - this.limitPageCountHalf + i)
}
return pages
}
return []
},
// 后置分页
afterPages () {
if (this.pageCount <= this.maxPageCount) {
return []
}
// 当前页码在尾部(当前页码 >= pageCount - limitPageCount + 1
if (this.modelValue.page > this.pageCount - this.limitPageCount + 1) {
return new Array(this.limitPageCount)
.fill(0)
.map((item, index) => {
return this.pageCount - this.limitPageCount + index + 1
})
}
return [this.pageCount]
}
},
watch: {
// 监听分页数据发生变化,刷新目标页码
'modelValue.page': {
immediate: true,
handler (newValue) {
if (this.targetPage !== newValue) {
this.targetPage = newValue
this.$router.replace(this.getPageUrl(newValue))
}
}
}
},
methods: {
// 跳转指定页
jump () {
if (this.targetPage < 1) {
this.targetPage = 1
}
this.$router.push(this.getPageUrl(this.targetPage))
}
}
}
</script>
<style scoped lang="scss">
.pagination {
--page-item-size: 35px;
width: 100%;
text-align: center;
.pagination-content {
width: 800px;
overflow: hidden;
margin: 0 auto;
display: flex;
justify-content: center;
align-items: center;
}
// 页码列表
ul {
display: flex;
margin: 0 30px;
li {
width: var(--page-item-size);
height: var(--page-item-size);
background: var(--background-color);
margin-right: 3px;
display: flex;
justify-content: center;
align-items: center;
a {
display: block;
width: 100%;
height: 100%;
line-height: var(--page-item-size);
text-align: center;
color: #333;
&:hover {
color: var(--primary-color);
}
.el-icon {
position: relative;
top: 2px;
}
}
// 选中状态
&.selected {
a {
color: var(--color-white);
background: var(--primary-color);
&:hover {
color: var(--color-white);
}
}
}
// 禁用状态
&.disabled {
color: var(--color-gray);
cursor: not-allowed;
}
// 页面控制
&.page-control {
a {
display: flex;
justify-content: center;
align-items: center;
}
.el-icon {
display: none;
position: relative;
top: 0;
}
&:hover {
span {
display: none;
}
.el-icon {
display: block;
}
}
}
}
}
// 附加信息
.pagination-info {
color: var(--color-gray);
em {
color: var(--font-color);
font-style: normal;
}
}
// 跳转
.pagination-jump {
display: flex;
align-items: center;
.el-input-number {
width: 70px;
text-align: center;
margin: 0 10px;
}
}
}
</style>