SpringBoot3+Vue3 前后端分离项目实现基于RBAC权限访问控制-(1)权限管理
SpringBoot3+Vue3 前后端分离项目实现基于RBAC权限访问控制-(2)角色管理
1、SpringBoot 3 后端实现角色管理
1.1 Entity-实体类
1.1.1 Role
package com.dragon.springboot3vue3.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* <p>
* 角色表
* </p>
*/
@Data
@Schema(name = "Role", description = "角色表")
public class Role implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "主键")
@TableId(value = "id", type = IdType.ASSIGN_UUID)
private String id;
@Schema(description = "角色名称")
private String roleName;
@Schema(description = "角色")
private String role;
@Schema(description = "创建人ID")
private String creatorId;
@Schema(description = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@Schema(description = "更新时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime ts;
@Schema(description = "逻辑删除字段 0.正常 1.删除")
@TableLogic
private Integer deleteFlag;
}
1.1.2 RoleMenu
package com.dragon.springboot3vue3.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* <p>
* 角色菜单表
* </p>
*/
@Data
@TableName("role_menu")
@Schema(name = "RoleMenu", description = "角色菜单表")
public class RoleMenu implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "主键")
@TableId(value = "id", type = IdType.ASSIGN_UUID)
private String id;
@Schema(description = "角色ID")
private String roleId;
@Schema(description = "菜单ID")
private String menuId;
@Schema(description = "创建人ID")
@TableField(fill = FieldFill.INSERT)
private String creatorId;
@Schema(description = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@Schema(description = "更新时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime ts;
@Schema(description = "逻辑删除字段 0.正常 1.删除")
private Integer deleteFlag;
}
1.2 RoleController
package com.dragon.springboot3vue3.controller;
import cn.dev33.satoken.util.SaResult;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dragon.springboot3vue3.controller.dto.entityDto.RoleDto;
import com.dragon.springboot3vue3.controller.dto.pageDto.RolePageDto;
import com.dragon.springboot3vue3.entity.Menu;
import com.dragon.springboot3vue3.entity.Role;
import com.dragon.springboot3vue3.entity.RoleMenu;
import com.dragon.springboot3vue3.mapper.RoleMapper;
import com.dragon.springboot3vue3.service.IMenuService;
import com.dragon.springboot3vue3.service.IRoleMenuService;
import com.dragon.springboot3vue3.service.IRoleService;
import com.dragon.springboot3vue3.utils.StringDTO;
import com.dragon.springboot3vue3.utils.StringIdsDTO;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* <p>
* 角色表 前端控制器
* </p>
*/
@Tag(name = "角色接口")
@RestController
@RequestMapping("/role")
public class RoleController {
@Autowired
private IRoleService roleService;
@Autowired
private RoleMapper roleMapper;
@Autowired
private IMenuService menuService;
@Autowired
private IRoleMenuService roleMenuService;
@Operation(summary = "分页列表")
@PostMapping("/list")
public SaResult list(@RequestBody RolePageDto pageDto){
// 创建分页对象
Page<RoleDto> page=new Page<>(pageDto.getCurrentPage(), pageDto.getPageSize());
// 构造查询条件
MPJLambdaWrapper<Role> qw=new MPJLambdaWrapper<Role>()
.like(StringUtils.isNotBlank(pageDto.getRoleName()),Role::getRoleName, pageDto.getRoleName());
// 根据查询条件,将结果封装到分页对象
Page<RoleDto> response=roleMapper.selectJoinPage(page, RoleDto.class,qw);
// 遍历所有角色,设置角色对应的菜单列表
response.getRecords().forEach(item->{
List<RoleMenu> roleMenuList = roleMenuService.lambdaQuery().eq(RoleMenu::getRoleId, item.getId()).list();
List<String> menuIds = roleMenuList.stream().map(RoleMenu::getMenuId).toList();
// 菜单id不为空,查询菜单信息填充到 RoleDto.menuList
if(CollectionUtil.isNotEmpty(menuIds)){
List<Menu> menuList = menuService.lambdaQuery().in(Menu::getId, menuIds).list();
item.setMenuList(menuList);
}
});
return SaResult.ok().setData(response);
}
@Operation(summary = "所有角色列表")
@GetMapping("/getAllList")
public SaResult getAllList(){
return SaResult.ok().setData(roleService.list());
}
@Operation(summary = "新增或更新")
@PostMapping("/saveOrUpdate")
public SaResult saveOrUpdate(@RequestBody @Validated Role roleDto){
Role role=new Role();
BeanUtils.copyProperties(roleDto,role);
roleService.saveOrUpdate(role);
return SaResult.ok();
}
@Operation(summary = "删除")
@DeleteMapping("/remove")
public SaResult remove(@RequestBody @Validated StringIdsDTO stringIdsDTO){
roleService.removeByIds(stringIdsDTO.getIds());
return SaResult.ok();
}
@Operation(summary = "根据角色标识获取角色对象")
@PostMapping("/getRole")
public SaResult getRole(@RequestBody @Validated StringDTO stringDTO){
Role role = roleService.lambdaQuery().eq(Role::getRole, stringDTO.getStr()).one();
return SaResult.ok().setData(role);
}
@Operation(summary = "新增或更新角色-菜单关联关系")
@PostMapping("/saveRoleMenu")
public SaResult saveRoleMenu(@RequestBody List<RoleMenu> list){
// 先删除关联关系
List<RoleMenu> list1 = roleMenuService.lambdaQuery().eq(RoleMenu::getRoleId, list.getFirst().getRoleId()).list();
roleMenuService.removeBatchByIds(list1);
// 新增关联关系
roleMenuService.saveOrUpdateBatch(list);
return SaResult.ok();
}
}
1.3 IRoleService & IRoleMenuService
package com.dragon.springboot3vue3.service;
import com.dragon.springboot3vue3.entity.Role;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 角色表 服务类
* </p>
*/
public interface IRoleService extends IService<Role> {
}
package com.dragon.springboot3vue3.service;
import com.dragon.springboot3vue3.entity.RoleMenu;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 角色菜单表 服务类
* </p>
*/
public interface IRoleMenuService extends IService<RoleMenu> {
}
1.4 RoleServiceImpl & RoleMenuServiceImpl
package com.dragon.springboot3vue3.service.impl;
import com.dragon.springboot3vue3.entity.Role;
import com.dragon.springboot3vue3.mapper.RoleMapper;
import com.dragon.springboot3vue3.service.IRoleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 角色表 服务实现类
* </p>
*/
@Service
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements IRoleService {
}
package com.dragon.springboot3vue3.service.impl;
import com.dragon.springboot3vue3.entity.RoleMenu;
import com.dragon.springboot3vue3.mapper.RoleMenuMapper;
import com.dragon.springboot3vue3.service.IRoleMenuService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 角色菜单表 服务实现类
* </p>
*/
@Service
public class RoleMenuServiceImpl extends ServiceImpl<RoleMenuMapper, RoleMenu> implements IRoleMenuService {
}
1.5 RoleMapper & RoleMenuMapper
package com.dragon.springboot3vue3.mapper;
import com.dragon.springboot3vue3.entity.Role;
import com.github.yulichang.base.MPJBaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* <p>
* 角色表 Mapper 接口
* </p>
*/
@Mapper
public interface RoleMapper extends MPJBaseMapper<Role> {
}
package com.dragon.springboot3vue3.mapper;
import com.dragon.springboot3vue3.entity.RoleMenu;
import com.github.yulichang.base.MPJBaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* <p>
* 角色菜单表 Mapper 接口
* </p>
*/
@Mapper
public interface RoleMenuMapper extends MPJBaseMapper<RoleMenu> {
}
1.6 Swagger 请求后端角色分页数据(包括角色拥有的权限列表)
{
"code": 200,
"msg": "ok",
"data": {
"records": [
{
"id": "1",
"roleName": "管理员",
"role": "ADMIN",
"creatorId": null,
"createTime": "2024-05-11 13:52:33",
"ts": "2024-06-13 16:29:41",
"deleteFlag": 0,
"menuList": [
{
"id": "1",
"parentId": "0",
"menuName": "首页",
"type": 2,
"icon": "HomeFilled",
"path": "/home/index",
"auth": "",
"sort": 99,
"status": 1,
"creatorId": null,
"createTime": "2024-05-13 09:43:42",
"ts": "2024-06-07 10:15:49",
"deleteFlag": 0
},
{
"id": "10",
"parentId": "2",
"menuName": "用户管理",
"type": 2,
"icon": "UserFilled",
"path": "/sys/user/index",
"auth": "sys.user.list",
"sort": 100,
"status": 1,
"creatorId": null,
"createTime": "2024-05-21 09:14:19",
"ts": "2024-06-07 10:34:17",
"deleteFlag": 0
},
{
"id": "125ae0e59d0d00d8ef4fc5f735557e0c",
"parentId": "2",
"menuName": "图标管理",
"type": 2,
"icon": "Files",
"path": "/sys/icon/index",
"auth": "sys.icon.list",
"sort": 103,
"status": 1,
"creatorId": "1",
"createTime": "2024-05-28 17:02:18",
"ts": "2024-06-14 16:07:33",
"deleteFlag": 0
},
{
"id": "13",
"parentId": "2",
"menuName": "角色管理",
"type": 2,
"icon": "User",
"path": "/sys/role/index",
"auth": "sys.role.list",
"sort": 101,
"status": 1,
"creatorId": null,
"createTime": "2024-05-21 09:19:01",
"ts": "2024-06-07 11:12:04",
"deleteFlag": 0
},
{
"id": "155aa550a6d10c94932d83f5e1f42d9c",
"parentId": "19",
"menuName": "个人信息",
"type": 2,
"icon": "User",
"path": "/user/Info",
"auth": "user.info",
"sort": 99,
"status": 1,
"creatorId": "1",
"createTime": "2024-05-25 17:02:39",
"ts": "2024-06-07 10:15:58",
"deleteFlag": 0
},
{
"id": "16",
"parentId": "2",
"menuName": "菜单管理",
"type": 2,
"icon": "Menu",
"path": "/sys/menu/index",
"auth": "sys.menu.list",
"sort": 99,
"status": 1,
"creatorId": null,
"createTime": "2024-05-21 09:22:05",
"ts": "2024-06-07 10:15:59",
"deleteFlag": 0
},
{
"id": "19",
"parentId": "0",
"menuName": "个人中心",
"type": 1,
"icon": "UserFilled",
"path": null,
"auth": null,
"sort": 102,
"status": 1,
"creatorId": null,
"createTime": "2024-05-25 17:00:07",
"ts": "2024-06-07 10:18:27",
"deleteFlag": 0
},
{
"id": "2",
"parentId": "0",
"menuName": "系统管理",
"type": 1,
"icon": "Setting",
"path": null,
"auth": "",
"sort": 101,
"status": 1,
"creatorId": null,
"createTime": "2024-05-13 09:44:38",
"ts": "2024-06-07 10:18:24",
"deleteFlag": 0
},
{
"id": "3",
"parentId": "0",
"menuName": "文章管理",
"type": 1,
"icon": "Management",
"path": "",
"auth": "",
"sort": 100,
"status": 1,
"creatorId": null,
"createTime": "2024-05-13 09:46:16",
"ts": "2024-06-13 16:02:31",
"deleteFlag": 0
},
{
"id": "4",
"parentId": "3",
"menuName": "文章",
"type": 2,
"icon": "Notebook",
"path": "/article/index",
"auth": "article.list",
"sort": 99,
"status": 1,
"creatorId": null,
"createTime": "2024-05-14 17:51:30",
"ts": "2024-06-07 10:16:05",
"deleteFlag": 0
},
{
"id": "4d4d9ea9fb38520109b904008d42c158",
"parentId": "2",
"menuName": "文件管理",
"type": 2,
"icon": "Folder",
"path": "/sys/files/index",
"auth": "sys.files.list",
"sort": 102,
"status": 1,
"creatorId": "1",
"createTime": "2024-06-13 16:03:24",
"ts": "2024-06-14 16:07:10",
"deleteFlag": 0
},
{
"id": "7",
"parentId": "3",
"menuName": "文章分类",
"type": 2,
"icon": "Collection",
"path": "/article/category/index",
"auth": "article.category.list",
"sort": 99,
"status": 1,
"creatorId": null,
"createTime": "2024-05-14 18:59:39",
"ts": "2024-06-07 10:16:08",
"deleteFlag": 0
},
{
"id": "9626a79c1e689e01e100ad5cf6a265c6",
"parentId": "19",
"menuName": "重置密码",
"type": 2,
"icon": "Switch",
"path": "/user/ResetPassword",
"auth": "user.ResetPassword",
"sort": 99,
"status": 1,
"creatorId": "1",
"createTime": "2024-05-25 17:03:34",
"ts": "2024-06-07 10:16:12",
"deleteFlag": 0
}
]
},
{
"id": "2",
"roleName": "普通用户",
"role": "USER",
"creatorId": null,
"createTime": "2024-05-11 13:53:15",
"ts": "2024-05-24 17:58:24",
"deleteFlag": 0,
"menuList": [
{
"id": "1",
"parentId": "0",
"menuName": "首页",
"type": 2,
"icon": "HomeFilled",
"path": "/home/index",
"auth": "",
"sort": 99,
"status": 1,
"creatorId": null,
"createTime": "2024-05-13 09:43:42",
"ts": "2024-06-07 10:15:49",
"deleteFlag": 0
},
{
"id": "3",
"parentId": "0",
"menuName": "文章管理",
"type": 1,
"icon": "Management",
"path": "",
"auth": "",
"sort": 100,
"status": 1,
"creatorId": null,
"createTime": "2024-05-13 09:46:16",
"ts": "2024-06-13 16:02:31",
"deleteFlag": 0
},
{
"id": "4",
"parentId": "3",
"menuName": "文章",
"type": 2,
"icon": "Notebook",
"path": "/article/index",
"auth": "article.list",
"sort": 99,
"status": 1,
"creatorId": null,
"createTime": "2024-05-14 17:51:30",
"ts": "2024-06-07 10:16:05",
"deleteFlag": 0
}
]
},
{
"id": "3",
"roleName": "店长",
"role": "DZ",
"creatorId": null,
"createTime": "2024-05-19 16:08:00",
"ts": "2024-05-28 15:21:20",
"deleteFlag": 0,
"menuList": [
{
"id": "1",
"parentId": "0",
"menuName": "首页",
"type": 2,
"icon": "HomeFilled",
"path": "/home/index",
"auth": "",
"sort": 99,
"status": 1,
"creatorId": null,
"createTime": "2024-05-13 09:43:42",
"ts": "2024-06-07 10:15:49",
"deleteFlag": 0
},
{
"id": "155aa550a6d10c94932d83f5e1f42d9c",
"parentId": "19",
"menuName": "个人信息",
"type": 2,
"icon": "User",
"path": "/user/Info",
"auth": "user.info",
"sort": 99,
"status": 1,
"creatorId": "1",
"createTime": "2024-05-25 17:02:39",
"ts": "2024-06-07 10:15:58",
"deleteFlag": 0
},
{
"id": "16",
"parentId": "2",
"menuName": "菜单管理",
"type": 2,
"icon": "Menu",
"path": "/sys/menu/index",
"auth": "sys.menu.list",
"sort": 99,
"status": 1,
"creatorId": null,
"createTime": "2024-05-21 09:22:05",
"ts": "2024-06-07 10:15:59",
"deleteFlag": 0
},
{
"id": "19",
"parentId": "0",
"menuName": "个人中心",
"type": 1,
"icon": "UserFilled",
"path": null,
"auth": null,
"sort": 102,
"status": 1,
"creatorId": null,
"createTime": "2024-05-25 17:00:07",
"ts": "2024-06-07 10:18:27",
"deleteFlag": 0
},
{
"id": "3",
"parentId": "0",
"menuName": "文章管理",
"type": 1,
"icon": "Management",
"path": "",
"auth": "",
"sort": 100,
"status": 1,
"creatorId": null,
"createTime": "2024-05-13 09:46:16",
"ts": "2024-06-13 16:02:31",
"deleteFlag": 0
},
{
"id": "4",
"parentId": "3",
"menuName": "文章",
"type": 2,
"icon": "Notebook",
"path": "/article/index",
"auth": "article.list",
"sort": 99,
"status": 1,
"creatorId": null,
"createTime": "2024-05-14 17:51:30",
"ts": "2024-06-07 10:16:05",
"deleteFlag": 0
},
{
"id": "9626a79c1e689e01e100ad5cf6a265c6",
"parentId": "19",
"menuName": "重置密码",
"type": 2,
"icon": "Switch",
"path": "/user/ResetPassword",
"auth": "user.ResetPassword",
"sort": 99,
"status": 1,
"creatorId": "1",
"createTime": "2024-05-25 17:03:34",
"ts": "2024-06-07 10:16:12",
"deleteFlag": 0
}
]
},
{
"id": "38a084734ea2b6e619438e7d186ac9f7",
"roleName": "WW",
"role": "WW",
"creatorId": null,
"createTime": "2024-05-23 19:02:29",
"ts": "2024-05-24 16:24:54",
"deleteFlag": 0,
"menuList": [
{
"id": "1",
"parentId": "0",
"menuName": "首页",
"type": 2,
"icon": "HomeFilled",
"path": "/home/index",
"auth": "",
"sort": 99,
"status": 1,
"creatorId": null,
"createTime": "2024-05-13 09:43:42",
"ts": "2024-06-07 10:15:49",
"deleteFlag": 0
},
{
"id": "3",
"parentId": "0",
"menuName": "文章管理",
"type": 1,
"icon": "Management",
"path": "",
"auth": "",
"sort": 100,
"status": 1,
"creatorId": null,
"createTime": "2024-05-13 09:46:16",
"ts": "2024-06-13 16:02:31",
"deleteFlag": 0
},
{
"id": "4",
"parentId": "3",
"menuName": "文章",
"type": 2,
"icon": "Notebook",
"path": "/article/index",
"auth": "article.list",
"sort": 99,
"status": 1,
"creatorId": null,
"createTime": "2024-05-14 17:51:30",
"ts": "2024-06-07 10:16:05",
"deleteFlag": 0
}
]
}
],
"total": 4,
"size": 10,
"current": 1,
"pages": 1
}
}
2、Vue 3 前端实现角色管理
2.1 前端效果
2.2 Vue 3 代码
2.2.1 api -> role.ts(相关接口)
import request from "@/utils/request";
// 菜单管理 API
export default{
// 分页列表
list(value:any){
return request.post('/role/list',value);
},
// 所有角色列表
getAllList(){
return request.get('/role/getAllList');
},
saveOrUpdate(value:any){
return request.post('/role/saveOrUpdate',value);
},
remove(ids:any){
return request.delete('/role/remove',{ data:{ ids } });
},
// 根据 角色标识 获取 角色对象
getRole(value:any){
return request.post('/role/getRole',value);
},
// 新增或更新角色-菜单关联关系
saveRoleMenu(value:any){
return request.post('/role/saveRoleMenu',value);
}
}
2.2.2 sys -> role - > index.vue
<template>
<el-card class="container">
<template #header>
<div class="header">
<el-breadcrumb :separator-icon="ArrowRight">
<el-breadcrumb-item :to="{ path: '/home/index' }" class="title">首页</el-breadcrumb-item>
<el-breadcrumb-item class="title">角色管理</el-breadcrumb-item>
</el-breadcrumb>
<div>
<el-button type="primary" @click="addButton">新增角色</el-button>
<el-button type="danger" @click="batchRemove">批量删除</el-button>
</div>
</div>
</template>
<!-- 搜索表单 -->
<el-form inline>
<el-form-item label="角色名称">
<el-input v-model="searchModel.roleName" placeholder="请输入角色名称" style="width: 150px" clearable></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="getRoleList">搜索</el-button>
<el-button @click="reset">重置</el-button>
</el-form-item>
</el-form>
<!-- 列表 -->
<el-table :data="roleList" border stripe style="width: 100%" height="550" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" />
<el-table-column label="角色名称" width="230" prop="roleName"></el-table-column>
<el-table-column label="角色表示" prop="role"></el-table-column>
<el-table-column label="权限">
<template #default="{ row }">
<el-button type="primary" @click="viewPermission(row)">查看权限</el-button>
</template>
</el-table-column>
<el-table-column label="创建时间" prop="createTime"></el-table-column>
<el-table-column label="更新时间" prop="ts"> </el-table-column>
<el-table-column label="操作" width="120">
<template #default="{ row }">
<el-button :icon="Edit" circle plain type="primary" @click="edit(row)"></el-button>
<el-button :icon="Delete" circle plain type="danger" @click="remove(row)"></el-button>
</template>
</el-table-column>
<template #empty>
<el-empty description="没有数据" />
</template>
</el-table>
<!-- 分页 -->
<el-pagination
v-model:current-page="searchModel.currentPage"
v-model:page-size="searchModel.pageSize"
:page-sizes="[10, 30, 50, 100]"
layout="jumper, total, sizes, prev, pager, next"
:total="searchModel.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
background
style="margin: 10px 0; justify-content: flex-end"
/>
<!-- 新增和修改弹窗 -->
<el-dialog v-model="dialogVisible" width="50%" center @close="close">
<template #header>
<h1>{{ title }}</h1>
</template>
<el-form :model="roleModel" ref="roleModelRef" label-width="120px" :rules="rules">
<el-form-item label="角色名称" prop="roleName">
<el-input v-model="roleModel.roleName" placeholder="请输入角色名称"/>
</el-form-item>
<el-form-item label="角色标识" prop="role">
<el-input v-model="roleModel.role" placeholder="请输入角色标识"/>
</el-form-item>
<el-form-item label="分配权限" prop="menuList">
<div class="down-tree">
<el-tree
ref="treeRef"
node-key="value"
:default-checked-keys="checkedKeys"
:data="menuSelectData"
show-checkbox
check-strictly
multiple
:props="defaultProps"
default-expand-all
/>
</div>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="save">确认</el-button>
</span>
</template>
</el-dialog>
<!-- 查看权限弹窗 -->
<el-dialog v-model="permissionDialogVisible" width="40%" center @close="close">
<template #header>
<h1>{{ title }}</h1>
</template>
<div class="down-tree">
<el-tree
ref="permissionRef"
node-key="value"
:default-checked-keys="checkedKeys"
:data="menuTreeData"
show-checkbox
check-strictly
multiple
:props="permissionProps"
default-expand-all
/>
</div>
</el-dialog>
</el-card>
</template>
<script setup lang="ts">
import { ref,reactive,onMounted } from 'vue'
import { Edit,Delete,ArrowRight } from '@element-plus/icons-vue'
import roleApi from '@/api/role'
import menuApi from '@/api/menu'
import { ElMessage, ElMessageBox } from 'element-plus'
// 角色列表
const roleList=ref()
// 角色数据模型
const roleModel=reactive({
id:'',
roleName:'',
role:''
})
// 角色-菜单关联模型
let roleMenuModel=reactive({
id:'',
roleId:'',
menuId:''
})
const initRoleModel={ ...roleModel }
// 批量删除的 id
const ids=ref<string[]>([])
// 新增或编辑弹窗
const dialogVisible = ref(false)
// 查看权限弹窗
const permissionDialogVisible=ref(false)
// 菜单列表
const menuList=ref()
// 回显角色的菜单列表
const menuSelectData=ref()
const defaultProps = {
children: 'children',
label: 'label'
}
const permissionProps = {
children: 'children',
label: 'label',
disabled: 'disabled'
}
// 新增或编辑
const title=ref();
const roleModelRef=ref()
// 分页&搜索模型
const searchModel=reactive({
currentPage:1,
pageSize:10,
total:0,
roleName:''
})
const initSearchModel={ ...searchModel }
const rules = reactive({
roleName:[
{ required: true, message:'请输入角色名称', trigger: 'blur'},
],
role:[
{ required: true, message: '请输入角色标识', trigger: 'blur' },
],
})
// pageSize 变化时触发
const handleSizeChange = (val: number) => {
searchModel.pageSize=val;
getRoleList();
}
// currentPage 变化时触发
const handleCurrentChange = (val: number) => {
searchModel.currentPage=val;
getRoleList();
}
// 角色列表
const getRoleList= async()=>{
const response= await roleApi.list(searchModel);
roleList.value=response.data.records;
searchModel.currentPage=response.data.current;
searchModel.pageSize=response.data.size;
searchModel.total=response.data.total;
}
// 新增、编辑是的树形多选框
const treeRef=ref(null) as any
// 选中的菜单
const checkedMenu=ref()
// 角色标识
const strModel=ref({
str:''
})
// 新增或编辑保存
const save= async()=>{
roleModelRef.value.validate(async(valid:any) => {
if (valid) {
const response=await roleApi.saveOrUpdate(roleModel) as any;
strModel.value.str=roleModel.role;
// 根据角色标识获取角色对象,从而获取角色id
const role=await roleApi.getRole(strModel.value);
// 处理权限,获取权限id
if (treeRef.value) {
// 选中的节点
checkedMenu.value = treeRef.value.getCheckedNodes();
if(checkedMenu.value!=null){
const roleMenuModels=reactive([]) as any;
checkedMenu.value.forEach((item:any) => {
roleMenuModel={} as any;
roleMenuModel.roleId=role.data.id;
roleMenuModel.menuId=item.value;
roleMenuModels.push(roleMenuModel);
});
// 新增角色-权限关联关系
await roleApi.saveRoleMenu(roleMenuModels);
}
}
ElMessage.success(response.msg);
dialogVisible.value=false;
// 刷新
getRoleList();
} else {
return false;
}
});
}
// 编辑
const edit= (row:any)=>{
dialogVisible.value=true;
title.value="编辑";
// 刷新菜单列表
getMenuList();
// 更新 checkedKeys 的值
checkedKeys.value = row.menuList.map((item:any) => item.id);
// 源对象赋值给目标对象,Object.assign(目标对象, 源对象)
Object.assign(roleModel, row);
}
// 回显菜单的id列表
let checkedKeys=ref([]) as any
// 点击查看权限按钮展示的数据
const menuTreeData=ref()
// 点击查看权限按钮
const viewPermission= (row:any)=>{
permissionDialogVisible.value=true;
title.value=`查看 [ ${row.roleName} ] 权限`;
// 刷新菜单列表
getMenuList();
// 更新 checkedKeys 的值
checkedKeys.value = row.menuList.map((item:any) => item.id);
menuTreeData.value = recursive(menuList.value,true);
}
// 新增菜单按钮
const addButton= ()=>{
dialogVisible.value=true;
title.value="添加角色";
}
// 关闭新增、编辑对话框时,清空数据和校验信息
const close= ()=>{
// 清空数据
Object.assign(roleModel, initRoleModel);
checkedKeys.value=[];
getMenuList();
// 清除校验信息
roleModelRef.value.clearValidate();
}
// 重置搜索表单
const reset= ()=>{
Object.assign(searchModel, initSearchModel);
getRoleList();
}
// 批量删除选择
const handleSelectionChange = (rows: any) => {
ids.value = rows.map((item:any) => item.id);
}
// 批量删除
const batchRemove= ()=>{
if(ids.value.length > 0){
ElMessageBox.confirm(
`是否批量删除?`,
'温馨提示',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(async() => {
await roleApi.remove(ids.value);
ElMessage({ type: 'success',message: '删除成功' });
getRoleList();
})
}else{
ElMessage.warning('请选择批量删除项');
}
}
// 单条删除
const remove= async(row:any)=>{
ElMessageBox.confirm(
`是否删除 [ ${row.roleName} ] 角色?`,
'温馨提示',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(async() => {
ids.value.push(row.id);
await roleApi.remove(ids.value);
ElMessage({ type: 'success', message: '删除成功' });
getRoleList();
})
}
// 菜单列表
const getMenuList= async()=>{
const response= await menuApi.menuList(searchModel);
menuList.value=response.data.records;
getMenuSelectData(menuList.value);
}
// 新增编辑时的下拉菜单
const getMenuSelectData = (val: any) => {
menuSelectData.value = recursive(val,false);
}
// 递归处理子菜单,将每个菜单转为 { value,label,disabled,children } 对象用于树结构展示
// disabledFlag 为false,则可操作树形多选框;为true,则不可操作,只能查看
const recursive = (data: any,disabledFlag:any) => {
return data.map((item: { id: any, menuName: any, children: any }) => ({
value: item.id,
label: item.menuName,
disabled: disabledFlag,
children: Array.isArray(item.children) ? recursive(item.children,disabledFlag) : []
}));
};
onMounted(()=>{
getRoleList();
getMenuList();
})
</script>
<style scoped lang="less">
.container{
height: 100%;
box-sizing: border-box;
}
.header{
display: flex;
align-items: center;
justify-content: space-between;
}
.title{
font-size: large;
font-weight: 600;
}
.down-tree{
height: 300px;
width: 100%;
overflow-y: scroll;
}
</style>