vue如何根據(jù)權(quán)限生成動(dòng)態(tài)路由、導(dǎo)航欄
基本思路
1、創(chuàng)建vueRouter,用公共路由實(shí)例化
2、創(chuàng)建需要根據(jù)權(quán)限篩選的路由對象(在路由對象,添加必要的權(quán)限判斷字段)
3、登錄完成,由后端配合返回當(dāng)前用戶的權(quán)限集合
4、篩選出有權(quán)限的路由對象,利用vueRouter的addRoutes方法,生成完整路由
5、處理刷新頁面導(dǎo)致vueRouter重新實(shí)例化導(dǎo)致路由對象不完善 (利用router.beforeEach導(dǎo)航守衛(wèi),,利用addRoutes()完善 路由對象 )
6、側(cè)邊導(dǎo)航欄相關(guān)代碼
相關(guān)代碼
根據(jù)上面的順序
1、如下
Vue.use(Router) // 公共路由 export const publicRoutes = [ { path: '/', name: 'login', component: login, meta: { title: '登錄' } } ] // 需要根據(jù)權(quán)限篩選的路由 export const asyncRoutes = [ ...Home, ...Seting, ...CRM, { path: '*', component: login, meta: { hidden: true }, redirect: '/' } ] const vr = new Router({ mode: 'history', routes: publicRoutes })
2、以seting模塊為例,role為判斷是的權(quán)限字段,角色有這個(gè)字段對應(yīng)的值就有當(dāng)前頁面的權(quán)限
import Layout from '@/views/layout/layout.vue' const account = r => require.ensure([], () => r(require('@/views/seting/account/account.vue')), 'seting'); const logs = r => require.ensure([], () => r(require('@/views/seting/logs/logs.vue')), 'seting'); const role = r => require.ensure([], () => r(require('@/views/seting/role/role.vue')), 'seting'); const Seting = [ { path: '/seting', component: Layout, redirect: '/seting/account', meta: { title: '系統(tǒng)設(shè)置', role: '123c6c6514d416472e640bc3f49297c550', icon: 'icon-xitong' }, children: [ { path: 'account', name: 'account', component: account, meta: { title: '賬號管理', role: '1325cdeb897cc7f8e951d647de9b1d8e11', } }, { path: 'logs', name: 'logs', component: logs, meta: { title: '日志管理', role: '14bfbb0337ad3e7e2c9fc101294c3fe645', } }, { path: 'role', name: 'role', component: role, meta: { title: '角色管理', role: '1559d1c05d15a0dce5549b8bf5a58c0cf9', } } ] } ] export default Seting
如果有一些詳情頁不需要在導(dǎo)航列表展示還可以添加字段,在生成導(dǎo)航欄時(shí)去掉
eg:
{ path: 'addJoiner', name: 'addJoiner', component: addJoiner, meta: { hidden: true, // 隱藏字段 title: '***詳情頁', role: '14bfbb0337ad3e7e2c9fc101294c3fe645', } },
3、登錄獲取權(quán)限集合和基本信息
// 登錄 login () { this.rememberPassword() this.$refs.form.validate((valid) => { if (valid) { this.loading = true let params = { login_name: this.form.user, password: this.form.password, code: this.form.yanzhenma, } this.$api.post('api/user/login', params).then(res => { if (res.err_code === 1) { // 把用戶的基本信息儲(chǔ)存在vuex,中 this.$store.dispatch('setBaseInfo', res.data).then(() => { // 獲取有權(quán)限的路由表,添加到路由 router.addRoutes(this.$store.getters.addRouters) this.$router.push({ name: 'home' }) }) } this.loading = false }) } })
部分vuex代碼
如果不太理解,點(diǎn)擊下面鏈接:
actions.js
// import api from '@/api' const actions = { setBaseInfo ({ commit }, data) { return new Promise(resolve => { commit('set_userInfo', data.userInfo) commit('set_token', data.token) commit('set_roles', data.menus) // 把基本信息保存在本地防止刷新之后丟失 sessionStorage.setItem('baseInfo', JSON.stringify(data)) resolve() }) } } export default actions
mutations.js
const setStorage = (key, value) => { if (typeof (value) === 'object') { value = JSON.stringify(value) } sessionStorage.setItem(key, value) } /* * 避免刷新之后vuex被重置,在sessionStorage做一個(gè)備份 */ const mutations = { set_userInfo (state, payload) { state.userInfo = payload setStorage('userInfo', payload) }, set_token (state, payload) { state.token = payload setStorage('token', payload) }, set_roles (state, payload) { state.roles = payload setStorage('roles', payload) }, set_breadcrumb (state, payload) { state.breadcrumb = payload setStorage('breadcrumb', payload)/* */ }, changeCollapsed (state, payload) { state.isCollapsed = payload } } export default mutations
getters.js
import createdRoutes from '@/utils/createdRoutes.js' import { asyncRoutes } from '@/router/index.js' let getStoryage = (item) => { let str = sessionStorage.getItem(item) return JSON.parse(str) } const getters = { get_userInfo: (state) => { return state.userInfo ? state.userInfo : getStoryage('userInfo') }, get_token: (state) => { return state.token ? state.token : sessionStorage.getItem('token') }, get_roles: (state) => { return state.roles.length ? state.roles : getStoryage('roles') }, addRouters: (state, getters) => { let routes = createdRoutes(asyncRoutes, getters.get_roles) return routes }, get_breadcrumb: (state, getters) => { return state.breadcrumb.length ? state.breadcrumb : getStoryage('getStoryage') } } export default getters;
4、核心的篩選需要權(quán)限的路由方法:createdRoutes()
也就是3的getters,用到的方法,毫無保留奉上
/** * 判單當(dāng)前的路由對象是否在登錄人的權(quán)限之內(nèi) * @param {Array} roles 權(quán)限 * @param {Object} route 路由 */ function hasPermission (roles, route) { if (route.meta && route.meta.role) { // 路由需要權(quán)限就要在權(quán)限數(shù)組里面判斷 return roles.includes(route.meta.role) } else { // 不需要權(quán)限就直接通過 return true } } /** * 根據(jù)接口獲取的權(quán)限列表動(dòng)態(tài)生成當(dāng)前用戶的側(cè)邊導(dǎo)航欄,返回通過權(quán)限驗(yàn)證的路由數(shù)組 * @param {Array} asyncRoutes 需要過濾的路由 * @param {Array} roles 權(quán)限 */ function createdRoutes (asyncRoutes, roles) { const accessedRouters = asyncRoutes.filter(route => { if (hasPermission(roles, route)) { // 當(dāng)前路由通過權(quán)限驗(yàn)證直接通過 if (route.children && route.children.length) { // 當(dāng)前路由有子路由,就遞歸驗(yàn)證 route.children = createdRoutes(route.children, roles) } return true } return false }) return accessedRouters } export default createdRoutes
5、處理刷新帶來的問題
其實(shí)這里的代碼是連接1的,注意注釋
// 全局的導(dǎo)航守衛(wèi) vr.beforeEach((to, from, next) => { // 刷新頁面之后導(dǎo)致vue-router和vuex重置,路由丟失,利用的就是刷新后vuex的state被重置判斷 if (to.name !== 'login' && !store.state.token) { // 避免直接不登陸進(jìn)頁面 if (!sessionStorage.getItem('token')) { location.href = '/' return } let data = JSON.parse(sessionStorage.getItem('baseInfo')) store.dispatch('setBaseInfo', data).then(() => { vr.addRoutes(store.getters.addRouters) }) } // 設(shè)置面包屑導(dǎo)航 let breadcrumb = to.matched.filter(item => item.meta.title) if (breadcrumb.length) { breadcrumb = breadcrumb.map(item => item.meta.title) store.commit('set_breadcrumb', breadcrumb) } // 設(shè)置title document.title = to.meta.title next() })
6、側(cè)邊導(dǎo)航欄的完整代碼
還是遍歷的根據(jù)權(quán)限生成的路由表,干掉一些需要隱藏的詳情頁之類,
這里的代碼和過濾l路由的核心函數(shù),要根據(jù)自己的業(yè)務(wù)做相應(yīng)的處理
element-ui的menu
<template> <el-menu class="el-menu-vertical-demo" :collapse="isCollapsed" background-color="#545c64" :default-active='activeIndex' text-color="#fff" active-text-color="#7EA8F5"> <section v-for="(item,index) in addRouters" :key="item.name" :class="isCollapsed ? 'collapsed':''"> <!-- 有子菜單 --> <el-submenu :index=" `${index+1}`" v-if="!item.meta.hidden && item.children && item.children.length"> <template slot="title"> <i :class="`icon iconfont ${item.meta.icon}`"></i> <span slot="title">{{item.meta.title}}</span> </template> <section v-for="(item2,index2) in item.children" :key="item2.name"> <!-- 二級菜單有子菜單 --> <el-submenu :index="`${index+1}-${index2+1}`" v-if="item2.children && item2.children.length" class="sub2"> <template slot="title"> <span slot="title">{{item2.meta.title}}</span> </template> <!-- 三級菜單 --> <el-menu-item v-for="(item3,index3) in item2.children" v-if="!item3.meta.hidden" :index="item3.name" :key="index3" @click.native="$router.push({name:item3.name})"> <span slot="title">{{item3.meta.title}}</span> </el-menu-item> </el-submenu> <!-- 二級菜單無子菜單 --> <!-- 不是隱藏的,詳情頁隱藏 --> <el-menu-item :index="item2.name" v-else-if="!item2.meta.hidden" @click.native="$router.push({name:item2.name})"> <span slot="title">{{item2.meta.title}}</span> </el-menu-item> </section> </el-submenu> <!-- 無子菜單 --> <el-menu-item v-else-if="item.meta.hidden && item.children && item.children.length" :index="item.children[0].name" @click.native="$router.push({name:item.children[0].name})" class="item"> <i :class="`iconfont ${item.children[0].meta.icon}`"></i> <span slot="title">{{item.children[0].meta.title}}</span> </el-menu-item> </section> </el-menu> </template>
<script> import { mapGetters } from 'vuex' export default { props: { isCollapsed: { type: Boolean, default: false } }, computed: { ...mapGetters(['addRouters']), activeIndex () { //集火的菜單 return this.$route.name } } } </script>
<style lang="scss" scoped> section { /deep/ .el-submenu__title { .icon { margin-right: 10px; } i { color: white; font-size: 14px; } } /deep/ .el-menu-item { padding-left: 50px !important; } /deep/ .el-menu-item.item { padding-left: 19px !important; i { color: white; font-size: 14px; margin-right: 12px; } } /deep/ .el-submenu .el-menu-item { min-width: 0; } /deep/ .el-submenu.sub2 .el-submenu__title { padding-left: 50px !important; i { margin-right: 0px; } } /* /deep/ .el-submenu.sub2 .el-menu-item { text-indent: 12px; } */ } .collapsed { width: 50px; /deep/ .el-submenu__title { .el-icon-arrow-right { display: none; } span[slot="title"] { display: none; } } } </style>
路由圖
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- 解決Vue路由導(dǎo)航報(bào)錯(cuò):NavigationDuplicated:?Avoided?redundant?navigation?to?current?location
- vue3容器布局和導(dǎo)航路由實(shí)現(xiàn)示例
- ant design vue導(dǎo)航菜單與路由配置操作
- 在vue中實(shí)現(xiàn)某一些路由頁面隱藏導(dǎo)航欄的功能操作
- vue 導(dǎo)航菜單刷新狀態(tài)不消失,顯示對應(yīng)的路由界面操作
- Vue 解決父組件跳轉(zhuǎn)子路由后當(dāng)前導(dǎo)航active樣式消失問題
- Vue 3 中使用 vue-router 進(jìn)行導(dǎo)航與監(jiān)聽路由變化的操作
相關(guān)文章
vue獲取路由詳細(xì)內(nèi)容信息方法實(shí)例
獲取路由詳細(xì)內(nèi)容信息是我們?nèi)粘i_發(fā)中經(jīng)常會(huì)遇到的需求,下面這篇文章主要給大家介紹了關(guān)于vue獲取路由詳細(xì)內(nèi)容信息的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-12-12Pycharm中開發(fā)vue?element項(xiàng)目時(shí)eslint的安裝和使用步驟
這篇文章主要介紹了Pycharm中開發(fā)vue?element項(xiàng)目時(shí)eslint的安裝和使用,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-05-05使用命令行工具npm新創(chuàng)建一個(gè)vue項(xiàng)目的方法
Vue.js 提供一個(gè)官方命令行工具,可用于快速搭建大型單頁應(yīng)用。下面小編給大家分享使用命令行工具npm新創(chuàng)建一個(gè)vue項(xiàng)目的方法,需要的朋友參考下吧2017-12-12Vue3 Ref獲取真實(shí)DOM學(xué)習(xí)實(shí)戰(zhàn)
這篇文章主要為大家介紹了Vue3 Ref獲取真實(shí)DOM學(xué)習(xí)實(shí)戰(zhàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06Element中el-table動(dòng)態(tài)合并單元格(span-method方法)
本文主要介紹了Element中el-table動(dòng)態(tài)合并單元格(span-method方法),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05vue-cli3使用mock數(shù)據(jù)的方法分析
這篇文章主要介紹了vue-cli3使用mock數(shù)據(jù)的方法,結(jié)合實(shí)例形式分析了vue-cli3使用mock數(shù)據(jù)的相關(guān)實(shí)現(xiàn)方法與操作注意事項(xiàng),需要的朋友可以參考下2020-03-03