国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Vue-根據(jù)角色獲取菜單動態(tài)添加路由

這篇具有很好參考價值的文章主要介紹了Vue-根據(jù)角色獲取菜單動態(tài)添加路由。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

如果大家寫過后臺管理系統(tǒng)的項目,那么動態(tài)路由一定是繞不開的,如果想偷懶的話,就把所有路由一開始都配置好,然后只根據(jù)后端返回的菜單列表渲染就好菜單就好了,但是這樣的隱患就是我在地址欄輸入的地址的時候,也會進入這個頁面,不偷懶的方法就是本文要介紹的,真動態(tài)路由了,當(dāng)然不會僅僅只是介紹使用數(shù)據(jù)怎么換成動態(tài)路由添加就好了,會從登錄獲取token后請求菜單列表…最后注冊完成,這一系列流程完整的實現(xiàn)一次,相信對于第一次接觸這個案例的朋友會有幫助

前提提要

  1. 本文有些東西我不會詳細的說,比如后端部分,前端代理啊,基于 element-ui 的遞歸菜單封裝等其他組件使用等等,我不會在做額外的贅述了,后端這個流程包裹這些封裝,后面我會單獨開一篇文章來說明
  2. 前端 vue 項目結(jié)構(gòu)部分也不會太過詳細的說明,所以觀看本文還是需要一定的基礎(chǔ),至少知道vue的基礎(chǔ)語法、用過 vue-router 和 vuex 吧,要求還是不高的

需求分析

  1. 在實現(xiàn)我們這個需求,不難想到主要就是完成登錄,通過登錄獲取到正確的菜單列表,通過菜單列表進行渲染

  2. 但是完成這個步驟的話,我們還需要捋一下頁面的關(guān)系,按照我們的開發(fā)時態(tài)來說,我們啟動一個項目之后,會通過 http://localhost:8080/ 這樣的一個地址在瀏覽中打開

  3. 打開這個地址之后,觸發(fā)的是什么路徑,是不是 /,表示根路徑,在后臺管理系統(tǒng)中,一般這個跟路徑我們會映射到什么組件上,是不是 layout 組件,比如這樣的,如圖:

    Vue-根據(jù)角色獲取菜單動態(tài)添加路由,vue.js,前端,javascript

  4. 但是這樣的話就和我們的需求有點不一樣了,我們要先登錄啊,都沒登錄怎么能打開這個呢?所以一把來說,我們一般會要么把 ‘/’ 的路徑觸發(fā)時,重定向到 ‘/login’,或者在全局路由前置守衛(wèi)中,通過登錄的狀態(tài)來決定是不是跳轉(zhuǎn)到登錄頁,一般我們使用第二種,因為后臺管理系統(tǒng)中,一定會有路由權(quán)限的判斷,到時候一樣會來改動這個,所以選擇后者,至于實現(xiàn)部分,我們后面再看

  5. 完成了上述的操作之后,就是登錄了,登錄之后獲取菜單列表數(shù)據(jù),拿到之后我們就直接注冊嗎?

  6. 我們知道,這種菜單,往往會有一級、二級、三級等等不同級別的菜單,而是不是每個菜單都需要注冊的呢,其實不然,我們需要注冊的僅僅是需要展示的那一部分菜單,比如在我們的案例中,設(shè)備管理是一個一級菜單,但是存在子級菜單,那么此時這個設(shè)備管理菜單就是不需要注冊的,如圖:

    Vue-根據(jù)角色獲取菜單動態(tài)添加路由,vue.js,前端,javascript

  7. 所以這一點我們也需要做一下區(qū)分,但是具體注冊那些呢?這些就還是要在前端先配置好,但是這個配置不會是直接配置到 route 中,是一個映射關(guān)系,比如定義了 a = 組件A,然后依次書寫,把所有會展示的頁面通過這樣的方式,用一個文件存儲起來,那么通過后端返回的菜單列表數(shù)據(jù)時,就可以進行一個對比,篩選,取出符合條件的數(shù)據(jù),組裝成一個適配業(yè)務(wù)的 route 進行注冊

  8. 而通過這樣的匹配,我們最后是可以得到一個數(shù)組的,[route1, route2, …],得到這個數(shù)組之后,使用 vueRouter的 addRoute 方法添加即可

  9. 這里需要注意的事情是,我所演示的案例中,所有的子組件都是在 main 區(qū)域顯示的,所以我就不需要在去單獨的關(guān)心這些子組件的層級關(guān)系了,但是如果某個項目中的,層級關(guān)系如圖:

    Vue-根據(jù)角色獲取菜單動態(tài)添加路由,vue.js,前端,javascript

  10. 像這種或者更多層級的,就需要額外處理一下 children 屬性了,但是方法都是差不的,無非就是數(shù)據(jù)處理的時候多處理一下,而且一般來說就是兩層,最外層第一個 router-view 來展示一級路由(比如登錄、404、layout),main 區(qū)域的 router-view 展示二級路由(比如 home、my、user…)

  11. 經(jīng)過這個分析之后,我們就是能確定,我們要做的事情就是,把這些獲取的菜單數(shù)據(jù),來找到對應(yīng)的組件,并把這些組件添加為 layout 組件的子組件,在 main 區(qū)域展示

具體實現(xiàn)

配置靜態(tài)路由

  1. 根據(jù)上面的粗略的分析,第一步就是創(chuàng)建路由,這一步非常簡單,我直接粘貼代碼了,如下:

    import Vue from 'vue'
    import VueRouter from 'vue-router'
    Vue.use(VueRouter)
    
    const router = new VueRouter({
    	mode: 'hash',
    	routes: []
    })
    
    export default router
    
  2. 這就是一個最基礎(chǔ)的結(jié)構(gòu)了,而在這個需求中,至少有兩個路由一定是靜態(tài)的,一個是 login,一個是 layout,當(dāng)然通常還有個一個任意路由,表示 404,這里我就不寫了,大家有時間自己添加一下就好,如下:

    import Vue from 'vue'
    import VueRouter from 'vue-router'
    Vue.use(VueRouter)
    
    const router = new VueRouter({
    	mode: 'hash',
    	routes: [
    		{
    			path: '/',
    			name: 'layout',
    			component: () => import('@/layout')
    		},
    		{
    			path: '/login',
    			name: 'login',
    			component: () => import('@/views/login')
    		}
    	]
    })
    
    export default router
    
  3. 添加兩個靜態(tài)路由非常簡單吧,然后把這個在 main js 頁面引入使用,我就不展示了

路由權(quán)限判斷

  1. 上面的配置如果我們直接在瀏覽器中打開 http://localhost:8080/ 這個地址,那么展示的就是 layout 組件,如果需要展示位 login 組件的話,我們就需要在全局前置路由守衛(wèi)上動一下手腳了

  2. 也非常簡單,一個用戶登沒登錄,就是判斷是否是存在了 token,如果有就是登錄了,如果沒有就是沒有登錄,根據(jù)這個,我們可以得出一張關(guān)系圖,如圖:

    Vue-根據(jù)角色獲取菜單動態(tài)添加路由,vue.js,前端,javascript

  3. 這只是一個簡單的路由權(quán)限判斷,具體的還需要根據(jù)業(yè)務(wù)來擴展,根據(jù)這個關(guān)系圖,我們可以寫出如下代碼:

    import Vue from 'vue'
    import VueRouter from 'vue-router'
    Vue.use(VueRouter)
    
    import store from '@/store'
    
    const router = new VueRouter({
    	mode: 'hash',
    	routes: [
    		{
    			path: '/',
    			name: 'layout',
    			component: () => import('@/layout')
    		},
    		{
    			path: '/login',
    			name: 'login',
    			component: () => import('@/views/login')
    		}
    	]
    })
    
    router.beforeEach((to, from, next) => {
    	const token = store.state.login.token
    	if (token) {
    		if (to.path === '/login') {
    			next(false)
    		} else {
    			next()
    		}
    	} else {
    		if (to.path === '/login') {
    			next()
    		} else {
    			next('/login')
    		}
    	}
    })
    
    export default router
    

登錄

  1. 實現(xiàn)這點的方法也不止一種,本文采用的是在 store 的 login 模塊中完成登錄,至于 axios 的封裝或者基于 xhr 等等的請求方面,我這里不做解析了

  2. store 的基礎(chǔ)配置不做贅述了,直接粘貼代碼,如下:

    import Vue from 'vue'
    import Vuex from 'vuex'
    Vue.use(Vuex)
    
    import login from './login'
    
    const store = new Vuex.Store({
    	modules: { login }
    })
    
    export default store
    
  3. 至于 login 模塊的話,書寫也非常簡單,編寫登錄函數(shù),登錄成功之后同步獲取菜單數(shù)據(jù),如下:

    import { loginApi, menuApi } from '@/api'
    import router from '@/router'
    
    export default {
    	namespaced: true,
    	state: {
    		userInfo: {} || localStorage.getItem('user_info'),
    		token: '' || localStorage.getItem('token'),
    		menuList: [] || localStorage.getItem('menu_list')
    	},
    	mutations: {
    		SET_MENU_LIST(state, payload) {
    			state.menuList = payload
    		},
    
    		SET_USER_INFO(state, payload) {
    			state.userInfo = payload
    			localStorage.setItem('user_info', JSON.stringify(payload))
    		},
    
    		SET_TOKEN(state, payload) {
    			state.token = payload
    			localStorage.setItem('token', payload)
    		},
            
            // 退出登錄
    		LOG_OUT() {
    			localStorage.removeItem('token')
    			localStorage.removeItem('user_info')
    			localStorage.removeItem('menu_list')
    			// 刷新頁面-因為路由權(quán)限的存在會導(dǎo)航到login,并且通過這個刷新可以避免重復(fù)添加路由
    			window.location.reload()
    		}
    	},
    	actions: {
    		async login({ commit }, payload) {
                // 登錄請求-獲取token
    			const loginResp = await loginApi.reqLogin(payload)
    			if (loginResp?.errorCode !== 0) return
    			commit('SET_USER_INFO', loginResp.data.userInfo)
    			commit('SET_TOKEN', loginResp.data.token)
    
    			// 請求菜單列表
    			const menuListResp = await menuApi.reqGetMenuList()
    			localStorage.setItem('menu_list', JSON.stringify(menuListResp.data))
    			commit('SET_MENU_LIST', menuListResp.data)
    
    			// 跳轉(zhuǎn)至首頁
    			router.push('/home')
    		}
    	}
    }
    
    
  4. 這部分代碼還是非常簡單的,在入口文件main.js 引用 store 和在登錄界面收集表單數(shù)據(jù)提交調(diào)用這個 login 方法登錄,大家就自己實現(xiàn)一下吧

  5. 現(xiàn)在我們獲取到這個數(shù)據(jù)之后,表示我們可以完成兩件事情,第一就是渲染側(cè)邊的菜單列表,第二就是根據(jù)這個來添加正確的動態(tài)路由

  6. 渲染菜單列表沒有什么好說的,如果沒有菜單欄的遞歸需求的話,菜單欄直接 cv 組件庫的代碼即可,需要遞歸的話就要自己封裝一下了

添加動態(tài)路由

  1. 要添加動態(tài)路由,需要有兩個數(shù)據(jù),一個是遠程獲取的菜單數(shù)據(jù),一個是前端的映射的組件關(guān)系。遠程數(shù)據(jù)已經(jīng)有了,前端映射的組件關(guān)系,就看你自己的業(yè)務(wù)來配置了,還是非常簡單的,把你前端需要展示的頁面都在一個 js 文件引入就好了,如下:

    export default [
    	{
    		name: 'home',
    		component: () => import('@/views/home')
    	},
    	{
    		name: 'my',
    		component: () => import('@/views/my')
    	},
    	{
    		name: 'device-add',
    		component: () => import('@/views/device/add')
    	},
    	{
    		name: 'device-list',
    		component: () => import('@/views/device/list')
    	},
    	{
    		name: 'user-add',
    		component: () => import('@/views/user/add')
    	},
    	{
    		name: 'user-list',
    		component: () => import('@/views/user/list')
    	}
    ]
    
  2. 具體需要多少配置項,就視個人業(yè)務(wù)而定,我這里使用 name 匹配,你也可以是 path 或者其他屬性

  3. 在看一下遠程的數(shù)據(jù)具體是什么樣的,有助于理解,如下:

    [
        {
            "id": 1,
            "name": "home",
            "path": "/home",
            "nickname": "首頁",
            "type": 2,
            "order": 1,
            "parentId": 0,
            "icon": "icon-tubiao_shouye-",
            "children": null
        },
        {
            "id": 2,
            "name": "device",
            "path": "/device",
            "nickname": "設(shè)備管理",
            "type": 1,
            "order": 2,
            "parentId": 0,
            "icon": "icon-guanli",
            "children": [
                {
                    "id": 3,
                    "name": "device-list",
                    "path": "/device/list",
                    "nickname": "設(shè)備列表",
                    "type": 2,
                    "order": 1,
                    "parentId": 2,
                    "icon": "icon-xuanzeweixuanze",
                    "children": null
                },
                {
                    "id": 4,
                    "name": "device-add",
                    "path": "/device/add",
                    "nickname": "設(shè)備添加",
                    "type": 2,
                    "order": 2,
                    "parentId": 2,
                    "icon": "icon-xuanzeweixuanze",
                    "children": null
                }
            ]
        },
        {
            "id": 5,
            "name": "my",
            "path": "/my",
            "nickname": "個人中心",
            "type": 2,
            "order": 3,
            "parentId": 0,
            "icon": "icon-xiazai",
            "children": null
        },
        {
            "id": 6,
            "name": "user",
            "path": "/user",
            "nickname": "用戶管理",
            "type": 1,
            "order": 4,
            "parentId": 0,
            "icon": "icon-yonghuguanli",
            "children": [
                {
                    "id": 7,
                    "name": "user-list",
                    "path": "/user-list",
                    "nickname": "用戶列表",
                    "type": 2,
                    "order": 1,
                    "parentId": 6,
                    "icon": "icon-xuanzeweixuanze",
                    "children": null
                },
                {
                    "id": 8,
                    "name": "user-add",
                    "path": "/user-add",
                    "nickname": "用戶添加",
                    "type": 2,
                    "order": 2,
                    "parentId": 6,
                    "icon": "icon-xuanzeweixuanze",
                    "children": null
                }
            ]
        }
    ]
    
  4. 剩下的就是遞歸遍歷的找出組裝出對應(yīng)的 route 配置的事情了,那么我們需要有這樣的一個函數(shù),來幫助我們完成這件事情,代碼如下:

    import router from '@/router'
    
    // 前端映射的組件關(guān)系配置
    import routeConfig from '@/router/route-config'
    
    export default function (menuList) {
    	const routeList = []
    	const deepMenu = menuList => {
    		for (const menu of menuList) {
    			if (menu.children && menu.children.length > 0) {
    				deepMenu(menu.children)
    			} else {
    				const item = routeConfig.find(item => item.name === menu.name)
    				if (!item) return
    				// 去掉第一項斜杠-子路由 path 屬性不需要攜帶開頭的 /
    				const path = menu.path.replace(/^\//, '')
                    // 路由元信息可以幫助我們完成一些其他操作的時候,需要的一些輔助數(shù)據(jù)
    				routeList.push({ ...item, path, meta: { title: menu.nickname } })
    			}
    		}
    	}
    	deepMenu(menuList)
    
    	for (const route of routeList) {
            // 遍歷添加路由
    		router.addRoute('layout', route)
    	}
    }
    
  5. 有了這個方法之后,自然就是使用,如下:

    import { loginApi, menuApi } from '@/api'
    import router from '@/router'
    import menuToRoute from '@/utils/menu-to-route'
    
    export default {
    	namespaced: true,
    	state: {
    		userInfo: {} || localStorage.getItem('user_info'),
    		token: '' || localStorage.getItem('token'),
    		menuList: [] || localStorage.getItem('menu_list')
    	},
    	mutations: {
    		SET_MENU_LIST(state, payload) {
    			state.menuList = payload
                // 調(diào)用菜單轉(zhuǎn)路由方法
    			menuToRoute(payload)
    		},
    
    		SET_USER_INFO(state, payload) {
    			state.userInfo = payload
    			localStorage.setItem('user_info', JSON.stringify(payload))
    		},
    
    		SET_TOKEN(state, payload) {
    			state.token = payload
    			localStorage.setItem('token', payload)
    		},
    		
            // 退出登錄
    		LOG_OUT() {
    			localStorage.removeItem('token')
    			localStorage.removeItem('user_info')
    			localStorage.removeItem('menu_list')
    			// 刷新頁面-因為路由權(quán)限的存在會導(dǎo)航到login,并且通過這個刷新可以避免重復(fù)添加路由
    			window.location.reload()
    		}
    	},
    	actions: {
    		async login({ commit }, payload) {
    			const loginResp = await loginApi.reqLogin(payload)
    			if (loginResp?.errorCode !== 0) return
    			commit('SET_USER_INFO', loginResp.data.userInfo)
    			commit('SET_TOKEN', loginResp.data.token)
    
    			// 請求菜單列表
    			const menuListResp = await menuApi.reqGetMenuList()
    			localStorage.setItem('menu_list', JSON.stringify(menuListResp.data))
    			commit('SET_MENU_LIST', menuListResp.data)
    
    			// 跳轉(zhuǎn)至首頁
    			router.push('/home')
    		}
    	}
    }
    
  6. 此時我們已經(jīng)完成了整個效果的實現(xiàn),當(dāng)然還有一個問題,但是這個問題后面再說,先看一下效果,如圖:

    Vue-根據(jù)角色獲取菜單動態(tài)添加路由,vue.js,前端,javascript

    Vue-根據(jù)角色獲取菜單動態(tài)添加路由,vue.js,前端,javascript

  7. 可以看到,不同的賬戶登錄會因為角色不同展現(xiàn)的菜單也不同

修復(fù)刷新路由丟失問題

  1. 現(xiàn)在我們這個看著沒什么問題,是因為我們沒有點擊刷新,先看看問題,如圖:

    Vue-根據(jù)角色獲取菜單動態(tài)添加路由,vue.js,前端,javascript

  2. 一旦刷新之后就會導(dǎo)致動態(tài)路由清空,但是又沒有重新注冊添加,自然就會找不到這個路由了,因此白屏就很正常了

  3. 解決也非常簡單,在每次刷新的時候,都在重新注冊一次動態(tài)路由就好了,所以在 store 的 login 模塊多添加一個方法,如下:

    import { loginApi, menuApi } from '@/api'
    import router from '@/router'
    import menuToRoute from '@/utils/menu-to-route'
    
    export default {
    	namespaced: true,
    	state: {
    		userInfo: {} || localStorage.getItem('user_info'),
    		token: '' || localStorage.getItem('token'),
    		menuList: [] || localStorage.getItem('menu_list')
    	},
    	mutations: {
    		SET_MENU_LIST(state, payload) {
    			state.menuList = payload
    			menuToRoute(payload)
    		},
    
    		SET_USER_INFO(state, payload) {
    			state.userInfo = payload
    			localStorage.setItem('user_info', JSON.stringify(payload))
    		},
    
    		SET_TOKEN(state, payload) {
    			state.token = payload
    			localStorage.setItem('token', payload)
    		},
    
    		LOG_OUT() {
    			localStorage.removeItem('token')
    			localStorage.removeItem('user_info')
    			localStorage.removeItem('menu_list')
    			window.location.reload()
    		}
    	},
    	actions: {
    		async login({ commit }, payload) {
    			const loginResp = await loginApi.reqLogin(payload)
    			if (loginResp?.errorCode !== 0) return
    			commit('SET_USER_INFO', loginResp.data.userInfo)
    			commit('SET_TOKEN', loginResp.data.token)
    
    			const menuListResp = await menuApi.reqGetMenuList()
    			localStorage.setItem('menu_list', JSON.stringify(menuListResp.data))
    			commit('SET_MENU_LIST', menuListResp.data)
    
    			router.push('/home')
    		},
    
    		// 加載本地數(shù)據(jù)
    		async loadLocal({ commit }) {
    			const menuList =  localStorage.getItem('menu_list')
    			if (menuList) {
    				commit('SET_MENU_LIST', JSON.parse(menuList))
    			}
    		}
    	}
    }
    
  4. loadLocal 這個方法還可以初始化一下其他你需要初始化的信息,包括但不限于這個菜單列表,其他是導(dǎo)出這個方法,讓其他人使用,可以直接從這個模塊使用,也可以其他地方導(dǎo)出,我這里就在 store/index.js 文件下導(dǎo)出,如下:

    import Vue from 'vue'
    import Vuex from 'vuex'
    Vue.use(Vuex)
    
    import login from './login'
    
    const store = new Vuex.Store({
    	modules: { login }
    })
    
    // 導(dǎo)出方法
    export function loadLocal() {
    	store.dispatch('login/loadLocal')
    }
    
    export default store
    
  5. 最后在 main.js 中調(diào)用此方法即可,導(dǎo)入和使用語句如下:

    import { loadLocal } from './store'
    
    loadLocal()
    
  6. 現(xiàn)在我們在來看看效果,如圖:

    Vue-根據(jù)角色獲取菜單動態(tài)添加路由,vue.js,前端,javascript

結(jié)語

這里只是給大家展示一種思路,具體的實現(xiàn)需要根據(jù)自己的業(yè)務(wù)來定,但是整體的流程都是差不多的

如果對于這個遞歸菜單,和后端部分這個實現(xiàn)登錄邏輯部分,可以查看我后續(xù)發(fā)布的其他文章,或者如果我沒忘記的話,我會來這里補上查看鏈接文章來源地址http://www.zghlxwxcb.cn/news/detail-819728.html

到了這里,關(guān)于Vue-根據(jù)角色獲取菜單動態(tài)添加路由的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包