关于vue-admin做动态路由生成菜单

212 阅读3分钟

本人对前端不太熟悉,如果有不对的地方欢迎指出。

先放上我的菜单表结构:

CREATE TABLE `sys_menu` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `pid` int(11) NOT NULL COMMENT '父菜单ID',
      `is_leaf` tinyint(4) NOT NULL COMMENT '0:不是叶子节点,1:是叶子节点',
      `name` varchar(16) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '菜单名称',
      `redirect` varchar(255) DEFAULT NULL COMMENT '跳转URL',
      `url` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '路由地址',
      `component` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '组件路径',
      `perms` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '权限标识',
      `icon` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '图标',
      `icon_color` varchar(16) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
      `sort` tinyint(4) DEFAULT NULL COMMENT '排序',
      `level` tinyint(4) NOT NULL COMMENT '菜单层级',
      `status` tinyint(4) NOT NULL COMMENT '0:启用,1:禁用',
      `remark` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '' COMMENT '备注',
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='系统菜单表';

然后经过后端代码处理之后返回到前端的结构是这样的:

{
	"success": true,
	"code": 20000,
	"message": "成功",
	"data": {
		"menu_list": [
			{
				"id": 1,
				"pid": 0,
				"isLeaf": 0,
				"name": "系统设置",
				"url": "/admin/system",
				"redirect": "/bbsacl/sysmenu",
				"component": "form/index",
				"perms": "bbs:bbsacl:sysmenu:list",
				"icon": "form",
				"iconColor": null,
				"sort": 0,
				"level": 1,
				"status": 0,
				"remark": "系统设置",
				"children": []
			},
			{
				"id": 2,
				"pid": 0,
				"isLeaf": 0,
				"name": "权限管理",
				"url": "/admin/auth/sys_role",
				"redirect": "/bbsacl/sysrole",
				"component": "form/index",
				"perms": "bbs:bbsacl:sysrole:list",
				"icon": "form",
				"iconColor": null,
				"sort": 0,
				"level": 1,
				"status": 0,
				"remark": "权限管理",
				"children": [
					{
						"id": 6,
						"pid": 2,
						"isLeaf": 1,
						"name": "角色管理",
						"url": "/admin/auth/sys_role/list",
						"redirect": "/admin/role/index",
						"component": "role/index",
						"perms": "bbs:bbsacl:sysrole:list",
						"icon": "form",
						"iconColor": "",
						"sort": 0,
						"level": 1,
						"status": 0,
						"remark": "角色管理",
						"children": []
					}
				]
			}
		]
	}
}

1、首先在store/modules/user.js 文件里的getinfo方法里将后端返回的菜单先存到vuex里

image.png 我这里取data.menu是因为后面修改的时候把菜单数据放在menu这个字段里返回了,相当于上面的menu_list改成了menu。 之后要去store/getters.js 里添加一行代码:

image.png

2、然后在router目录下创建这两个文件

image.png 内容分别是:

  • _import_development.js:module.exports = file => require('@/views/' + file + '.vue').default // vue-loader at least v13.0.0+
  • _import_production.js:module.exports = file => () => import('@/views/' + file + '.vue')

3、然后在utils目录下新建一个menu.js文件,将下面内容放进去

import Layout from '@/layout'
const _import = require('../router/_import_' + process.env.NODE_ENV) // 获取组件的方法

export default {
    //过滤菜单数组,作用是处理每一个菜单的component组件的引入
    filterAsyncRouter(routes) {
        const res = []
        routes.forEach(route => {
            const tmp = { ...route }
            if (tmp.component) {
                if (tmp.component === 'Layout') {
                    tmp.component = Layout
                } else {
                    //因为有的菜单的组件项到了这里会变成对象而不是字符串,所以进行一下判断
                    if (typeof (tmp.component) == 'string') {
                        tmp.component = _import(tmp.component)
                    } else {
                        tmp.component = tmp.component
                    }
                }
            }

            if (tmp.children && tmp.children.length > 0) {
                tmp.children = this.filterAsyncRouter(tmp.children)
            }

            res.push(tmp)

        })

        return res
    },


    //这里按照vue的路由格式生成路由内容
    generateRoutes(menus, flag = false) {
        const routes = []
        menus.forEach(menu => {
            const route = {
                path: menu.url,
                component: 'Layout',
                redirect: menu.redirect,
                name: menu.name,
                children: [],
                meta: {
                    title: menu.name,
                    icon: menu.icon
                }
            }

            if (flag) {
                route.component = menu.component;
            }

            if (menu.children && menu.children.length > 0) {
                route.children = this.generateRoutes(menu.children, true)
            } else {
                route.path = menu.redirect
                route.children = [
                    {
                        path: menu.url,
                        component: menu.component,
                        meta: { title: menu.name, icon: menu.icon }
                    },
                ];
            }

            routes.push(route)
        })

        return this.filterAsyncRouter(routes)
    }
}

4、然后去到src目录下的permission.js文件里,引入这两个文件:

image.png

然后在下面的代码里先通过getinfo方法获取到菜单数据(其实也可以通过store.getter.menu获取),然后将菜单数据放进menu.js 文件里的generateRoutes方法得到组装好的路由,最后再将组装好的留有添加到router中,即可完成动态路由的添加

  • 补充: menu.js 这个文件里的filterAsyncRouter方法是对后端返回的菜单数据的component组件引入, generateRoutes方法则是按照vue-router的格式组装路由并返回。

大致流程就是这样,如果有不明白的朋友欢迎留言提问,知无不言。