Vue3分页器(Pagination)

news2025/7/13 2:12:57

自定义传入:

  • 当前页数(current),默认为1
  • 每页条数(pageSize),默认为10
  • 只有一页时是否隐藏分页器(hideOnSinglePage),默认为false
  • 数据总数(total),默认为0
  • 显示的页码数组长度(pageListNum),默认为5
  • 是否可以快速跳转至某页(showQuickJumper),默认为false
  • 是否显示当前页数和数据总量(showTotal),默认为false
  • 分页器展示位置,靠左left,居中center,靠右right(placement),默认为'right'

效果图如下:

 鼠标悬浮时切换为箭头:

①创建自定义分页组件Pagination.vue:

<script setup lang="ts">
import { ref, computed, watch, onMounted } from 'vue'
interface Props {
  current: number, // 当前页数
  pageSize: number, // 每条页数
  total: number, // 数据总数
  pageListNum?: number, // 显示的页码数组长度
  hideOnSinglePage?: boolean, // 只有一页时是否隐藏分页器
  showQuickJumper?: boolean, // 是否可以快速跳转至某页
  showTotal?: boolean, // 是否显示当前页数和数据总量
  placement?: string // 分页器展示位置,靠左left,居中center,靠右right
}
const props = withDefaults(defineProps<Props>(), {
  current: 1,
  pageSize: 10,
  total: 0,
  pageListNum: 5,
  hideOnSinglePage: false,
  showQuickJumper: false,
  showTotal: false,
  placement: 'right'
})
const currentPage = ref(props.current) // 当前页数
const jumpNumber = ref('') // 跳转的页码
const forwardMore = ref(false) // 左省略号展示
const backwardMore = ref(false) // 右省略号展示
const forwardArrow = ref(false) // 左箭头展示
const backwardArrow = ref(false) // 右箭头展示

const totalPage = computed(() => { // 总页数
  return Math.ceil(props.total / props.pageSize) // 向上取整
})
const pageList = computed(() => { // 获取显示的页码数组
  return dealPageList(currentPage.value).filter(n => n !== 1 && n !== totalPage.value)
})
const emit = defineEmits(['change'])
watch(currentPage, (to: number): void => { // 通过更改当前页码,修改分页数据
  // loading,value = true
  console.log('change:', to)
  emit('change', {
    page: to,
    pageSize: props.pageSize
  })
})
onMounted(() => {
  // 监听键盘Enter按键
  document.onkeydown = (e): void => {
    const ev = e || window.event
    if (ev && ev.keyCode === 13 && jumpNumber.value) {
      jumpPage(jumpNumber.value)
    }
  }
})
function dealPageList (curPage: number): number[] {
  var resList = []
  var offset = Math.floor(props.pageListNum / 2) // 向下取整
  var pager = {
    start: curPage - offset,
    end: curPage + offset
  }
  if (pager.start < 1) {
    pager.end = pager.end + (1 - pager.start)
    pager.start = 1
  }
  if (pager.end > totalPage.value) {
    pager.start = pager.start - (pager.end - totalPage.value)
    pager.end = totalPage.value
  }
  if (pager.start < 1) {
    pager.start = 1
  }
  if (pager.start > 1) {
    forwardMore.value = true
  } else {
    forwardMore.value = false
  }
  if (pager.end < totalPage.value) {
    backwardMore.value = true
  } else {
    backwardMore.value= false
  }
  // 生成要显示的页码数组
  for (let i = pager.start; i <= pager.end; i++) {
    resList.push(i)
  }
  return resList
}
function onForward (): void {
  currentPage.value = currentPage.value - props.pageListNum > 0 ? currentPage.value - props.pageListNum : 1
}
function onBackward (): void {
  currentPage.value = currentPage.value + props.pageListNum < totalPage.value ? currentPage.value + props.pageListNum : totalPage.value
}
function jumpPage (jumpNum: string | number): void {
  if (Number(jumpNum)) {
    if (Number(jumpNum) < 1) {
      jumpNum = 1
    }
    if (Number(jumpNum) > totalPage.value) {
      jumpNum = totalPage.value
    }
    changePage(Number(jumpNum))
  }
  jumpNumber.value = '' // 清空跳转输入框
}
function changePage (pageNum: number): boolean | void {
  if (pageNum === 0 || pageNum === totalPage.value + 1) {
    return false
  }
  if (currentPage.value !== pageNum) {
    // 点击的页码不是当前页码
    currentPage.value = pageNum
  }
}
</script>
<template>
  <div :class="[`m-pagination ${placement}`, { hidden: hideOnSinglePage && total<=pageSize }]">
    <div class="m-pagination-wrap">
      <span class="mr8" v-if="showTotal">共 {{ totalPage }} 页 / {{ total }} 条</span>
      <span class="u-item" :class="{ disabled: currentPage === 1 }" @click="changePage(currentPage - 1)">
        <svg class="u-arrow" viewBox="64 64 896 896" data-icon="left" aria-hidden="true" focusable="false">
          <path
          d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 0 0 0 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"
          ></path>
        </svg>
      </span>
      <span :class="['u-item', { active: currentPage === 1 }]" @click="changePage(1)">1</span>
      <span
        class="m-arrow"
        ref="forward"
        v-show="forwardMore && pageList[0] - 1 > 1"
        @click="onForward"
        @mouseenter="forwardArrow = true"
        @mouseleave="forwardArrow = false">
        <span v-show="!forwardArrow" class="u-ellipsis">•••</span>
        <svg v-show="forwardArrow" class="u-icon" viewBox="64 64 896 896" data-icon="double-left" aria-hidden="true" focusable="false"><path d="M272.9 512l265.4-339.1c4.1-5.2.4-12.9-6.3-12.9h-77.3c-4.9 0-9.6 2.3-12.6 6.1L186.8 492.3a31.99 31.99 0 0 0 0 39.5l255.3 326.1c3 3.9 7.7 6.1 12.6 6.1H532c6.7 0 10.4-7.7 6.3-12.9L272.9 512zm304 0l265.4-339.1c4.1-5.2.4-12.9-6.3-12.9h-77.3c-4.9 0-9.6 2.3-12.6 6.1L490.8 492.3a31.99 31.99 0 0 0 0 39.5l255.3 326.1c3 3.9 7.7 6.1 12.6 6.1H836c6.7 0 10.4-7.7 6.3-12.9L576.9 512z"></path></svg>
      </span>
      <span :class="['u-item', { active: currentPage === page }]" v-for="(page, index) in pageList" :key="index" @click="changePage(page)">{{ page }}</span>
      <span
        class="m-arrow"
        ref="backward"
        v-show="backwardMore && pageList[pageList.length - 1]+1 < totalPage"
        @click="onBackward"
        @mouseenter="backwardArrow = true"
        @mouseleave="backwardArrow = false">
        <span v-show="!backwardArrow" class="u-ellipsis">•••</span>
        <svg v-show="backwardArrow" class="u-icon" viewBox="64 64 896 896" data-icon="double-right" aria-hidden="true" focusable="false"><path d="M533.2 492.3L277.9 166.1c-3-3.9-7.7-6.1-12.6-6.1H188c-6.7 0-10.4 7.7-6.3 12.9L447.1 512 181.7 851.1A7.98 7.98 0 0 0 188 864h77.3c4.9 0 9.6-2.3 12.6-6.1l255.3-326.1c9.1-11.7 9.1-27.9 0-39.5zm304 0L581.9 166.1c-3-3.9-7.7-6.1-12.6-6.1H492c-6.7 0-10.4 7.7-6.3 12.9L751.1 512 485.7 851.1A7.98 7.98 0 0 0 492 864h77.3c4.9 0 9.6-2.3 12.6-6.1l255.3-326.1c9.1-11.7 9.1-27.9 0-39.5z"></path></svg>
      </span>
      <span v-show="totalPage!==1" :class="['u-item', { active: currentPage === totalPage }]" @click="changePage(totalPage)">{{ totalPage }}</span>
      <span class="u-item" :class="{ disabled: currentPage === totalPage }" @click="changePage(currentPage + 1)">
        <svg class="u-arrow" viewBox="64 64 896 896" data-icon="right" aria-hidden="true" focusable="false">
          <path
          d="M765.7 486.8L314.9 134.7A7.97 7.97 0 0 0 302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 0 0 0-50.4z"
          ></path>
        </svg>
      </span>
      <span class="u-jump-page" v-if="showQuickJumper">跳至<input type="text" v-model="jumpNumber" />页</span>
    </div>
  </div>
</template>
<style lang="less" scoped>
.m-pagination {
  margin: 16px 0;
}
.hidden {
  display: none;
}
.left {
  text-align: left;
}
.center {
  text-align: center;
}
.right {
  text-align: right;
}
.m-pagination-wrap {
  display: inline-block;
  height: 32px;
  line-height: 30px;
  font-size: 14px;
  color: rgba(0, 0, 0, 0.65);
  text-align: center;
  .mr8 {
    margin-right: 8px;
  }
  .u-item {
    min-width: 30px;
    height: 30px;
    display: inline-block;
    user-select: none; // 禁止选取文本
    border: 1px solid #d9d9d9;
    border-radius: 4px;
    cursor: pointer;
    transition: all .3s;
    &:hover {
      .active();
    }
    .u-arrow {
      fill: rgba(0, 0, 0, 0.65);
      width: 12px;
      height: 12px;
      transition: all .3s;
    }
    &:not(:last-child) {
      margin-right: 8px;
    }
  }
  .active { // 悬浮/选中样式
    font-weight: 500;
    color: @themeColor;
    border-color: @themeColor;
    .u-arrow {
      fill: @themeColor;
    }
  }
  .disabled {
    // pointer-events: none; // 禁用鼠标事件
    color: rgba(0, 0, 0, 0.25);
    background: #fff;
    border-color: #d9d9d9;
    cursor: not-allowed;
    &:hover {
      font-weight: 400;
      color: rgba(0, 0, 0, 0.65);
      border-color: #d9d9d9;
      .u-arrow {
        fill: rgba(0, 0, 0, 0.25);
      }
    }
    .u-arrow {
      fill: rgba(0, 0, 0, 0.25);
    }
  }
  .m-arrow {
    display: inline-block;
    vertical-align: middle;
    margin-right: 8px;
    min-width: 32px;
    height: 32px;
    letter-spacing: 2px;
    font-size: 12px;
    color: rgba(0, 0, 0, 0.25);
    transition: all .3s;
    cursor: pointer;
    .u-ellipsis {
      transition: all .3s;
    }
    .u-icon {
      fill: @themeColor;
      width: 12px;
      height: 12px;
    }
  }
  .u-jump-page {
    margin-left: 8px;
    line-height: 32px;
    font-size: 14px;
    color: rgba(0, 0, 0, 0.65);
    input {
      vertical-align: top;
      width: 26px;
      height: 20px;
      padding: 5px 11px;
      margin: 0 8px;
      border: 1px solid #d9d9d9;
      border-radius: 4px;
      background: transparent;
      text-align: left;
      outline: none;
      transition: all 0.3s;
      &:hover {
        border-color: @themeColor
      }
      &:focus {
        border-color: @themeColor;
        box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
      }
    }
  }
}
</style>

②在要使用的页面引入分页器:

<script setup lang="ts">
import { ref } from 'vue'

const total = ref(100)
const pagination = ref({
  pageSize: 10,
  p: 1
})
function changePage (pager: object) { // 分页器回调
  console.log('pager:', pager)
}
</script>

<template>
  <Pagination
    @change="changePage"
    :current="pagination.p"
    :pageSize="pagination.pageSize"
    :total="total"
    :hideOnSinglePage="false"
    :showQuickJumper="true"
    :showTotal="true"
    placement="center"/>
</template>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/396504.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java进阶(下篇2)

Java进阶&#xff08;下篇2&#xff09;一、IO流01.File类的使用1.1、File类的实例化1.2、File类的常用方法11.3、File类的常用方法21.4、课后练习02、IO流原理及流的分类2.1、IO流原理2.2、流的分类2.3、IO 流体系03、节点流(或文件流)3.1、FileReader读入数据的基本操作3.2、…

Linux应用学习——多线程

多线程 PART1——线程相关概念 ​ 线程时参与系统调度的最小单位。被包含在进程之中&#xff0c;是进程中的实际运行单位。一个进程可以创建多个线程&#xff0c;多个线程实现并发运行&#xff0c;每个线程执行不同的任务。 线程时最基本的运行单位&#xff0c;而进程不能运行…

顺序表——“数据结构与算法”

各位CSDN的uu们你们好呀&#xff0c;今天小雅兰的内容是数据结构与算法里面的顺序表啦&#xff0c;在我看来&#xff0c;数据结构总体上是一个抽象的东西&#xff0c;关键还是要多写代码&#xff0c;下面&#xff0c;就让我们进入顺序表的世界吧 线性表 顺序表 线性表 线性表&…

为什么要用VR全景?5个答案告诉你

看中了刚上市的一款新车&#xff0c;再也不用等车展、去4s店才能仔细观赏&#xff0c;点开手机就能“置身”车内近距离观看每一处细节&#xff0c;点击关灯开灯、关门关门&#xff0c;除了摸不到&#xff0c;和在现场几乎没有区别&#xff1b; 准备买房的时候&#xff0c;没人愿…

Git 基础(一)—— Git 的安装及其配置

目录 一、Git 的下载与安装 1、Linux 环境 2、Windows 环境 (1) 下载 Git 安装包 (2) 安装 Git 二、Git 配置 1、配置用户信息 2、查看配置信息 3、Windows 环境下配置文件的位置 一、Git 的下载与安装 1、Linux 环境 在保证网络环境畅通的情况下&#xff0c;直接输…

L298N 直流电机驱动模块与 Arduino

L298N 直流电机驱动模块与 Arduino 原文地址 L298N 电机驱动器可以控制两个直流电机的速度和旋转方向。 此外&#xff0c;它还可以控制双极步进电机&#xff0c;例如NEMA 17。如果您想了解更多信息&#xff0c;请查看本教程。 L298N电机驱动器和Arduino控制步进电机&#xff…

基于C/C++综合训练 ----- 贪吃蛇

文章目录一、定义结构体对象二、游戏初始化1. 蛇初始化2. 食物初始化3. 围墙初始化4. 界面初始化三、逻辑编程1. 启动游戏2. 打印成绩3. main函数四、细节处理五、程序源码该篇环境为Visual Studio2022 游戏简述 &#xff1a;在控制终端绘画出一个矩阵表示游戏界面(围墙)&…

android h5餐饮管理系统myeclipse开发mysql数据库编程服务端java计算机程序设计

一、源码特点 android h5餐饮管理系统是一套完善的WEBandroid设计系统&#xff0c;对理解JSP java&#xff0c;安卓app编程开发语言有帮助&#xff08;系统采用web服务端APP端 综合模式进行设计开发&#xff09;&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要…

javaEE初阶 — HTML 中的常见标签

文章目录注释标签标题标签&#xff1a;h1 h6段落标签&#xff1a;p换行标签&#xff1a;br格式化标签图片标签&#xff1a;img1. img 的 alt 属性2. img 的 title 属性3. width 与 heigth 属性用来描述图的尺寸超链接标签&#xff1a;a表格标签列表标签表单标签1. from 标签2. …

【C++的OpenCV】第十一课-OpenCV图像常用操作(八):直方图计算(cv.calc())

&#x1f389;&#x1f389;&#x1f389;欢迎各位来到小白piao的学习空间&#xff01;\color{red}{欢迎各位来到小白piao的学习空间&#xff01;}欢迎各位来到小白piao的学习空间&#xff01;&#x1f389;&#x1f389;&#x1f389; &#x1f496;&#x1f496;&#x1f496…

发布新闻稿的流程与步骤

发布新闻稿需要遵循一定的流程和步骤&#xff0c;以下是一般的新闻发布流程&#xff1a;1、编写新闻稿新闻稿的内容应当简洁、明确、准确&#xff0c;力求突出新闻价值和亮点。企业和组织可以根据新闻稿的主题和目的&#xff0c;选择不同的写作风格和语言表达方式&#xff0c;以…

春季训练营 | 前端+验证直通车-全实操项目实践,履历加成就业无忧

“芯动的offer”是2023年E课网联合企业全新推出集训培优班&#xff08;线下&#xff09;&#xff0c;针对有一定基础&#xff08;linux、verilog、uvm等&#xff09;在校学生以及想要通过短时间的学习进入到IC行业中的转行人士&#xff0c;由资深IC设计工程师带教&#xff0c;通…

openpnp - 贴片前, 放入一块新板子后, 对板子的坐标矫正

文章目录openpnp - 贴片前, 放入一块新板子后, 对板子的坐标矫正概述笔记实验前置条件实验开始建立自己板子上的Mark点封装, 用于自己人工圈定判断Mark点位置是否正确建立mark点封装根据多个mark点, 来精确定位板子左下角原点坐标ENDopenpnp - 贴片前, 放入一块新板子后, 对板子…

图像边缘检测

文章目录前言一、图像边缘检测二、边缘检测算子1. Roberts算子2. Prewitt算子3. Sobel算子三、代码实现总结前言 有了图像放大缩小&#xff0c;图像灰度化处理等相关基础知识过后&#xff0c;就可以进行图像边缘检测了。边缘检测最后也会在FPGA上面实现&#xff0c;此处小编已经…

神经网络分类任务(手写数字识别)

1.Mnist分类任务 网络基本构建与训练方法&#xff0c;常用函数解析 torch.nn.functional模块 nn.Module模块 学习方法&#xff1a;边用边查&#xff0c;多打印&#xff0c;duogua 使用jupyter的优点&#xff0c;可以打印出每一个步骤。 2.读取数据集 自动下载 %matplotl…

移动设备配置文件管理

什么是移动设备上的设备配置文件 随着移动设备在工作中使用量的迅速增加&#xff0c;有必要将这些设备置于企业管理之下&#xff0c;以确保企业数据安全且设备符合行业标准。移动设备上的配置文件允许 IT 管理员通过对员工使用的智能手机、平板电脑和笔记本电脑实施公司策略和…

三维人脸实践:基于Face3D的渲染、生成与重构 <一>

face3d: Python tools for processing 3D face git code: https://github.com/yfeng95/face3d paper list: PaperWithCode 该方法广泛用于基于三维人脸关键点的人脸生成、属性检测&#xff08;如位姿、深度、PNCC等&#xff09;&#xff0c;能够快速实现人脸建模与渲染。推荐…

学生使用的台灯该怎么选择?2023适合学生房间的灯推荐

随着社会的进步发展&#xff0c;我们的生活水平越来越高&#xff0c;很多家庭的孩子都开始使用台灯这种家居产品&#xff0c;对于学习任务繁重的他们来说&#xff0c;台灯确实可以起到保护眼睛、提高学习专注度的作用。那么不知道朋友们是否了解过&#xff0c;台灯该怎么选择呢…

本地开发vue项目联调遇到访问接口跨域问题

本地开发vue项目联调遇到访问接口跨域问题 修改本地的localhost 一&#xff1a;按winr打开运行窗口&#xff0c;输入drivers &#xff0c;然后回车 二&#xff1a;打开etc文件夹&#xff0c;然后用记事本的方式打开里面的hosts文件&#xff0c; 三&#xff1a;这时我们就可…

oneblog_justauth_三方登录配置【QQ】

文章目录oneblog添加第三方平台QQ互联平台创建三方应用完善信息登录oneblog添加第三方平台 1.oneblog管理端&#xff0c;点击左侧菜单 网站管理——>社会化登录配置管理 ,添加一个社会化登录 2.编辑信息如下&#xff0c;选择QQ平台后复制redirectUri,然后去QQ互联平台获取…