Vue3分页器(Pagination)

news/2024/7/10 0:36:45 标签: vue, typescript, less

自定义传入:

  • 当前页数(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.niftyadmin.cn/n/131647.html

相关文章

各种各样的锁

1.悲观锁和乐观锁 一个共享数据加了悲观锁&#xff0c;那线程每次想操作这个数据前都会假设其他线程也可能会操作这个数据&#xff0c;所以每次操作前都会上锁&#xff0c;这样其他线程想操作这个数据拿不到锁只能阻塞了。 synchronized 和 ReentrantLock是典型的悲观锁 共享…

FreePBX 安装配置初学记录

FreePBX 是开源IPPBX,提供一个WEB界面管理底层的Asterisk. 免费的部分提供了完整的PBX功能,中文文档比较少,简单记录一下. 1. 安装 安装部分很简单,去官网下载发行版,然后直接装就行,基本上一路NEXT到结束. 但安装过程很漫长,和一般基于LINUX的发行版不大一样的是,别人是打包…

12.并发编程

1.并发并发&#xff1a;逻辑流在时间时重叠构造并发程序&#xff1a;进程&#xff1a;每个逻辑控制流是一个进程&#xff0c;由内核调度和维护进程有独立的虚拟地址空间&#xff0c;想要通信&#xff0c;控制流必须使用某种显式的进程间通信机制(IPC)I/O多路复用&#xff1a;程…

中级数据开发工程师养成计

目标 工作之后就很少时间用来沉淀知识了&#xff0c;难得用空闲时间沉淀一下自己。 成为一名中级数据开发工程师。偏向于数据仓库&#xff0c;数据治理方向。 整体排期 1 hive 2 hadoop 3 flink 4 spark 5 闲杂工具 kafka maxwell cancal 6 数据建模&#xff08;偏向于kimbo…

jUnit的学习笔记

jUnit学习笔记要不要做单元测试的思考jUnit的使用jUnit的一些注意事项注解Assert断言JUnitCoreTestCaseTestResultTestSuite 测试套件忽略测试时间测试异常测试参数化测试jUnit中遇到的问题1.多线程测试时&#xff0c;jUnit自动退出了&#xff1f;2.测试入库的数据时&#xff0…

SpringMVC-0308

五、域对象共享数据0、三个域对象范围request&#xff1a;一次请求 第1&#xff5e;6都是向request共享session&#xff1a;一次会话&#xff08;浏览器开启到浏览器关闭&#xff0c;与服务器关闭无关&#xff0c;session有钝化和活化操作&#xff0c;可以持久化数据&#xff0…

Java中 Json、String、jsonObject、jsonArray格式之间互相转换

每次遇到json转换的问题吗&#xff0c;都是一顿百度&#xff0c;倒不如自己写一篇&#xff0c;后期遇到自己查看方便 目录 一、依赖 二、JSON各种转换 1.json格式的字符串 转 JSONObject 2.json格式的字符串 转 JSONArray 3. json格式的字符串 转 JSONObject 、JSONArray…

韩国绿芯1~16通道触摸芯片型号推荐

随着技术的发展&#xff0c;触摸感应技术正日益受到更多关注和应用&#xff0c;目前实现触摸感应的方式主要有两种&#xff0c;一种是电阻式&#xff0c;另一种是电容式。电容式触摸具有感应灵敏、功耗低、寿命长等特点&#xff0c;因此逐步取代电阻式触摸&#xff0c;成为当前…