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

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

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

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

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

1、SpringBoot 3 后端实现用户管理

1.1 Entity-实体类

1.1.1 User

package com.dragon.springboot3vue3.entity;

import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email;
import lombok.Data;
import org.hibernate.validator.constraints.URL;
import java.io.Serializable;
import java.time.LocalDateTime;

@Data
@Schema(name = "User", description = "用户表")
public class User implements Serializable {
    private static final long serialVersionUID=1L;

    @Schema(description = "主键")
    @TableId(value = "id", type = IdType.ASSIGN_UUID)
    private String id;

    @Schema(description = "用户名")
    private String username;

    @Schema(description = "密码")
    @JsonIgnore
    private String password;

    @Schema(description = "姓名")
    private String name;

    @Schema(description = "邮箱")
    @Email
    private String email;

    @Schema(description = "头像地址")
    @URL
    private String avatarUrl;

    @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.删除")
    @TableLogic
    private Integer deleteFlag;
}

1.1.2 UserRole

package com.dragon.springboot3vue3.entity;

import com.baomidou.mybatisplus.annotation.*;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;

/**
 * <p>
 * 用户角色表
 * </p>
 */
@Data
@TableName("user_role")
@Schema(name = "UserRole", description = "用户角色表")
public class UserRole 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 userId;

    @Schema(description = "角色ID")
    private String roleId;

    @Schema(description = "创建人ID")
    @TableField(fill = FieldFill.INSERT)
    private String creatorId;

    @Schema(description = "创建时间")
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    @Schema(description = "更新时间")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime ts;

    @Schema(description = "逻辑删除字段 0.正常 1.删除")
    private Integer deleteFlag;
}

1.2 UserController

package com.dragon.springboot3vue3.controller;

import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dragon.springboot3vue3.common.Constant;
import com.dragon.springboot3vue3.controller.dto.entityDto.RegisterOrLoginDto;
import com.dragon.springboot3vue3.controller.dto.entityDto.UpdatePwdDto;
import com.dragon.springboot3vue3.controller.dto.entityDto.UserDto;
import com.dragon.springboot3vue3.controller.dto.pageDto.UserPageDto;
import com.dragon.springboot3vue3.entity.Role;
import com.dragon.springboot3vue3.entity.User;
import com.dragon.springboot3vue3.entity.UserRole;
import com.dragon.springboot3vue3.mapper.UserMapper;
import com.dragon.springboot3vue3.service.IImportExportService;
import com.dragon.springboot3vue3.service.IUserRoleService;
import com.dragon.springboot3vue3.service.IUserService;
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 jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Tag(name = "用户接口")
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private IUserService userService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private IUserRoleService userRoleService;
    @Autowired
    private IImportExportService importExportService;


    @Operation(summary = "注册")
    @PostMapping("/register")
    public SaResult register(@RequestBody @Validated RegisterOrLoginDto registerDto){
        User user=userService.lambdaQuery().eq(User::getUsername,registerDto.getUsername()).one();
        if(user!=null){
            return SaResult.error(Constant.USERNAME_OCCUPIED_MSG).setCode(Constant.USERNAME_OCCUPIED_CODE);
        }
        user=new User();
        BeanUtils.copyProperties(registerDto,user);
        // BCrypt.hashpw() 密码加密
        user.setPassword(BCrypt.hashpw(registerDto.getPassword(), BCrypt.gensalt()));
        userService.save(user);
        return SaResult.ok();
    }

    @Operation(summary = "登录")
    @PostMapping("/login")
    public SaResult login(@RequestBody @Validated RegisterOrLoginDto loginDto){
        User user=userService.lambdaQuery().eq(User::getUsername,loginDto.getUsername()).one();
        // BCrypt.checkpw(前端明文,后端密文)
        if(user!=null && BCrypt.checkpw(loginDto.getPassword(),user.getPassword())){
            // 登录认证
            StpUtil.login(user.getId());

            // 生成token,token 信息自动存入redis,在yml里配置 sa-token 相关信息
            String token = StpUtil.getTokenValue();

            // 将用户信息存入 redis
            stringRedisTemplate.opsForValue().set(user.getId(), JSON.toJSONString(user),1, TimeUnit.DAYS);

            return SaResult.ok().setData(token);
        }
        return SaResult.error(Constant.USERNAME_OR_PASSWORD_ERROR_MSG).setCode(Constant.USERNAME_OR_PASSWORD_ERROR_CODE);
    }

    @Operation(summary = "注销")
    @PostMapping("/logout")
    public SaResult logout(){
        StpUtil.logout();
        return SaResult.ok();
    }

    @Operation(summary = "分页列表")
    @PostMapping("/list")
    public SaResult list(@RequestBody UserPageDto pageDto){
        // 创建分页对象
        Page<UserDto> page=new Page<>(pageDto.getCurrentPage(), pageDto.getPageSize());

        // 构造多表查询条件
        MPJLambdaWrapper<User> qw=new MPJLambdaWrapper<User>()
                .selectAll(User.class)
                .selectCollection(Role.class,UserDto::getRoleList)
                .leftJoin(UserRole.class,UserRole::getUserId,User::getId)
                .leftJoin(Role.class,Role::getId,UserRole::getRoleId)
                .like(StringUtils.isNotBlank(pageDto.getUsername()),User::getUsername, pageDto.getUsername())
                .like(StringUtils.isNotBlank(pageDto.getName()),User::getName, pageDto.getName())
                .like(StringUtils.isNotBlank(pageDto.getEmail()),User::getEmail, pageDto.getEmail())
                .orderByDesc(User::getCreateTime);

        // 根据查询条件,将结果封装到分页对象
        Page<UserDto> response=userMapper.selectJoinPage(page, UserDto.class,qw);

        return SaResult.ok().setData(response);
    }

    @Operation(summary = "新增或更新")
    @PostMapping("/saveOrUpdate")
    public SaResult saveOrUpdate(@RequestBody @Validated UserDto userDto){
        return userService.saveOrUpdate(userDto);
    }

    @Operation(summary = "删除")
    @DeleteMapping("/remove")
    public SaResult remove(@RequestBody @Validated StringIdsDTO stringIdsDTO){
        userService.removeByIds(stringIdsDTO.getIds());
        return SaResult.ok();
    }

    @Operation(summary = "获取登录用户信息")
    @GetMapping("/userInfo")
    public SaResult userInfo(){
        return SaResult.ok().setData(userService.userInfo());
    }

    @Operation(summary = "更新用户基本信息")
    @PutMapping("/updateUserInfo")
    public SaResult updateUserInfo(@RequestBody @Validated User userDto){
        User user=userService.getById(StpUtil.getLoginIdAsString());
        BeanUtils.copyProperties(userDto,user);
        // user 存入 redis
        stringRedisTemplate.opsForValue().set(user.getId(), JSON.toJSONString(user),1, TimeUnit.DAYS);
        userService.saveOrUpdate(user);
        return SaResult.ok();
    }

    @Operation(summary = "更新头像")
    @PutMapping("/updateAvatar")
    public SaResult updateAvatar(@RequestBody StringDTO stringDTO){
        User user = userService.userInfo();
        user.setAvatarUrl(stringDTO.getStr());
        userService.saveOrUpdate(user);
        // user 存入 redis
        stringRedisTemplate.opsForValue().set(user.getId(), JSON.toJSONString(user),1, TimeUnit.DAYS);

        return SaResult.ok().setData(user);
    }

    @Operation(summary = "修改密码")
    @PutMapping("/resetPwd")
    public SaResult resetPwd(@RequestBody @Validated UpdatePwdDto updatePwdDto,@RequestHeader(Constant.TOKEN) String token){
        String oldPwd = updatePwdDto.getOldPwd();
        String newPwd = updatePwdDto.getNewPwd();
        String confirmPwd = updatePwdDto.getConfirmPwd();

        User user= userService.userInfo();

        if(StringUtils.isBlank(oldPwd) || StringUtils.isBlank(newPwd) || StringUtils.isBlank(confirmPwd)){
            return SaResult.error(Constant.MISSING_NECESSARY_PARAMETERS_MSG).setCode(Constant.MISSING_NECESSARY_PARAMETERS_CODE);
        }
        if(!BCrypt.checkpw(oldPwd,user.getPassword())){
            return SaResult.error(Constant.ORIGINAL_PASSWORD_ERROR_MSG).setCode(Constant.ORIGINAL_PASSWORD_ERROR_CODE);
        }
        if(!newPwd.equals(confirmPwd)){
            return SaResult.error(Constant.PASSWORD_INCONSISTENCY_MSG).setCode(Constant.PASSWORD_INCONSISTENCY_CODE);
        }
        user.setPassword(BCrypt.hashpw(newPwd, BCrypt.gensalt()));
        userService.saveOrUpdate(user);
        stringRedisTemplate.opsForValue().getOperations().delete(token);
        return SaResult.ok();
    }

    @Operation(summary = "用户信息导出")
    @GetMapping("/userInfoExport")
    public void userInfoExport(HttpServletResponse response) throws Exception {
        List<User> list=userService.list();
        importExportService.export(list,"用户信息",response);
    }

    @Operation(summary = "用户信息导入")
    @PostMapping("/userInfoImport")
    public SaResult userInfoImport(@RequestParam MultipartFile file) throws Exception {
        List<User> list = importExportService.imp(file, User.class);
        userService.saveBatch(list);
        return SaResult.ok();
    }

    @Operation(summary = "用户总数")
    @GetMapping("/count")
    public SaResult count(){
        return SaResult.ok().setData(userService.count());
    }

}

1.3 IUserService & IUserRoleService

package com.dragon.springboot3vue3.service;

import cn.dev33.satoken.util.SaResult;
import com.baomidou.mybatisplus.extension.service.IService;
import com.dragon.springboot3vue3.controller.dto.entityDto.UserDto;
import com.dragon.springboot3vue3.entity.User;

public interface IUserService extends IService<User> {
    User userInfo();
    SaResult saveOrUpdate(UserDto userDto);

}
package com.dragon.springboot3vue3.service;

import cn.dev33.satoken.util.SaResult;
import com.dragon.springboot3vue3.entity.UserRole;
import com.baomidou.mybatisplus.extension.service.IService;

/**
 * <p>
 * 用户角色表 服务类
 * </p>
 */
public interface IUserRoleService extends IService<UserRole> {
    // 根据用户id删除用户-角色关联关系
    SaResult deleteByUserId(String id);
}

1.4 UserServiceImpl & UserRoleServiceImpl

package com.dragon.springboot3vue3.service.impl;

import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dragon.springboot3vue3.common.Constant;
import com.dragon.springboot3vue3.controller.dto.entityDto.UserDto;
import com.dragon.springboot3vue3.entity.User;
import com.dragon.springboot3vue3.entity.UserRole;
import com.dragon.springboot3vue3.mapper.UserMapper;
import com.dragon.springboot3vue3.service.IUserRoleService;
import com.dragon.springboot3vue3.service.IUserService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private IUserRoleService userRoleService;

    /**
     * 获取 redis 中登录的用户信息
     * @return
     */
    @Override
    public User userInfo() {
        String userInfo = stringRedisTemplate.opsForValue().get(StpUtil.getLoginIdAsString());
        User user= JSON.parseObject(userInfo,User.class);
        return user;
    }

    /**
     * 管理员新增或更新用户
     * @param userDto
     * @return
     */
    @Override
    public SaResult saveOrUpdate(UserDto userDto) {
        if(StringUtils.isBlank(userDto.getId())){
            // 1.新增时查询用户名是否存在
            User getUser=this.lambdaQuery().eq(User::getUsername,userDto.getUsername()).one();
            if(getUser!=null){
                return SaResult.error(Constant.USERNAME_OCCUPIED_MSG).setCode(Constant.USERNAME_OCCUPIED_CODE);
            }
            // 2.新增用户时设置默认密码
            if(StringUtils.isBlank(userDto.getPassword())){
                userDto.setPassword(BCrypt.hashpw(Constant.USER_PASSWORD, BCrypt.gensalt()));
            }
        }

        // 3.新增或更新用户
        User user=new User();
        BeanUtils.copyProperties(userDto,user);
        this.saveOrUpdate(user);

        // 更新用户-角色关联关系,先要删除关联关系,再更新
        if(StringUtils.isNotBlank(userDto.getId())){
            userRoleService.deleteByUserId(userDto.getId());
        }

        // 4.新增或更新用户-角色关联关系
        List<UserRole> userRoleList=new ArrayList<>();
        User getUserInfo=this.lambdaQuery().eq(User::getUsername,userDto.getUsername()).one();
        userDto.getRoleList().forEach(item->{
            UserRole userRole=new UserRole();
            userRole.setUserId(getUserInfo.getId());
            userRole.setRoleId(item.getId());
            userRoleList.add(userRole);
        });
        userRoleService.saveOrUpdateBatch(userRoleList);

        return SaResult.ok();
    }
}
package com.dragon.springboot3vue3.service.impl;

import cn.dev33.satoken.util.SaResult;
import com.dragon.springboot3vue3.entity.UserRole;
import com.dragon.springboot3vue3.mapper.UserRoleMapper;
import com.dragon.springboot3vue3.service.IUserRoleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;

/**
 * <p>
 * 用户角色表 服务实现类
 * </p>
 */
@Service
public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRole> implements IUserRoleService {

    /**
     * 根据用户id删除用户-角色关联关系
     * @param userId
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public SaResult deleteByUserId(String userId) {
        List<UserRole> list = this.lambdaQuery().eq(UserRole::getUserId, userId).list();
        this.removeBatchByIds(list);
        return SaResult.ok();
    }
}

1.5 Swagger 请求后端用户分页数据(包括用户拥有的角色列表)

{
  "code": 200,
  "msg": "ok",
  "data": {
    "records": [
      {
        "id": "f3e7119032075080e92a0b31cac1aca3",
        "username": "zzz",
        "name": "",
        "email": "",
        "avatarUrl": "",
        "creatorId": "1",
        "createTime": "2024-06-14 10:16:18",
        "ts": "2024-06-14 10:16:18",
        "deleteFlag": 0,
        "roleId": null,
        "role": null,
        "roleIds": [],
        "roleList": [],
        "authList": []
      },
      {
        "id": "dd5f700cd9247df52031de9bf8ac7ed0",
        "username": "yyy",
        "name": "",
        "email": "",
        "avatarUrl": "",
        "creatorId": "0",
        "createTime": "2024-06-04 20:16:10",
        "ts": "2024-06-04 20:16:10",
        "deleteFlag": 0,
        "roleId": null,
        "role": null,
        "roleIds": [],
        "roleList": [],
        "authList": []
      },
      {
        "id": "63277e25cf2b0f4d952a2a2c0b5ec435",
        "username": "xxx",
        "name": "",
        "email": "",
        "avatarUrl": "",
        "creatorId": "0",
        "createTime": "2024-06-04 19:56:09",
        "ts": "2024-06-04 20:01:17",
        "deleteFlag": 0,
        "roleId": null,
        "role": null,
        "roleIds": [],
        "roleList": [],
        "authList": []
      },
      {
        "id": "5cdfeea8ba2cdb7af15b50f950e75242",
        "username": "vvv",
        "name": "",
        "email": "",
        "avatarUrl": "",
        "creatorId": "1",
        "createTime": "2024-06-04 18:54:11",
        "ts": "2024-06-04 18:54:11",
        "deleteFlag": 0,
        "roleId": null,
        "role": null,
        "roleIds": [],
        "roleList": [],
        "authList": []
      },
      {
        "id": "cb247a49dc5e9543e13fb64301144156",
        "username": "www",
        "name": "",
        "email": "",
        "avatarUrl": "",
        "creatorId": "1",
        "createTime": "2024-06-04 18:54:11",
        "ts": "2024-06-04 18:57:54",
        "deleteFlag": 0,
        "roleId": null,
        "role": null,
        "roleIds": [],
        "roleList": [],
        "authList": []
      },
      {
        "id": "8027385e5ec77a34403ccf9753373bc0",
        "username": "uuu",
        "name": "uuu昵称",
        "email": "uuu@qq.com",
        "avatarUrl": "http://localhost:8080/files/1df758c1617a46538ceb464a479f9fa5.jpg",
        "creatorId": "1",
        "createTime": "2024-05-19 17:15:08",
        "ts": "2024-06-13 19:36:33",
        "deleteFlag": 0,
        "roleId": null,
        "role": null,
        "roleIds": [],
        "roleList": [
          {
            "id": "3",
            "roleName": "店长",
            "role": "DZ",
            "creatorId": null,
            "createTime": "2024-05-19 16:08:00",
            "ts": "2024-05-28 15:21:20",
            "deleteFlag": 0
          }
        ],
        "authList": []
      },
      {
        "id": "0911a653ea11e3284d6ab9eb78b51588",
        "username": "ttt",
        "name": "ttt昵称",
        "email": "ttt@qq.com",
        "avatarUrl": "http://localhost:8080/files/7eee9c76b69b4a43b9142604cfcd2696.png",
        "creatorId": null,
        "createTime": "2024-05-19 16:26:45",
        "ts": "2024-06-13 19:35:12",
        "deleteFlag": 0,
        "roleId": null,
        "role": null,
        "roleIds": [],
        "roleList": [
          {
            "id": "1",
            "roleName": "管理员",
            "role": "ADMIN",
            "creatorId": null,
            "createTime": "2024-05-11 13:52:33",
            "ts": "2024-06-13 16:29:41",
            "deleteFlag": 0
          }
        ],
        "authList": []
      },
      {
        "id": "0e4b11a77cead75d2fdb78dfeb6c58c1",
        "username": "sss",
        "name": null,
        "email": null,
        "avatarUrl": null,
        "creatorId": null,
        "createTime": "2024-05-14 11:02:38",
        "ts": "2024-05-14 11:02:38",
        "deleteFlag": 0,
        "roleId": null,
        "role": null,
        "roleIds": [],
        "roleList": [],
        "authList": []
      },
      {
        "id": "56e16f3e121b8cc76396b2951c4753d4",
        "username": "loong",
        "name": null,
        "email": null,
        "avatarUrl": null,
        "creatorId": null,
        "createTime": "2024-05-14 10:57:50",
        "ts": "2024-05-14 11:09:38",
        "deleteFlag": 0,
        "roleId": null,
        "role": null,
        "roleIds": [],
        "roleList": [],
        "authList": []
      },
      {
        "id": "c4ea8cfded683689e6757c3bff61dbca",
        "username": "rrr",
        "name": null,
        "email": null,
        "avatarUrl": null,
        "creatorId": null,
        "createTime": "2024-04-22 16:54:43",
        "ts": "2024-05-14 11:09:48",
        "deleteFlag": 0,
        "roleId": null,
        "role": null,
        "roleIds": [],
        "roleList": [],
        "authList": []
      }
    ],
    "total": 28,
    "size": 10,
    "current": 1,
    "pages": 3
  }
}

2、Vue 3 前端实现用户管理

2.1 前端效果

2.2 Vue 3 代码

2.2.1 api -> user.ts(相关接口)

import type { RegisterFormType,LoginFormType } from "@/types"
import request from "@/utils/request";


export default{
  register(value:RegisterFormType){
    return request.post('/user/register',value);
  },
  login(value:LoginFormType){
    return request.post('/user/login',value);
  },
  logout(){
    return request.post('/user/logout');
  },
  userInfo(){
    return request.get('/user/userInfo');
  },
  updateUserInfo(value:any){
    return request.put('/user/updateUserInfo',value);
  },
  resetPwd(value:any){
    return request.put('/user/resetPwd',value);
  },
  updateAvatar(url:string){
    return request.put('/user/updateAvatar',{ str: url });
  },
  list(value:any){
    return request.post('/user/list',value);
  },
  saveOrUpdate(value:any){
    return request.post('/user/saveOrUpdate',value);
  },
  remove(ids:any){
    return request.delete('/user/remove',{
      data:{ ids }
    });
  },
  // 导出的url
  exportUrl(){
    return 'http://localhost:8080/user/userInfoExport';
  },
  // 导入的url
  importUrl(){
    return 'http://localhost:8080/user/userInfoImport';
  },
  count(){
    return request.get('/user/count');
  }
}

2.2.2 sys -> user- > 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.username" placeholder="请输入用户名" style="width: 150px" clearable></el-input>
      </el-form-item>
      <el-form-item label="昵称">
        <el-input v-model="searchModel.name" placeholder="请输入昵称" style="width: 150px" clearable></el-input>
      </el-form-item>
      <el-form-item label="邮箱">
        <el-input v-model="searchModel.email" placeholder="请输入邮箱" style="width: 150px" clearable></el-input>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getUserList">搜索</el-button>
        <el-button @click="reset">重置</el-button>
      </el-form-item>
      <el-form-item class="importExport">
        <el-upload :action="importUrl" :show-file-list="false" accept=".xlsx" :on-success="handleImportSuccess" :on-error="handleImportError" :headers="{'X-Token':useTokenStore().token}">
          <el-button :icon="Download" @click="imp" type="primary">导入</el-button>
        </el-upload>
        <el-button :icon="Upload" @click="exp" type="primary" style="margin-left: 10px;">导出</el-button>
      </el-form-item>
    </el-form>

    <!-- 列表 -->
    <el-table :data="userList" border stripe style="width: 100%" height="550" @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="50" />
      <el-table-column label="用户名" prop="username"></el-table-column>
      <el-table-column label="昵称" prop="name"></el-table-column>
      <el-table-column label="邮箱" prop="email"> </el-table-column>
      <el-table-column label="头像" prop="avatarUrl" width="100"> 
        <template #default="{ row }">
          <el-avatar :size="60" :src="row.avatarUrl" />
        </template>
      </el-table-column>
      <el-table-column label="角色">
        <template #default="{ row }">
          <el-tag class="tag" v-if="row.roleList!=null" v-for="item in row.roleList">{{ item.roleName }}</el-tag>
        </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="userModel" ref="userModelRef" label-width="120px" :rules="rules">
        <el-form-item label="用户名" prop="username">
          <el-input v-model="userModel.username" placeholder="请输入用户名"/>
        </el-form-item>
        <el-form-item label="昵称" prop="name">
          <el-input v-model="userModel.name" placeholder="请输入昵称"/>
        </el-form-item>
        <el-form-item label="邮箱" prop="email">
          <el-input v-model="userModel.email" placeholder="请输入邮箱"/>
        </el-form-item>
        <el-form-item label="头像" prop="avatar">
          <el-upload :action="url" accept=".png,.jpeg,.jpg" :show-file-list="false" :on-success="handleUploadSuccess" :on-error="handleUploadError" >
            <!-- <el-image class="img" v-if="avatarUrl" :src="avatarUrl" /> -->
            <el-avatar :size="100" v-if="userModel.avatarUrl" :src="userModel.avatarUrl" fit="fill" />
            <el-icon v-else><Plus /></el-icon>
          </el-upload>
        </el-form-item>
        <el-form-item label="角色" prop="roleList">
          <!-- value-key 绑定对象要设置的唯一标识符 -->
          <el-select placeholder="请选择" v-model="userModel.roleList" value-key="id" multiple>
            <el-option v-for="item in roleListData" :key="item.id" :value="item" :label="item.roleName"></el-option>
          </el-select>
        </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-card>
</template>

<script setup lang="ts">
  import { ref,reactive,onMounted } from 'vue'
  import { Edit,Delete,ArrowRight,Upload,Download,Plus } from '@element-plus/icons-vue'
  import userApi from '@/api/user';
  import roleApi from '@/api/role';
  import { ElMessage, ElMessageBox, type FormRules } from 'element-plus'
  import '@vueup/vue-quill/dist/vue-quill.snow.css'
  import filesApi from '@/api/files'
  import { useTokenStore } from '@/stores/token'

  // 用户列表
  const userList=ref()
  // 角色列表
  const roleListData=ref()
  const userModel=reactive({
    id:'',
    username:'',
    password:'',
    name:'',
    email:'',
    roleList:[],
    avatarUrl:''
  })
  const initUserModel={ ...userModel }
  // 批量删除的 id
  const ids=ref<string[]>([])

  const dialogVisible = ref(false)

  // 导出url
  const exportUrl=ref()
  // 导入url
  const importUrl=ref()
  // 文件上传url
  const url=ref('')

  // 新增或编辑
  const title=ref();
  const userModelRef=ref()

  // 分页&搜索模型
  const searchModel=reactive({
    currentPage:1,
    pageSize:10,
    total:0,
    username:'',
    name:'',
    email:''
  })
  const initSearchModel={ ...searchModel }

  const rules:FormRules = reactive({
    username:[
      { required: true, message:'请输入用户名', trigger: 'blur'},
      { min:3, max:16, message:"用户名长度3-16位", trigger: 'blur'}
    ],
    password:[
      { required: true, message:'请输入密码', trigger: 'blur'},
      { min:6, max:16, message:"密码长度6-16位", trigger: 'blur'}
    ],
    name:[
      { required: true, message:'请输入昵称', trigger: 'blur'},
    ],
    email: [
        { required: true, message: '请输入邮箱', trigger: 'blur' },
        { type: 'email', message: '邮箱格式不正确', trigger: 'blur' }
    ],
    roleList:[
      { required: true, message:'请选择角色', trigger: 'blur'},
    ],
  })

  // pageSize 变化时触发
  const handleSizeChange = (val: number) => {
    searchModel.pageSize=val;
    getUserList();
  }

  // currentPage 变化时触发
  const handleCurrentChange = (val: number) => {
    searchModel.currentPage=val;
    getUserList();
  }

  // 菜单列表
  const getUserList= async()=>{
    const response= await userApi.list(searchModel);
    userList.value=response.data.records;
    searchModel.currentPage=response.data.current;
    searchModel.pageSize=response.data.size;
    searchModel.total=response.data.total;
  }

  // 获取角色列表
  const getRoleList= async()=>{
    const response= await roleApi.getAllList();
    roleListData.value=response.data;
  }

  // 新增或编辑保存
  const save= async()=>{
    console.log(userModel)
    userModelRef.value.validate(async(valid:any) => {
      if (valid) {
        const response=await userApi.saveOrUpdate(userModel) as any;
        ElMessage.success(response.msg);
        dialogVisible.value=false;
        getUserList();
      } else {
        return false;
      }
    })
  }

  // 编辑
  const edit= (row:any)=>{
    dialogVisible.value=true;
    title.value="编辑用户";
    // 源对象赋值给目标对象,Object.assign(目标对象, 源对象)
    Object.assign(userModel, row);
  }

  // 新增菜单按钮
  const addButton= ()=>{
    dialogVisible.value=true;
    title.value="添加用户";
  }

  // 关闭新增、编辑对话框时,清空数据和校验信息
  const close= ()=>{
    // 清空数据
    Object.assign(userModel, initUserModel);
    // 清除校验信息
    userModelRef.value.clearValidate();
  }

  // 重置搜索表单
  const reset= ()=>{
    Object.assign(searchModel, initSearchModel);
    getUserList();
  }

  // 批量删除选择
  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 userApi.remove(ids.value);
        ElMessage({ type: 'success',message: '删除成功' });
        getUserList();
      })
    }else{
      ElMessage.warning('请选择批量删除项');
    }
  }

  // 单条删除
  const remove= async(row:any)=>{
    ElMessageBox.confirm(
      `是否删除 [ ${row.username} ] 用户?`,
      '温馨提示',
      {
        confirmButtonText: '确认',
        cancelButtonText: '取消',
        type: 'warning',
      }
    )
    .then(async() => {
      ids.value.push(row.id);
      await userApi.remove(ids.value);
      ElMessage({ type: 'success', message: '删除成功' });
      getUserList();
    })
  }

  // 获取导出url
  const getExportUrl= ()=>{
    exportUrl.value=userApi.exportUrl();
  }

  // 获取导入url
  const getImportUrl= ()=>{
    importUrl.value=userApi.importUrl();
  }

  // 导出
  const exp= ()=>{
    getExportUrl();
    window.open(exportUrl.value);
  }
  // 导入
  const imp= ()=>{
    getImportUrl();
  }

  // 导入成功处理的事件
  const handleImportSuccess= ()=>{
    ElMessage.success("导入成功");
    // 刷新列表
    getUserList();
  }
  
  // 导入失败处理的事件
  const handleImportError = () => {
    ElMessage.error("上传失败,请上传.xlsx文件");
  }

  // 上传成功处理的事件
  const handleUploadSuccess = async(response:any) => {
    userModel.avatarUrl=response.data;
  }

  // 上传失败处理的事件
  const handleUploadError = () => {
    ElMessage.error("上传失败");
  }

  // 获取图片上传的url
  const getUrl= ()=>{
    url.value=filesApi.url();
  }

  onMounted(()=>{
    getUserList();
    getRoleList();
    getUrl();
  })

</script>

<style scoped lang="less">
  .container{
    height: 100%;
    box-sizing: border-box; 
  }
  .header{
    display: flex;
    align-items: center;
    justify-content: space-between;
  }
  .editor{
    width: 100%;
    :deep(.ql-editor) {
      min-height: 200px;
    }
  }
  .title{
    font-size: large;
    font-weight: 600;
  }
  .tag{
    margin: 0 5px 0 0;
  }
</style>

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值