技术栈
vue3、ts、less
封装代码
<template>
<div class="paging">
<div>
<p>共 <span>{{ total }}</span> 条</p>
<select @change="sizeChange">
<option :value="5">每条5页</option>
<option :value="10">每条10页</option>
<option :value="15">每条15页</option>
<option :value="20">每条20页</option>
<option :value="100">每条100页</option>
</select>
<div class="down_up">
<img src="./../assets/images/left2.png" @click="jump_to(1)">
<img src="./../assets/images/left.png" @click="jump_to(2)">
<template v-if="(maxPage as number) <= 8">
<div v-for="item in maxPage" :class="{ 'active': current == item }" @click="pointPage(item)">
{{ item }}
</div>
</template>
<template v-else>
<div :class="{ 'active': current == 1 }" @click="pointPage(1)">
{{ 1 }}
</div>
<template v-if="current as number < 5">
<div v-for="item in 4" :class="{ 'active': current == item + 1 }" @click="pointPage(item + 1)">
{{ item + 1 }}
</div>
<div>
...
</div>
</template>
<template v-else-if="(Number(maxPage) as any - (current as number)) < 4">
<div>
...
</div>
<div v-for="item in 4" :class="{ 'active': current == Number(maxPage) as number - 5 + item }"
@click="pointPage(Number(maxPage) as number - 5 + item)">
{{ Number(maxPage) as number - 5 + item }}
</div>
</template>
<template v-else>
<div>
...
</div>
<div v-for="item in 3" :class="{ 'active': current == Number(current) - 2 + item }"
@click="pointPage(Number(current) - 2 + item)">
{{ Number(current) as number - 2 + item }}
</div>
<div>
...
</div>
</template>
<div :class="{ 'active': current == maxPage }" @click="pointPage(maxPage)">
{{ maxPage }}
</div>
</template>
<img src="./../assets/images/right.png" @click="jump_to(3)">
<img src="./../assets/images/right2.png" @click="jump_to(4)">
</div>
<div class="go_to">
跳至
<a-input @blur="inputChange" placeholder="1" :value="current"></a-input>
页
</div>
</div>
</div>
</template>
<script lang='ts' setup>
import { ref, watch, onMounted } from 'vue'
import { message } from 'ant-design-vue';
onMounted(() => {
})
message.config({
maxCount: 3,
});
const father = defineProps<{
total: number | undefined,
current: number | string,
size: number | string,
}>()
const fatherFn = defineEmits(["changePagingData", "update:current", "update:size"])
const trigger = function () {
fatherFn("changePagingData", {
size: father.size,
current: father.current
})
}
const bigPageFn = function (): number {
let num: any = 0;
(num as any) = parseInt(num + (father.total as any) / (father.size as any));
((father.total as any) % (father.size as any)) == 0 ? num : ++num
return num
}
let maxPage = ref<number>(bigPageFn())
watch(
() => [father.size, father.current, father.total],
(newvalue, oldvalue) => {
if (newvalue[0] != (oldvalue as any)[0]) {
(maxPage.value as any) = bigPageFn();
// father.current = 1;
fatherFn("update:current", 1)
}
if (newvalue[2] != (oldvalue as any)[2]) {
(maxPage.value as any) = bigPageFn();
fatherFn("update:current", 1)
}
},
{
deep: true,
}
)
const jump_to = function (num: number): void {
switch (num) {
case 1:
fatherFn("update:current", 1);
trigger()
return;
case 2:
if (father.current as number > 1) {
fatherFn("update:current", father.current as any - 1)
// father.current = father.current as any - 1
} else {
message.warning("已经是第一页了")
}
trigger()
return;
case 3:
if ((father.current as number) < (maxPage.value as number)) {
// father.current = father.current as any + 1
fatherFn("update:current", father.current as any + 1)
} else {
message.warning("已经是最后一页了")
}
trigger()
return;
case 4:
// father.current = maxPage.value as number
fatherFn("update:current", maxPage.value)
trigger()
return;
}
}
const inputChange = function (e: any) {
if (e.target.value > maxPage.value) {
message.warning("请输入正确页数")
return;
}
fatherFn("update:current", e.target.value)
// father.current = e.target.value
trigger()
}
const pointPage = function (num: number) {
// father.current = num
fatherFn("update:current", num)
trigger()
}
const sizeChange = function (e: any) {
fatherFn("update:size", e.target.value)
trigger()
}
</script>
<style scoped lang="less">
.paging {
width: 100%;
height: 80px;
display: flex;
justify-content: center;
align-items: center;
>div {
display: flex;
justify-content: center;
align-items: center;
height: 32px;
width: auto;
p {
height: 32px;
line-height: 32px;
margin: 0;
}
select {
height: 32px;
margin: 0 16px;
}
.down_up {
display: flex;
img {
width: 32px;
margin-left: 16px;
cursor: pointer;
}
>div {
width: 32px;
height: 32px;
cursor: pointer;
background-color: RGBA(244, 244, 244, 1);
text-align: center;
line-height: 32px;
font-size: 14px;
margin-left: 16px;
}
.active {
background-color: rgba(13, 152, 162, 1);
color: #fff;
}
}
.go_to {
display: flex;
height: 32px;
line-height: 32px;
margin-left: 16px;
.ant-input {
width: 80px;
height: 32px;
margin: 0 16px;
}
}
}
}
</style>
组件使用
<template>
<Paging :total="total" v-model:current="current" v-model:size="size" @changePagingData="changePagingData"></Paging>
</template>
<script setup lang="ts">
import Paging from "路径"
import {ref} from "vue"
interface PageType{
current:number | stirng;
size:number | stirng;
}
const total = ref<number>(50)
const currentPage = ref<PageType>({
current:1,
size:5
})
const changePagingData = function(val:PageType){
console.log(val);//组件返回的当前页和每页数
}
</script>
解析
1.首先是父子传参:需要一个total(数据有多少条)和一个函数changePaginData(父组件函数,切换页码时需要传给父组件),利用v-model中update函数实现父子current和size的联动。拿到total我们需要先根据size(每页渲染的数量)计算出总共的页码数量
2.定义一个函数,每次页码变化或者每页数据数量变化,都会触发父组件函数,并将current和size传给父组件。
3.向左向右的按钮,一共有四个,向左和直接到第一页,向右和直接到最后,这个比较简单,往前时,只需要判断是否是第一页,往后时,只需要判断是否是最后一页即可
4.点击页码跳转和输入页码跳转,这两个也比较简单,注意:输入页码跳转对值可能需要判断输入值是否在1到最大页码数之间。这里我写的是输入框失焦跳转,自己喜欢什么样可以自己改一下,用按钮也行
5.渲染html,这一环可以根据自己的喜好渲染,但是需要设置好参数,不然会出错,我的参数如上代码备注
6.最后就是写watch事件,watch中主要监听的参数有current,size,total这三个参数,其中最重要的就是size和total,因为size的改变可能会改变最大页码数,total的改变也同样会。我这里处理size带来的改变是让current直接变为1,也就是直接跳回第一页。而total改变,比如现在一共三页,我们删除最后一条数据后,应该回到第二页,我就是做了一个这个处理。注意,这两个参数都可能会改变最大页码数,所以一定要进行计算,否则会出问题。
总结
这个组件总的来说还是比较简单,但是需要注意很多细节,无论是html渲染和js都需要考虑到一定的细节,才会保证尽量不会出错。