栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SpringBoot整合SpringSecurity实现权限控制(六):菜单管理

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringBoot整合SpringSecurity实现权限控制(六):菜单管理

系列文章目录
《SpringBoot整合SpringSecurity实现权限控制(一):实现原理》
《SpringBoot整合SpringSecurity实现权限控制(二):权限数据基本模型设计》
《SpringBoot整合SpringSecurity实现权限控制(三):前端动态装载路由与菜单》
《SpringBoot整合SpringSecurity实现权限控制(四):角色管理》
《SpringBoot整合SpringSecurity实现权限控制(五):用户管理》


本文目录
  • 一、前言
  • 二、需求分析
  • 三、后端实现
    • 3.1 创建菜单实体表
    • 3.2 添加操作菜单表的Mapper接口
    • 3.3 实现菜单的增删改查服务
    • 3.4 编写Controller层
  • 四、前端实现
    • 4.1 添加菜单api访问接口
    • 4.2 编写前端页面
    • 4.3 菜单渲染
  • 五、效果演示
  • 六、源码

一、前言
  • 后台管理系统可以通过菜单管理来实现系统的功能模块管理。通过清晰的树形菜单结构展现各种系统功能,无疑会大大提升系统的使用效率。

二、需求分析
  1. 系统功能模块需要按各个分类,形成菜单结构。比如说系统管理分类目录下,存在用户管理、角色管理、菜单管理等功能;系统设置分类目录下,存在商品设置、仓库设置、储位设置等功能。
  2. 每个菜单都需要包含以下信息:菜单id,菜单名称,父级菜单id,路由地址(vue-router),组件页面,图标,排序顺序等

    3、菜单管理需要实现基本的增删改查
三、后端实现 3.1 创建菜单实体表
  • 根据菜单的基本信息,创建菜单实体类。
@ApiModel(value = "菜单表")
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@TableName("sys_menu")
public class SysMenu {

    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    private String name;

    private String path;

    private String component;

    private String type;

    @TableField(value = "p_id", updateStrategy = FieldStrategy.IGNORED,jdbcType = JdbcType.BIGINT)
    private Long pid;

    private String icon;

    private Integer sort;

    private Boolean hidden;

    private Boolean cache;

    private String redirect;

    private String url;

    private Integer level;

    @JsonIgnore
    @Builder.Default
    @TableLogic
    private Boolean enabled = true;

    private Timestamp createTime;

    @Builder.Default
    private Timestamp updateTime = Timestamp.valueOf(LocalDateTime.now());

    public String getLabel() {
        return name;
    }
}
3.2 添加操作菜单表的Mapper接口
  • 通过继承mybatis-plus的baseMapper接口创建操作菜单表的DAO接口,该baseMapper接口已经包含了基本的增删改查操作。
@Mapper
public interface SysMenuMapper extends baseMapper {

    
    @Select("select * from sys_menu where p_id=#{pid} ")
    List selectChilds(Long pid);
}
3.3 实现菜单的增删改查服务
  • 服务接口定义:
public interface SysMenuService {

    
    SysMenu create (SysMenu menu);

    
    Boolean delete (Set ids);

    
    SysMenu update (SysMenu menu);

    
    SysMenu findById(Long id);

    
    SysMenu findByName(String name);

    
    SysMenu findByMenuPath(String path,Long pId);


    
    List list(SysMenuQueryDto sysMenuQueryDto);
}
  • 服务实现类:
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(propagation = Propagation.SUPPORTS, readonly = true, rollbackFor = Exception.class)
public class SysMenuServiceImpl implements SysMenuService {

    private final SysMenuMapper sysMenuMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public SysMenu create(SysMenu menu) {
        if (findByName(menu.getName()) != null) {
            throw new RuntimeException("该菜单名称已存在,不得重复添加!!");
        }
        if (findByMenuPath(menu.getPath(), menu.getPid()) != null) {
            throw new RuntimeException("该菜单路由已存在,不得重复添加!!");
        }
        menu.setCreateTime(Timestamp.valueOf(LocalDateTime.now()));
        if (sysMenuMapper.insert(menu) > 0) {
            return menu;
        }

        throw new RuntimeException("增加菜单失败!!");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean delete(Set ids) {
        if (sysMenuMapper.deleteBatchIds(ids) > 0) {
            return true;
        }
        throw new RuntimeException("删除菜单失败!!");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public SysMenu update(SysMenu menu) {
        SysMenu sysMenu = findByName(menu.getName());
        if (sysMenu != null && !sysMenu.getId().equals(menu.getId())) {
            throw new RuntimeException("该菜单名称已存在,不得重复添加!!");
        }
        sysMenu = findByMenuPath(menu.getPath(), menu.getId());
        if (sysMenu != null && !sysMenu.getId().equals(menu.getId())) {
            throw new RuntimeException("该菜单路由已存在,不得重复添加!!");
        }

        // 判断修改菜单的上级菜单不能是该修改菜单原有的子菜单
        if (menu.getPid() != null) {
            List childMenus = new ArrayList<>();
            childLoop(menu.getId(), childMenus);
            if (childMenus.stream().filter(m -> m.getId().equals(menu.getPid())).count() > 0) {
                throw new RuntimeException("上级菜单不能设置为下级子菜单,防止引起嵌套循环错误!!");
            }
        }

        if (sysMenuMapper.updateById(menu) > 0) {
            return menu;
        }
        throw new RuntimeException("更新菜单失败!!");

    }

    
    private void childLoop(Long id, List childMenus) {
        List sysMenus = sysMenuMapper.selectChilds(id);
        if (sysMenus == null || sysMenus.size() ==0) {
            return;
        }
        for (SysMenu m : sysMenus) {
            childMenus.add(m);
            childLoop(m.getId(), childMenus);
        }

    }

    @Override
    public SysMenu findById(Long id) {
        return sysMenuMapper.selectById(id);
    }

    @Override
    public SysMenu findByName(String name) {
        return sysMenuMapper.selectOne(new QueryWrapper().lambda().eq(SysMenu::getName, name));
    }

    @Override
    public SysMenu findByMenuPath(String path, Long pId) {
        return sysMenuMapper.selectOne(new QueryWrapper().lambda().eq(SysMenu::getPath, path)
                .and(wrapper -> wrapper.eq(SysMenu::getPid, pId)));
    }

    @Override
    public List list(SysMenuQueryDto sysMenuQueryDto) {
        QueryWrapper queryWrapper = new QueryWrapper<>();
        if (!StringUtils.isEmpty(sysMenuQueryDto.getName())) {
            queryWrapper.lambda().like(SysMenu::getName, sysMenuQueryDto.getName());
        }

        if (!StringUtils.isEmpty(sysMenuQueryDto.getCreateTimeStart())
                && !StringUtils.isEmpty(sysMenuQueryDto.getCreateTimeEnd())) {
            queryWrapper.lambda().between(SysMenu::getCreateTime,
                    new Timestamp(sysMenuQueryDto.getCreateTimeStart()),
                    new Timestamp(sysMenuQueryDto.getCreateTimeEnd()));
        }

        return sysMenuMapper.selectList(queryWrapper);
    }
}

3.4 编写Controller层
  • 形成以下API访问接口
@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("/api/menu")
@Api(tags = "菜单资源接口")
public class SysMenuController {

    private final SysMenuService sysMenuService;

    @ApiOperation("根据件查询菜单资源")
    @PostMapping("/list")
    public ResponseEntity getMenuList(@RequestBody SysMenuQueryDto sysMenuQueryDto) {
        return ResponseEntity.ok(sysMenuService.list(sysMenuQueryDto));
    }

    @ApiOperation("根据id获取单个菜单资源")
    @GetMapping("{id}")
    public ResponseEntity getMenuById(@PathVariable Long id) {
        return ResponseEntity.ok(sysMenuService.findById(id));
    }

    @ApiOperation("保存菜单资源")
    @PostMapping
    public ResponseEntity saveMenu(@RequestBody SysMenu sysMenu) {
        if (sysMenu.getId() != null) {
            return ResponseEntity.ok(sysMenuService.update(sysMenu));
        } else {
            return ResponseEntity.ok(sysMenuService.create(sysMenu));
        }
    }

    @ApiOperation("删除菜单资源")
    @DeleteMapping
    public ResponseEntity deleteMenu(@RequestBody Set ids) {
        return ResponseEntity.ok(sysMenuService.delete(ids));
    }
}

 
四、前端实现 
4.1 添加菜单api访问接口 
  • 根据后端的API在前端添加相应的访问接口
// menu.js 
import request from '@/utils/request'
// 根据条件查询
export function getMenuList(params) {
  return request({
    url: '/api/menu/list',
    method: 'post',
    data: JSON.stringify(params)
  })
}
// 根据菜单id获取菜单信息
export function getMenuById(id) {
  return request({
    url: '/api/menu/' + id,
    method: 'get'
  })
}
// 保存菜单信息
export function saveMenu(data) {
  return request({
    url: '/api/menu',
    method: 'post',
    data
  })
}
// 删除菜单
export function deleteMenu(ids) {
  return request({
    url: '/api/menu',
    method: 'delete',
    data: ids
  })
}

4.2 编写前端页面
  1. 构成查询条件增删改查按钮菜单树形表的布局
  2. 树形菜单可以展开或折叠
  3. 点击增加或编辑菜单按钮,填写相应菜单信息后,进行保存。
  • 专门编写了一个图标选择组件,该组件可以选择element-ui自带的图标
// SelectIcon.js




  • 上级菜单选择时,引用了TreeSelect组件,使用可参考https://www.vue-treeselect.cn/

  • 前端完整代码
    – /src/menu/index.vue









4.3 菜单渲染
  • 通过element-ui的 NavMenu可以实现菜单的前端渲染,本期请大家先了解一下该组件的基本情况,菜单动态渲染将在下期文章中详细说明。

  • 五、效果演示

六、源码
  • 前端
    https://gitee.com/zhuhuix/startup-frontend
    https://github.com/zhuhuix/startup-frontend
  • 后端
    https://gitee.com/zhuhuix/startup-backend
    https://github.com/zhuhuix/startup-backend
转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号