一、动态菜单
(一). 前端代码
- 登录页
新星电商后台管理系统
登录
- 侧边栏
首页
{{item.title}}
{{childItem.title}}
- 主页
退出登录
- 令牌存储store.js
var store=window.localStorage;
export function setToken(val){
store.token=val;
}
export function deleteKey(key){
store.removeItem(key)
}
export function getToken(){
return store.token;
}
- 路由处理router.js
import dynamicRouter from "@/router/dynamicRouter.js";
import { getToken } from '@/utils/store.js';
import Vue from 'vue';
import Router from 'vue-router';
import { getRouter } from "@/api/menu.js";
Vue.use(Router)
/**
* 重写路由的push方法
*/
const routerPush = Router.prototype.push
Router.prototype.push = function push(location) {
return routerPush.call(this, location).catch(error=> error)
}
const router = new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'login',
component: () => import('@/views/Login.vue')
}
]
});
/**
* 避免循环执行
*/
let newRoutes = null;
router.beforeEach((to, from, next) => {
// 判断是否是登录界面
if (to.path != '/') {
// 从获取store中Token
if (getToken()) {
// 刷新动态路由丢失
if (!newRoutes) {
// 调用API获取动态路由
getRouter().then(res => {
res.data.data.forEach(element => {
// 重要:赋值给变量
let value = element.component;
element.component = function component(resolve) {
require(["@/views" + value], resolve);
};
});
dynamicRouter.routes[0].children = res.data.data;
newRoutes = dynamicRouter.routes;
// 添加路由数据
router.addRoutes(newRoutes);
next({ ...to, replace: true });
});
} else {
next()
}
}else{
next("/");
}
} else {
// 如果是登录界面放行
next();
}
});
export default router;
- 动态路由定义dynamicRouter.js
/**
* 动态路由定义
*/
let dynamicRouter = {
routes: [
{
path: "/layout",
name: "layout",
component: () => import("@/views/layout/Layout.vue"),
children: []
}
]
}
export default dynamicRouter;
- 登录API
import request from "@/utils/request.js";
/**
* 登录方法
* @param {} data
*/
export function login(data){
return request({
url:"/admin-service/auth/login",
method:"post",
data:data
});
}
- 菜单路由API
import request from "@/utils/request.js";
/**
* 获取菜单数据
*/
export function getMenu(){
return request({
url:"/admin-service/menu/admin",
method:"get"
});
}
/**
* 获取路由数据
*/
export function getRouter(){
return request({
url:"/admin-service/menu/router/admin",
method:"get"
});
}
(二). 后台代码
- 数据表结构与数据
在此只列表菜单表,其它表,用户、角色、权限未列。
CREATE TABLE `system_menu` (
`id` bigint(20) NOT NULL,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '路由名',
`path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '路径',
`menu_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '显示名称',
`parent_id` int(11) NULL DEFAULT NULL COMMENT '父级ID',
`icon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '图标',
`component` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '组件地址',
`deleted` int(11) NULL DEFAULT NULL COMMENT '逻辑删除(1 已删除 0未删除)',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of system_menu
-- ----------------------------
INSERT INTO `system_menu` VALUES (1, 'userManage', '', '会员管理', 0, 'el-icon-s-custom', NULL, 0, '2019-09-11 14:33:17', NULL);
INSERT INTO `system_menu` VALUES (2, 'user', 'user', '会员列表', 1, 'el-icon-s-custom', '/user/index.vue', 0, '2019-09-11 14:35:26', NULL);
INSERT INTO `system_menu` VALUES (3, 'role', 'role', '角色管理', 0, 'el-icon-s-custom', '/role/index.vue', 0, '2019-09-12 10:49:40', NULL);
INSERT INTO `system_menu` VALUES (4, 'goods', '', '商品管理', 0, 'el-icon-shopping-cart-full', NULL, 0, '2019-09-19 10:28:36', NULL);
INSERT INTO `system_menu` VALUES (5, 'category', 'category', '分类管理', 4, NULL, '/category/index.vue', 0, '2019-09-19 10:29:58', NULL);
INSERT INTO `system_menu` VALUES (6, 'goods', 'goods', '商品管理', 4, NULL, '/goods/index.vue', 0, '2019-09-19 10:30:52', NULL);
- 菜单VO类代码
@Data
public class MenuVO {
private Long id;
private String title;
private String icon;
private String key;
private String path;
private Set children;
}
- 路由VO类代码
@Data
public class RouterVO {
/**
* 名称
*/
private String name;
/**
* 路径
*/
private String path;
/**
* 组件
*/
private String component;
}
- 菜单路由业务类代码
@Override
public List getMenusByUsername(String username) {
// 1.根据用户名查找角色
List roles = roleMapper.getRolesByUsername(username);
List menuVOList=new ArrayList<>();
for (RoleVO roleVO : roles) {
List
- 获取菜单与路由控制器代码
@GetMapping("{username}")
public Result> getMenus(@PathVariable("username") String username) {
return Result.ok(adminService.getMenusByUsername(username));
}
@GetMapping("router/{username}")
public Result> getRouter(@PathVariable("username") String username) {
return Result.ok(menuService.getRouter(username));
}
二、常见问题
- 动态路中添加之坑
vue lazy recursive ^./.*$
解决:
数据赋值给局部变量后添加。
let value = element.component;
element.component = function component(resolve) {
require(["@/views" + value], resolve);
};
- 刷新页面空白
解决:
在beforeEach中读取后台数据进行路由添加。