Vue 2.0 制作列表組件,實(shí)現(xiàn)分頁(yè)、搜索、批量操作等

本文僅講解如何使用 Vue 創(chuàng)建一個(gè)實(shí)現(xiàn)分頁(yè)、搜索、批量操作的列表組件,所以只提供此小組件的代碼及說(shuō)明,不提供其之外的代碼或配置

github 地址: https://github.com/jothy1023/vue-component

使用到的技術(shù)或框架


  • Vue.js (2.0) 一套基于 MVVM 的 progressive framework,可以幫助我們迅速搭建用戶界面
  • semantic UI 一套漂亮的 UI 框架
  • localStorage HTML5 的本地存儲(chǔ) API 之一(另一個(gè)為 sessionStorage),顧名思義, localStorage 保存在當(dāng)前設(shè)備內(nèi)存中,除非主動(dòng)刪除,否則一直存在

準(zhǔn)備工作


  • 安裝 Vue 2.0 (rc.6)
  • 引入 semantic.js 文件
  • 創(chuàng)建 Audit.vue 文件

具體過(guò)程


一個(gè)簡(jiǎn)單的 Vue 組件由三部分組成:html 模板 、javascript 腳本 以及 css 樣式。下面我們一一來(lái)看。
效果圖如下:

預(yù)覽圖1
預(yù)覽圖2
預(yù)覽圖2

用到的數(shù)據(jù)結(jié)構(gòu)是一個(gè)簡(jiǎn)單的 user 對(duì)象數(shù)組,對(duì)象格式為

user {
  id: int
  name: String,
  audit: int
}

其中 audit 用來(lái)標(biāo)記是否審核成功,共 4 個(gè)狀態(tài)從 0 到 3,分別為 未審核、正在審核、已審核、審核不通過(guò)。

html 模板

由一對(duì)<template></template>標(biāo)簽作為唯一父標(biāo)簽包裹住模板內(nèi)容,模板內(nèi)分為 filter (過(guò)濾)、container (主體)兩部分。

container

container 主體是一個(gè) table 表格,表格內(nèi)容 tbody 部分使用 Vue 的 v-for 指令,基于 users 數(shù)組渲染出列表。主要代碼如下

<tr v-for="user in filteredUsers">
  <td class="collapsing">
    <div class="ui toggle slider checkbox" v-if="aKey!==''">
      <input type="checkbox" :value="user" v-model="selectedUsers"> <label></label>
    </div>
  </td>
  <td>{{ user.id }}</td>
  <td>{{ user.name }}</td>
  <td>
    <i class="radio black icon" v-if="user.audit==0"></i>
    <i class="minus blue icon" v-if="user.audit==1"></i>
    <i class="checkmark green icon" v-if="user.audit==2"></i>
    <i class="remove red icon" v-if="user.audit==3"></i>
  </td>
  <td>
    <ui-button css="primary" v-if="user.audit==0" @click="user.audit=1">審核</ui-button>
    <ui-button css="green" v-if="user.audit==1" @click="user.audit=2">通過(guò)審核</ui-button>
    <ui-button css="red" v-if="user.audit==1" @click="user.audit=3">不通過(guò)審核</ui-button>
    <ui-button css="grey" v-if="user.audit==2 || user.audit==3" @click="user.audit=1">再次審核</ui-button>
  </td>
</tr>

其中,filteredUsers 為計(jì)算屬性 computed 的一個(gè) getter,用于從模板中分理出較復(fù)雜的邏輯,此處返回過(guò)濾后的 users 。
下來(lái)有一個(gè) type 為 checkbox 的 input,當(dāng)按照某一審核要求過(guò)濾時(shí)顯示,其 v-model 指向 selectedUsers 數(shù)組,用來(lái)保存批量操作中選中的 user ,之后可以直接對(duì)此數(shù)組進(jìn)行操作,改動(dòng)會(huì)同步到 users 。

filter

filter 部分由一個(gè) input 輸入框以及一個(gè) select 選擇框組成,分別可以通過(guò)用戶名和審核狀態(tài)過(guò)濾。具體代碼如下:

<div class="filter">
    <ui-input css="icon">
        <input type="text" v-model="fKey" placeholder="輸入姓名搜索.." />
        <i class="search icon"></i>
    </ui-input>
    <!-- dropdown -->
    <ui-dropdown :setting="{allowAdditions: true}" css="selection">
        <input name="aKey" type="hidden" v-model.lazy="aKey">
        <div class="default text">Select a id</div>
        <i class="dropdown icon"></i>
        <div class="menu">
            <div class="item" data-value="">所有</div>
            <div class="item" data-value="0">未審核</div>
            <div class="item" data-value="1">正在審核</div>
            <div class="item" data-value="2">已審核</div>
            <div class="item" data-value="3">審核不通過(guò)</div>
        </div>
    </ui-dropdown>
</div>

select 使用 semanticUI 的 dropdown 實(shí)現(xiàn),將其 class 設(shè)置為 selection。 dropdown 中包含一個(gè) input 標(biāo)簽,此標(biāo)簽屬性為 <input name="aKey" type="hidden" v-model.lazy="aKey">,其中,name 對(duì)應(yīng) v-model 的屬性名,負(fù)責(zé)將值從下面的 .item 中的 data-value 取回并傳遞。
此處有一個(gè)地方需要引起注意!!(敲黑板
由于此處的 input 標(biāo)簽其實(shí)算是 select 組件的,改動(dòng)時(shí)默認(rèn)行為是 change 而非 input ,也沒(méi)有輸入框,因此就算添加了 v-model 也無(wú)法觸發(fā)。但是添加 @change 卻可以正常觸發(fā),百思不得姐,經(jīng)過(guò)千難萬(wàn)苦跋山涉水之后,終于發(fā)現(xiàn)!!在 Vue 的源碼中有這么一斷代碼:

源碼
源碼

我們只關(guān)注首末行,噢.. 大意就是,判斷有無(wú) lazy 屬性(或者在 IE 環(huán)境下并且類型為 range ),true 時(shí)監(jiān)聽(tīng) change 事件,否則監(jiān)聽(tīng) input 事件。真相大白啦,機(jī)智地給 v-model 加上個(gè)小尾巴 .lazy ,hot-loader 馬上刷新,一試果然成功了!贊 b( ̄▽ ̄)d
P.S: 后來(lái)又翻了翻 Vue 的教程,發(fā)現(xiàn)尤大大其實(shí)在教程中已經(jīng)說(shuō)明過(guò)了,惹..附圖

Vue 文檔
Vue 文檔

并且.. 1.0 版本與 2.0 版本也有點(diǎn)細(xì)微差別,1.0 是直接寫在 input 標(biāo)簽的末尾,而 2.0 必須緊跟 v-model 之后。

當(dāng)二者的 v-model 即 fKey 或 aKey 發(fā)生改變時(shí),觸發(fā) computed 屬性重新計(jì)算 filteredUsers ,重新渲染 users 列表,還有個(gè) paginate 為分頁(yè)方法。

filteredUsers () {
  let fUsers = this.queryFilter('name', this.fKey, this.users)
  fUsers = this.queryFilter('audit', this.aKey, fUsers)
  return this.paginate(fUsers)
}

javascript 腳本

初始數(shù)據(jù)

定義了 initialUsers 數(shù)組,存進(jìn)去若干個(gè) user 對(duì)象,定義 userStorage 對(duì)象,存放 fetch 與 save 方法,分別調(diào)用 localStorage 的 getItem 與 setItem 方法向 localStorage 獲取及存儲(chǔ)數(shù)據(jù)。

var userStorage

function init() {
  var STORAGE_KEY = 'users';

  userStorage = {
    fetch: function () {
      return JSON.parse(localStorage.getItem(STORAGE_KEY)) || initialUsers
    },
    save: function (users) {
      localStorage.setItem(STORAGE_KEY, JSON.stringify(users))
    }
  };
}

init(window);

接著是 Vue 實(shí)例的各屬性方法。

data () {
  return {
    users: userStorage.fetch(), // users 數(shù)據(jù)
    selectedUsers: [], // 保存選中的 users 數(shù)組
    fKey: '', // 過(guò)濾 name 的關(guān)鍵字
    name: '', // 上一次過(guò)濾的 name 關(guān)鍵字,初始化為''
    aKey: '', // select audit 的關(guān)鍵字
    audit: '', // 上一次過(guò)濾的 audit關(guān)鍵字,初始化為''
    limit: 10, // 每頁(yè)顯示行數(shù)
    totalPage: 0, // 總頁(yè)數(shù)
    currentPage: 0, // 當(dāng)前頁(yè)
    jPage: 1 // 跳轉(zhuǎn)到某頁(yè)
  }
}

此處的 name 與 audit 看起來(lái)有些多余,其實(shí)它是有很重要的作用的,它用于保存上一次過(guò)濾的 key與 本次的對(duì)比。

過(guò)濾方法 queryFilter
queryFilter (prop, key, arr) {
  // none query string, return arr
  if (!key) {
    return arr
  }
  // filtering
  arr = arr.filter((user) => {
    if (user[prop].toString().indexOf(key) !== -1) {
      return true 
    }
  })
  // if it's a new filter query, refilter and turn to page one
  if (key !== this[prop]) {
    this.currentPage = 0
    // save last filter query
    this[prop] = key
  }
  return arr
}

這個(gè)設(shè)置應(yīng)用情境是:未設(shè)置這個(gè)變量時(shí),若用戶在一次搜索之后進(jìn)行翻頁(yè),假設(shè)停留在了第 n 頁(yè),這時(shí)候再重新搜索,頁(yè)面會(huì)停留在本次搜索的結(jié)果的第 n 頁(yè),非常不方便。因此檢測(cè)如果重新搜索,則充值 currentPage 屬性,記錄新的 key 。

分頁(yè)方法 paginate
paginate (arr) {
  this.totalPage = Math.ceil(arr.length / this.limit)
  let page = this.currentPage
  let curLimit = this.limit
  // 返回指定條數(shù)的數(shù)組
  arr = arr.slice(curLimit * page, curLimit * (page + 1))
  return arr
}

此處用到了數(shù)組的 slice 方法進(jìn)行淺復(fù)制。

翻頁(yè)方法 turnPage

接受一個(gè)數(shù)組,1 為向后翻頁(yè),-1 為向前翻頁(yè)

  • HTML
<div class="jtp">
  <span>跳轉(zhuǎn)到第 </span>
  <ui-input css="icon">
    <input type="text" v-model="jPage" @keyup.enter="jumpToPage">
  </ui-input><span> 頁(yè)</span>
</div>
  • Javascript
turnPage (num) {
  if (num === 1) {
    if (this.currentPage === this.totalPage - 1) {
      return
    } else {
      this.currentPage++
    }
  } else {
    if (this.currentPage === 0) {
      return
    } else {
      this.currentPage--          
    }
  }
}
單個(gè)操作修改 audit

為單獨(dú)操作的 button 添加 @click 事件,直接修改

批量操作方法

由于 selectedUsers 數(shù)組保存了被選中 users 的數(shù)據(jù),因此只要調(diào)用 setAuditId 方法,傳入 selectedUsers 以及要設(shè)置的 audit ,遍歷 selectedUsers 進(jìn)行設(shè)置即可。代碼舉例如下:

  • HTML
<ui-button css="green" v-if="aKey==='1'" @click="pass">通過(guò)審核</ui-button>
<ui-button css="red" v-if="aKey==='1'" @click="reject">不通過(guò)審核</ui-button>
<ui-button css="small" v-if="aKey==='0'" @click="approveSel">審核</ui-button>
<ui-button css="small" v-if="aKey==='0'" @click="approveAll">全部審核</ui-button>
<ui-button css="small" v-if="aKey==='2' || aKey==='3'" @click="approveSel">再次審核</ui-button>
<ui-button css="small" v-if="aKey==='2' || aKey==='3'" @click="approveAll">全部再次審核</ui-button>
  • Javascript
approveSel () {
  this.setAuditId(this.selectedUsers, 1)
},

approveAll () {
  this.setAuditId(this.filteredUsers, 1)
},

pass () {
  this.setAuditId(this.selectedUsers, 2)
},

reject () {
  this.setAuditId(this.selectedUsers, 3)
},

setAuditId (users, aId) {
  users.forEach((user) => {
    user.audit = aId
  })
}

此外,還添加了 watch 對(duì) users 進(jìn)行檢測(cè),每當(dāng)其改變(例如 修改了 audit)時(shí),將新的 users 存入 localStorage 中。

// watch
watch: {
  users: {
    handler () {
      userStorage.save(this.users)
    },
    deep: true
  }
}
CSS

這部分其實(shí)沒(méi)什么好說(shuō)的,基本上都用了 semanticUI 的 css ,只在最后的跳轉(zhuǎn)頁(yè)碼輸入框的地方重設(shè)了 input 的大小而已。

至此,整個(gè)組件就完成啦~~撒花★,°:.☆( ̄▽ ̄)/$:.°★
初試 Vue ,對(duì)一些 api 的使用可能有不準(zhǔn)確的,有寫得不好的地方歡迎指正。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容