實現(xiàn)一個可無限折疊的table

如何在table上實現(xiàn)一個可折疊展開子節(jié)點的table?先看下最終實現(xiàn)效果圖:

技術棧:

  • vue
  • javascript

動手實現(xiàn)

為有興趣的同學先準備一下大綱目錄預覽,為了不讓同學們看入迷,讓大綱為你 指明前行的道路 !

大綱預覽

  1. 明確需求
  2. 樹形結構數(shù)據(jù)準備
  3. 數(shù)據(jù)扁平化 (重點)
  4. 層級展示
  5. 折疊展開功能實現(xiàn)

1. 明確需求

實現(xiàn)一個可折疊展開的table,看上去很迷茫的樣子。但實際上就是:

  1. table 上點擊時對其 子集 進行 隱藏顯示
  2. 通過縮進的距離來表現(xiàn)層級關系

在代碼里很東西其實都是偽裝出來的,例如我們要實現(xiàn)的這個可無限折疊的table。但在用戶操作的時候看來就是那么回事咯 ~ ~

2. 樹形結構數(shù)據(jù)準備

這里已經(jīng)準備好了樹形結構的數(shù)據(jù),存放于 data.js 的文件中,節(jié)點通過 Children 連接。如標題所說,可無限折疊,無論這里的樹形數(shù)據(jù)有多少層級,都能給它辦妥當了。先把牛吹在這,接下來看看如何實現(xiàn)。

3. 數(shù)據(jù)扁平化

條件梳理

樹形結構數(shù)據(jù)扁平化并不難,難點在于在扁平化的時候要做的處理。這里深入梳理一下:

  1. 我們將一個樹節(jié)點的所有父節(jié)點稱為 family ,好比我們要知道的自己的大領導、二領導、直系領導...因為他們的命令咱們都得執(zhí)行。在這個表單中就是當父節(jié)點或更高的祖輩節(jié)點發(fā)命令時,做一個懂事的子節(jié)點當然要聽話辦事。 __family 字段就是用來存放所有所有的父節(jié)點的數(shù)組。在扁平化數(shù)據(jù)是通過 數(shù)組字段 __family 收集其所有 父節(jié)點 。這樣一來,就像族譜一樣,就能清晰知道該節(jié)點的所有父節(jié)點。 至于如和折疊收縮呢? **我們通過一個 foldList 數(shù)組來記錄要 **折疊的節(jié)點 ,也稱為 死亡名單 。如此一來,只要 包含父節(jié)點標識節(jié)點 就乖乖的隱藏吧。

  2. 每個成員都因該是唯一的,那么我們在遍歷時都應該為每個成員添加一個唯一標識 __identity 。正是上一個步驟中說的 節(jié)點標志

  3. 為了明確知道各個節(jié)點之間的層級關系,通過 __level 明確層級關系。那么我們規(guī)定 __level 的值越小等級越高。 __level=0 代表首層節(jié)點。

  4. 既然是無限層級,肯定是用遞歸來實現(xiàn)了。 formatConversion() 方法實現(xiàn)遞歸。 那何時跳出當前遍歷的遞歸呢 ?當前節(jié)點沒有子集時 Children.length = 0 的時候跳出本次遞歸,進行下一次遞歸。該方法可能需要花點時間理解,代碼里注釋已寫的比較詳細,有問題歡迎留言溝通~~

數(shù)據(jù)扁平化

/*********************************
 ** Fn: formatConversion
 ** Intro: 將樹形接口數(shù)據(jù)扁平化
 ** @params: parent 為當前累計的數(shù)組  也是最后返回的數(shù)組
 ** @params: children 為當前節(jié)點仍需繼續(xù)扁平子節(jié)點的數(shù)據(jù)
 ** @params: index 默認等于0, 用于在遞歸中進行累計疊加 用于層級標識
 ** @params: family 裝有當前包含元素自身的所有父級 身份標識
 ** @params: elderIdentity 父級的  唯一身份標識
 ** Author: zyx
 *********************************/
    formatConversion (parent, children, index = 0, family = [], elderIdentity = 'x') {
      // children如果長度等于0,則代表已經(jīng)到了最低層
      // let page = (this.startPage - 1) * 10
      if (children.length > 0) {
        children.map((x, i) => {
          // 設置 __level 標志位 用于展示區(qū)分層級
          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
    }

我們來對比一下扁平話的數(shù)據(jù)

  • 原數(shù)據(jù)
  • 扁平化后的數(shù)據(jù)

對比數(shù)據(jù)的前后,先明確(父節(jié)點: Name: "App" ), (子節(jié)點: Name: "企業(yè)查詢" )。我們先看看__level字段,分別對應0和1,沒問題。 __family 包含了本身節(jié)點。 __identity 的個格式就是 前綴 x 加上在 數(shù)據(jù)中的位置 。例如節(jié)點 App 在數(shù)據(jù)中的位置是第一個節(jié)點,那對應的 __identity 即是 x_0 。 企業(yè)查詢 位于 App 節(jié)點下的第一個元素, 則在父節(jié)點的標識的基礎上追加一位0 ,那對應的 __identity 即是 x_0_0 。其他依次類推。一切已準備妥當~~

4. 層級展示

如圖所示:

如果展示層級呢?利用css即可實現(xiàn)

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

//    js methods:

    /*********************************
      ** Fn: toggleFoldingClass
      ** Intro: 如果子集長度為0,則不返回字體圖標。
      ** Intro: 如果子集長度為不為0,根據(jù)foldList是否存在當前節(jié)點的標識返回相應的折疊或展開圖標
      ** Intro: 關于class說明:permission_placeholder返回一個占位符,具體查看class
      ** @params: params 當前行的數(shù)據(jù)對象
      ** 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 字段就是這時候發(fā)揮用處。 __level 值越大,則將 margin-left 值增大。如代碼:
<p :style="`margin-left: ${scope.row.__level * 20}px;`">...

基本的樣子已經(jīng)有了,那接下來實現(xiàn)點擊功能。

5. 折疊展開功能實現(xiàn)

記錄點擊的節(jié)點標識

通過 死亡名單 foldList 來記錄點擊。點擊事件在點擊折疊展開圖標時觸發(fā) toggleFoldingStatus(scope.row) 事件,前面也說過, foldList 存在的標識,對于所有的子節(jié)點都要隱藏起來。如果 foldList 沒有數(shù)據(jù),則全部展開,萬事大吉。代碼如圖所示,就是那么簡單。

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

折疊展開功能實現(xiàn)

萬事俱備,只欠東風。最后一件要做的大事就是根據(jù) 死亡名單 foldList 來進行顯示和隱藏數(shù)據(jù)。 這里借助el-table中的 row-style 實現(xiàn)。同學們也可以自己實現(xiàn)。來看下官方的說明

![](http://upload-images.jianshu.io/upload_images/13661853-aeb3373889e7cc00.png!web?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

該方法就是為table中的每一行數(shù)據(jù)設置樣式。

/*********************************
 ** Fn: toggleDisplayTr
 ** Intro: 該方法會對每一行數(shù)據(jù)都做判斷 如果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 ''
    },
  • 在來看看效果


錦上添花

如果數(shù)據(jù)太多的話,一個層級層級的去搜索確實也麻煩,所以那么我們再來添加兩個按鈕 全部折疊全部展開 好了。 咨詢分析一下,其實這個功能很簡單。還是關于前面提到過的 死亡名單foldList 。

  • 全部展開 : 如果 foldList 為空,則萬事大吉,數(shù)據(jù)全部展開。
  • 全部折疊 :我們的設計是只要存在于死亡名單的所有包含該標識的節(jié)點都要隱藏。那么只要將所有的首層節(jié)點添加進去就可以了。 this.foldList = this.tableListData.map(x => x.__identity) 即取出首層節(jié)點的標識。

結語

更復雜的需求是結合分頁,當從其他頁回到之前頁時,之前頁的折疊狀態(tài)要依舊保持。這里就不在說明了,因為應用場景很少。

  • 點贊給個鼓勵喲~

文章出處:https://juejin.im/post/5be797456fb9a04a0378bb91。歡迎大家前去點贊支持~!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容