Vue-admin-template结合后端配置动态路由+动态侧边栏

Vue-admin-template结合后端配置动态路由+动态菜单

在使用这个vue-admin-template的时候,发现其官方文档的推荐是使用动静结合的方法去配置动态路由
Vue-admin-template结合后端配置动态路由+动态侧边栏_第1张图片

其官方大致过程就是

//此处路由数组为通用跳转路径,意思是不需要权限认证即可通过
export const constantRoutes = [{path:'/login'} ...]
//此路由数组为所有需要进行认证的后才能决定可不可以跳转
export const asyncRoutes = [{path:'user'}...]

随后将用户的角色数组交给vuex进行分配路由:

generateRoutes({ commit }, roles) {
... 进行构造路由 具体代码可看官网,大致是遍历树的子节点做判断是否push到constantRoutes中
}

这就意味这,你需要在前端和后端,同时做一个数据校验,即role表和permission表或者menu表与前端的关系要一一对应

 {
    path: '/permission',
    component: Layout,
    redirect: '/permission/page',
    alwaysShow: true, // will always show the root menu
    name: 'Permission',
    meta: {
      title: 'Permission',
      icon: 'lock',
      roles: ['admin', 'editor'] // 你需要在这里进行与数据库对比
    },
    children: [
      {
        path: 'page',
        component: () => import('@/views/permission/page'),
        name: 'PagePermission',
        meta: {
          title: 'Page Permission',
          roles: ['admin'] //你需要在这里进行对比
        }
      },

这对快速开发来说,是比较快而且方便的,但是如果要减少这一对比的过程,我们就需要动态的来加载新的route数组

那么我们现在需要实现的功能就是:


可以通过改数据库上的数据来实时修改到前端的侧边栏和路由


准备工作

经典权限五张表,关系表user_role,role_menu已省略
表一
用户表

类型 描述
user_id integer 用户id
user_name string 用户名
user_password string 用户密码

表二
角色表

类型 描述
role_id integer 角色id
role_name string 角色名
role_key string 角色标识符

表三
权限菜单表

类型 描述
menu_id integer 资源id
menu_name string 资源名
menu_url string 资源路径
menu_perms string 对应组件路径
menu_perms_name string 组件名称
menu_parent_id string 父资源id

注意,后端使用任意语言即可
主要需要实现以下请求接口:
名字任意

参数 返回值 前端例子
角色标识符 该角色下的所有权限菜单 getMenusByRoleKey(roleKey)

我这里使用springboot作为后端测试Vue-admin-template结合后端配置动态路由+动态侧边栏_第2张图片

前端使用vue-admin-template

新建文件src/store/permission

const getDefaultState = () => {
  return {
    // 当前角色名,若为#TEST则为混合模式
    roleName: '#TEST',
    routes: [],
    addRoutes: []
  }
}
const state = getDefaultState()
const mutations = {
  SET_PERMISSION: (state, role) => {
    state.roleName = role
  },
  SET_ROUTES: (state, routes) => {
    state.addRoutes = routes
    state.routes = constantRoutes.concat(routes)
  }
}
const actions = {
	createRoute({ commit }, rolesNames) {
	return new Promise(resolve => {
      commit('SET_PERMISSION', rolesNames)
      //此处使用的是我的接口
      getMenuByRoleNames(rolesNames).then(resp => {
        const menus = resp.data
        menus.forEach(menu => {
          menu['children'] = []
        })
        // console.log(filterRoute(menus))
        commit('SET_ROUTES', filterRoute(menus))
        resolve(filterRoute(menus))
      })
    })
	}
}

export function filterRoute(menus) {
  const result = []
  const test = []
  for (let i = 0; i < menus.length; i++) {
  //也就是说一个最基本的路由,需要你满足以下几个属性才能让他在侧边栏上发挥作用
    test.push({
      path: '/' + menus[i].permsName,
      name: menus[i].permsName,
      component: (resolve) => require(['@/views/' + menus[i].perms + '/index'], resolve),
      children: [],
      meta: { title: menus[i].menuName, 'icon': menus[i].icon }
    })
  }


  // 寻找子节点并装入子节点数组 此处逻辑需要自行根据项目进行构建
  for (let i = 0; i < menus.length; i++) {
    for (let j = 0; j < menus.length; j++) {
      if (menus[i].parentId === menus[j].menuId) {
        // console.log(test[j])
        test[j].children.push(test[i])
        menus[j].children.push(menus[i])
      }
    }
    if (menus[i].parentId === 0) {
      test[i].component = Layout
      if (menus[i].children.length === 0) {
        test[i].children.push(
          {
            path: '/'+menus[i].permsName+'/index',
            name: menus[i].permsName,
            //这里使用懒加载组件时,需要用require拼接
            component: (resolve) => require(['@/views/' + menus[i].perms + '/index'], resolve),
            meta: { title: menus[i].menuName, icon: menus[i].icon }
          }
        )
      }
      // test[i]['redirect'] = test[i].path + test[i].children[0].path
      result.push(test[i])
    }
  }

  return result
}

export default {
  namespaced: true,
  state,
  mutations,
  actions
}

注意⚠️:path属性,无论是子路由还是父路由,都不能有一点重复
比如

{
path: '/test'
...
children:{
  path: '/test'  //这个地方不能使用test了,原因是侧边栏使用了v-for 设置了:key为path
  ...
  }
}

getter中添加

const getters = {
  sidebar: state => state.app.sidebar,
  device: state => state.app.device,
  token: state => state.user.token,
  avatar: state => state.user.avatar,
  cachedViews: state => state.tagsView.cachedViews,
  name: state => state.user.name,
  roles: state => state.user.roles,
  roleName: state => state.permission.roleName,
	//添加此处,用于从vuex中取出拼接好的路由
  permission_routes: state => state.permission.routes
}

在src/permission 中添加

 if (hasToken) {
    if (to.path === '/login') {
      // 如果有token值了访问到login可以直接到/路径
      next({ path: '/' })
      NProgress.done()
    } else {
      const hasGetUserInfo = store.getters.name
      if (hasGetUserInfo) {
        next()
      } else {
        // 没有用户信息
        try {
          // get user info
          await store.dispatch('user/getInfo')
          const roleNames = []
          store.state.user.roles.forEach(role => {
            roleNames.push(role.roleName)
          })

		// 这里这里这里这里,下面两行添加上去就好了
          const accessRoutes = await store.dispatch('permission/createRoute', roleNames)
          router.addRoutes(accessRoutes)
          // console.log(accessRoutes)
          next()
        } catch (error) {
          // remove token and go to login page to re-login
          await store.dispatch('user/resetToken')
          Message.error(error || 'Has Error')
          next(`/login?redirect=${to.path}`)
          NProgress.done()
        }

随后修改src/layout/components/Sidebar/index.vue

...
									
       <sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" />
      el-menu>
    el-scrollbar>
  div>
template>

<script>
import { mapGetters } from 'vuex'
import Logo from './Logo'
import SidebarItem from './SidebarItem'
import variables from '@/styles/variables.scss'

export default {
  components: { SidebarItem, Logo },
  computed: {
  //此处修改。。。
    ...mapGetters([
      'sidebar',
      'permission_routes'
    ]),
    routes() {
      return this.$router.options.routes
    },

然后启动页面查看

Vue-admin-template结合后端配置动态路由+动态侧边栏_第3张图片
修改数据库,改变其结构
Vue-admin-template结合后端配置动态路由+动态侧边栏_第4张图片
可以看到确实改变了
Vue-admin-template结合后端配置动态路由+动态侧边栏_第5张图片
那么可能就有人问了,你这个能实现三级菜单吗,能实现路由嵌套吗
Vue-admin-template结合后端配置动态路由+动态侧边栏_第6张图片

可以是可以,但是你必须在你写的组件下去写route-view啊
这种的话,建议直接使用上一级的路由,在数据库中修改父id为0就好了,三级也是可以的,直接去修改数据库中的嵌套关系就好了,笔者只写出修改的思路,其中的具体实现过程,大伙可以灵活应用

你可能感兴趣的:(vue,vue,阿里云,极限编程)