完整项目地址:
最终效果:
一、数据库
本着以用户为基准而不是角色,所以菜单数据库表,我分了3个部分,其中菜单有一些是公共菜单,所以加了公共属性区分:
1.用户
-- Table structure for user
DROP TABLE IF EXISTS
user; CREATE TABLEuser(idvarchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,open_idvarchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '绑定微信ID',namevarchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '用户名',sexint(255) NULL DEFAULT 1 COMMENT '性别:1:男,2:女',phonevarchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '电话',photovarchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '照片',create_timedatetime(0) NULL DEFAULT NULL COMMENT '创建时间',remarksvarchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '备注', PRIMARY KEY (id) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin COMMENT = '用户表' ROW_FORMAT = Dynamic;
2.菜单
-- Table structure for menu
DROP TABLE IF EXISTS
menu; CREATE TABLEmenu(idvarchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,pIdvarchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '父阶ID',iconvarchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '图标',namevarchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '菜单名称',urlvarchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '菜单地址',snfloat(255, 2) NULL DEFAULT NULL COMMENT '排序',publicint(1) NULL DEFAULT 0 COMMENT '0: 公共菜单,1:私有菜单', PRIMARY KEY (id) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;
3.用户-菜单关系表
-- Table structure for user_menu
DROP TABLE IF EXISTS
user_menu; CREATE TABLEuser_menu(idvarchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,user_idvarchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '用户ID',menu_idvarchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '菜单ID', PRIMARY KEY (id) USING BTREE, INDEXfk_user_userMenu(user_id) USING BTREE, INDEXfk_menu_userMenu(menu_id) USING BTREE, CONSTRAINTfk_menu_userMenuFOREIGN KEY (menu_id) REFERENCESmenu(id) ON DELETE RESTRICT ON UPDATE RESTRICT, CONSTRAINTfk_user_userMenuFOREIGN KEY (user_id) REFERENCESuser(id) ON DELETE RESTRICT ON UPDATE RESTRICT ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;SET FOREIGN_KEY_CHECKS = 1;
二、前端
1.前端使用的是 PanJiaChen 大神的vue-element-admin,想要改为动态菜单,主要需要改两个.js文件/src/permission.js; /src/store/modules/permission.js
2./src/permission.js 修改如下
3./src/store/modules/permission.js 修改如下,加入了generateMyRoutes方法(实力有限,写的比较繁琐,但是可以参考参考)
generateMyRoutes({ commit }, menus) {
return new Promise(resolve => {
const accessedRoutes = buildAsyncRoutes(menus)
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
})
}
buildAsyncRoutes(menus) 方法
/**
* 要求数据,符合如下要求,
* 为什么首页两个呢,是这样设计的,菜单必须于代码目录严格对应,父级别首页,
* 对应于首页代码放的目录dashboard,子级别首页对应具体vue index;实际目录就是dashboard/index.vue(菜单中,子节点只有一个的不显示父节点)
*
* 房屋管理url 是house/index,对应父级别生活url为live;所以房屋管理实际代码目录就是 live/house/index
* <p>
* [{
"children": [{
"children": [],
"name": "首页",
"pId": "1",
"id": "home",
"url": "index"
}],
"name": "首页",
"pId": "0",
"id": "1",
"url": "dashboard"
},{
"children": [{
"children": [],
"name": "房屋管理",
"pId": "live",
"id": "live_1",
"url": "house/index"
}, {
"children": [],
"name": "账单管理",
"pId": "live",
"id": "live_2",
"url": "bill"
}],
"name": "生活",
"pId": "0",
"id": "live",
"url": "live"
}]
* </p>
* @param menus 菜单数据
* @returns
*/
export function buildAsyncRoutes(menus) {
debugger
const res = []
menus.forEach(menu => {
const tmp = {
path: '/' + menu.url,
component: Layout,
meta: {
title: menu.name,
icon: menu.icon
}
}
if (menu.children && menu.children.length > 0) {
handleChildren(menu, tmp)
} else {
// 如果个别页面没有父界别目录走这个,因为必须在外面套一个Layout组件否则无法显示菜单等框架元素
// const path = '@/' + menu.url
tmp.children = []
tmp.children.push({
path: '',
// component: () => import(path),
component(resolve) {
require(['@/views/' + menu.url], resolve)
},
meta: {
title: menu.name,
icon: menu.icon
}
})
}
res.push(tmp)
})
// 404 page must be placed at the end !!!
const page404 = { path: '*', redirect: '/404', hidden: true }
res.push(page404)
return res
}
handleChildren(menu, tmp) 方法
function handleChildren(menu, tmp, isChildren) {
const children = []
menu.children.forEach(child => {
const tmpc = {
path: child.url,
component: Layout,
meta: {
title: child.name,
icon: child.icon
}
}
children.push(tmpc)
if (child.children && child.children.length > 0) {
handleChildren(child, tmpc, true)
} else {
tmpc.component = resolve => require.ensure([], () => resolve(require('@/views/' + menu.url + '/' + child.url)))
}
})
tmp['children'] = children
}