點擊tab左側的菜單會對應的選中這個菜單
首先做一點修改,本節課用mock來做攔截。
路由列表做了一些修改,給每一個路由對象都加了meta對象。這是路由源信息對象,每一個都加一個title屬性。
並且沒一個路由對象都有一個name值,並且是不重復的
嵌套路由
路由列表所有的name都寫在這
路由列表生成左側菜單
寬度改成200
默認是false
store的嚴格模式先關掉
左側的菜單之前是這里我們寫死的
現在左側的菜單喲啊根據路由列表去實現。下面這里把menuList刪掉。
我們要獲取store里面促成黏糊的routers,開面是過濾路由列表權限后的數據
引入mapState
我們還需要對結果進行過濾。匹配的*和 /login這兩個頁面我們是不希望他出現在左側的菜單的。
所以在這里做一個簡單的過濾,使用filter篩選出來不等於*和不等於login的
篩選出來上之后賦值
加上.meta
現在就是根據路由列表生成的菜單
菜單超出屏幕高度讓它滾動
給sider加個類名
高度為100%,超出隱藏,
當前菜單超出被隱藏了,但是它沒法滾動了。
它的里面還有一個容器
讓ivu的這個元素,y軸超出滾動,水平方向超出 隱藏。
這樣就有滾動態條了。
滾動條看起來比較丑,而且mac電腦和window電腦滾動台的樣式還不一樣。
所以如果不希望看到這個滾動條的話
這樣它的寬度就變寬了
超出的部分被它的父容器,超出隱藏了。
所以當前我們看不到滾動條,而且還能滾動。也有個缺點就是有的人看不到滾動條,反而不知道這里可以滾動。根據自己的需求進行添加。
多標簽
用到iview的tab組件
復制這里代碼
router-view下注釋掉。然后代碼復制到這里。
tab不放在card里面,在上面嵌套在div里面。
在加一個div,把card和里面的router-view嵌套在里面。
先把標簽的內容都去掉。
修改樣式
首先是不希望下面有間距
只用TabPane生成標簽。在里面不做內容的渲染,本頁面還是只有一個router-view ,還是在router-view里面渲染頁面。
標簽肯定是通過一個數組來v-for循環生成。我們把這個數組存到store里面。在store里面新建tabNav
index引入這個模塊。
先來定義一個數組,用來保存打開頁面的列表。所有tab的標簽是通過這個數組來渲染的。
拿到store里面的tabList,然后循環生成TabPane。這里不應該簡單的存一個路由的name,因為這里如果要做動態路由,還有帶參數的頁面的話,同樣name
把這里的name改成params
同樣把這里也改成params
這里區別是后面的參數不同,頁面其實是不同的頁面。這樣就沒法用params來區別頁面。
所以我們把name、params、query都存進去,存一個對象,
這里先把key寫上。
待會會來寫一下 ,先來解決的簡單的,不帶參數的路由。不帶動態參數的路由。就這樣直接用item.name就可以了。
現在是沒有標簽的
接下來是打開一個頁面。現在的菜單點擊沒有做跳轉。
在這里我們直接做跳轉,直接push 然后里面傳入路由的name就可以了。這是es6的簡寫形式,
當我們點擊菜單
跳轉到對應的頁面上。
countTo是我們直接在路由列表里面定義的。匹配的就是count-to這個組件。
沒有使用layout這個父組件。所以我們點擊是跳轉到了單獨的頁面。
當點擊這三個菜單的時候
變化的是中間的內容區域
是因為我們使用了layout這個父組件。這三個頁面都是作為它的嵌套路由來渲染的,
路徑上的component是一級路由,后面的是二級路由。切換的是后面的
演示我們就先用這三個頁面來做演示,其他的地方就先不改了。
當前打開的頁面保存到tabList里面
定義一個mutation方法。接收一個route
把這個路由對象拿到后,我們添加到tabList里面。
當路由變化的時候添加。所以這里可以用watch,監聽路由的變化
引入mapMutations,它是一個方法。
把這個方法引進來
當路由發生變化,把跳轉的新的路由對象傳進去。
多次點擊的效果。有重復添加的問題。
當前tab如果存在,那么就不再添加,我們先來做簡單的判斷,只有name不同。
用find做判斷,沒有找到name相同的。那么才去添加。
直接不添加tab了
前面用嘆號,后面用 三等號
重復的點擊,不會重復的添加了
點擊菜單,對應的tab選中效果。tab組件可以綁定一個value
value是name
給他綁定一個value,當前點擊的是誰,路由對象就是誰。
所以這里直接局勢$router.name
label改成meta.title 標簽上顯示的name值。
再給他一個name值
這樣他就知道你當前選中的是哪一個。這里刪掉
切換不同的頁
如果你這個頁面是動態路由,后面有參數,參數是變化的。根據不同的參數來顯示不同的內容。
還有就是帶query參數的。如果這兩種情況,你都希望不同的參數,打開不同的標簽頁的話。只通過name是不夠的
name值應該包括當前路由的name值,還包裹它的參數。還有query參數相關的信息。
加一個按鈕和事件
params加上一個隨機數,得到秒。
然后進行跳轉
這改成random
點擊打開了新的tab頁面
這里就是隨即生成的參數。
打開多個params的頁面,不會重復添加。因為我們tab添加的時候,判斷的是tab的name
所以需要一個方法來判斷,如果name相同,參數也一樣,才算是一樣。
用一下之前在tolls內定義的方法
tabList[index]就是當前遍歷的項。當前這里也可以用forEach
我們要來定義一個新的方法routeEqual 兩個參數,
params1等於route1.params或者是一個空對象。如果route1沒有這個params那么就是個空對象。
至少route的name是相同的
&&繼續往下判斷。如果name相同,那么就繼續來判斷param。判斷這連個params和query我們還要來定義方法。判斷兩個對象里面的屬性名和屬性值一一對應相等。
方法定義在tools。判斷兩個對象的值和屬性都相等。
個數也相等,值也相等。作為一個工具方法,定義在tools 內。
用Object.keys獲取到它所有的屬性名。
屬性的個數不相同,那么直接return false
長度都是0,那么就是倆空對象,那么長度就都是相等的。
使用some方法,會遍歷這個數組,它里面傳入一個回調函數。
如果有任何一個這兩個屬性的key不同,那么就返回true。
回調函數內有任何一條遍歷結果返回true,那么就是true,如果所有的都是false,那么就是false
和some對應的方法還有every 只有所有的回調函數都返回true,它才是true,有任何一條是false,它就是false
只要有一條不相等就是true,所以這里我們要做一個取反。
使用定義好的方法
這個地方來判斷。
如果params1和2這倆對象相等
再來判斷query1和2
判斷當前tabList索引和 當前路由對象是相等,那么就返回true
這樣mutations里面判斷,就是用我們定義的方法routeExist
還是會出現重復的,好像有點問題
這里改成直接調用方法,取反
這樣就不會有重復的了
打開參數頁面
再來打開
參數不相同,依然心打開的是tab頁
出現的問題,這兩個tab都被選中的狀態。因為我們的name是相同的,選中的狀態我們是通過name來綁定的。
所以這里就不能用當前路由的name來做標識了。
name、params和query里面的信息通通都包含。所以我們要拼一個字符串。定義一個方法getTabNameByRoute
我們把這個方法定義在Utils里
如果想在這里用,我們必須把他掛載到data上面。
$route變量,都是掛載到vue實例上 的。
所以我們必須要把他放到這個vue實例上。才能在template里面使用。
util里面定義方法
首先傳過來一個路由對象,把它解析過來。首先把name賦值給res返回的結果。
如果params不是undefined說明你這個路由對象里面是包含了params這個字段的,並且params它的屬性,
Object.keys 它會把它里面所有的屬性名取出來,然后放到一個數組里面,如果length不為0 說明它里面是有屬性的。
包含了params就給這個res拼字符串。拼成下面這種格式的,id是參數用下划線和value分隔開。 &后面拼接的是query的參數。
定義一個方法,取出來params里面的鍵值對。
Object.entries
直接可以把一個對象變成一個二維數組,沒一個元素是一個數組,數組里面第一個值是key,第二只是value,。但是我們這里不用entries方法。
取鍵值對有個es6的方法
如果有多個屬性,那么就是另外一個數組
需要對它進行排序,因為你的param是一個對象。里面所有屬性讀出來順序是不一定的。所以如果你直接拿來拼字符串,雖然里面鍵值對可能都相同,但是你最后拼出來的順序不同。也會導致最后比較不正確。所以這里先進行排序。
傳兩個參數,a[0]減去b[0]。 這是比較兩個數組第一個值做排序,
拍完序后做遍歷,這里有個參數 就是遍歷當前的數組,它里面有個值,第一個值是key,第二個值是value,所以這里用解構賦值。獲取到第一個值用key來表示,第二個值用value來表示。
然后用下划線,把他們憑借起來。
接下來處理query。前面 &符號來拼接。
最后把res返回,這樣這個方法就定義好了。
故意把參數改成26,會選中26的tab
實現tab頁被點擊時,事件觸發,同時選中左側菜單
添加tab事件,綁定一個handleClickTab
參數是點擊的tab的name值。拿到這個name值,我們首先要做跳轉。
先做跳轉push傳入name是不行的。
這幾個參數的tab的name都是argu,只不過他們的params不同,所以直接push一個name是不行的 ,
所以我們要根據name得到一個路由對象。這個name是我們拼接出來的里面包含了param和query信息。
getRouteById這個方法我們在util里面
先用includes判斷是不是包含&符號。說明就包含query,
分割
為了保險起見,取數組長度減一,就是這個數組的最后一個元素。
在用下划線來切割。
如果我們的屬性分別是a和b 。。那么拼接出來的就是下面這樣。
應下划線分割出來就是下面這樣
所以每次循環我們+2
query和這部分一樣,所以我們把這部分代碼拆出來。拆成一個函數。
封城成一個函數
有冒號就表示有params
自后這個res就是包含name或者包含params或者包含query的這樣一個對象。
那么通過id我們就可以拿到一個對象了。這里輸出看一下
點擊tab輸出,
這里就得到了一個對象
這里有點問題
我們把這個id也輸出來看下
看一下拿到的id
這里分隔字符串,改成用變量
點擊,路由也發生了改變
點擊表格,路由在變,
菜單的聯動
菜單我們用的iview的menu組件。它有個屬性叫做。它有個屬性叫做active-name這里的值取的是路由的name
現在點擊文件夾,左側的菜單也聯動的被選中了。
希望點擊里面的子菜單的時候,他的父級 可以展開。
用到菜單的另一個屬性:openName
展開父級別菜單
這是一個計算屬性,我們要通過一個當前打開激活的路由菜單,展開他的父級menu。
所以當前的$router變化了,它的展開就應該也要變化,
根據當前打開的路由對象的name值,然后把這個routers傳進去。
這是一個計算屬性。這是在state的router模塊里面。
下面來封裝這個方法
傳入兩個參數,一個name,一個是routerList
先定義一個結果數組arr,然后遍歷這個routerList。如果當前點擊的name和路由里面路由對象是相同的。
push到arr數組。為什么這里用some ,而不是用foreach呢,因為如果用foreach 他會遍歷所有的元素。就算你中間滿足條件已經找到這個元素了。還是會把后面的元素都遍歷了。而這里用some,一旦下面return 返回了true。那么后面沒有遍歷到的元素,他都不會再進行遍歷了。這樣節省一些時間。
判斷當前有子菜單,並且子菜單的數量不為0.
遞歸,調用下自身。
name還是那個name。參數2 就是item的children了。
如果childArr的長度不為0的話,說明 上面是返回了。說明你當前遍歷的這個路由對象,它的children里面,有一個路由對象它是當前激活的路由對象,
把返回來的childArr和 arr 合並。
最后把這個arr返回。
這里是vuex,之前寫錯
點擊沒展開
如果激活的是表格的話,那么得到的應該有component.
輸出最后得到的值
展開,iview的組件,需要手動的觸發更新才會展開
做一個監聽。給菜單加一個ref
監聽,openNames這個變量。nextTick會保證你視圖渲染完之后。再調用里面的邏輯。
這樣就自動打開父級菜單了。
當前菜單是關閉的,當我點擊文件夾這個tab
點擊后會展開父級別的菜單。
標簽關閉
加一個可關閉的屬性
這里點擊關閉后。實際上tabList里面的數據是沒有清空的。
所以我們不用它自帶的關閉。這里有個on-tab-remove。當你點擊差號關閉的時候,它會觸發這個事件。但是還存在一個問題。你怎么在這里面處理你的tabList數組。
自己實現tab的關閉
label可以傳入render函數。
這里傳一個labelRender函數,給它傳一個render函數。
默認參數是h就是我們的渲染函數
這里我們需要給render函數傳遞一個當前的item參數
所以這里我們要用到一個閉包。返回一個函數。
在這里面做渲染
渲染一個div
看頁面效果。tab是被渲染的。
加一個圖標,是關閉按鈕。區別原來的差號,我們給它一個圖標。
給圖標綁定一個事件。icon組件本身是沒有事件的,所以我們要用nativeOn這個前綴來表示給icon組件最外層的標簽綁定一個click事件。
要給他傳參數,這里要用bind
關閉要去tabList里面刪掉一個路由對象。還是要通過name、params、query這些信息找出來。
把item傳進去,調用方法getTabNameByRoute 上面方法接收id。
當設置點擊事件的時候,它其實會觸發父級的點擊事件。
所以這里不希望事件冒泡。接收一個event對象。調用stopPropagation方法來阻止冒泡。
移除方法,我們定義到store的action里面。
我們返回一個Promise,因為我們還要做后續的操作。
route加上$符號
首先通過id獲取當前的路由對象
獲取到這個路由對象之后呢,我們來找它的索引,這個路由對象在tabList數組里面是在哪個位置。使用findIndex方法,可以傳入一個回調函數,通過這個函數傳入你查找的條件,最后它會返回給你索引值。
我們來比較一下,如果相等就返回。
最后找到這個路由后,把它刪掉。刪除操作我們放到mutations里面去操作。
commit提交一個mutation 傳入索引
頁面內使用action
測試
點擊關閉
報了個錯誤
輸出看下id是什么
再打印下,得到的路由
傳遞的參數是一個對象,傳入id 然后當前的路由對象。
然后方法的名字也寫錯了,重新改一下
點擊關閉
tab被關閉了。
關閉了標簽后,應該跳到別的頁面
例如有兩個tab。關閉了一個tab 后,應該默認選中另外一個tab。
所以在這要判斷一下,這就是為什么要傳當前那個路由對象
做操作。關閉標簽后,我們應該跳轉到哪個頁面。
你要關閉的就是要打開的這個頁的話。表示右邊還有標簽 那么就是下一個。
如果當前打開的這個標簽是最后一個。那么nextRoute就是它的前一個。
最后獲取下name、params、query.。如果取不到了 就默認用home_index來做跳轉。
把這三個值返回 回去,是個對象
接收promise進行跳轉
測試
關閉后 自動跳轉到下一個。
如果關閉的不是當前
那么就直接關掉,不用走跳轉。
關閉最后一個
那么現實的就是左邊的tab
本地存儲tabList
tabList是存在store里面的。一刷新頁面就沒了。
utils里面定義兩個方法
tabLlist默認應該存localStorage中讀取,如果有就讀取,沒有就是空數組。
在這保存
我們只存有用的,得到一個能夠王localStorage里面存的列表。然后存map后的對象。
這里刪除后,也重新存一下
刷新
刷新后 依然存在,而且還能做跳轉。
在首頁這,清空瀏覽器的緩存
添加標簽是在watch里面,監聽route的變化。如果變化了就往里面添加。如果有就不往里面添加。
點擊表格,並沒有往下面添加tab。好像沒有監聽到變化一樣。
ruter里面,首頁是layout
這里的父組件也是layout
當下面的路由做變化的時候,/component這一級就變了。 這里你配置的是component.
這里配置的是home
這個路由變了,那么這個組件就會重新去渲染。
它是在app.vue這里。是這個地方渲染改變了。
所以這個layout組件。watch都沒有進行,這個組件已經被注銷又重新去渲染了。
所以這里是沒有生效的
如果想讓他有效果。把這個邏輯放到。app.vue
替換這里
方法也引用一下,復制過去
layout之類相關的刪掉就可以了
再次測試
先清空緩存。
默認登陸頁也被添加進來了。
這里簡單的做下過濾
再次清空緩存,刷新頁面重新來測試
每次點擊也都是做的跳轉。
以上多tab頁就算是是開發完成了。
本節代碼
結束