25.Vue技術棧開發實戰-多Tab頁開發








點擊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頁就算是是開發完成了。

本節代碼


 

結束

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM