實現一個可無限摺疊的table

前言

如何在table上實現一個可摺疊展開子節點的table?先看下最終實現效果圖:javascript

其實這個項目在兩個月之前就以上上傳在github了,但當時沒有寫詳細的實現過程。本身前幾天發表的一篇技術貼當下拉列表數據過大時,該如何應對?獲得你們的很多支持,猶如歌曲《紅日》裏的歌詞像紅日之火 點燃真的我~ 因此繼續爲掘金社區作奉獻本身微不足道的力量啦 ~~css

在週末聽着嗨歌寫文仍是很nice的~ 嗨起來 ~ ~html

技術棧vue

  • vue
  • javascript

動手實現

爲有興趣的同窗先準備一下大綱目錄預覽,爲了避免讓同窗們看入迷,讓大綱爲你指明前行的道路java

大綱預覽

  1. 明確需求
  2. 樹形結構數據準備
  3. 數據扁平化(重點)
  4. 層級展現
  5. 摺疊展開功能實現

1. 明確需求

實現一個可摺疊展開的table,看上去很迷茫的樣子。但實際上就是:git

  1. table上點擊時對其子集進行隱藏顯示
  2. 經過縮進的距離來表現層級關係

在代碼裏很東西其實都是假裝出來的,例如咱們要實現的這個可無限摺疊的table。但在用戶操做的時候看來就是那麼回事咯 ~ ~github

2. 樹形結構數據準備

這裏已經準備好了樹形結構的數據,存放於data.js的文件中,節點經過Children鏈接。如標題所說,可無限摺疊,不管這裏的樹形數據有多少層級,都能給它辦穩當了。先把牛吹在這,接下來看看如何實現。數組

3. 數據扁平化

條件梳理

樹形結構數據扁平化並不難,難點在於在扁平化的時候要作的處理。這裏深刻梳理一下:bash

  1. 咱們將一個樹節點的全部父節點稱爲family,比如咱們要知道的本身的大領導、二領導、直系領導...由於他們的命令我們都得執行。在這個表單中就是當父節點或更高的祖輩節點發命令時,作一個懂事的子節點固然要聽話辦事。__family字段就是用來存放全部全部的父節點的數組。在扁平化數據是經過數組字段__family收集其全部父節點。這樣一來,就像族譜同樣,就能清晰知道該節點的全部父節點。至於如和摺疊收縮呢?咱們經過一個foldList數組來記錄要摺疊的節點,也稱爲死亡名單。如此一來,只要包含父節點標識節點就乖乖的隱藏吧。
  1. 每一個成員都因該是惟一的,那麼咱們在遍歷時都應該爲每一個成員添加一個惟一標識__identity。正是上一個步驟中說的節點標誌
  1. 爲了明確知道各個節點之間的層級關係,經過__level明確層級關係。那麼咱們規定__level的值越小等級越高。__level=0表明首層節點。
  1. 既然是無限層級,確定是用遞歸來實現了。formatConversion()方法實現遞歸。那什麼時候跳出當前遍歷的遞歸呢?當前節點沒有子集時Children.length = 0的時候跳出本次遞歸,進行下一次遞歸。該方法可能須要花點時間理解,代碼裏註釋已寫的比較詳細,有問題歡迎留言溝通~~

數據扁平化

/*********************************
      ** Fn: formatConversion
      ** Intro: 將樹形接口數據扁平化
      ** @params: parent 爲當前累計的數組  也是最後返回的數組
      ** @params: children 爲當前節點仍需繼續扁平子節點的數據
      ** @params: index 默認等於0, 用於在遞歸中進行累計疊加 用於層級標識
      ** @params: family 裝有當前包含元素自身的全部父級 身份標識
      ** @params: elderIdentity 父級的  惟一身份標識
      ** Author: zyx
    *********************************/
    formatConversion (parent, children, index = 0, family = [], elderIdentity = 'x') {
      // children若是長度等於0,則表明已經到了最低層
      // let page = (this.startPage - 1) * 10
      if (children.length > 0) {
        children.map((x, i) => {
          // 設置 __level 標誌位 用於展現區分層級
          Vue.set(x, '__level', index)
          // 設置 __family 爲家族關係 爲全部父級,包含自己在內
          Vue.set(x, '__family', [...family, elderIdentity + '_' + i])
          // 自己的惟一標識  能夠理解爲我的的身份證咯 必定惟一。
          Vue.set(x, '__identity', elderIdentity + '_' + i)
          parent.push(x)
          // 若是仍有子集,則進行遞歸
          if (x.Children.length > 0) this.formatConversion(parent, x.Children, index + 1, [...family, elderIdentity + '_' + i], elderIdentity + '_' + i)
        })
      } return parent
    }
複製代碼

咱們來對比一下扁平話的數據ide

  • 原數據

  • 扁平化後的數據

對比數據的先後,先明確(父節點: Name: "App"), (子節點:Name: "企業查詢")。咱們先看看__level字段,分別對應0和1,沒問題。__family包含了自己節點。__identity的個格式就是前綴 x加上在數據中的位置。例如節點App在數據中的位置是第一個節點,那對應的__identity便是x_0企業查詢位於App節點下的第一個元素,則在父節點的標識的基礎上追加一位0,那對應的__identity便是x_0_0。其餘依次類推。一切已準備穩當~~

4. 層級展現

如圖所示:

若是展現層級呢?利用css便可實現

  • 字體圖標準備: 這裏先補充一點,這裏涉及兩個字體圖標,圖中紅色框標註的,資源存放於src目錄下的iconfont目錄下中。層級最低的字段時不須要展現該圖標呢該如何判斷呢? 經過判斷當前節點是否還有子集params.Children.length === 0便可判斷。若沒有子集則不設置字體圖標便可。點擊時還須要對圖標進行切換,這又如何實現呢?前臺提到過的死亡名單foldList,若是該存在該名單中,表明該數據已經被摺疊,那返回摺疊圖標。不然返回展開圖標。如代碼:
//    html
<i :class="toggleFoldingClass(scope.row)"></i>

//    js methods:

    /*********************************
      ** Fn: toggleFoldingClass
      ** Intro: 若是子集長度爲0,則不返回字體圖標。
      ** Intro: 若是子集長度爲不爲0,根據foldList是否存在當前節點的標識返回相應的摺疊或展開圖標
      ** Intro: 關於class說明:permission_placeholder返回一個佔位符,具體查看class
      ** @params: params 當前行的數據對象
      ** Author: zyx
    *********************************/
    toggleFoldingClass (params) {
      return params.Children.length === 0 ? 'permission_placeholder' : (this.foldList.indexOf(params.__identity) === -1 ? 'iconfont icon-minus-square-o' : 'iconfont icon-plussquareo')
    },
複製代碼
  • 層級展現: __level字段就是這時候發揮用處。__level值越大,則將margin-left值增大。如代碼:
<p :style="`margin-left: ${scope.row.__level * 20}px;`">...
複製代碼

基本的樣子已經有了,那接下來實現點擊功能。

5. 摺疊展開功能實現

記錄點擊的節點標識

經過死亡名單foldList來記錄點擊。點擊事件在點擊摺疊展開圖標時觸發toggleFoldingStatus(scope.row)事件,前面也說過,foldList存在的標識,對於全部的子節點都要隱藏起來。若是foldList沒有數據,則所有展開,萬事大吉。代碼如圖所示,就是那麼簡單。

//    html
<i  @click="toggleFoldingStatus(scope.row)" class="permission_toggleFold" :class="toggleFoldingClass(scope.row)"></i>
//    js methods
    /*********************************
      ** Fn: toggleFoldingStatus
      ** Intro: 切換展開 仍是摺疊
      ** @params: params 當前點擊行的數據
      ** Author: zyx
    *********************************/
    toggleFoldingStatus (params) {
      this.foldList.includes(params.__identity) ? this.foldList.splice(this.foldList.indexOf(params.__identity), 1) : this.foldList.push(params.__identity)
    },
複製代碼

摺疊展開功能實現

萬事俱備,只欠東風。最後一件要作的大事就是根據死亡名單foldList來進行顯示和隱藏數據。 這裏藉助el-table中的row-style實現。同窗們也能夠本身實現。來看下官方的說明

該方法就是爲table中的每一行數據設置樣式。

/*********************************
      ** Fn: toggleDisplayTr
      ** Intro: 該方法會對每一行數據都作判斷 若是foldList 列表中的元素 也存在與當前行的 __family列表中  則該行不展現
      ** @params:
      ** Author: zyx
    *********************************/
    toggleDisplayTr ({row, index}) {
      for (let i = 0; i < this.foldList.length; i++) {
        let item = this.foldList[i]
        // 若是foldList中元素存在於 row.__family中,則該行隱藏。  若是該行的自身標識等於隱藏元素,則表明該元素就是摺疊點
        if (row.__family.includes(item) && row.__identity !== item) return 'display:none;'
      }
      return ''
    },
複製代碼
  • 在來看看效果

錦上添花

若是數據太多的話,一個層級層級的去搜索確實也麻煩,因此那麼咱們再來添加兩個按鈕所有摺疊所有展開好了。 諮詢分析一下,其實這個功能很簡單。仍是關於前面提到過的死亡名單foldList

  • 所有展開: 若是foldList爲空,則萬事大吉,數據所有展開。
  • 所有摺疊:咱們的設計是隻要存在於死亡名單的全部包含該標識的節點都要隱藏。那麼只要將全部的首層節點添加進去就能夠了。this.foldList = this.tableListData.map(x => x.__identity)即取出首層節點的標識。

結語

更復雜的需求是結合分頁,當從其餘頁回到以前頁時,以前頁的摺疊狀態要依舊保持。這裏就不在說明了,由於應用場景不多。

  • 點贊給個鼓勵喲~
  • 又是下午3點了,該回去吃午餐了。
  • 版權說明:本文首發於掘金,如需轉載請註明出處。
相關文章
相關標籤/搜索