vue+elementui根据权限动态生成菜单添加对应router路由

说明: 根据从后台获取角色权限数据配合Element-ui 中NavMenu组件生成对应菜单结构.并通过addRoutes方法动态添加路由
- 之前做项目时是通过beforeEach钩子里通过判断当前角色的权限是否能访问要去往的路由页面来实现的,但是后来发现vue-router 2.x版本提供了addRoutes就试试能不能优雅一些.

– 先看看router.js

	import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

export default new Router({
  // mode: 'hash',
  routes: [
    {
      path: '/',
      component: () => import('@/views/layout/index.vue'),
      redirect: '/index',
      children: [
        {
          path: 'index',
          name: 'index',
          component: () => import('@/views/index'),
          meta: {
            target: 'router',
            icon: "el-icon-s-promotion",
            title: "首页",
            allPath: "/index"
          }
        }
      ]
    },
    {
      path: '/error404',
      component: () => import('@/views/error/404.vue')
    }
  ]
})
// 这里提醒一下,以上代码是根据我工作里实际情况来了,也就是基于后端返回的数据结构来的,你们具体如何要看实际情况. 你在阅读此文章时只需要参考思路即可.
我的菜单都是要放在根路径'/'下的children里的,如果有一个和首页同级的一级菜单那么就是在routes[0].children里push进去 解释完了就继续吧
  • 现在我们看看vuex里是什么样
import router from '@/router'
export default {
    state: {
        isinitRoutes: false, // 用来判断是否初始化过的标识符
        routes: [
            {
                path: '/',
                component: () => import('@/views/layout/index.vue'),
                redirect: '/theme/theme',
                children: []
            }
        ]
    },
    mutations: {
        UPDATAMENUS (state, data) {
            let newData = [...data]
            // 因为我获取来的数据以菜单为基准,包括了router里事件定义过的首页,避免重复添加所以删除第一个
            newData.shift()
            router.addRoutes([{
                path: '/',
                component: () => import('@/views/layout/index.vue'),
                children: newData
            }])
            state.routes[0].children = data
            state.isinitRoutes = true
        }
    },
    actions: {
        updateMenus ( { commit }, data ) {
            commit('UPDATAMENUS', data)
        }
    }
}
  • 上面的代码其实应该最后看,接着我们看看layout.vue里的代码,先看生成菜单部分
<template>
	<el-menu class="el-menu-vertical-demo" :default-active="$router.path" :collapse='isCollapse' @open='handleOpen'>
	    <!-- 侧边菜单渲染 -->
	    <sidebar-item v-if="routes && routes[0] && routes[0].children"  :submenus="routes[0].children"></sidebar-item>
	</el-menu>
</template>

import sidebarItem from './components/sidebarItem.vue'
import { getMenus } from '@/api/layout.js'
import { mapGetters, mapActions } from "vuex"
export default {
	components: { sidebarItem },
	computed: {
        ...mapGetters(['routes', 'isinitRoutes', 'edit']),
    },
    created () {
        this.initMenu();
    },
    methods: {
    	...mapActions(['updateMenus']),
    	initMenu() {
            if (!this.isinitRoutes) {
                getMenus().then(res => {
                // 调用vuex actions方法设置菜单和路由
                    this.updateMenus(this.recursiveData(res.funcList))
                })
            }
        },
        // 菜单数据递归处理方法 动态添加路由
        recursiveData (data) {
            let result = []
            function resolveData (oldMenus,menus) {
                oldMenus.forEach((ele, index) => {
                    if (ele.children.length > 0) {
                        let item = {
                            path: ele.path,
                            name: ele.name,
                            component: () => import(`@/views/${ele.component}`),
                            meta: {
                                target: ele.target,
                                icon: ele.icon,
                                title: ele.title,
                                allPath: ele.allPath,
                                url: ele.url
                            },
                            children: []
                        }
                        menus.push(item)
                        resolveData(ele.children, menus[index].children)
                    }  else {
                        let item = {
                            path: ele.path,
                            name: ele.name,
                            component: () => import(`@/views/${ele.component}`),
                            meta: {
                                target: ele.target,
                                icon: ele.icon,
                                title: ele.title,
                                allPath: ele.allPath,
                                url: ele.url
                            }
                        }
                        menus.push(item)
                    }
                })
            }
            resolveData(data, result)
            return result
        },
        // 伸缩侧边菜单
        collapseNavmenu () {
            this.isCollapse = !this.isCollapse;
        }
    }
}
  • 再看一下sidebar-item这个子组件
	<template>
	    <div>
	        <template v-for="item in submenus">
	            <el-menu-item v-if="Object.keys(item).indexOf('children') < 0" :key="'menuItem'+item.path" :index="item.meta.allPath">
	                <i :class="item.meta.icon"></i>
	                <router-link class="text" v-if="item.meta.target == 'router'" :to="item.meta.allPath">{{ item.meta.title }}</router-link>
	                // 这里是我司业务需求,需要接入一些老项目,有的菜单不能走vue-router跳转,需要通过iframe或者新打开一个页面,因此这里加了判断
	                <a class="text" v-else :href="item.meta.url" :target="item.meta.target">{{ item.meta.title }}</a>
	            </el-menu-item>
	            <el-submenu v-else :key="'submenu'+item.path" :index="item.meta.allPath" :class="{ 'last-sub': item.children }">
	                <template slot="title">
	                    <i :class="item.meta.icon"></i>
	                    <span class="text">{{ item.meta.title }}</span>
	                </template>
	                <sidebar-item :submenus="item.children"></sidebar-item>
	            </el-submenu>
	        </template>
	    </div>
</template>

<script>
export default {
    name:'sidebarItem',
    props: {
        submenus: {
            type: Array
        }
    }
}

至此就介绍完了,但是特别注意一个地方

  • 通过addRoutes动态添加路由时,如果component不确定需要根据后台数据或者变量决定的话,一定要写成这样
    component: () => import(`@/views/${ele.component}`) 如果写成component: () => import(“ele.component”),就不行,这是因为webpack并不能完全动态编译,它需要你给他指定一个路径,因此我们可以用静态和动态数据拼接的方式

  • 还有就是记得吧路由或菜单数据存到sessionStorage里去,否则手动刷新后,因为没有数据会导致当前页面为空

你可能感兴趣的:(vue,vue-router,element-ui,navmenu,addRoutes)