本集定位:
分页器这个组件也算是个老朋友了, 还记得刚学js的时候, 写个分页器要300行代码,要是能穿越回去, 我得好好教教我本身设计模式😹.css
随着如今手机地位的提高, 大部分人上网的时间都用在了手机上, pc端的确是少了不少不少, 而分页器这种类型的组件, 真的并不很适合手机, 已经不符合人类的操做体验了, 人们如今都是划划划或拉拉拉的动做驱动翻页, 分页器其实要配合鼠标才能有很好的感觉, 人的手指点击并不精准, 并且点击的时候还会遮盖住视野, 反正本人更推荐pc端使用分页器来跳转, 移动端可不要这么玩, 多想点让用户更舒服的操做吧.vue
本次编写参考了饥人谷的视频, 同时也看了element的源码, 但最终仍是按我本身的想法构建了这个组件.webpack
一. 基本结构
这个结构是参考的别人的源码... 还有挺好的, 虽然dom写的有点丑, 可是逻辑清晰, 易维护.git
老样子
vue-cc-ui/src/components/Pagination/index.jsgithub
import Pagination from './main/pagination.vue'; Pagination.install = function(Vue) { Vue.component(Pagination.name, Pagination); }; export default Pagination;
躯壳web
<template> <div class='cc-pagination'> <button class="btn-prev"> <slot name='previous'> < </slot> </button> <ul class='cc-pagination__box'> <li>1</li> <li>··</li> <li ....>{{item}}</li> <li>··</li> <li v-if="总页数!== 1">{{总页数}}</li> </ul> <button class="btn-prev"> <slot name="next"> > </slot> </button> </div> </template>
先展现一下基本的样子vue-router
css 方面vuex
@include b(pagination) { cursor: pointer; color: #606266; // 这个颜色很柔和的黑 align-items: center; display: inline-flex; justify-content: center; .btn-prev { // 按钮去掉默认样式 border: none; outline: none; background-color: transparent; &:hover { // 这个nomal是个柔和的蓝色 color: $--color-nomal } } }
二. 功能的定义 编程
pageSize: { type: Number, default: 5, validator: function(value){ if (value < min || value !== ~~value) { throw new Error(`最小为5的整数`); } return true; } }, value: { // 选中页 type: Number, required: true, validator: function(value){ if (value < min || value !== ~~value) { throw new Error(`最小为1的整数`); } return true; } }, pageTotal: { // 总数 type: Number, default: 1, required: true, validator: function(value){ if (value < 1 || value !== ~~value) { throw new Error(`最小为1的整数`); } return true; } }
抽离工具函数
vue-ui/my/vue-cc-ui/src/assets/js/utils.js设计模式
// inspect单词就是检测的意思, 暂时业务只须要传入一个最小值; export function inspect(min) { // 返回一个函数做为真正的校验函数 return function(value) { // 小于这个最小值或不是整数的都要抛错 // ~~这个位运算符的写法的意思就是取整, 取整以后与没取整相等, 固然就不是浮点数 // ~运算符是对位求反,1变0,0变1,也就是求二进制的反码 if (value < min || value !== ~~value) { throw new Error(`最小为${min}的整数`); } return true; }; }
通过抽离, 我这里就能够化简了, 清爽了不少
pageSize: { type: Number, default: 5, validator: inspect(5) }, value: { type: Number, required: true, validator: inspect(1) }, pageTotal: { type: Number, default: 1, required: true, validator: inspect(1) }
三. 完善页码的展现(重点)
逐一分析:
showPages() { // 习惯性的定义返回的变量 let result = [], // 拿到所需的变量 value = this.value, pageTotal = this.pageTotal, // 由于要去掉头尾, 因此-2 pageSize = this.pageSize - 2; // 防止用户输入错误引发的混乱, 好比用户的缓存, 要返给用户, 让用户去处理, 由于极可能v-model出现死循环 if (value > pageTotal) { // 友好的触发一个错误事件 this.$emit("error", value, pageTotal); value = pageTotal; } // 若是被激活的页面在1与end之间, 则把value放入数组, 否则的话会出现重复值 if (value > 1 && value < pageTotal) result.push(value); // 双管齐下, 求出当前激活的页码左右的数据 for (let i = 1; i <= pageSize; i++) { // 加法, 因此检测小于总数就行 if (value + i < pageTotal) { result.push(value + i); // 随时甄别是否已经符合条件, 取值已够就退出; if (result.length >= pageSize) break; } // 减法, 只要检测大于1就行 if (value - i > 1) { result.unshift(value - i); if (result.length >= pageSize) break; } } return result; },
上面的li标签 放心遍历了
<li v-for="item in showPages" :key='item' :class="{'is-active':value === item}">{{item}}</li>
四. 定义事件
说了这么多, 结构已经作好了, 那么就须要事件的驱动了;
handlClick(page) { if (page < 1) page = 1; if (page > this.pageTotal) { page = this.pageTotal; } let isNoChange = this.value === page; this.$emit("input", page); // 当前值, 与当前值相比是否有变化 this.$emit("onChange", page, isNoChange); }
// 左侧的... previous() { // 左侧未显示的第一个 let page = this.showPages[0]; this.handlClick(page - 1); }, // 右侧的... next() { // 右侧未显示的第一个 let len = this.showPages.length, page = this.showPages[len - 1] + 1; this.handlClick(page + 1); },
把时间放到dom上吧
<template> <div class='cc-pagination'> <button class="btn-prev" @click="handlClick(value-1)" // 到头了要提示用户, 显示出禁止点击的样式 :style="{'cursor': (value === 1)?'not-allowed':'pointer'}"> <slot name='previous'> < </slot> </button> <ul class='cc-pagination__box'> <li @click="handlClick(1)" :class="{'is-active':value === 1}">1</li> <li v-if='showLeft' @click="previous">··</li> <li v-for="item in showPages" :key='item' @click="handlClick(item)" :class="{'is-active':value === item}">{{item}}</li> <li v-if='showRight' @click="next">··</li> <li v-if="pageTotal !== 1" @click="handlClick(pageTotal)" :class="{'is-active':value === pageTotal}">{{pageTotal}}</li> </ul> <button class="btn-prev" @click="handlClick(value+1)" // 到头了要提示用户, 显示出禁止点击的样式 :style="{'cursor': (value === pageTotal)? 'not-allowed':'pointer'}"> <slot name="next"> > </slot> </button> </div> </template>
为了判断左右的 ...是否显示, 咱们也要抽离出判断的逻辑
好比说中间的那个数组两边的元素链接上了, 就不显示.. 不然出现.
showLeft() { let { showPages, pageTotal, pageSize } = this; // 左边不是2, 而且pageTotal超出规定数才显示, 否则1 ... ... 2 很尴尬 return showPages[0] !== 2 && pageTotal > pageSize; }, showRight() { let { showPages, pageTotal, pageSize } = this, len = showPages.length; return showPages[len - 1] !== pageTotal - 1 && pageTotal > pageSize; }
至此, 功能性的东西才告一段落
五. 丰富样式与效果
代码实现一下
// background是关键字, 尤为涉及到css 不要直接使用 // js里面为了方便用户, 能够适当使用 <ul class='cc-pagination__box' :class="{'ground-box':background}">
css
单独抽离出ground样式, 为之后的扩展作准备
.ground { background-color: #f4f4f5; ; border-radius: 4px; &:hover { background-color: $--color-nomal; color: white; } } .ground-box { // 背景色是关键字 &>li { @extend .ground; } &>.is-active{ background-color: $--color-nomal; color: white; } }
hideOne 属性, 开启只有一页的时候不显示组件
// 最外层的父级定义就行了 <div class='cc-pagination' v-if='!(hideOne && pageTotal === 1)'>
total: 开启左侧显示条数模式
我作的与别人不一样, 你传了我就显示, 没传就无所谓, 没有附加的功能
<p v-if="total" class="total-number">总共 <span> {{total}}</span> 条</p>
麻雀虽小, 五脏俱全, 作这个也花费了半天的时间, 测出好多问题, 都一一改进了.
end
另外最近计划作一个vue,vuex, vue-router, webpack 原理解析的系列,也是一点点从零开始, 期待你们继续一块儿学习,一块儿进步, 实现自我价值!!
下一集准备聊聊 计数器...上期就这么说的😓;
更多好玩的效果请关注我的博客:连接描述
github地址:连接描述