vue-router 基於後端permissions動態生成導航菜單的示例代碼
Vue.js
- vue-router
- vuex
1、註冊全局守衛
核心邏輯
1、token身份驗證(後端) => token失效返回登錄頁面
2、獲取用戶權限
3、校驗permissions,動態添加路由菜單
router.beforeResolve 註冊一個全局守衛。和 router.beforeEach 類似,區別是在導航被確認之前,同時在所有組件內守衛和異步路由組件被解析之後,解析守衛就被調用。
router.beforeResolve(async (to, from, next) => { let hasToken = store.getters['User/accessToken'] if (!settings.loginInterception) hasToken = true if (hasToken) { if (to.path === '/auth/sign-in') { next({ path: '/' }) } else { const hasPermissions = store.getters['User/permissions'] && store.getters['User/permissions'].length > 0 if (hasPermissions) { next() } else { try { let permissions if (!constant.loginInterception) { // settings.js loginInterception為false時,創建虛擬權限 await store.dispatch('User/setPermissions', ['admin']) permissions = ['admin'] } else { permissions = await store.dispatch('User/getUserInfo') } let accessRoutes = [] accessRoutes = await store.dispatch('Routes/setRoutes', permissions) // 添加路由 router.addRoutes(accessRoutes) next({ ...to, replace: true }) } catch { await store.dispatch('User/resetAccessToken') } } } } else { if (settings.routesWhiteList.indexOf(to.path) !== -1) { next() } else { next('/auth/sign-in') } } document.title = getPageTitle(to.meta.title) })
settings.js 全局設置
export default { // 是否開啟登錄攔截 loginInterception: true, // 不經過token校驗的路由 routesWhiteList: ['/auth/sign-in', '/auth/register', '/401', '/404'], }
2、Vuex狀態管理 全局緩存routes
- state :對數據的全局存儲
- getter: 可以理解為computed ,對數據進行計算
- mutations :對數據的同步更改
- actions:對數據的異步更改(實現異步操作)
- module: 將 store 分割成模塊
/** * @author Alan * @description 路由攔截狀態管理 */ import { asyncRoutes, constantRoutes } from '@/router' import { filterAsyncRoutes } from '@/Utils/handleRoutes' const state = () => ({ routes: [], partialRoutes: [] }) const getters = { routes: (state) => state.routes, partialRoutes: (state) => state.partialRoutes } const mutations = { setRoutes (state, routes) { state.routes = constantRoutes.concat(routes) }, setPartialRoutes (state, routes) { state.partialRoutes = constantRoutes.concat(routes) } } const actions = { async setRoutes ({ commit }, permissions) { const finallyAsyncRoutes = await filterAsyncRoutes( [...asyncRoutes], permissions ) commit('setRoutes', finallyAsyncRoutes) return finallyAsyncRoutes }, setPartialRoutes ({ commit }, accessRoutes) { commit('setPartialRoutes', accessRoutes) return accessRoutes } } export default { namespaced: true, state, getters, mutations, actions }
3、路由攔截
/** * @author Alan * @description 判斷當前路由是否包含權限 * @param permissions * @param route * @returns {boolean|*} */ export function hasPermission (permissions, route) { if (route.meta && route.meta.permissions) { return permissions.some((role) => route.meta.permissions.includes(role)) } else { return true } } /** * @author Alan * @description 根據permissions數組攔截路由 * @param routes * @param permissions * @returns {[]} */ export function filterAsyncRoutes (routes, permissions) { const finallyRoutes = [] routes.forEach((route) => { const item = { ...route } if (hasPermission(permissions, item)) { if (item.children) { item.children = filterAsyncRoutes(item.children, permissions) } finallyRoutes.push(item) } }) return finallyRoutes }
4、路由菜單
/* * @author Alan * @description 公共路由 */ export const constantRoutes = [ { path: '/auth', name: 'auth1', component: AuthLayout, children: authChildRoutes('auth1'), hidden: true // 隱藏菜單 }, { path: '/', name: 'dashboard', component: VerticleLayout, meta: { title: 'Dashboard', name: 'sidebar.dashboard', is_heading: false, is_active: false, link: '', class_name: '', is_icon_class: true, icon: 'ri-home-4-line', permissions: ['admin'] }, children: childRoutes('dashboard') } ] /* * @author Alan * @description 異步路由 */ export const asyncRoutes = [ { path: '/menu-design', name: 'horizontal-dashboard', component: HorizantalLayout, meta: { title: 'Menu Design', name: 'sidebar.MenuDesign', is_heading: false, is_active: false, link: '', class_name: '', is_icon_class: true, icon: 'ri-menu-3-line', permissions: ['admin'] }, children: horizontalRoute('dashboard') }, { path: '/core', name: 'core', component: VerticleLayout, meta: { title: 'UI Elements', name: 'sidebar.uiElements', is_heading: false, is_active: false, class_name: '', link: '', is_icon_class: true, icon: 'ri-pencil-ruler-line', permissions: ['admin'] }, children: coreChildRoute('core') } ]
5、遞歸菜單vue組件
<template> <b-collapse tag="ul" :class="className" :visible="open" :id="idName" :accordion="accordianName"> <li v-for="(item,index) in items" :key="index" :class=" !hideListMenuTitle? 'p-0' : item.meta.is_heading ? 'iq-menu-title' :activeLink(item) && item.children ? 'active' : activeLink(item) ? 'active' : ''"> <template v-if="!item.hidden"> <i v-if="item.meta.is_heading && hideListMenuTitle" class="ri-subtract-line" /> <span v-if="item.meta.is_heading && hideListMenuTitle">{{ $t(item.meta.name) }}</span> <router-link :to="item.meta.link" v-if="!item.is_heading" :class="`iq-waves-effect ${activeLink(item) && item.children ? 'active' : activeLink(item) ? 'active' : ''}`" v-b-toggle="item.meta.name"> <i :class="item.meta.icon" v-if="item.meta.is_icon_class"/> <template v-else v-html="item.meta.icon"> </template> <span>{{ $t(item.meta.name) }}</span> <i v-if="item.children" class="ri-arrow-right-s-line iq-arrow-right" /> <small v-html="item.meta.append" v-if="hideListMenuTitle" :class="item.meta.append_class" /> </router-link> <List v-if="item.children" :items="item.children" :sidebarGroupTitle="hideListMenuTitle" :open="item.meta.link.name !== '' && activeLink(item) && item.children ? true : !!(item.meta.link.name !== '' && activeLink(item))" :idName="item.meta.name" :accordianName="`sidebar-accordion-${item.meta.class_name}`" :className="`iq-submenu ${item.meta.class_name}`" /> </template> </li> </b-collapse> </template> <script> import List from './CollapseMenu' // 自身組件 import { core } from '../../../config/pluginInit' export default { name: 'List', props: { items: Array, className: { type: String, default: 'iq-menu' }, open: { type: Boolean, default: false }, idName: { type: String, default: 'sidebar' }, accordianName: { type: String, default: 'sidebar' }, sidebarGroupTitle: { type: Boolean, default: true } }, components: { List }, computed: { hideListMenuTitle () { return this.sidebarGroupTitle } }, mounted () { }, methods: { activeLink (item) { return core.getActiveLink(item, this.$route.name) } } } </script>
到此這篇關於vue-router 基於後端permissions動態生成導航菜單的示例代碼的文章就介紹到這瞭,更多相關vue-router permissions導航菜單內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!