本集定位:
分頁器這個組件也算是個老朋友了, 還記得剛學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地址:連接描述