vue router 根据路由区分用户权限并生成动态侧边栏

    技术2025-05-30  11

    背景

    往往在管理后台系统中,vue-router 要做更多的事情

    挂载组件到页面根据不同的用户角色,如:超级管理员,审计用户,普通用户等来做权限管理生成复杂的侧边栏

    那么,如何使用一套定义好的 router 来做到以上三个需求呢?

    分析

    首先,我们来细致的分析一下这三个需求有哪些具体需要

    挂载组件到页面中

    想必不用多说了,vue-router 本来就是做这个的

    区分不同用户角色

    用户角色是用户登录后后台返回的,比如这个字段是user_role,那么,

    首先,我们可以把这个字段首先存储在 vuex 中,这样可以全局访问其次,在 vue-router 的每一个路由对象中插入一个字段,比如叫permission来记录当前这个路由能够被谁访问根据user_role和permission来筛选所有的路由,筛选后的结果,就是当前用户能够访问的路由

    生成复杂的侧边栏

    侧边栏是管理后台都有的功能,这里参考 vue-element-admin 的实现。 本质上就是遍历 router,利用 elementui 的 el-menu 组件来生成就可以了。

    实现

    router,js 中实现了用户权限的区分,并抛出结果用于生成侧边栏 sidebar 则是生成侧边栏的逻辑

    // router.js import Vue from 'vue' import VueRouter from 'vue-router' import layout from '@/layout' Vue.use(VueRouter) // hidden: 是否显示在侧边栏 // name: 侧边栏对应的文字内容,当name不存在,则children中的路由为一级路由,否则为二级路由,侧边栏最多显示到二级路由 // iconName:侧边栏的 icon 图标 // permission:可以访问辞路由的用户列表 const router1 = [ { path: '/', redirect: '/homepage', hidden: true, meta: { permission: [1, 2, 3] } }, { path: '/homepage', meta: { permission: [1, 2, 3] }, redirect: '/homepage/dashboard', component: layout, children: [ { path: 'dashboard', name: '安全状态监控', meta: { iconName: 'icon-erji-loudongsaomiaoguanli', permission: [1, 2, 3] }, component: () => import('../views/dashboard/index.vue'), }, ] }, { path: '/assetManagement', name: '资产管理', meta: { iconName: 'icon-erji-loudongsaomiaoguanli', permission: [1, 2, 3] }, redirect: '/assetManagement/assetList', component: layout, children: [ { path: 'assetList', name: '资产列表', meta: { permission: [1, 2, 3] }, component: () => import('../views/assetManagement/assetList.vue'), }, { path: 'assetDetail', name: '资产详情', meta: { permission: [1, 2, 3] }, component: () => import('../views/assetManagement/assetList.vue'), }, ] }, { path: '/login', component: () => import('@/views/login.vue'), hidden: true, meta: { permission: [1, 2, 3] }, }, { path: '*', redirect: '/', hidden: true, meta: { permission: [1, 2, 3] }, } ] const roleType = store.getters.user_role function permissionRouter(router=router1) { return router.filter(item => { if(item.children) item.children = permissionRouter(item.children) return item.meta.permission.includes(roleType) }) } export const routes = permissionRouter() // 根据这个路由去生成侧边栏 export default new VueRouter({ routes })

    router 写好以后就可以准备侧边栏了,侧边栏逻辑复杂一点,所以我也是拆成了三个文件

    sidebar ├── index.vue ├── item.vue ├── sidebarItem.vue // index.vue <template> <div class="sidebar-container"> <el-menu :unique-opened="true" :collapse-transition="false" background-color="rgba(0,0,0,0)" :default-active="activeMenu"> <sidebar-item v-for="(item, index) in routes" :key="index" :item="item" /> </el-menu> </div> </template> <script> import { routes } from '@/router' import SidebarItem from './sidebarItem.vue' export default { components: { SidebarItem }, data() { return { routes } }, computed: { activeMenu() { return this.$route.path } } } </script> // item.vue <template> <el-menu-item :index="compilePath(child.path)" @click="clickEvent(child)" v-if="!child.hidden"> <i v-if="child.meta" class="iconfont" :class="child.meta.iconName"></i> <span slot="title">{{child.name}}</span> </el-menu-item> </template> <script> import path from 'path' export default { props: { child: { type: Object }, basePath:{ type: String } }, methods: { compilePath(childpath) { return path.resolve(this.basePath, childpath) }, clickEvent(child) { const path = this.compilePath(child.path) this.$router.push(path) } } } </script> // sidebarItem.vue <template> <el-submenu v-if="(item.children && hasName(item)) && !item.hidden" :index="item.path"> <template slot="title"> <i v-if="item.meta" class="iconfont" :class="item.meta.iconName"></i> <span slot="title">{{item.name}}</span> </template> <menu-item v-for="child in item.children" :key="compilePath(item.path,child.path)" :child="child" :basePath="item.path" /> </el-submenu> <menu-item :child="item.children[0]" v-else-if="item.children && !item.hidden" :basePath="item.path" /> </template> <script> import path from 'path' import MenuItem from './item.vue' export default { props: { item: { type: Object } }, components: { MenuItem }, methods: { compilePath(itempath, childpath) { return path.resolve(itempath, childpath) }, hasName(item){ return item.name? true: false } } } </script>

    这样,我们的整个需求就完成了,然后将 sidebar 组件正常引入,加入自己的样式,就可以了。下面是引入

    <template> <div class="flex-box app-content"> <side-bar class="sidebar" /> </div> </template> <script lang="es6"> import sideBar from './sidebar/index.vue' export default { components: {sideBar}, data(){ return { Tony: 'Tony' } } } </script>

    看下结果

    最后的最后,代码未经测试,有任何问题请私信或评论,谢谢。

    Processed: 0.010, SQL: 12