开源框架RuoYi-Vue学习之角色/菜单/权限
1.请参见项目代码地址文章目录一.菜单1.1 菜单表结构2.1 在system模块中编写菜单业务层接口ISysMenuService及实现类以及Mapper层,在admin模块中编写菜单控制层SysMenuController。在common模块中编写实体类SysMenu。2.2 根据用户id获取菜单列表一.菜单1.1 菜单表结构CREATE TABLE `sys_menu` (`menu_id`
·
1.请参见项目代码地址
一.菜单
1.1 菜单表结构
CREATE TABLE `sys_menu` (
`menu_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '菜单ID',
`menu_name` varchar(50) NOT NULL COMMENT '菜单名称',
`parent_id` bigint(20) DEFAULT '0' COMMENT '父菜单ID',
`order_num` int(4) DEFAULT '0' COMMENT '显示顺序',
`path` varchar(200) DEFAULT '' COMMENT '路由地址',
`component` varchar(255) DEFAULT NULL COMMENT '组件路径',
`query` varchar(255) DEFAULT NULL COMMENT '路由参数',
`is_frame` int(1) DEFAULT '1' COMMENT '是否为外链(0是 1否)',
`is_cache` int(1) DEFAULT '0' COMMENT '是否缓存(0缓存 1不缓存)',
`menu_type` char(1) DEFAULT '' COMMENT '菜单类型(M目录 C菜单 F按钮)',
`visible` char(1) DEFAULT '0' COMMENT '菜单状态(0显示 1隐藏)',
`status` char(1) DEFAULT '0' COMMENT '菜单状态(0正常 1停用)',
`perms` varchar(100) DEFAULT NULL COMMENT '权限标识',
`icon` varchar(100) DEFAULT '#' COMMENT '菜单图标',
`create_by` varchar(64) DEFAULT '' COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`remark` varchar(500) DEFAULT '' COMMENT '备注',
PRIMARY KEY (`menu_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2000 DEFAULT CHARSET=utf8mb4 COMMENT='菜单权限表';
2.1 在system模块中编写菜单业务层接口ISysMenuService及实现类以及Mapper层,在admin模块中编写菜单控制层SysMenuController。在common模块中编写实体类SysMenu。
/**
* 菜单 业务层
*/
public interface ISysMenuService {
}
/**
* 菜单 业务层处理
*/
@Service
public class SysMenuServiceImpl implements ISysMenuService {
@Autowired
private SysMenuMapper menuMapper;
}
/**
* 菜单表 数据层
*/
public interface SysMenuMapper {
}
/**
* 菜单信息
*/
@RestController
@RequestMapping("/system/menu")
public class SysMenuController extends BaseController {
@Autowired
private ISysMenuService menuService;
}
/**
* 菜单权限表 sys_menu
*/
public class SysMenu extends BaseEntity {
private static final long serialVersionUID = 1L;
//菜单ID
private Long menuId;
//菜单名称
private String menuName;
//父菜单ID
private Long parentId;
//显示顺序
private Integer orderNum;
//路由地址
private String path;
//组件路径
private String component;
//路由参数
private String query;
//是否为外链(0是 1否)
private String isFrame;
//是否缓存(0缓存 1不缓存)
private String isCache;
//类型(M目录 C菜单 F按钮)
private String menuType;
//显示状态(0显示 1隐藏)
private String visible;
//菜单状态(0显示 1隐藏)
private String status;
//权限字符串
private String perms;
//菜单图标
private String icon;
//备注
private String remark;
//************************以下为非数据库字段********************888
//父菜单名称
private String parentName;
//子菜单
private List<SysMenu> children = new ArrayList<SysMenu>();
//......省略getter和setter.....
二.根据用户id获取菜单列表
2.1 后端代码如下
<resultMap type="com.mashirro.common.domain.SysMenu" id="SysMenuResult">
<id property="menuId" column="menu_id" />
<result property="menuName" column="menu_name" />
<result property="parentId" column="parent_id" />
<result property="orderNum" column="order_num" />
<result property="path" column="path" />
<result property="component" column="component" />
<result property="query" column="query" />
<result property="isFrame" column="is_frame" />
<result property="isCache" column="is_cache" />
<result property="menuType" column="menu_type" />
<result property="visible" column="visible" />
<result property="status" column="status" />
<result property="perms" column="perms" />
<result property="icon" column="icon" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateTime" column="update_time" />
<result property="updateBy" column="update_by" />
<result property="remark" column="remark" />
<result property="parentName" column="parent_name" />
</resultMap>
<sql id="selectMenuVo">
select menu_id, menu_name, parent_id, order_num, path, component, `query`, is_frame, is_cache, menu_type, visible, status, ifnull(perms,'') as perms, icon, create_time
from sys_menu
</sql>
<select id="selectMenuList" parameterType="com.mashirro.common.domain.SysMenu" resultMap="SysMenuResult">
<include refid="selectMenuVo"/>
<where>
<if test="menuName != null and menuName != ''">
AND menu_name like concat('%', #{menuName}, '%')
</if>
<if test="visible != null and visible != ''">
AND visible = #{visible}
</if>
<if test="status != null and status != ''">
AND status = #{status}
</if>
</where>
order by parent_id, order_num
</select>
<select id="selectMenuListByUserId" parameterType="com.mashirro.common.domain.SysMenu" resultMap="SysMenuResult">
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
from sys_menu m
left join sys_role_menu rm on m.menu_id = rm.menu_id
left join sys_user_role ur on rm.role_id = ur.role_id
left join sys_role ro on ur.role_id = ro.role_id
where ur.user_id = #{params.userId}
<if test="menuName != null and menuName != ''">
AND m.menu_name like concat('%', #{menuName}, '%')
</if>
<if test="visible != null and visible != ''">
AND m.visible = #{visible}
</if>
<if test="status != null and status != ''">
AND m.status = #{status}
</if>
order by m.parent_id, m.order_num
</select>
2.2 使用postman测试
{
"msg": "操作成功",
"code": 200,
"data": [
{
"createBy": null,
"createTime": "2022-03-07 02:53:29",
"updateBy": null,
"updateTime": null,
"params": {},
"menuId": 1,
"menuName": "系统管理",
"parentId": 0,
"orderNum": 1,
"path": "system",
"component": null,
"query": "",
"isFrame": "1",
"isCache": "0",
"menuType": "M",
"visible": "0",
"status": "0",
"perms": "",
"icon": "system",
"remark": null,
"parentName": null,
"children": []
},
{
"createBy": null,
"createTime": "2022-03-07 02:53:29",
"updateBy": null,
"updateTime": null,
"params": {},
"menuId": 4,
"menuName": "若依官网",
"parentId": 0,
"orderNum": 4,
"path": "http://ruoyi.vip",
"component": null,
"query": "",
"isFrame": "0",
"isCache": "0",
"menuType": "M",
"visible": "0",
"status": "0",
"perms": "",
"icon": "guide",
"remark": null,
"parentName": null,
"children": []
},
{
"createBy": null,
"createTime": "2022-03-07 02:53:29",
"updateBy": null,
"updateTime": null,
"params": {},
"menuId": 100,
"menuName": "用户管理",
"parentId": 1,
"orderNum": 1,
"path": "user",
"component": "system/user/index",
"query": "",
"isFrame": "1",
"isCache": "0",
"menuType": "C",
"visible": "0",
"status": "0",
"perms": "system:user:list",
"icon": "user",
"remark": null,
"parentName": null,
"children": []
},
{
"createBy": null,
"createTime": "2022-03-07 02:53:29",
"updateBy": null,
"updateTime": null,
"params": {},
"menuId": 102,
"menuName": "菜单管理",
"parentId": 1,
"orderNum": 3,
"path": "menu",
"component": "system/menu/index",
"query": "",
"isFrame": "1",
"isCache": "0",
"menuType": "C",
"visible": "0",
"status": "0",
"perms": "system:menu:list",
"icon": "tree-table",
"remark": null,
"parentName": null,
"children": []
},
{
"createBy": null,
"createTime": "2022-03-07 02:53:29",
"updateBy": null,
"updateTime": null,
"params": {},
"menuId": 107,
"menuName": "通知公告",
"parentId": 1,
"orderNum": 8,
"path": "notice",
"component": "system/notice/index",
"query": "",
"isFrame": "1",
"isCache": "0",
"menuType": "C",
"visible": "0",
"status": "0",
"perms": "system:notice:list",
"icon": "message",
"remark": null,
"parentName": null,
"children": []
},
{
"createBy": null,
"createTime": "2022-03-07 02:53:29",
"updateBy": null,
"updateTime": null,
"params": {},
"menuId": 1001,
"menuName": "用户查询",
"parentId": 100,
"orderNum": 1,
"path": "",
"component": "",
"query": "",
"isFrame": "1",
"isCache": "0",
"menuType": "F",
"visible": "0",
"status": "0",
"perms": "system:user:query",
"icon": "#",
"remark": null,
"parentName": null,
"children": []
},
{
"createBy": null,
"createTime": "2022-03-07 02:53:29",
"updateBy": null,
"updateTime": null,
"params": {},
"menuId": 1013,
"menuName": "菜单查询",
"parentId": 102,
"orderNum": 1,
"path": "",
"component": "",
"query": "",
"isFrame": "1",
"isCache": "0",
"menuType": "F",
"visible": "0",
"status": "0",
"perms": "system:menu:query",
"icon": "#",
"remark": null,
"parentName": null,
"children": []
},
{
"createBy": null,
"createTime": "2022-03-07 02:53:29",
"updateBy": null,
"updateTime": null,
"params": {},
"menuId": 1036,
"menuName": "公告查询",
"parentId": 107,
"orderNum": 1,
"path": "#",
"component": "",
"query": "",
"isFrame": "1",
"isCache": "0",
"menuType": "F",
"visible": "0",
"status": "0",
"perms": "system:notice:query",
"icon": "#",
"remark": null,
"parentName": null,
"children": []
}
]
}
2.3 前端将list处理为树结构
/**
* 构造树型结构数据
* @param {*} data 数据源
* @param {*} id id字段 默认 'id'
* @param {*} parentId 父节点字段 默认 'parentId'
* @param {*} children 孩子节点字段 默认 'children'
*/
export function handleTree(data, id, parentId, children) {
let config = {
id: id || 'id',
parentId: parentId || 'parentId',
childrenList: children || 'children'
};
var childrenListMap = {};
var nodeIds = {};
var tree = [];
for (let d of data) {
let parentId = d[config.parentId];
if (childrenListMap[parentId] == null) {
childrenListMap[parentId] = [];
}
nodeIds[d[config.id]] = d;
childrenListMap[parentId].push(d);
}
for (let d of data) {
let parentId = d[config.parentId];
if (nodeIds[parentId] == null) {
tree.push(d);
}
}
for (let t of tree) {
adaptToChildrenList(t);
}
function adaptToChildrenList(o) {
if (childrenListMap[o[config.id]] !== null) {
o[config.childrenList] = childrenListMap[o[config.id]];
}
if (o[config.childrenList]) {
for (let c of o[config.childrenList]) {
adaptToChildrenList(c);
}
}
}
return tree;
}
[
{
"searchValue": null,
"createBy": null,
"createTime": "2022-03-07 10:53:29",
"updateBy": null,
"updateTime": null,
"remark": null,
"params": {},
"menuId": 1,
"menuName": "系统管理",
"parentName": null,
"parentId": 0,
"orderNum": 1,
"path": "system",
"component": null,
"query": "",
"isFrame": "1",
"isCache": "0",
"menuType": "M",
"visible": "0",
"status": "0",
"perms": "",
"icon": "system",
"children": [
{
"searchValue": null,
"createBy": null,
"createTime": "2022-03-07 10:53:29",
"updateBy": null,
"updateTime": null,
"remark": null,
"params": {},
"menuId": 100,
"menuName": "用户管理",
"parentName": null,
"parentId": 1,
"orderNum": 1,
"path": "user",
"component": "system/user/index",
"query": "",
"isFrame": "1",
"isCache": "0",
"menuType": "C",
"visible": "0",
"status": "0",
"perms": "system:user:list",
"icon": "user",
"children": [
{
"searchValue": null,
"createBy": null,
"createTime": "2022-03-07 10:53:29",
"updateBy": null,
"updateTime": null,
"remark": null,
"params": {},
"menuId": 1001,
"menuName": "用户查询",
"parentName": null,
"parentId": 100,
"orderNum": 1,
"path": "",
"component": "",
"query": "",
"isFrame": "1",
"isCache": "0",
"menuType": "F",
"visible": "0",
"status": "0",
"perms": "system:user:query",
"icon": "#"
}
]
},
{
"searchValue": null,
"createBy": null,
"createTime": "2022-03-07 10:53:29",
"updateBy": null,
"updateTime": null,
"remark": null,
"params": {},
"menuId": 102,
"menuName": "菜单管理",
"parentName": null,
"parentId": 1,
"orderNum": 3,
"path": "menu",
"component": "system/menu/index",
"query": "",
"isFrame": "1",
"isCache": "0",
"menuType": "C",
"visible": "0",
"status": "0",
"perms": "system:menu:list",
"icon": "tree-table",
"children": [
{
"searchValue": null,
"createBy": null,
"createTime": "2022-03-07 10:53:29",
"updateBy": null,
"updateTime": null,
"remark": null,
"params": {},
"menuId": 1013,
"menuName": "菜单查询",
"parentName": null,
"parentId": 102,
"orderNum": 1,
"path": "",
"component": "",
"query": "",
"isFrame": "1",
"isCache": "0",
"menuType": "F",
"visible": "0",
"status": "0",
"perms": "system:menu:query",
"icon": "#"
}
]
},
{
"searchValue": null,
"createBy": null,
"createTime": "2022-03-07 10:53:29",
"updateBy": null,
"updateTime": null,
"remark": null,
"params": {},
"menuId": 107,
"menuName": "通知公告",
"parentName": null,
"parentId": 1,
"orderNum": 8,
"path": "notice",
"component": "system/notice/index",
"query": "",
"isFrame": "1",
"isCache": "0",
"menuType": "C",
"visible": "0",
"status": "0",
"perms": "system:notice:list",
"icon": "message",
"children": [
{
"searchValue": null,
"createBy": null,
"createTime": "2022-03-07 10:53:29",
"updateBy": null,
"updateTime": null,
"remark": null,
"params": {},
"menuId": 1036,
"menuName": "公告查询",
"parentName": null,
"parentId": 107,
"orderNum": 1,
"path": "#",
"component": "",
"query": "",
"isFrame": "1",
"isCache": "0",
"menuType": "F",
"visible": "0",
"status": "0",
"perms": "system:notice:query",
"icon": "#"
}
]
}
]
},
{
"searchValue": null,
"createBy": null,
"createTime": "2022-03-07 10:53:29",
"updateBy": null,
"updateTime": null,
"remark": null,
"params": {},
"menuId": 4,
"menuName": "若依官网",
"parentName": null,
"parentId": 0,
"orderNum": 4,
"path": "http://ruoyi.vip",
"component": null,
"query": "",
"isFrame": "0",
"isCache": "0",
"menuType": "M",
"visible": "0",
"status": "0",
"perms": "",
"icon": "guide"
}
]
三.角色
3.1 角色表结构
CREATE TABLE `sys_role` (
`role_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '角色ID',
`role_name` varchar(30) NOT NULL COMMENT '角色名称',
`role_key` varchar(100) NOT NULL COMMENT '角色权限字符串',
`role_sort` int(4) NOT NULL COMMENT '显示顺序',
`data_scope` char(1) DEFAULT '1' COMMENT '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)',
`menu_check_strictly` tinyint(1) DEFAULT '1' COMMENT '菜单树选择项是否关联显示',
`dept_check_strictly` tinyint(1) DEFAULT '1' COMMENT '部门树选择项是否关联显示',
`status` char(1) NOT NULL COMMENT '角色状态(0正常 1停用)',
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
`create_by` varchar(64) DEFAULT '' COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=101 DEFAULT CHARSET=utf8mb4 COMMENT='角色信息表';
3.2 在system模块中编写角色业务层接口ISysRoleService及实现类以及Mapper层,在admin模块中编写角色控制层SysRoleController。在common模块中编写实体类SysRole。
/**
* 角色业务层
*/
public interface ISysRoleService {
}
/**
* 角色表 数据层
*/
public interface SysRoleMapper {
}
/**
* 角色 业务层处理
*/
@Service
public class SysRoleServiceImpl implements ISysRoleService {
@Autowired
private SysRoleMapper roleMapper;
}
/**
* 角色信息
*/
@RestController
@RequestMapping("/system/role")
public class SysRoleController extends BaseController {
@Autowired
private ISysRoleService roleService;
}
/**
* 角色表 sys_role
*/
public class SysRole extends BaseEntity {
private static final long serialVersionUID = 1L;
//角色ID
private Long roleId;
//角色名称
private String roleName;
//角色权限字符串
private String roleKey;
//角色排序(显示顺序)
private String roleSort;
//数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限)
private String dataScope;
//菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示)
private boolean menuCheckStrictly;
//部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 )
private boolean deptCheckStrictly;
//角色状态(0正常 1停用)
private String status;
//删除标志(0代表存在 2代表删除)
private String delFlag;
//备注
private String remark;
//*************************以下字段为非数据库字段***************
//用户是否存在此角色标识 默认不存在
private boolean flag = false;
//菜单id数组
private Long[] menuIds;
//部门id数组(数据权限)
private Long[] deptIds;
//.....省略getter和setter方法
}
四.新建角色时获取菜单树下拉框
4.1 在SysMenuController中添加treeselect方法,重点是menuService.buildMenuTreeSelect构建树结构。在common模块中新增TreeSelect树结构实体类。
/**
* 获取菜单下拉树列表
*/
@GetMapping("/treeselect")
public AjaxResult treeselect(SysMenu menu) {
SysUser sysUser = new SysUser();
sysUser.setUserName("admin");
sysUser.setUserId(1L);
List<SysMenu> menus = menuService.selectMenuList(menu, sysUser);
return AjaxResult.success(menuService.buildMenuTreeSelect(menus));
}
/**
* Treeselect树结构实体类
*/
public class TreeSelect implements Serializable {
private static final long serialVersionUID = 1L;
//节点ID
private Long id;
//节点名称
private String label;
//子节点
private List<TreeSelect> children;
public TreeSelect() {
}
public TreeSelect(SysMenu menu) {
this.id = menu.getMenuId();
this.label = menu.getMenuName();
//TreeSelect::new->构造方法引用->在每个SysMenu上调用构造器(map方法会为各个列表元素调用TreeSelect(SysMenu menu)构造器)。
//如果有多个TreeSelect构造器,编译器会选择有一个SysMenu参数的构造器,因为它从上下文推导出这是在对一个SysMenu调用构造器
this.children = menu.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList());
}
//....省略getter和setter方法....
}
/**
* 构建前端所需要下拉树结构
* @param menus 菜单列表
* @return 下拉树结构列表
*/
@Override
public List<TreeSelect> buildMenuTreeSelect(List<SysMenu> menus) {
List<SysMenu> menuTrees = buildMenuTree(menus);
return menuTrees.stream().map(TreeSelect::new).collect(Collectors.toList());
}
/**
* 构建前端所需要树结构
* @param menus 菜单列表
* @return 树结构列表
*/
private List<SysMenu> buildMenuTree(List<SysMenu> menus) {
List<SysMenu> returnList = new ArrayList<SysMenu>();
List<Long> tempList = new ArrayList<Long>();
for (SysMenu m : menus) {
tempList.add(m.getMenuId());
}
for (Iterator<SysMenu> iterator = menus.iterator(); iterator.hasNext(); ) {
SysMenu menu = iterator.next();
// 如果是顶级节点, 遍历该父节点的所有子节点(顶级节点的parentId在sys_menu表中无记录)
if (!tempList.contains(menu.getParentId())) {
if (hasChild(menus, menu)) {
//如果有子节点,递归
recursionFn(menus, menu);
}
returnList.add(menu);
}
}
if (returnList.isEmpty()) {
returnList = menus;
}
return returnList;
}
/**
* 递归列表
* @param list
* @param t
*/
private void recursionFn(List<SysMenu> list, SysMenu t) {
// 得到子节点列表
List<SysMenu> childList = getChildList(list, t);
t.setChildren(childList);
for (SysMenu tChild : childList) {
if (hasChild(list, tChild)) {
recursionFn(list, tChild);
}
}
}
/**
* 得到子节点列表
*/
private List<SysMenu> getChildList(List<SysMenu> list, SysMenu t) {
List<SysMenu> tlist = new ArrayList<SysMenu>();
Iterator<SysMenu> it = list.iterator();
while (it.hasNext()) {
SysMenu n = it.next();
if (n.getParentId().longValue() == t.getMenuId().longValue()) {
tlist.add(n);
}
}
return tlist;
}
/**
* 判断是否有子节点
*/
private boolean hasChild(List<SysMenu> list, SysMenu t) {
return getChildList(list, t).size() > 0;
}
4.2 使用postman测试
五.修改角色时获取菜单树下拉框同时勾选展示角色对应的菜单
5.1 在SysMenuController中添加roleMenuTreeselect方法,重点勾选展示角色对应的菜单权限->menuService.selectMenuListByRoleId。
/**
* menus:菜单下拉树列表
* checkedKeys:角色对应的菜单权限
*/
@GetMapping(value = "/roleMenuTreeselect/{roleId}")
public AjaxResult roleMenuTreeselect(@PathVariable("roleId") Long roleId) {
SysUser sysUser = new SysUser();
sysUser.setUserName("admin");
sysUser.setUserId(1L);
List<SysMenu> menus = menuService.selectMenuList(new SysMenu(), sysUser);
AjaxResult ajax = AjaxResult.success();
ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId));
ajax.put("menus", menuService.buildMenuTreeSelect(menus));
return ajax;
}
/**
* 根据角色ID查询菜单id列表
* @param roleId 角色ID
* @return 选中菜单id列表
*/
@Override
public List<Long> selectMenuListByRoleId(Long roleId) {
SysRole role = roleMapper.selectRoleById(roleId);
return menuMapper.selectMenuListByRoleId(roleId, role.isMenuCheckStrictly());
}
<select id="selectMenuListByRoleId" resultType="Long">
select m.menu_id
from sys_menu m
left join sys_role_menu rm on m.menu_id = rm.menu_id
where rm.role_id = #{roleId}
<!-- 菜单树选择项是否关联显示:与前端相关 -->
<if test="menuCheckStrictly">
and m.menu_id not in (select m.parent_id from sys_menu m inner join sys_role_menu rm on m.menu_id = rm.menu_id and rm.role_id = #{roleId})
</if>
order by m.parent_id, m.order_num
</select>
5.2 使用postman测试
更多推荐
已为社区贡献4条内容
所有评论(0)