欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時間:2024年01月15日 11:22:28   作者:劫辭  
這篇文章主要介紹了Vue如何根據(jù)角色獲取菜單動態(tài)添加路由,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧

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

前提提要

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

需求分析

  • 在實(shí)現(xiàn)我們這個需求,不難想到主要就是完成登錄,通過登錄獲取到正確的菜單列表,通過菜單列表進(jìn)行渲染
  • 但是完成這個步驟的話,我們還需要捋一下頁面的關(guān)系,按照我們的開發(fā)時態(tài)來說,我們啟動一個項(xiàng)目之后,會通過 http://localhost:8080/ 這樣的一個地址在瀏覽中打開
  • 打開這個地址之后,觸發(fā)的是什么路徑,是不是 /,表示根路徑,在后臺管理系統(tǒng)中,一般這個跟路徑我們會映射到什么組件上,是不是 layout 組件,比如這樣的,如圖:

  • 但是這樣的話就和我們的需求有點(diǎn)不一樣了,我們要先登錄啊,都沒登錄怎么能打開這個呢?所以一把來說,我們一般會要么把 ‘/’ 的路徑觸發(fā)時,重定向到 ‘/login’,或者在全局路由前置守衛(wèi)中,通過登錄的狀態(tài)來決定是不是跳轉(zhuǎn)到登錄頁,一般我們使用第二種,因?yàn)楹笈_管理系統(tǒng)中,一定會有路由權(quán)限的判斷,到時候一樣會來改動這個,所以選擇后者,至于實(shí)現(xiàn)部分,我們后面再看
  • 完成了上述的操作之后,就是登錄了,登錄之后獲取菜單列表數(shù)據(jù),拿到之后我們就直接注冊嗎?
  • 我們知道,這種菜單,往往會有一級、二級、三級等等不同級別的菜單,而是不是每個菜單都需要注冊的呢,其實(shí)不然,我們需要注冊的僅僅是需要展示的那一部分菜單,比如在我們的案例中,設(shè)備管理是一個一級菜單,但是存在子級菜單,那么此時這個設(shè)備管理菜單就是不需要注冊的,如圖:

  • 所以這一點(diǎn)我們也需要做一下區(qū)分,但是具體注冊那些呢?這些就還是要在前端先配置好,但是這個配置不會是直接配置到 route 中,是一個映射關(guān)系,比如定義了 a = 組件A,然后依次書寫,把所有會展示的頁面通過這樣的方式,用一個文件存儲起來,那么通過后端返回的菜單列表數(shù)據(jù)時,就可以進(jìn)行一個對比,篩選,取出符合條件的數(shù)據(jù),組裝成一個適配業(yè)務(wù)的 route 進(jìn)行注冊
  • 而通過這樣的匹配,我們最后是可以得到一個數(shù)組的,[route1, route2, …],得到這個數(shù)組之后,使用 vueRouter的 addRoute 方法添加即可
  • 這里需要注意的事情是,我所演示的案例中,所有的子組件都是在 main 區(qū)域顯示的,所以我就不需要在去單獨(dú)的關(guān)心這些子組件的層級關(guān)系了,但是如果某個項(xiàng)目中的,層級關(guān)系如圖:

  • 像這種或者更多層級的,就需要額外處理一下 children 屬性了,但是方法都是差不的,無非就是數(shù)據(jù)處理的時候多處理一下,而且一般來說就是兩層,最外層第一個 router-view 來展示一級路由(比如登錄、404、layout),main 區(qū)域的 router-view 展示二級路由(比如 home、my、user…)
  • 經(jīng)過這個分析之后,我們就是能確定,我們要做的事情就是,把這些獲取的菜單數(shù)據(jù),來找到對應(yīng)的組件,并把這些組件添加為 layout 組件的子組件,在 main 區(qū)域展示

具體實(shí)現(xiàn)

配置靜態(tài)路由

根據(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

這就是一個最基礎(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

添加兩個靜態(tài)路由非常簡單吧,然后把這個在 main js 頁面引入使用,我就不展示了

路由權(quán)限判斷

  • 上面的配置如果我們直接在瀏覽器中打開 http://localhost:8080/ 這個地址,那么展示的就是 layout 組件,如果需要展示位 login 組件的話,我們就需要在全局前置路由守衛(wèi)上動一下手腳了
  • 也非常簡單,一個用戶登沒登錄,就是判斷是否是存在了 token,如果有就是登錄了,如果沒有就是沒有登錄,根據(jù)這個,我們可以得出一張關(guān)系圖,如圖:

這只是一個簡單的路由權(quán)限判斷,具體的還需要根據(jù)業(yè)務(wù)來擴(kuò)展,根據(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

登錄

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

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

至于 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')
			// 刷新頁面-因?yàn)槁酚蓹?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')
		}
	}
}

這部分代碼還是非常簡單的,在入口文件main.js 引用 store 和在登錄界面收集表單數(shù)據(jù)提交調(diào)用這個 login 方法登錄,大家就自己實(shí)現(xiàn)一下吧

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

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

添加動態(tài)路由

要添加動態(tài)路由,需要有兩個數(shù)據(jù),一個是遠(yuǎn)程獲取的菜單數(shù)據(jù),一個是前端的映射的組件關(guān)系。遠(yuǎ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')
	}
]

具體需要多少配置項(xiàng),就視個人業(yè)務(wù)而定,我這里使用 name 匹配,你也可以是 path 或者其他屬性

在看一下遠(yuǎn)程的數(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
            }
        ]
    }
]

剩下的就是遞歸遍歷的找出組裝出對應(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
				// 去掉第一項(xiàng)斜杠-子路由 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)
	}
}

有了這個方法之后,自然就是使用,如下:

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')
			// 刷新頁面-因?yàn)槁酚蓹?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')
		}
	}
}

此時我們已經(jīng)完成了整個效果的實(shí)現(xiàn),當(dāng)然還有一個問題,但是這個問題后面再說,先看一下效果,如圖:

可以看到,不同的賬戶登錄會因?yàn)榻巧煌宫F(xiàn)的菜單也不同

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

現(xiàn)在我們這個看著沒什么問題,是因?yàn)槲覀儧]有點(diǎn)擊刷新,先看看問題,如圖:

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

解決也非常簡單,在每次刷新的時候,都在重新注冊一次動態(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')
		},
		// 加載本地?cái)?shù)據(jù)
		async loadLocal({ commit }) {
			const menuList =  localStorage.getItem('menu_list')
			if (menuList) {
				commit('SET_MENU_LIST', JSON.parse(menuList))
			}
		}
	}
}

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

最后在 main.js 中調(diào)用此方法即可,導(dǎo)入和使用語句如下:

import { loadLocal } from './store'
loadLocal()

現(xiàn)在我們在來看看效果,如圖:

結(jié)語

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

如果對于這個遞歸菜單,和后端部分這個實(shí)現(xiàn)登錄邏輯部分,可以查看我后續(xù)發(fā)布的其他文章,或者如果我沒忘記的話,我會來這里補(bǔ)上查看鏈接

到此這篇關(guān)于Vue如何根據(jù)角色獲取菜單動態(tài)添加路由的文章就介紹到這了,更多相關(guān)Vue根據(jù)角色添加路由內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vue3?邏輯復(fù)用的實(shí)現(xiàn)示例

    vue3?邏輯復(fù)用的實(shí)現(xiàn)示例

    在項(xiàng)目開發(fā)中,有兩個功能特別類似,如果單獨(dú)實(shí)現(xiàn),會有很多重復(fù)的代碼,這時候就會用到邏輯復(fù)用,本文主要介紹了vue3?邏輯復(fù)用的實(shí)現(xiàn)示例,具有一定的參考價值,感興趣的可以了解一下
    2024-02-02
  • vue實(shí)現(xiàn)修改圖片后實(shí)時更新

    vue實(shí)現(xiàn)修改圖片后實(shí)時更新

    今天小編就為大家分享一篇vue實(shí)現(xiàn)修改圖片后實(shí)時更新,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-11-11
  • uniapp和vue如何獲取屏幕或盒子內(nèi)容的寬高

    uniapp和vue如何獲取屏幕或盒子內(nèi)容的寬高

    在實(shí)際開發(fā)中我們會遇到不確定高度的情況,下面這篇文章主要給大家介紹了關(guān)于uniapp和vue如何獲取屏幕或盒子內(nèi)容的寬高,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-02-02
  • Vue獲取圖片MD5的方法詳解

    Vue獲取圖片MD5的方法詳解

    計(jì)算圖片的MD5可以將其作為圖片的唯一標(biāo)識,從而優(yōu)化對圖片的存儲和檢索效率,本文主要介紹了Vue獲取圖片MD5的常用方法,感興趣的可以了解下
    2024-12-12
  • vue 項(xiàng)目打包通過命令修改 vue-router 模式 修改 API 接口前綴

    vue 項(xiàng)目打包通過命令修改 vue-router 模式 修改 API 接口前綴

    這篇文章主要介紹了vue 項(xiàng)目打包通過命令修改 vue-router 模式 修改 API 接口前綴的相關(guān)知識,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友參考下吧
    2018-06-06
  • Vue發(fā)送ajax請求方法介紹

    Vue發(fā)送ajax請求方法介紹

    這篇文章介紹了Vue發(fā)送ajax請求的方法,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-01-01
  • Vue手寫橫向輪播圖的實(shí)例

    Vue手寫橫向輪播圖的實(shí)例

    這篇文章主要介紹了Vue手寫橫向輪播圖的實(shí)例,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • Vue3實(shí)現(xiàn)下拉選擇框多選功能的方法詳解

    Vue3實(shí)現(xiàn)下拉選擇框多選功能的方法詳解

    在vue的實(shí)際開發(fā)過程中,我們?nèi)绾螌⒁赃x中的值直接渲染到頁面中,下面這篇文章主要給大家介紹了關(guān)于Vue3實(shí)現(xiàn)下拉選擇框多選功能的相關(guān)資料,需要的朋友可以參考下
    2023-09-09
  • Vue的混合繼承詳解

    Vue的混合繼承詳解

    這篇文章主要介紹了Vue的混合繼承,有需要的朋友可以借鑒參考下,希望能夠有所幫助,希望能夠給你帶來幫助
    2021-11-11
  • vue 基于element-ui 分頁組件封裝的實(shí)例代碼

    vue 基于element-ui 分頁組件封裝的實(shí)例代碼

    這篇文章主要介紹了vue 基于element-ui 分頁組件封裝的實(shí)例代碼,代碼簡單易懂,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2018-12-12

最新評論