背景

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

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

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

分析

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

挂载组件到页面中

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

区分不同用户角色

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

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

生成复杂的侧边栏

侧边栏是管理后台都有的功能,这里参考 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>

看下结果
在这里插入图片描述

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

Logo

前往低代码交流专区

更多推荐