(十六)VUE权限菜单之动态路由

您所在的位置:网站首页 vueelementui动态菜单 (十六)VUE权限菜单之动态路由

(十六)VUE权限菜单之动态路由

#(十六)VUE权限菜单之动态路由| 来源: 网络整理| 查看: 265

新手做毕设-后台管理系统 任务十四 [VUE权限菜单之菜单管理](https://blog.csdn.net/wdyan297/article/details/128759641)任务十五 [VUE权限菜单之角色管理](https://blog.csdn.net/wdyan297/article/details/128759644)任务十六 VUE权限菜单之动态路由涉及到的实体类1. 用户实体类User2. 菜单实体类Menu3.角色实体类 Role4. 角色菜单实体类RoleMenu5. 登录用户类UserDTO 一、后端登录接口1. 修改userService类中的login方法 二、前端登录页面login.vue完整代码 三、动态路由index.js1. 修改router里的index.js2. 修改Aside组件 四、vue全局状态管理1.安装vuex2. 在src下新建文件夹store,并在文件夹中新建文件index.js3. 在main.js中引用,并添加到组件中4.引用 五、运行项目1. 登录2.菜单管理-新增一级菜单3.菜单管理-新增两个子菜单4.角色管理5.新增一个Test1.vue组件6.保存重新运行登录 任务总结

任务十四 VUE权限菜单之菜单管理 任务十五 VUE权限菜单之角色管理 任务十六 VUE权限菜单之动态路由

在任务十四中完成了权限菜单的菜单管理,主要实现了一级、二级等菜单的添加。任务十五中完成了权限菜单的角色管理,主要实现了角色增删改查已经为角色分配权限菜单。这些基本内容完成之后,就需要做本次任务的内容,即根据登录用户的角色进行动态路由分配,通过本次任务,大家能够: (1)理解用户认证、授权的概念; (2)学会登录验证以及基于网页的HTML5浏览器存储localStorage,存储用户信息和用户授权菜单; (3)掌握动态路由设计; (4)学会登录login基本方法。 首先梳理一下整个逻辑; (1)用户登录需要做两件事,一件事是验证用户的合法性,设置TOKEN,然后将信息进行浏览器存储;另一件事是得到这个合法用户的role(也就是角色标识,如ROLE_ADMIN),根据这个角色标识获取到角色ID,根据角色ID获取权限菜单。 (2)获取到权限菜单后,只是一个列表,在确保确实是menu表中的数据之外,还需要与menu表中数据进行匹配,对每一个menuid判断出它是一级菜单还是二级菜单; (3)将权限菜单数据同样进行浏览器存储; (4)用户验证合法、获取授权菜单后登录,进入主页面,原先固定的Aside组件需要根据动态存储的menus值重新设置。

涉及到的实体类

表结构为: 在这里插入图片描述 具体数据表的讲解请参考任务十五。 为了方便期间,如果只做这一个任务实现登录并能动态路由分配,把涉及到的实体类再罗列出,如果是跟着前面的任务在做,就不需要重复做了。

1. 用户实体类User package com.example.demo.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonFormat; import com.sun.javafx.beans.IDProperty; import lombok.Data; import java.sql.Date; @Data //可以使用 @TableName 表名注解指定当前实体类对应的表名,比如下面 User 实体类对应表名为 sys_user @TableName(value="sys_user") public class User { //可以使用 @TableId 注解(标注在主键上)和 @TableField 注解(标注在其他成员属性上)来指定对应的字段名 @TableId(value = "id",type = IdType.AUTO) private Integer id; private String username; private String password; private String email; private String phone; private String nickname; private String address; @TableField(value="created_time")//这样处理的主要目的是java对带有下划线的字段不识别,所以改为驼峰形式 private Date createdTime;//如果需要年月日格式的可以使用Date类型,如果需要具体到时分秒就使用String类型 private String avatar; private String role; } 2. 菜单实体类Menu package com.example.demo.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import java.util.List; @Data //可以使用 @TableName 表名注解指定当前实体类对应的表名,比如下面 Menu 实体类对应表名为 sys_menu @TableName(value="sys_menu") public class Menu { //可以使用 @TableId 注解(标注在主键上)和 @TableField 注解(标注在其他成员属性上)来指定对应的字段名 @TableId(value = "id",type = IdType.AUTO) private Integer id; private String name; private String path; private String icon; private String description; //在数据表中没有children这个字段,这个在做菜单的时候会用到,所以使用exist=false忽略 @TableField(exist = false) private List children; private Integer pid; @TableField(value="page_path")//这样处理的主要目的是java对带有下划线的字段不识别,所以改为驼峰形式 private String pagePath; } 3.角色实体类 Role package com.example.demo.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import java.sql.Date; @Data //可以使用 @TableName 表名注解指定当前实体类对应的表名,比如下面 Role 实体类对应表名为 sys_role @TableName(value="sys_role") public class Role { //可以使用 @TableId 注解(标注在主键上)和 @TableField 注解(标注在其他成员属性上)来指定对应的字段名 @TableId(value = "id",type = IdType.AUTO) private Integer id; private String name; private String description; private String flag; } 4. 角色菜单实体类RoleMenu package com.example.demo.entity; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; @Data //可以使用 @TableName 表名注解指定当前实体类对应的表名,比如下面 RoleMenu 实体类对应表名为 sys_role_menu @TableName(value="sys_role_menu") public class RoleMenu { private Integer roleId; private Integer menuId; } 5. 登录用户类UserDTO package com.example.demo.controller.dto; import com.example.demo.entity.Menu; import lombok.Data; import java.util.List; //UserDTO用来接受前端登录时传递的用户名和密码 @Data public class UserDTO { private String username; private String password; private String nickname; private String avatar; private String token; //把当前登录用户的角色以及他的菜单项带出来 private String role; private List menus; } 一、后端登录接口 1. 修改userService类中的login方法

基于任务十三 JWT+SpringSecurity实现基于Token的登录修改userService类中添加login方法。

package com.example.demo.service; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.demo.common.Constants; import com.example.demo.controller.dto.UserDTO; import com.example.demo.entity.Menu; import com.example.demo.entity.User; import com.example.demo.exception.ServiceException; import com.example.demo.mapper.RoleMapper; import com.example.demo.mapper.RoleMenuMapper; import com.example.demo.mapper.UserMapper; import com.example.demo.utils.TokenUtils; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.ArrayList; import java.util.List; @Service public class UserService extends ServiceImpl{ private UserMapper userMapper; @Resource private RoleMenuMapper roleMenuMapper; @Resource private RoleMapper roleMapper; @Resource private MenuService menuService; public Boolean saveUser(User user) { return saveOrUpdate(user); } public UserDTO login(UserDTO userDTO) { QueryWrapper queryWrapper=new QueryWrapper(); queryWrapper.eq("username",userDTO.getUsername()); queryWrapper.eq("password",userDTO.getPassword()); User one; try{ one=getOne(queryWrapper); }catch (Exception e){ throw new ServiceException(Constants.CODE_500,"系统错误");//这里假设查询了多于1条记录,就让他报系统错误 } if(one!=null){ //以下是登录判断业务 BeanUtil.copyProperties(one,userDTO,true); //设置token String token=TokenUtils.genToken(one.getId().toString(),one.getPassword().toString()); userDTO.setToken(token); String role=one.getRole();//查询出用户的角色标识,比如ROLE_ADMIN //设置用户的菜单列表 List roleMenus=getRoleMenus(role); userDTO.setMenus(roleMenus); return userDTO; }else { throw new ServiceException(Constants.CODE_600,"用户名或密码错误"); } } /** * 获取当前用户的菜单列表 */ private List getRoleMenus(String roleFlag){ //根据角色标识获取角色Id Integer roleId=roleMapper.selectByflag(roleFlag); //当前角色Id的所有菜单id集合 List menuIds=roleMenuMapper.selectByRoleId(roleId); //查出系统所有菜单 List menus=menuService.findMenus(""); //筛选当前用户菜单 List roleMenus=new ArrayList(); for(Menu menu:menus){ if(menuIds.contains(menu.getId())){ roleMenus.add(menu); } List children=menu.getChildren(); //removeIf移除children里面不在menuIds集合中的元素 children.removeIf(child->!menuIds.contains(child.getId())); } return roleMenus; } }

其中涉及到的方法在任务十四和任务十五中均有,如果前面没有做的童鞋,可以在前面的任务中找到。 roleMapper.selectByflag(roleFlag);在任务十五有详细讲解,完整代码为:

package com.example.demo.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.demo.entity.Role; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; public interface RoleMapper extends BaseMapper { //根据角色唯一标识flag查找角色id @Select("select id from sys_role where flag=#{flag}") Integer selectByflag(@Param("flag") String role); }

roleMenuMapper.selectByRoleId(roleId);在任务十五有详细讲解,完整代码为:

package com.example.demo.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.demo.entity.RoleMenu; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import java.util.List; public interface RoleMenuMapper extends BaseMapper { //根据角色id删除角色菜单数据 @Delete("delete from sys_role_menu where role_id=#{roleId}") int deleteByRoleId(@Param("roleId") Integer roleId); //根据角色id查找菜单id @Select("select menu_Id from sys_role_menu where role_id=#{roleId}") List selectByRoleId(@Param("roleId") Integer roleId); }

menuService.findMenus(“”);在任务十四有详细讲解,完整代码为:

package com.example.demo.service; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; importcom.example.demo.entity.Menu;; import com.example.demo.mapper.MenuMapper; import org.springframework.stereotype.Service; import java.util.List; import java.util.stream.Collectors; @Service public class MenuService extends ServiceImpl { public List findMenus(String name) { QueryWrapper queryWrapper=new QueryWrapper(); if(StrUtil.isNotBlank(name)){ queryWrapper.like("name",name); } List list = list(queryWrapper); // 找出pid为null的一级菜单 List parentNodes=list.stream().filter(menu -> menu.getPid()==null).collect(Collectors.toList()); //找出一级菜单为null的二级菜单放到Children中 for(Menu menu:parentNodes){ menu.setChildren(list.stream().filter(m->menu.getId().equals(m.getPid())).collect(Collectors.toList())); } System.out.println(parentNodes); return parentNodes; } } 二、前端登录页面 login.vue完整代码

这个页面与任务十三登录页面没有太多大的改动,因为那里已经实现了基于Token的认证,并且保存了用户信息。 继续添加一个获取菜单的接口即可。 完整代码如下:

登录 登录 重置 import {setRoutes} from "@/router"; import {resetRouter} from "@/router"; export default { name: "Login", data() { return { loginForm: { username:'', password:'' }, LoginFormRules:{ username:[ { required: true, message: '请输入用户名', trigger: 'blur' }, ], password:[ { required: true, message: '请输入密码', trigger: 'blur' }, ] } } }, methods:{ login(){ this.$refs['LoginFormRef'].validate(async (valid) => { if (valid) { this.request.post("http://localhost:8084/user/login",this.loginForm).then(res=>{ if(res.code=='200'){ localStorage.setItem("user",JSON.stringify(res.data));//存储用户信息到浏览器 localStorage.setItem("menus",JSON.stringify(res.data.menus));//存储用户权限菜单信息到浏览器 //动态设置当前用户的路由 setRoutes() this.$router.push("/home"); this.$message.success("登录成功"); }else{ this.$message.error(res.msg); } }) } }) }, resetLoginForm(){ this.$refs.LoginFormRef.resetFields() } } } .login_container{ background-color: #2b4b6b; height: 100%; } .login_box{ width: 350px; height: 300px; background-color: #fff; border-radius: 3px; position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%) } >

在这里插入图片描述 特别注意:这里调用了 setRoutes(),进行动态设置当前用户的路由

三、动态路由index.js 1. 修改router里的index.js

在这里插入图片描述 完整代码如下:

import Vue from 'vue' import VueRouter from 'vue-router' import Login from '../views/Login.vue' Vue.use(VueRouter) const routes = [ { path: '/login', name: 'Login', component: Login } ] const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes }) // 提供一个重置路由的方法 export const resetRouter=()=>{ router.matcher=new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes }) } //刷新页面会重置路由 export const setRoutes=()=>{ const storeMenus=localStorage.getItem("menus"); if(storeMenus){ //拼装动态路由 const manageRoute={ path: '/', name:'Manage', component: () => import('../views/Manage.vue'),redirect: '/login', children: []} const menus=JSON.parse(storeMenus) menus.forEach(item=>{ if(item.path){//当且仅当path不为空的时候才去设置路由 let itemMenu={ path: item.path.replace("/",""),name: item.name,component: () => import('../views/'+item.pagePath+'.vue')} manageRoute.children.push(itemMenu) }else if(item.children.length){ item.children.forEach(item=>{ if(item.path){ let itemMenu={ path: item.path.replace("/",""),name: item.name,component: () => import('../views/'+item.pagePath+'.vue')} manageRoute.children.push(itemMenu) } }) } }) //获取当前的路由对象名称数组 const currentRouteNames=router.getRoutes().map(v=>v.name) if(!currentRouteNames.includes('Manage')){ //动态加载到想在的路由对象 router.addRoute(manageRoute) } } } setRoutes() export default router 2. 修改Aside组件

任务十VUE侧边菜单栏导航 中我们修改过VUE侧边菜单栏导航,但那时候还是固定的。现在根据动态路由生成侧边菜单栏。 完整代码如下:

后台管理系统 {{item.name}} {{item.name}} {{subItem.name}} export default { name: "Aside", props:{ }, data(){ return{ menus:localStorage.getItem("menus")?JSON.parse(localStorage.getItem("menus")):[], opens:localStorage.getItem("menus")?JSON.parse(localStorage.getItem("menus")).map(v=>v.id+''):[], } }, methods:{ showmst(){ console(this.opens) } } }

理解 的重点就是根据已存储的menus动态部署侧栏。

四、vue全局状态管理

对于一些全局变量或者方法,可以进行全局设置,然后在任何时候都可以直接使用。比如常用的登出logout方法等。 具体方法为:

1.安装vuex npm install vuex --save

安装完成后注意观察一个vuex的版本。 vuex分3.x版本和4.x版本,分别对应vue2.0与3.0。 这里用的是VUE2.0,所以需要vuex是3.x版本。具体安装方法也可以到网上查找。 在这里插入图片描述

2. 在src下新建文件夹store,并在文件夹中新建文件index.js

在这里插入图片描述 index.js完整代码如下:

import Vue from 'vue' import Vuex from 'vuex' import router from '@/router' Vue.use(Vuex) const store=new Vuex.Store({ state:{ currntPathName:'' }, mutations:{ logout(){ localStorage.removeItem("user") localStorage.removeItem("menus") router.push("/login") } } }) export default store 3. 在main.js中引用,并添加到组件中

新增代码段如下:

import store from './store/index' new Vue({ router, store,//定义成全局变量 render: h => h(App) }).$mount('#app')

在这里插入图片描述

4.引用

比如在任务十五中当判断给管理员重新分配了权限,就需要重新登录,这时候先登出,就调用store中的logout方法。 在这里插入图片描述

五、运行项目 1. 登录

在这里插入图片描述

2.菜单管理-新增一级菜单

在这里插入图片描述

3.菜单管理-新增两个子菜单

在这里插入图片描述

4.角色管理

为管理员角色分配菜单 在这里插入图片描述 因为是给管理员重新分配权限菜单,所以点击“确定”后需要重新登录。

5.新增一个Test1.vue组件

在这里插入图片描述 代码为:

测试一个新路由 6.保存重新运行登录

在这里插入图片描述

任务总结

本次任务完成后,整个项目将根据不同用户部署不同菜单,实现动态路由,而且,只要有新的页面生成,只要添加到菜单中即可,就可以实现动态访问。 通过本次任务,大家能够: (1)理解动态路由的概念; (2)VUE对整个框架的管理机制有所了解; (3)学会VUE 状态管理定义与使用。 下一个任务将使用Echart做一个简单Home页面。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3