分页是WEB开发中很常用的功能,尤其是在各种前后端分离的今天,后端API返回数据,前端根据数据的count以及当前页码pageIndex来计算分页页码并渲染到页面上已经是一个很普通很常见的功能了。从最开始的jquery时代到现在的各种各样的前端框架时代,分页功能都是必不可少的。
分页大多数(基本上)情况下都是对异步数据列表的处理,这里首先需要明白一下分页的流程。
在已知每页显示数据量pageSize以及当前页码pageIndex的情况下:
- 请求API,返回第一屏数据(pageSize内)以及所有相关条件的数据总量count
- 将数据总量传递给page组件,来计算页码并渲染到页面上
- 点击页码,发送请求获取该页码的数据,返回数据总量count以及该页码下的数据条目。
由于获取数据条件的变化(假设是个搜索,关键词变了),count是不定的;再或者,有个select下拉框,来控制每页显示的数据量pageSize,当它变化的时候,总页码肯定也是要变化的。因此很多情况下要重新计算页码并渲染。
了解了流程,在Vue中实现一个分页组件也就变得简单了。
简单处理,样式类似于bootstrap的分页组件,在第一页时,禁用上一页,以及首页按钮;在最后一页时,禁用下一页,以及尾页按钮;超出范围的页码以…来代替,效果图如下:
由于获取数据条件的变化(假设是个搜索,关键词变了),count是不定的;再或者,有个select下拉框,来控制每页显示的数据量pageSize,当它变化的时候,总页码肯定也是要变化的。因此很多情况下要重新计算页码并渲染。
了解了流程,在Vue中实现一个分页组件也就变得简单了。
简单处理,样式类似于bootstrap的分页组件,在第一页时,禁用上一页,以及首页按钮;在最后一页时,禁用下一页,以及尾页按钮;超出范围的页码以…来代替,效果图如下:
分页组件
template
<
template
>
<
ul
class
=
"mo-paging"
>
<!-- prev -->
<
li
:class
=
"['paging-item', 'paging-item--prev', {'paging-item--disabled' : index === 1}]"
@
click
=
"prev"
>prev</
li
>
<!-- first -->
<
li
:class
=
"['paging-item', 'paging-item--first', {'paging-item--disabled' : index === 1}]"
@
click
=
"first"
>first</
li
>
<
li
:class
=
"['paging-item', 'paging-item--more']"
v-if
=
"showPrevMore"
>...</
li
>
<
li
:class
=
"['paging-item', {'paging-item--current' : index === pager}]"
v-for
=
"pager in pagers"
@
click
=
"go(pager)"
>{{ pager }}</
li
>
<
li
:class
=
"['paging-item', 'paging-item--more']"
v-if
=
"showNextMore"
>...</
li
>
<!-- last -->
<
li
:class
=
"['paging-item', 'paging-item--last', {'paging-item--disabled' : index === pages}]"
@
click
=
"last"
>last</
li
>
<!-- next -->
<
li
:class
=
"['paging-item', 'paging-item--next', {'paging-item--disabled' : index === pages}]"
@
click
=
"next"
>next</
li
>
</
ul
>
</
template
>
|
style(scss)
.mo-paging {
display
: inline-
block
;
padding
:
0
;
margin
:
1
rem
0
;
font-size
:
0
;
list-style
:
none
;
user-select:
none
;
> .paging-item {
display
:
inline
;
font-size
:
14px
;
position
:
relative
;
padding
:
6px
12px
;
line-height
:
1.42857143
;
text-decoration
:
none
;
border
:
1px
solid
#ccc
;
background-color
:
#fff
;
margin-left
:
-1px
;
cursor
:
pointer
;
color
:
#0275d8
;
&:first-child {
margin-left
:
0
;
}
&:hover {
background-color
:
#f0f0f0
;
color
:
#0275d8
;
}
&.paging-item--disabled,
&.paging-item--more{
background-color
:
#fff
;
color
:
#505050
;
}
//禁用
&.paging-item--disabled {
cursor
: not-allowed;
opacity: .
75
;
}
&.paging-item--more,
&.paging-item--current {
cursor
:
default
;
}
//选中
&.paging-item--current {
background-color
:
#0275d8
;
color
:
#fff
;
position
:
relative
;
z-index
:
1
;
border-color
:
#0275d8
;
}
}
}
|
javascript
export
default
{
name :
'MoPaging'
,
//通过props来接受从父组件传递过来的值
props : {
//页面中的可见页码,其他的以...替代, 必须是奇数
perPages : {
type : Number,
default
: 5
},
//当前页码
pageIndex : {
type : Number,
default
: 1
},
//每页显示条数
pageSize : {
type : Number,
default
: 10
},
//总记录数
total : {
type : Number,
default
: 1
},
},
methods : {
prev(){
if
(
this
.index > 1) {
this
.go(
this
.index - 1)
}
},
next(){
if
(
this
.index <
this
.pages) {
this
.go(
this
.index + 1)
}
},
first(){
if
(
this
.index !== 1) {
this
.go(1)
}
},
last(){
if
(
this
.index !=
this
.pages) {
this
.go(
this
.pages)
}
},
go (page) {
if
(
this
.index !== page) {
this
.index = page
//父组件通过change方法来接受当前的页码
this
.$emit(
'change'
,
this
.index)
}
}
},
computed : {
//计算总页码
pages(){
return
Math.ceil(
this
.size /
this
.limit)
},
//计算页码,当count等变化时自动计算
pagers () {
const array = []
const perPages =
this
.perPages
const pageCount =
this
.pages
let current =
this
.index
const _offset = (perPages - 1) / 2
const offset = {
start : current - _offset,
end : current + _offset
}
//-1, 3
if
(offset.start < 1) {
offset.end = offset.end + (1 - offset.start)
offset.start = 1
}
if
(offset.end > pageCount) {
offset.start = offset.start - (offset.end - pageCount)
offset.end = pageCount
}
if
(offset.start < 1) offset.start = 1
this
.showPrevMore = (offset.start > 1)
this
.showNextMore = (offset.end < pageCount)
for
(let i = offset.start; i <= offset.end; i++) {
array.push(i)
}
return
array
}
},
data () {
return
{
index :
this
.pageIndex,
//当前页码
limit :
this
.pageSize,
//每页显示条数
size :
this
.total || 1,
//总记录数
showPrevMore :
false
,
showNextMore :
false
}
},
watch : {
pageIndex(val) {
this
.index = val || 1
},
pageSize(val) {
this
.limit = val || 10
},
total(val) {
this
.size = val || 1
}
}
}
|
父组件中使用
<
template
>
<
div
class
=
"list"
>
<
template
v-if
=
"count"
>
<
ul
>
<
li
v-for
=
"item in items"
>...</
li
>
</
ul
>
<
mo-paging
:page-index
=
"currentPage"
:totla
=
"count"
:page-size
=
"pageSize"
@
change
=
"pageChange"
>
</
mo-paging
>
</
template
>
</
div
>
</
template
>
<
script
>
import MoPaging from './paging'
export default {
//显示的声明组件
components : {
MoPaging
},
data () {
return {
pageSize : 20 , //每页显示20条数据
currentPage : 1, //当前页码
count : 0, //总记录数
items : []
}
},
methods : {
//获取数据
getList () {
//模拟
let url = `/api/list/?pageSize=${this.pageSize}¤tPage=${this.currentPage}`
this.$http.get(url)
.then(({body}) => {
//子组件监听到count变化会自动更新DOM
this.count = body.count
this.items = body.list
})
},
//从page组件传递过来的当前page
pageChange (page) {
this.currentPage = page
this.getList()
}
},
mounted() {
//请求第一页数据
this.getList()
}
}
</
script
>
|
本文标题:Vue实现一个分页组件
本文链接:https://smohan.net/blog/pgk1qr
本站使用「 署名 4.0 国际」创作共享协议,转载或使用请署名并注明出处。 相关说明 »