由于是管理系統,多標簽頁應該算是一個常用方式。有了多標簽頁,使用者在使用這個系統進行日常工作時,就可以在同一個頁面中進行多個業務功能模塊之間切換,不必重新去找各個同的業務菜單重新打開。而且已經打開的tab頁來回切換時,能各自保持原狀態。
Ant Design 提供了TAB組件,我們就想辦法看看如何引進來。
菜單必須有,這是用戶尋找業務功能的入口。如果使用多tab,那么菜單點擊的時候要完成兩件事:
- 如果當前沒有對應的標簽頁,就新打開一個
- 如果當前已經有了對應的標簽頁,就切換到那個標簽頁上
但是,在傳統方式下,菜單上的鏈接是Link組件生成的,這個Link是路由提供的組件,用它生成的鏈接點擊效果是:會激活路由,然后找到對應的Component,然后將這個組件作為children,傳給該路由所在的父組件。然后,父組件重新渲染自己,把這個children放到對應的地方就好了。
如果有多TAB,就不能這么玩了。
我認為,我們必須有一個全局的tab列表,用來保存已經打開的tab。每個tab都有key的。然后有個對象用來記錄當前活躍的tab的key。
所以,修改為:菜單點擊的時候,還是走路由的那一套,拿到children,傳給父組件。但是父組件就先別加載它了。
那父組件怎么做呢?
- 對于菜單增加點擊事件,點擊的時候,將菜單的url作為key,push進全局的tab列表。
- 對于菜單的點擊事件,可以拿到當前活躍的location和children(注意拿到的是點擊菜單之前的),不過這也沒關系,起碼location和children組件的對應關系是正確的。我們可以把location中的url提取出來,拿來和在tab列表中的key們對比,如果已存在,就將對應的key和這個children對應上,就完成了菜單項和里面的組件的對應。
- 但是,你肯定就會問了,這也的話,新打開的標簽,從tab列表中找組件,肯定找不到啊。沒關系,新打開的時候,除了點擊事件,也走了路由了,也會將新路由對應的children組件傳進來,我們在組件渲染的時候,直接將這個組件放到tab中就好了。
- 組件渲染:路由點擊事件觸發父組件重新渲染,同時傳入children。所以,我們在父組件渲染的時候,生成TAB。根據tab列表中的元素生成所有標簽頁,活躍的標簽頁是根據當前活躍頁的那個對象值確定。然后,如果這個列表中有children的,就把children放進去,如果沒有children的,就拿父組件中當前的children放到標簽頁中去。
- 對于關閉還有切換的處理,注意寫一下就好。
那到底具體是怎么實現的呢?
- 增加全局變量
tabList
。這是個對象,共其他文件用import方式引用。它里面包含一個列表,列表項是每個菜單的信息:包括key,name即標簽上的中文顯示名,children即tab頁中的children組件,location,即該tab對應的location。 - 側邊欄的Menu組件傳入點擊事件方法。點擊事件的方法在容器組件中實現。即,上面提到的父組件。這個方法接受當前鏈接為入參,同時獲取到點擊之前的當前children和location。然后dispatch到model中的新增tab方法實現。
- model中的新增tab方法:更新currentTab、判斷tabList:如果沒有就push進一個新的進去,如果沒有children就把children放進去。并且判斷當前是不是一個tab都沒有。(如果沒有tab,父組件渲染的時候會加載歡迎頁)
- 在model中做了關閉tab的方法:與上面那個方法差不多相反,需要適當的從tablist中刪元素。currentTab的設定原則是:如果關的不是活躍頁則不變。如果關閉了活躍頁,則找list中的它前面那個設為活躍頁。
其他細節:
用routerRedux.push方式帶參數進行路由重新調用時,期待是走一下model中的注冊的方法,即用戶點擊業務菜單目錄url后進行一次查詢。這沒問題。問題是,正常情況下,用戶再點擊目錄菜單,則會進行一次無參數查詢,這會導致我們的標簽頁的內容重置。如何防止?我的做法是:
- 在目錄中生成的鏈接url中,拼裝上requery=false。當點擊菜單時,所有requery是false的不查詢。但是有例外:如果這時候這個菜單在tablist中沒有children,那也是需要查一次的。
- 如果業務需要routerRedux.push的方式引發查詢,那么一定要把requery設為true。
- 當requery是true的時候,如果沒有children,也是不查的。因為如果你直接用requery=true來訪問,而頁面上是默認刷新的,這時沒有標簽頁的,如果查了反而是浪費。
- 后來又做了和目錄的聯動、以及解決iPad上點擊目錄時tab內容不正確的問題。