基于Vue的简单通用分页组件

时间:2022-05-17 17:43:28

分页组件是每一个系统里必不可少的一个组件,分页组件分为两部分。第一部分是模版部分,用于显示当前分页组件的状态,例如正在获取数据、没有数据、没有下一页等等;第二部分是分页数据对象,用于封装一个分页组件的属性和方法,例如获取数据的 url、当前第几页(page)、每次加载条数(count)、一共有多少页(totalPage)等等,方法可能会有上一页、下一页、处理数据等等。

分页数据对象

import base from '@/api/base'

export default class Pagination {
constructor({ url, processFunc, processExt, count = 10, isMock = false }) {
// 数据访问地址
this.url = url
// 数据集合
this.list = []
// 第几页
this.page = 1
// 一共几页
this.totalPage = 1
// 加载数据条数
this.count = count
// 数据处理函数
this.processFunc = processFunc
// 错误处理函数
this.processExt = processExt
// 正在加载中
this.loading = false
// 参数
this.params = {}
// 是否底部
this.reachBottom = false
// 是否为空
this.empty = true
// 是否需要清除
this.toClear = false
// 是否为mock数据
this.isMock = isMock
} /**
* 加载下一页数据
*/
async next(args) {
if (this.loading) {
// console.warn('page loading!')
return this
}
const param = {
pageNo: this.page,
pageSize: this.count
}
// 附加参数
this.loading = true
try {
Object.assign(param, args)
let res
let data
try {
res = await base.get(this.url, param)
data = res.data
} catch (e) {
if (typeof this.processExt === 'function') {
data = this.processExt(e)
} else {
throw new Error(e)
}
}
// 底部判断
if (data === null || data.length < 1) {
if (this.toClear) {
this.clear()
} else {
this.reachBottom = true
}
return this
}
this.empty = false
// 处理数据
this._processData(data)
// 设置数据
if (this.toClear) {
this.list = data
this.toClear = false
} else {
this.list = this.list.concat(data)
}
++this.page
this.totalPage = res.page.totalPages
if (
(res.page && res.page.page === res.page.totalPages) ||
data.length < this.count
) {
this.reachBottom = true
}
return this
} finally {
this.loading = false
}
} /**
* 恢复到第一页
*/
reset() {
this.empty = true
this.toClear = true
this.page = 1
this.reachBottom = false
} clear() {
this.toClear = false
this.page = 1
this.list = []
} /**
* 处理数据(私有)
*/
_processData(data) {
if (this.processFunc) {
for (let i in data) {
const result = this.processFunc(data[i])
if (result) {
data[i] = result
}
}
}
}
}

分页模版

<template>
<div class="z-page-stat">
<p v-show="page.loading" class="page-loading">
<span class="ign-loading"></span>
</p>
<div
class="u-more-btn"
v-show="showLoadMore && !page.reachBottom && !page.loading && !page.empty"
@click="$emit('nextPage')"
>
<span>查看更多</span>
</div>
<p class="reach-btm" v-show="showBtmTx && !page.empty && page.reachBottom">
到底了~
</p>
<div class="page-empty" v-show="!page.loading && page.empty">
<div class="empty-inner">
<div class="img-bg" v-if="emptyImg">
<img
v-if="!emptyImg || emptyImg == 1"
src="../../img/empty-page.png"
alt=""
/>
</div> <p class="tx">{{emptyText}}</p>
<div class="empty-ctn">
<slot name="empty"></slot>
</div>
</div>
</div>
<slot name="other"></slot>
</div>
</template> <script>
export default {
name: 'pageStatus',
data() {
return {}
},
props: {
page: {},
emptyImg: {},
emptyText: {
type: String,
default: '暂时没有数据'
},
showLoadMore: {
// 是否显示加载更多按钮
type: Boolean,
default: false
},
showBtmTx: {
// 到底了文字要不要显示
type: Boolean,
default: true
}
},
components: {},
created: function() {},
mounted: function() {},
methods: {}
}
</script> <style lang="stylus" rel="stylesheet/stylus">
.z-page-stat
text-align center
letter-spacing 2px
color #757575
line-height 60px
.page-loading
.ign-loading
border-radius 100%
margin 16px 0
animation-fill-mode both
border 2px solid #e8473f /* no */
border-bottom-color transparent
height 25px /* no */
width 25px /* no */
background transparent !important
display inline-block
animation rotate 1s 0s linear infinite
.page-empty
position absolute
left 0
width 100%
top 50%
transform translate(0, -50%)
.empty-inner
width 320px
margin 0 auto
.img-bg
position relative
display inline-block
width 254px
height 254px
background #d6d6d6
border-radius 50%
margin-bottom 20px
img
width 94px
margin-top 28px
.tx
color #8c8c8c
.empty-ctn
.u-btn
margin-top 90px
margin-left 20px
border 2px solid #464646 /* no */
box-shadow none
width 168px
height 62px
line-height 62px
</style>

使用组件

<template>
<div>
<div class="card-content" v-for="act in page.list" :key="act.id">
<p>
{{act.title}}
</p>
</div>
<p-status :page="page"></p-status>
</div>
</template> <script>
import { mainList } from '@/api/activity'
import PageStatus from 'comps/pageStatus.vue' export default {
data() {
return {
page: mainList()
}
},
mixins: [appPage],
computed: {},
created: async function() {
this.page.next({
/*传参*/
})
},
components: { 'p-status': PageStatus }
}
</script>
// @/api/activity
import Pagination from '@/utils/Pagination' /**
* 列表
*/
export function mainList() {
const url = `/activity/activity/list.do`
return new Pagination({
url: url
})
}