如何在table上實現一個可摺疊展開子節點的table?先看下最終實現效果圖:javascript
其實這個項目在兩個月之前就以上上傳在 github了,但當時沒有寫詳細的實現過程。本身前幾天發表的一篇技術貼 當下拉列表數據過大時,該如何應對?獲得你們的很多支持,猶如歌曲《紅日》裏的歌詞 像紅日之火 點燃真的我~ 因此繼續爲掘金社區作奉獻本身微不足道的力量啦 ~~在週末聽着嗨歌寫文仍是很nice的~ 嗨起來 ~ ~css
demo: demo傳送門html
技術棧:vue
爲有興趣的同窗先準備一下大綱目錄預覽,爲了避免讓同窗們看入迷,讓大綱爲你指明前行的道路!java
實現一個可摺疊展開的table,看上去很迷茫的樣子。但實際上就是:
在代碼裏很東西其實都是假裝出來的,例如咱們要實現的這個可無限摺疊的table。但在用戶操做的時候看來就是那麼回事咯 ~ ~git
這裏已經準備好了樹形結構的數據,存放於data.js的文件中,節點經過Children
鏈接。如標題所說,可無限摺疊,不管這裏的樹形數據有多少層級,都能給它辦穩當了。先把牛吹在這,接下來看看如何實現。github
樹形結構數據扁平化並不難,難點在於在扁平化的時候要作的處理。這裏深刻梳理一下:數組
- 咱們將一個樹節點的全部父節點稱爲family,比如咱們要知道的本身的大領導、二領導、直系領導...由於他們的命令我們都得執行。在這個表單中就是當父節點或更高的祖輩節點發命令時,作一個懂事的子節點固然要聽話辦事。
__family
字段就是用來存放全部全部的父節點的數組。在扁平化數據是經過數組字段__family
收集其全部父節點。這樣一來,就像族譜同樣,就能清晰知道該節點的全部父節點。至於如和摺疊收縮呢?咱們經過一個foldList
數組來記錄要摺疊的節點,也稱爲死亡名單。如此一來,只要包含父節點標識的節點就乖乖的隱藏吧。- 每一個成員都因該是惟一的,那麼咱們在遍歷時都應該爲每一個成員添加一個惟一標識
__identity
。正是上一個步驟中說的節點標誌。- 爲了明確知道各個節點之間的層級關係,經過
__level
明確層級關係。那麼咱們規定__level
的值越小等級越高。__level=0
表明首層節點。- 既然是無限層級,確定是用遞歸來實現了。
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
。其餘依次類推。一切已準備穩當~~
如圖所示:post
若是展現層級呢?利用css便可實現
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;`">...
基本的樣子已經有了,那接下來實現點擊功能。
經過死亡名單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)
即取出首層節點的標識。更復雜的需求是結合分頁,當從其餘頁回到以前頁時,以前頁的摺疊狀態要依舊保持。這裏就不在說明了,由於應用場景不多。