SpringBoot3+Vue3 前后端分离项目实现基于RBAC权限访问控制-(2)角色管理

SpringBoot3+Vue3 前后端分离项目实现基于RBAC权限访问控制-(1)权限管理

SpringBoot3+Vue3 前后端分离项目实现基于RBAC权限访问控制-(2)角色管理

SpringBoot3+Vue3 前后端分离项目实现基于RBAC权限访问控制-(3)用户管理

SpringBoot3+Vue3 前后端分离项目基于RBAC权限访问控制-实现动态路由、动态菜单

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>
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
权限管理系统是一种用于管理用户权限角色的系统,可以根据用户的身份和角色来控制其访问系统中的各种资源。基于SpringBootVue和Redis的前后端分离模式,可以更好地实现权限管理系统的功能。 在这个系统中,SpringBoot作为后端框架,提供了强大的功能和稳定的性能,可以处理用户的请求并进行权限验证。Vue作为前端框架,提供了友好的界面和良好的用户体验,可以让用户方便地进行权限管理操作。而Redis作为缓存数据库,可以用来存储权限信息和用户的登录状态,加快系统的响应速度和提高系统的性能。 在权限管理系统中,我们可以使用RBAC(基于角色权限控制)模型,将用户分配到不同的角色,再将角色分配到不同的权限,从而实现对用户访问资源的控制。通过这种方式,可以实现灵活的权限管理,并且可以根据实际需求动态地调整用户的权限角色。 通过使用SpringBootVue,我们可以实现前后端分离,让前端和后端分别进行开发和维护,降低了系统的耦合度,同时也增加了系统的灵活性和可维护性。而通过使用Redis,我们可以充分利用其高速的读取和写入能力,有效地提升系统的性能和响应速度。 综上所述,基于SpringBootVue和Redis的权限管理系统,可以实现灵活、高效和安全的权限管理功能,满足用户对于权限管理的各种需求。同时,前后端分离模式也使得系统更加灵活和易于维护。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值