实战15 动态菜单和路由

目录

一、用户表增加角色

二、前端增加角色信息

三、服务端增加角色代码

四、动态菜单

五、动态路由

六、404页面

七、菜单部分选中


一、用户表增加角色

user增加role字段

role表增加唯一标示flag

二、前端增加角色信息

user.vue dialog 增加字段 列表增加role

三、服务端增加角色代码

1、UserDto增加角色和菜单属性

2、定义角色枚举类型

public enum RoleEnum {
    ROLE_ADMIN,ROLE_USER;
}

3、查询所有菜单

public List<Menu> findMenus(String name) {
    QueryWrapper<Menu> queryWrapper = new QueryWrapper<>();
    queryWrapper.like("name",name);
    List<Menu> list = list(queryWrapper);
    //找pid=null的一级菜单
    List<Menu> parentNodes = list.stream().filter(menu -> menu.getPid()==null).collect(Collectors.toList());
    //找出一级菜单的子菜单
    for(Menu menu:parentNodes){
        menu.setChildren(list.stream().filter(m ->menu.getId().equals(m.getPid())).collect(Collectors.toList()));
    }
    return parentNodes;
}

4、UserServiceImpl 新增获取当前角色的菜单列表

/**
 * 获取当前角色的菜单列表
 * @param flag  角色标签
 * @return
 */
private List<Menu> getRoleMenus(String flag){
    Integer roleId = roleMapper.selectByFlag(flag);
    //当前角色的所有菜单
    List<Integer> menuIds = roleMenuMapper.selectByRoleId(roleId);
    //筛选完成之后的list
    List<Menu> roleMenus = new ArrayList<>();
    // 查询所有菜单
    List<Menu> menus = iMenuService.findMenus("");
    //筛选当前用户菜单
    for (Menu menu: menus){
        if (menuIds.contains(menu.getId())){
            roleMenus.add(menu);
        }
        List<Menu> children = menu.getChildren();
        //移除children里面不在menuIds集合中的元素
        children.removeIf(child->!menuIds.contains(child.getId()));
    };
    return roleMenus;
}

5、UserController.java

public UserDto login(UserDto userDto) {
    User one = getUserInfo(userDto);
    if (one != null) {
        BeanUtil.copyProperties(one, userDto, true);
        String token = TokenUtils.genToken(one.getId().toString(),one.getPassword());
        userDto.setToken(token);
        String flag = one.getRole();
        //获取当前角色的菜单
        List<Menu> roleMenus = getRoleMenus(flag);
        userDto.setMenus(roleMenus);
        return userDto;
    } else {
        throw new ServiceException(Constants.CODE_600, "用户名或密码错误");             //自定义异常
    }
}

四、动态菜单

1、Login.vue 登录存储menus

    login() {
      this.$refs['userform'].validate((valid) => {
        if (valid) {
          this.request.post("user/login", this.user).then(res => {
            if (res.code === '200') {
              localStorage.setItem("user",JSON.stringify(res.data))
              localStorage.setItem("menus",JSON.stringify(res.data.menus))
              this.$router.push("/")
              this.$message.success("登录成功")
            } else {
              this.$message.error(res.msg);
            }
          })
        }
      });
    },

2、Aside.vue 动态菜单

       <div style="height: 60px;line-height: 60px;text-align: center;">
          <img src="../assets/logo.png" alt="" style="width: 20px;position: relative; top: 5px;margin-right: 5px;">
          <b style="color: white;" v-show="logoTextShow">后台管理系统</b>
        </div>
        <div v-for="item in menus" :key="item.id">
          <div v-if="item.path">
            <el-menu-item :index="item.path">
              <i :class="item.icon"></i>
              <span slot="title">{{item.name}}</span>
            </el-menu-item>
          </div>
          <div v-else>
            <el-submenu :index="item.id+ ''">
              <template slot="title">
                <i :class="item.icon"></i>
                <span slot="title">{{item.name}}</span>
              </template>
              <div v-for="subItem in item.children" :key="subItem.id">
                <el-menu-item :index="subItem.path">
                  <i :class="subItem.icon"></i>
                  <span slot="title">{{subItem.name}}</span>
                </el-menu-item>
              </div>
            </el-submenu>
          </div> 
        </div>
  data(){
    return{
      menus: localStorage.getItem("menus")?JSON.parse(localStorage.getItem("menus")): []
    }
  }

3、菜单默认展开

  data(){
    return{
      menus: localStorage.getItem("menus")?JSON.parse(localStorage.getItem("menus")): [],
      opens:localStorage.getItem("menus")?JSON.parse(localStorage.getItem("menus")).map(v=>v.id+''): []
    }
  }

五、动态路由

如何将后台返回的menus转化为路由,

menu表添加页面路径字段

Menu.java 添加PagePath

Menu.vue

表格列表新增页面路径字段

        <el-table-column prop="pagePath" label="页面路径" align="center">
        </el-table-column>

添加对话框添加页面路径字段

        <el-form-item label="页面路径">
          <el-input v-model="form.pagePath" autocomplete="off"></el-input>
        </el-form-item>

router.vue将固定路由改造成动态路由

export const setRoutes = ()=>{
  const storeMenus = localStorage.getItem("menus")
  if(storeMenus){
    const manageRoute = {path: '/',name: 'Manage',component: () => import('../views/Manage.vue'),redirect: '/home',children:[]}
    const menus = JSON.parse(storeMenus)
    menus.forEach(item => {
      let itemMenu = {path:item.path.replace("/",""), name: item.name ,component: ()=>import ('../views/'+item.pagePath+'.vue')}
      manageRoute.children.push(itemMenu)
    });
    //拼装动态路由
    router.addRoute(manageRoute)
  }
}

Login.vue 使用设置当前用户路由

export const setRoutes = ()=>{
  const storeMenus = localStorage.getItem("menus")
  if(storeMenus){
    const manageRoute = {path: '/',name: 'Manage',component: () => import('../views/Manage.vue'),redirect: '/home',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)   
          }

        })
      }
    });
    //拼装动态路由
    router.addRoute(manageRoute)
  }
}

错误路径刷新,后在正确路径刷新页面报空白

export const setRoutes = ()=>{
  const storeMenus = localStorage.getItem("menus")
  if(storeMenus){
    const manageRoute = {path: '/',name: 'Manage',component: () => import('../views/Manage.vue'),redirect: '/home',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 currentRoutesNames = router.getRoutes().map(v=>v.name)
    if(!currentRoutesNames.includes('Manage')){
      //拼装动态路由
      router.addRoute(manageRoute)
    }
    // router.addRoute(manageRoute)

  }
}
setRoutes()

六、404页面

<template>
  <div style="overflow: hidden; height: 100vh;">
    <img src="../assets/404.png" alt="" style="width: 100%;height:100%;">
  </div>
</template>
<script>
export default {
  name: "Not Found",
}
</script>
<style scoped>
</style>
  {
    path: '*',
    name: '404',
    component: () => import( '../views/404.vue')
  },
 

七、菜单部分选中

需要将父级菜单插入数据库

RoleServiceImpl.java

public void setRoleMenu(Integer roleId, List<Integer> menuIds){
    //删除当前所有绑定关系
    roleMenuMapper.deleteByRoleId(roleId);
    //再绑定
    for (Integer menuId : menuIds){
        Menu menu= iMenuService.getById(menuId);
        if (menu.getPid()!=null &&!menuIds.contains(menu.getPid())){//二级菜单,并且传过来的menuID数组里面没有它的父级ID
            //那么得补上这个父级ID
            RoleMenu roleMenu = new RoleMenu();
            roleMenu.setRoleId(roleId);
            roleMenu.setMenuId(menu.getPid());
            roleMenuMapper.insert(roleMenu);
        }
        RoleMenu roleMenu = new RoleMenu();
        roleMenu.setRoleId(roleId);
        roleMenu.setMenuId(menuId);
        roleMenuMapper.insert(roleMenu);
    }

store.js :登录

    logOut(){
      localStorage.removeItem("user")
      localStorage.removeItem("menus")
      router.push("/login")
    }

Role.vue :管理员操作需要重新登录

    saveRoleMenu(){
      this.request.post("/role/roleMenu/"+this.roleId,this.$refs.tree.getCheckedKeys()).then(res=>{
        console.log(res)
        if (res.code === '200') {
          this.$message.success("绑定成功")
          this.menuDialogVis = false 
          //操作管理员要重新登录
          if(this.roleFlag ==='ROLE_ADMIN'){
            this.$store.commit("logOut")
          }
          
        }else{
          this.$message.error(res.msg)
        } 
      })
    }

父菜单被勾选,子菜单部分勾选时,分配菜单窗口显示全部勾选问题处理

    selectMenu(role) {
      this.menuDialogVis = true
      this.roleId = role.id
      this.roleFlag = role.roleFlag
       //请求菜单数据
       this.request.get("/menu/", {}).then(res => {
        this.menuData = res.data
        //把后台返回菜单数据处理成id数组
        this.expends = this.menuData.map(v=>v.id)})

        //请求角色菜单
       this.request.get("/role/roleMenu/"+ this.roleId).then(res => {
        this.checks = res.data
        this.request.get("/menu/ids").then(r=>{
          const ids = r.data
          ids.forEach(id => {
            if (!this.checks.includes(id)) {
              this.$refs.tree.setChecked(id,false)
            }    
          })
        })
        this.menuDialogVis = true
      })
    },

MenuController.java

@GetMapping("/ids")
public Result findAllIds() {
    return Result.success(menuService.list().stream().map(Menu::getId));
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值