vue+element-ui实现动态的权限管理和菜单渲染

前言:

需求:需要根据不用的用户匹配不同的管理权限,既:匹配不同的操作导航,尤其体现在后台管理系统内,如果仅仅只是在导航菜单内不予显示,仍然是可以通过路径直接打开页面,因为其路由信息已经在路由信息对象(new Router({}))函数中进行了注册

当然 这里可以通过全局导航守卫来区分不同的用户,允许其进入不同的路径,但是这只能进行简单的权限判断,且前端已经写死,灵活性不高,不能针对每个用户,做定制化权限区分

项目地址:https://github.com/cgq001/vue-admin

欢迎start

使用到的规则

1、动态设置权限的UI展示

这里使用element-ui的Three树形控件,数据结构如下:

data: [{
          id: 1,
          label: '一级 1',
          children: [{
            id: 4,
            label: '二级 1-1',
            children: [{
              id: 9,
              label: '三级 1-1-1'
            }, {
              id: 10,
              label: '三级 1-1-2'
            }]
          }]
        }, {
          id: 2,
          label: '一级 2',
          children: [{
            id: 5,
            label: '二级 2-1'
          }, {
            id: 6,
            label: '二级 2-2'
          }]
        }, {
          id: 3,
          label: '一级 3',
          children: [{
            id: 7,
            label: '二级 3-1'
          }, {
            id: 8,
            label: '二级 3-2'
          }]
        }]

这里选择这个树形控件,是因为其数据结构和我们的 注册路由信息结构十分接近,不需要再重新修改路由信息的数据结构,即可完美的 展现在 Three树形控件里

2、将侧边导航所需要的路由信息对象 抽离成一个数组,根据后台返回的数组,筛选出对应的路由信息,通过addRoutes添加到路由信息对象里,即可完成路由信息的动态添加

详情

// 在router.js 路由文件 新建数组路由存储 '/'  父路由下的所有子路由(这里所有的动态路由均为 '/' 的子路由)
let routerLists=[ 
  {
    id:1,
    path: '',
    label: '首页',
    redirect: '/index',  //重定向到
    meta:{
      title: '首页',
      table: true,
      display: false,
      icon: 'el-icon-s-home'
    }
  },
  {
    id: 2,
    path: '/index',   
    name: 'index',
    label: '首页',
    component: _import('Index/Index'),
    meta:{
      title: '首页',
      table: true,
      display:true,
      icon: 'el-icon-s-home'
    }
  },
  {
    id: 3,
    path: '/shop',
    name: 'shop',
    label: '商品管理',
    component: _import('Shop/Shop'),
    meta:{
      title: '商品列表',
      table: true,
      display:true,
      icon: 'el-icon-s-operation'
    }
  },
  {
    id:20,
    path: '/admin',
    label: '管理员列表',
    component: _import('admin/index'),
    meta:{
        title: '管理员列表',
        table: true,
        display:true,
        icon: 'el-icon-s-custom'
    },
    children:[
      {
        id:21,
        path: '/admin/index',
        label: '管理员列表',
        component: _import('admin/admin'),
        meta:{
          title: '管理员列表',
          table: true,
          display:true,
          icon:'el-icon-tickets'
        }
      },
      {
        id:22,
        path: '/admin/adminlist',
        label: '添加管理员',
        component: _import('admin/adminlist'),
        meta:{
          title: '添加管理员',
          table: true,
          display:true,
          icon:'el-icon-document-remove'
        }
      }
    ]
  }
]

//定义 上面数组的父路由
let routerAlls=[   //这里为routerLists的父路由
        {
          path: '/',
          component: Home
        }
]

1 .权限配置表
// 先把路由信息对象字符串化,然后去除component字段 ,再传递给 权限配置表
 let routerListString =JSON.stringify(routerLists)
 let src= routerListStr(routerListString)
 store.commit('serRouterList',src)

 let arr=[1,2,3,20,21,22]   //这里为权限列表数组(既后台根据用户身份返回的对应的路由数组)

 //根据权限配置表(arr数组)和动态路由信息对象(routerLists数组) 获取本用户的路由信息表,并添加到 routerAlls 路由的二级路由里
 
 2.获取动态路由
 
routerAlls[0].children = routerListFun(arr,routerLists)  
//获取 路径 '/' 的子路由,并添加至 routerAlls   这里的  routerListFun函数 为 根据权限列表数组(arr)筛选动态路由信息对象(routerLists) PS:函数内容见 文章末尾:附录


3.获取侧边导航栏 的 渲染菜单 数组

//根据权限配置表数组(arr)和动态路由信息对象(routerLists) 获取本用户的菜单列表
let mentParse =JSON.parse(JSON.stringify(src))
let menuList = routerListFun(arr,mentParse)   //这里routerListFun函数注意去除返回数组中的component
store.commit('setMents',menuList)   //将其添加到vuex  


// 注册路由
let routers =new Router({
  mode: 'history',
  // base: process.env.BASE_URL,
  routes: [
    {
      path: '/loading',
      name: 'loading',
      component: () => import('../views/Load/Loading.vue'),
      meta:{
        title: '登陆',
        table: false
      }
    }
  ]
})

// 将筛选后的路由信息对象添加至路由表
routers.addRoutes(routerAlls)


// 进行全局导航守卫
routers.beforeEach((to,from,next)=>{
    
      if(to.path != '/loading'){
          
          let username=store.state.load.userList.username
          
          if(username){
            next()
          }else{

            next({
              path:'/loading',
              query:{
                path:to.path
              }
            }) 
          }
      }else{
        next()
      }   
})

export default routers;

附录

1.动态路由书写规则

* 路由书写规则
 *    1、只有一级路由(实际为二级路由):
 *     {
          id: 2,                                  //ID全局不能重复
          path: '/index',                         //路由路径 全局不能重复
          name: 'index',                          //名字,全局不能重复,存在二级路由,则一级路由不能有名字
          label: '首页',                          // 页面名称(用于权限配置时 显示名称使用)
          component: _import('Index/Index'),      // 文件地址(此处对应的是views目录)
          meta:{
            title: '首页',                        //页面名称(横向teble标签切换)
            table: true,                          // 是否显示 teable 切换按钮
            display:true,                         //  是否在侧边导航菜单显示
            icon: 'el-icon-s-home'                //  侧边导航的icon图标
          }
        }
      2、包含二级路由(实际为三级路由)
        {
            id:20,                                  //ID全局不能重复
            path: '/admin',                         //路由路径 全局不能重复(此处为父级))
            label: '管理员列表',                     // 页面名称(用于权限配置时 显示名称使用)
            component: _import('admin/index'),      // 文件地址(此处对应的是views目录)注意:此文件下 应包含(router-view 标签 来显示子页面)
            meta:{
                title: '管理员列表',                   //页面名称(横向teble标签切换)
                table: true,
                display:true,                         //  是否在侧边导航菜单显示(注意 这里是父级,如果为false,则子级不在折叠)
                icon: 'el-icon-s-custom'              //  侧边导航的icon图标
            },
            children:[
              {
                id:21,                              //ID全局不能重复
                path: '/admin/index',               //路由路径 全局不能重复(此处为父级))
                label: '管理员列表',                  // 页面名称(用于权限配置时 显示名称使用)
                component: _import('admin/admin'),   // 文件地址(此处对应的是views目录)
                meta:{
                  title: '管理员列表',                //页面名称(横向teble标签切换)
                  table: true,                      // 是否显示 teable 切换按钮
                  display:true,                     //  是否在侧边导航菜单显示
                  icon:'el-icon-tickets'            //  侧边导航的icon图标
                }
              }
            ]
          }

2.routerListFun、routerListStr函数

// 根据后台返回的权限数组,筛选对应的路由信息对象
export function routerListFun(arr,allList){


    let arrArray=[]
            
    for(let i=0;i<arr.length;i++){
        
        for(let k=0;k<allList.length;k++){
            if(arr[i] == allList[k].id){
                arrArray.push(allList[k])
            }
        }
    }
    
    
    
    for(let i=0;i<arrArray.length;i++){
        if(arrArray[i].children && arrArray[i].children.length>0){
            arrArray[i].childrens=[]
            for(let k=0;k<arrArray[i].children.length;k++){
                for(let j=0;j<arr.length;j++){
                    if(arrArray[i].children[k].id == arr[j]){
                        arrArray[i].childrens.push(arrArray[i].children[k])
                    }
                }
            }
            arrArray[i].children=arrArray[i].childrens
        }
    }
    return arrArray;
}

// 根据全部的路由信息对象 返回 权限列表

export function  routerListStr(routerStr){
    
        let routerJson= JSON.parse(routerStr)
        for(let i=0;i<routerJson.length;i++){
            if(routerJson[i].component){
                routerJson[i].component=''
            }

            if(routerJson[i].children && routerJson[i].children.length>0){
                for(let k=0;k<routerJson[i].children.length;k++){
                    routerJson[i].children[k].component=''
                }
            }
        }

        return routerJson;
}

3.路由渲染

<!-- 这里的 routerList  为 从 router.js动态获取到了路由信息 -->
 <div v-for="item in routerList" :key="item.id" >
        <!--包含二级导航-->
        <el-submenu :index="item.id.toString()" v-if="item.children && item.children.length > 0 && item.meta.display==true">
                        <template slot="title">
                            <i :class="item.meta.icon"></i>
                            <span>{{item.label}}</span>
                        </template>
                <el-menu-item v-show="items.meta.display" v-for="(items,indexs) in item.children" :key="indexs" :index="items.path">
                            <i :class="items.meta.icon"></i>
                            {{items.label}}
                </el-menu-item>
        </el-submenu>
        <!-- 如果 二级导航的 父级 设置display:false  则直选渲染二级导航为一级导航 -->
       <el-menu-item v-for="(items,indexs) in item.children" :key="indexs" :index="items.path" v-show="items.meta.display"  v-else-if="item.children && item.children.length > 0 && item.meta.display==false" >
                        <i :class="item.meta.icon"></i>
                        <span slot="title">{{item.label}}</span>
         </el-menu-item>
        <!-- 渲染一级导航 -->
        <el-menu-item  v-show="item.meta.display" :index="item.path"  v-else >
                        <i :class="item.meta.icon"></i>
                        <span slot="title">{{item.label}}</span>
        </el-menu-item>
    </div>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容