• Vue3手写分页器


    在学习电商项目时,有一个手写分页器的功能模块,但是使用Vue2实现的,所以用Vue3尝试着写了一个。

    首先,有四个点要了解

    1)分页器展示,需要哪些数据(条件)?

    • 当前是第几个: pageNo字段表示当前页数

    • 每一页需要展示多少条数据:pageSize字段

    • 整个分页器一共有多少条数据: total字段 —— 【一共多少页: total/pageSize,向上取整】

    • 分页器连续页码个数,一般是5或者7: continues字段

    2)自定义分页器,在开发的时候先自己传递假的数据进行调试,调试成功再用服务器数据

    3)对于分页器而言,很重要的一个地方是 【算出:连续页码起始数字和结束数字】

    当前如果是第8页且连续页码数为5,则为 6 7 8 9 10。但还需要兼顾到特殊情况(如起始页不能小于1,结束页不能超过总页数,以及连续页码数必须要小于总页数),否则就要特别的赋值

    4)分页器动态展示

    v-for: 可以遍历 数组|数字|字符串|对象

     

    然后开始正式写代码。分页器的组件名为PaginationIndex,在搜索页SearchIndex使用该组件。

     

    静态页面

    首先,找一个分页器的静态界面,先把它展示出来,之后再做各功能的添加

    1. <template>
    2. <div class="pagination">
    3. <button>1</button>
    4. <button>上一页</button>
    5. <button>···</button>
    6. <button>3</button>
    7. <button>4</button>
    8. <button>5</button>
    9. <button>6</button>
    10. <button>7</button>
    11. <button>···</button>
    12. <button>9</button>
    13. <button>上一页</button>
    14. <button style="margin-left: 30px">60</button>
    15. </div>
    16. </template>
    17. <script>
    18. export default {
    19. name: "PaginationIndex",
    20. }
    21. </script>
    22. <style lang="less" scoped>
    23. .pagination {
    24. button {
    25. margin: 0 5px;
    26. background-color: #f4f4f5;
    27. color: #606266;
    28. outline: none;
    29. border-radius: 2px;
    30. padding: 0 4px;
    31. vertical-align: top;
    32. display: inline-block;
    33. font-size: 13px;
    34. min-width: 35.5px;
    35. height: 28px;
    36. line-height: 28px;
    37. cursor: pointer;
    38. box-sizing: border-box;
    39. text-align: center;
    40. border: 0;
    41. &[disabled] {
    42. color: #c0c4cc;
    43. cursor: not-allowed;
    44. }
    45. &.active {
    46. cursor: not-allowed;
    47. background-color: #409eff;
    48. color: #fff;
    49. }
    50. }
    51. }
    52. </style>

     因为数据,页数都是自己写死的,所以接下来需要获得我们真正所需要的数据(需要从父组件中传过来)

     SearchIndex中使用该组件时,传递相应的值

    1. <PaginationIndex
    2. :pageNo="1"
    3. :pageSize="10"
    4. :total="91"
    5. :continues="5"
    6. />

    父元素给子元素传递数据,这里采用props接收。 子组件中

    1. export default {
    2. name: "PaginationIndex",
    3. props: ["pageNo", "pageSize", "total", "continues"],

    分页器起始与结束——计算属性

    同时,应该根据pageNo(当前所在页),计算出它的前后连续页(这里因为continues=5,也就是前后连续5页)。这里使用计算属性,分别用了两种方法。

    这里的计算属性因为自己用的不是很熟练,可能写的有点繁琐或者不太对的地方,有问题的地方大佬看到了可以教教我。

    1. setup(props, context) {
    2. const PageInfo = reactive({});
    3. // 总共多少页
    4. (PageInfo.totalPage = computed({
    5. get() {
    6. return Math.ceil(props.total / props.pageSize);
    7. },
    8. })),
    9. // 计算出连续的页码的起始数字和结束数字【最少5页】
    10. (PageInfo.startNumAndEndNum = computed(() => {
    11. const { pageNo, pageSize, total, continues } = props;
    12. // 定义两个变量,存储起始与结束 数字
    13. let start = 0;
    14. let end = 0;
    15. // 连续页码数字5 【至少为5页】,如果没有5页
    16. if (continues > PageInfo.totalPage) {
    17. start = 1;
    18. end = PageInfo.totalPage;
    19. } else {
    20. // 总页数 > 连续页数
    21. start = pageNo - Math.floor(continues / 2);
    22. end = start + continues - 1;
    23. // 把不正常的现象纠正,比如start小于1,或者end超过total了
    24. if (start < 1) {
    25. start = 1;
    26. end = continues;
    27. }
    28. if (end > PageInfo.totalPage) {
    29. end = PageInfo.totalPage;
    30. start = PageInfo.totalPage - continues + 1;
    31. }
    32. }
    33. return { start, end };
    34. }));

    计算连续页数这里要考虑算法健壮性,比如连续页码数比总页数还大的情况,或者通过计算后,连续起始页小于1,连续结束页大于总页数。 这里返回以对象形式返回

    不仅如此,还要考虑到如果起始页为1,上一页功能应该禁用,结束页到底了,下一页按钮也要禁用。还有如果起始页为2,1后面的省略号就不需要了。 

    DOM结构的代码如下:

    1. <template>
    2. <div class="pagination">
    3. <button :disabled="pageNo == 1">
    4. 上一页
    5. button>
    6. <button
    7. v-show="PageInfo.startNumAndEndNum.start > 1"
    8. >
    9. 1
    10. button>
    11. <button v-if="PageInfo.startNumAndEndNum.start > 2">···button>
    12. <button
    13. v-for="(page, index) in continues"
    14. :key="index"
    15. >
    16. {{ page + PageInfo.startNumAndEndNum.start - 1 }}
    17. button>
    18. <button v-if="PageInfo.startNumAndEndNum.end < PageInfo.totalPage - 1">
    19. ···
    20. button>
    21. <button
    22. v-show="PageInfo.startNumAndEndNum.end < PageInfo.totalPage"
    23. >
    24. {{ PageInfo.totalPage }}
    25. button>
    26. <button :disabled="pageNo == PageInfo.totalPage">下一页button>
    27. <button style="margin-left: 30px">共 {{ total }} 条button>
    28. div>
    29. template>

     这里和vue2里有个小区别就是,vue2中之前的代码里 v-for循环展示中间连续页时,v-for和v-if一起用的。v-for可以循环遍历数字,所以vue2里面是v-for的结束页,假设结束页是10,也就是连续页码为6 7 8 9 10,v-for="(page,index) in PageInfo.startNumAndEndNum.end",这里就会遍历10次,vue2中的做法是,加上v-if判断,只保留后面的5次。但是vue3里面v-for和v-if没法放一起用。所以我的想法是,要么外面包一层div,给外层div加上v-if。我这就是采用v-for 连续页数。也就是只遍历5次,page就是从1-5,然后展示的时候只需要page + 起始页 - 1即为对应的连续页。

     分页器动态展示

    将分页器按照需求改好后,页面部分就算是差不多了,接下来需要点击相应的页码,进行相应数据的改动以及重新发送请求。这里就涉及到子组件给父组件传递数据,所以采用自定义事件。

    vue3中自定义事件相关的记录写在了下面这个博客里

    https://blog.csdn.net/m0_56698268/article/details/126578810

     中间等等过程就先不写了。最后放上分页器组件的代码

    PaginationIndex.vue

    1. <template>
    2. <div class="pagination">
    3. <button :disabled="pageNo == 1" @click="getPageNo(pageNo - 1)">
    4. 上一页
    5. button>
    6. <button
    7. v-show="PageInfo.startNumAndEndNum.start > 1"
    8. @click="getPageNo(1)"
    9. :class="{ active: pageNo == 1 }"
    10. >
    11. 1
    12. button>
    13. <button v-if="PageInfo.startNumAndEndNum.start > 2">···button>
    14. <button
    15. v-for="(page, index) in continues"
    16. :key="index"
    17. @click="getPageNo(page + PageInfo.startNumAndEndNum.start - 1)"
    18. :class="{ active: pageNo == page + PageInfo.startNumAndEndNum.start - 1 }"
    19. >
    20. {{ page + PageInfo.startNumAndEndNum.start - 1 }}
    21. button>
    22. <button v-if="PageInfo.startNumAndEndNum.end < PageInfo.totalPage - 1">
    23. ···
    24. button>
    25. <button
    26. v-show="PageInfo.startNumAndEndNum.end < PageInfo.totalPage"
    27. @click="getPageNo(PageInfo.totalPage)"
    28. :class="{ active: pageNo == PageInfo.totalPage }"
    29. >
    30. {{ PageInfo.totalPage }}
    31. button>
    32. <button :disabled="pageNo == PageInfo.totalPage">下一页button>
    33. <button style="margin-left: 30px">共 {{ total }} 条button>
    34. div>
    35. template>
    36. <script>
    37. import { computed, reactive } from "vue";
    38. export default {
    39. name: "PaginationIndex",
    40. props: ["pageNo", "pageSize", "total", "continues"],
    41. emits: ["getPageNo"],
    42. setup(props, context) {
    43. const PageInfo = reactive({});
    44. // 总共多少页
    45. (PageInfo.totalPage = computed({
    46. get() {
    47. return Math.ceil(props.total / props.pageSize);
    48. },
    49. })),
    50. // 计算出连续的页码的起始数字和结束数字【最少5页】
    51. (PageInfo.startNumAndEndNum = computed(() => {
    52. const { pageNo, pageSize, total, continues } = props;
    53. // 定义两个变量,存储起始与结束 数字
    54. let start = 0;
    55. let end = 0;
    56. // 连续页码数字5 【至少为5页】,如果没有5页
    57. if (continues > PageInfo.totalPage) {
    58. start = 1;
    59. end = PageInfo.totalPage;
    60. } else {
    61. // 总页数 > 连续页数
    62. start = pageNo - Math.floor(continues / 2);
    63. end = start + continues - 1;
    64. // 把不正常的现象纠正,比如start小于1,或者end超过total了
    65. if (start < 1) {
    66. start = 1;
    67. end = continues;
    68. }
    69. if (end > PageInfo.totalPage) {
    70. end = PageInfo.totalPage;
    71. start = PageInfo.totalPage - continues + 1;
    72. }
    73. }
    74. return { start, end };
    75. }));
    76. function getPageNo(pageNo) {
    77. context.emit("getPageNo", pageNo);
    78. }
    79. return {
    80. PageInfo,
    81. getPageNo,
    82. };
    83. },
    84. };
    85. script>
    86. <style lang="less" scoped>
    87. .pagination {
    88. text-align: center;
    89. button {
    90. margin: 0 5px;
    91. background-color: #f4f4f5;
    92. color: #606266;
    93. outline: none;
    94. border-radius: 2px;
    95. padding: 0 4px;
    96. vertical-align: top;
    97. display: inline-block;
    98. font-size: 13px;
    99. min-width: 35.5px;
    100. height: 28px;
    101. line-height: 28px;
    102. cursor: pointer;
    103. box-sizing: border-box;
    104. text-align: center;
    105. border: 0;
    106. &[disabled] {
    107. color: #c0c4cc;
    108. cursor: not-allowed;
    109. }
    110. &.active {
    111. cursor: not-allowed;
    112. background-color: #409eff;
    113. color: #fff;
    114. }
    115. }
    116. }
    117. .active {
    118. background-color: skyblue;
    119. }
    120. style>

    父组件 SearchIndex.vue

    1. DOM
    2. <PaginationIndex
    3. :pageNo="searchParams.pageNo"
    4. :pageSize="searchParams.pageSize"
    5. :total="total"
    6. :continues="5"
    7. @getPageNo="getPageNo"
    8. />
    9. JS
    10. export default {
    11. name: "SearchIndex",
    12. components: {
    13. SearchSelector,
    14. },
    15. setup() {
    16. const store = useStore();
    17. const $route = useRoute();
    18. const $router = useRouter();
    19. const internalInstance = getCurrentInstance();
    20. const $bus = internalInstance.appContext.config.globalProperties.$bus;
    21. // 带给服务器的参数
    22. const searchParams = reactive({
    23. // 一级分类id
    24. category1Id: "",
    25. // 二级分类id
    26. category2Id: "",
    27. // 三级分类id
    28. category3Id: "",
    29. // 分类名字
    30. categoryName: "",
    31. // 关键字
    32. keyword: "",
    33. // 排序 : 初始状态应该是综合|降序
    34. order: "1:desc",
    35. // 分页器
    36. pageNo: 1,
    37. // 每页展示数据个数
    38. pageSize: 10,
    39. // 平台售卖属性操作带的参数
    40. props: [],
    41. // 品牌
    42. trademark: "",
    43. });
    44. let total = computed({
    45. get() {
    46. return store.state.search.searchList.total;
    47. },
    48. });
    49. // 自定义事件回调——获取当前第几页
    50. function getPageNo(pageNo) {
    51. // console.log("getPageNo", pageNo);
    52. // 整理带给服务器参数
    53. searchParams.pageNo = pageNo;
    54. getData();
    55. }

     

     

  • 相关阅读:
    .NET开源强大、易于使用的缓存框架 - FusionCache
    并发编程中的锁、条件变量和信号量
    [学习记录] SpringBoot 4. 开发技巧
    vue+Select+Tree实现单选 默认选中全部
    Kotlin学习笔记(六)作用域函数run,with, apply,also,let,takeIf,takeUnless,repeat
    王道考研操作系统——I/O管理
    java基础知识点总结,零基础怎么学Java?
    Python和Java应该如何选择?该怎么学?
    shardingJdbc分库分表实战
    【Qt】【模型视图架构】 在项目视图中启用拖放
  • 原文地址:https://blog.csdn.net/m0_56698268/article/details/126644677