开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第14天,点击查看活动详情
前言
上文实现了对于角色资源(权限)的获取和分配。但是还有一个功能是提供给前端所有的权限选项以便它们能够选择资源,这是必要的,尽管这在测试时传一个权限id的数组就可以实现对角色分配资源,但前端要求的是可视化,所以我们继续来实现这个接口。与此同时,还有删除角色和删除权限时都会涉及到角色权限表的改动,所以我们也来实现一下。
获取所有权限资源
获取权限资源的时候不需要获取所有的权限字段,一般而言只需要权限id、权限标题即可。所以我们需要将之前获取的菜单列表再进行改动。还有就是最好将菜单和按钮按不同的字段名分开,例如菜单为children数组,按钮为perms数组。结构如下示例:
[
{
label:"系统管理",
value:1,
children:[
{
label:"用户管理",
value:2,
perms:[
{
label:'用户新增',
value:10
},
{
...
}
]
},
{
...
}
]
}
]
首先我们先通过之前定义的方法获取到完整的权限树状结构,如图:
我们根据得到的结构进行改造。我们可以看到每个菜单可能会有子菜单或者按钮,要根据它们的类别给定children或者perms数组存储,然后若存在孩子再进行递归,实现如下。
router_handler/menu.js
// 将菜单格式化为{value,label}的形式
function filterRoutes(routes) {
const res = [];
routes.forEach((item) => {
// 目录、菜单是否存在子菜单、按钮
if (item.children) {
// 检测子菜单是否存在按钮
if (item.children.some((item) => item.type === 'B')) {
const perms = [];
const children = [];
// 若是按钮的用perms数组存储、是菜单的用children存储
item.children.forEach((_item) => {
if (_item.type === 'B') {
perms.push({
value: _item.menu_id,
label: _item.title,
permission: _item.permission
});
} else {
children.push(_item);
}
});
const menuItem = {
value: item.menu_id,
label: item.title,
children: children || undefined,
perms: perms || undefined
};
// 继续递归判断菜单之下是否还有孩子
if (menuItem.children && menuItem.children.length) {
menuItem.children = filterRoutes(menuItem.children);
}
res.push(menuItem);
}
// 子菜单不存在按钮
else {
const menuItem = {
value: item.menu_id,
label: item.title,
children: item.children || undefined
};
// 继续递归判断菜单之下是否还有孩子
if (menuItem.children && menuItem.children.length) {
menuItem.children = filterRoutes(menuItem.children);
}
res.push(menuItem);
}
}
// 不存在子菜单
else {
const menuItem = {
value: item.menu_id,
label: item.title
};
res.push(menuItem);
}
});
return res;
}
实现了此方法后添加获取菜单项的路由。
router/menu.js
// 获取菜单项
router.get('/listMenuOptions', handler.getMenuOptions);
定义路由的回调函数,先获取到权限的树状结构。再调用上述实现的方法即可。
router_handler/menu.js
exports.getMenuOptions = (req, res) => {
MenusModel.getListTree(req.query).then(function (menuTree) {
const filterTree = filterRoutes(menuTree);
return res.send({
code: 0,
message: '获取成功',
data: filterTree || []
});
});
};
删除权限
首先,我们在删除接口获取到要删除的权限id。但是,删除权限不只是删除权限表里的该id的权限,可能有以该id作为父类菜单的子菜单、按钮,也需要删除。随后,因为角色、权限表具有联系,也需要将角色权限表中含有该权限id的记录删除。那我们便着手来实现下。
model/menu.js
// 删除菜单、子菜单及其角色权限表中含有此id权限的记录
MenusModel.deleteMenu = async function (menu_id) {
const t = await sequelize.transaction();
try {
let delete_ids = [];
// 找到菜单表中所有为此menu_id和父id为此menu_id的记录
const menus = await MenusModel.findAll({
where: { [Op.or]: [{ menu_id: menu_id }, { parent_id: menu_id }] }
});
// 只保留菜单id
delete_ids = menus.map((item) => {
return item.menu_id;
});
// 删除权限表中对应记录
await MenusModel.destroy({
where: { menu_id: delete_ids }
});
// 删除角色权限表对应记录
await RolesMenusModel.destroy({
where: { menu_id: delete_ids }
});
t.commit();
return true;
} catch (e) {
t.rollback();
return e.message;
}
};
然后还是添加路由再调用此方法返回信息即可。
删除角色
我们在手把手教你实现一个vue3+ts+nodeJS后台管理系统(十)已经实现过删除角色的方法,只需要再添加删除角色时删去角色权限表对应角色的记录的功能即可。
model/role.js
// 删除角色的方法
RolesModel.delRole = async function (role_ids) {
const t = await sequelize.transaction();
try {
// 删除角色表中角色id数组的角色
await RolesModel.destroy({
where: { role_id: role_ids }
});
// 删除用户角色表中角色id数组的角色记录
await UsersRolesModel.destroy({
where: { role_id: role_ids }
});
// 添加
// 删除角色权限表中角色id数组的角色记录
await RolesMenusModel.destroy({
where: { role_id: role_ids }
});
t.commit();
return true;
} catch (e) {
t.rollback();
return false;
}
};