云尚办公系统是一套自动办公系统,系统主要包含:管理端和员工端
管理端包含:权限管理、审批管理、公众号菜单管理
员工端采用微信公众号操作,包含:办公审批、微信授权登录、消息推送等功能
项目服务器端架构:SpringBoot + MyBatisPlus + SpringSecurity + Redis + Activiti+ MySQL
前端架构:vue-admin-template + Node.js + Npm + Vue + ElementUI + Axios
工具 | 版本 |
---|---|
后台 | SpringBoot 2.3.6 + MyBatisPlus 3.4.1 |
数据库 | MySQL 8.0.27 |
开发工具 | IDEA 2022.3 |
前端工具 | vsCode |
前端技术 | Vue + ElementUI + Node.js 14.15.0 |
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.7.13version>
<relativePath/>
parent>
<groupId>com.gdhdgroupId>
<artifactId>guigu-oa-parentartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>pompackaging>
<name>guigu-oa-parentname>
<description>guigu-oa-parentdescription>
<modules>
<module>commonmodule>
<module>common/common-utilmodule>
<module>common/service-utilmodule>
<module>common/spirng-securitymodule>
<module>modelmodule>
<module>service-oamodule>
modules>
<properties>
<java.version>1.8java.version>
<mybatis-plus.version>3.4.1mybatis-plus.version>
<mysql.version>8.0.30mysql.version>
<knife4j.version>3.0.3knife4j.version>
<jwt.version>0.9.1jwt.version>
<fastjson.version>2.0.21fastjson.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>${mybatis-plus.version}version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>${mysql.version}version>
dependency>
<dependency>
<groupId>com.github.xiaoymingroupId>
<artifactId>knife4j-spring-boot-starterartifactId>
<version>${knife4j.version}version>
dependency>
<dependency>
<groupId>io.jsonwebtokengroupId>
<artifactId>jjwtartifactId>
<version>${jwt.version}version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>${fastjson.version}version>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.1version>
<configuration>
<source>1.8source>
<target>1.8target>
configuration>
plugin>
plugins>
build>
project>
common公共模块
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>com.gdhdgroupId>
<artifactId>guigu-oa-parentartifactId>
<version>0.0.1-SNAPSHOTversion>
parent>
<artifactId>commonartifactId>
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
properties>
project>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<scope>provided scope>
dependency>
<dependency>
<groupId>io.jsonwebtokengroupId>
<artifactId>jjwtartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
dependency>
dependencies>
<dependency>
<groupId>com.gdhdgroupId>
<artifactId>common-utilartifactId>
<version>0.0.1-SNAPSHOTversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>com.github.xiaoymingroupId>
<artifactId>knife4j-spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>net.minidevgroupId>
<artifactId>json-smartartifactId>
<version>2.4.11version>
<scope>compilescope>
dependency>
dependencies>
<dependencies>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
<dependency>
<groupId>com.github.xiaoymingroupId>
<artifactId>knife4j-spring-boot-starterartifactId>
<scope>provided scope>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<scope>provided scope>
dependency>
dependencies>
<dependencies>
<dependency>
<groupId>com.gdhdgroupId>
<artifactId>modelartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
<dependency>
<groupId>com.gdhdgroupId>
<artifactId>service-utilartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<build>
<finalName>${project.artifactId}finalName>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
@SpringBootApplication
@ComponentScan("com.gdhd")
@MapperScan("com.gdhd.*.mapper")
public class ServiceAuthApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceAuthApplication.class, args);
}
}
直接到后面资料进行获取
也是直接到后面资料获取
实体类,放在工程Model模块中
关于每一个数据库和实体类的后台环境直接使用代码生成器进行构建
package Code;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
public class CodeGet {
public static void main(String[] args) {
// 1、创建代码生成器
AutoGenerator mpg = new AutoGenerator();
// 2、全局配置
// 全局配置
GlobalConfig gc = new GlobalConfig();
//你项目的位置
gc.setOutputDir("F:\\code\\guigu-oa-parent\\service-oa"+"/src/main/java");
gc.setServiceName("%sService"); //去掉Service接口的首字母I
gc.setAuthor("gdhd");
gc.setOpen(false);
mpg.setGlobalConfig(gc);
// 3、数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/guigu-oa?serverTimezone=GMT%2B8&useSSL=false");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("root");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
// 4、包配置
PackageConfig pc = new PackageConfig();
pc.setParent("com.gdhd");
pc.setModuleName("auth"); //模块名
pc.setController("controller");
pc.setService("service");
pc.setMapper("mapper");
mpg.setPackageInfo(pc);
// 5、策略配置
StrategyConfig strategy = new StrategyConfig();
//数据表
strategy.setInclude("sys_role");
strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作
strategy.setRestControllerStyle(true); //restful api风格控制器
strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符
mpg.setStrategy(strategy);
// 6、执行
mpg.execute();
}
}
配置MySQL数据库的相关配置及Mybatis-Plus日志
操作在:service-oa模块
spring:
application:
name: service-oa
profiles:
active: dev
server:
port: 8800
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 查看日志
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/guigu-oa?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
username: root
password: root
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
mvc:
# SpringBoot2.6.x与Swagger2 3.0.0版本冲突解决
pathmatch:
matching-strategy: ant_path_matcher
注意:连接地址url
3. MySQL5.7版本的url:jdbc:mysql://localhost:3306/guigu-oa?characterEncoding=utf-8&useSSL=false
4. MySQL8.0版本的url:jdbc:mysql://localhost:3306/guigu-oa?
serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
5. 否则运行测试用例报告如下错误:java.sql.SQLException: The server time zone value ‘Öйú±ê׼ʱ¼ä
package com.gdhd.common.config.mp;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan("com.gdhd.auth.mapper")
public class MybatisPlusConfig {
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> configuration.setUseDeprecatedExecutor(false);
}
}
vue-element-admin是基于element-ui 的一套后台管理系统集成方案。
功能:https://panjiachen.github.io/vue-element-admin-site/zh/guide/#功能
GitHub地址:https://github.com/PanJiaChen/vue-element-admin
项目在线预览:https://panjiachen.gitee.io/vue-element-admin
vue-admin-template是基于vue-element-admin的一套后台管理系统基础模板(最少精简版),可作为模板进行二次开发。
GitHub地址:https://github.com/PanJiaChen/vue-admin-template
建议:你可以在 vue-admin-template 的基础上进行二次开发,把 vue-element-admin当做工具箱,想要什么功能或者组件就去 vue-element-admin 那里复制过来。
#修改项目名称 vue-admin-template 改为 guigu-auth-ui
# 解压压缩包
# 进入目录
cd guigu-auth-ui
# 安装依赖
npm install
# 启动。执行后,浏览器自动弹出并访问http://localhost:9528/
npm run dev
|-dist 生产环境打包生成的打包项目
|-mock 使用mockjs来mock接口
|-public 包含会被自动打包到项目根路径的文件夹
|-index.html 唯一的页面
|-src
|-api 包含接口请求函数模块
|-table.js 表格列表mock数据接口的请求函数
|-user.js 用户登陆相关mock数据接口的请求函数
|-assets 组件中需要使用的公用资源
|-404_images 404页面的图片
|-components 非路由组件
|-SvgIcon svg图标组件
|-Breadcrumb 面包屑组件(头部水平方向的层级组件)
|-Hamburger 用来点击切换左侧菜单导航的图标组件
|-icons
|-svg 包含一些svg图片文件
|-index.js 全局注册SvgIcon组件,加载所有svg图片并暴露所有svg文件名的数组
|-layout
|-components 组成整体布局的一些子组件
|-mixin 组件中可复用的代码
|-index.vue 后台管理的整体界面布局组件
|-router
|-index.js 路由器
|-store
|-modules
|-app.js 管理应用相关数据
|-settings.js 管理设置相关数据
|-user.js 管理后台登陆用户相关数据
|-getters.js 提供子模块相关数据的getters计算属性
|-index.js vuex的store
|-styles
|-xxx.scss 项目组件需要使用的一些样式(使用scss)
|-utils 一些工具函数
|-auth.js 操作登陆用户的token cookie
|-get-page-title.js 得到要显示的网页title
|-request.js axios二次封装的模块
|-validate.js 检验相关工具函数
|-index.js 日期和请求参数处理相关工具函数
|-views 路由组件文件夹
|-dashboard 首页
|-login 登陆
|-App.vue 应用根组件
|-main.js 入口js
|-permission.js 使用全局守卫实现路由权限控制的模块
|-settings.js 包含应用设置信息的模块
|-.env.development 指定了开发环境的代理服务器前缀路径
|-.env.production 指定了生产环境的代理服务器前缀路径
|-.eslintignore eslint的忽略配置
|-.eslintrc.js eslint的检查配置
|-.gitignore git的忽略配置
|-.npmrc 指定npm的淘宝镜像和sass的下载地址
|-babel.config.js babel的配置
|-jsconfig.json 用于vscode引入路径提示的配置
|-package.json 当前项目包信息
|-package-lock.json 当前项目依赖的第三方包的精确信息
|-vue.config.js webpack相关配置(如: 代理服务器)
位置:src/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
/* Layout */
import Layout from '@/layout'
/**
* Note: sub-menu only appear when route children.length >= 1
* Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
*
* hidden: true if set true, item will not show in the sidebar(default is false)
* alwaysShow: true if set true, will always show the root menu
* if not set alwaysShow, when item has more than one children route,
* it will becomes nested mode, otherwise not show the root menu
* redirect: noRedirect if set noRedirect will no redirect in the breadcrumb
* name:'router-name' the name is used by (must set!!!)
* meta : {
roles: ['admin','editor'] control the page roles (you can set multiple roles)
title: 'title' the name show in sidebar and breadcrumb (recommend set)
icon: 'svg-name'/'el-icon-x' the icon show in the sidebar
breadcrumb: false if set false, the item will hidden in breadcrumb(default is true)
activeMenu: '/example/list' if set path, the sidebar will highlight the path you set
}
*/
/**
* constantRoutes
* a base page that does not have permission requirements
* all roles can be accessed
*/
export const constantRoutes = [
{
path: '/login',
component: () => import('@/views/login/index'),
hidden: true
},
{
path: '/404',
component: () => import('@/views/404'),
hidden: true
},
{
path: '/',
component: Layout,
redirect: '/dashboard',
children: [{
path: 'dashboard',
name: 'Dashboard',
component: () => import('@/views/dashboard/index'),
meta: { title: 'Dashboard', icon: 'dashboard' }
}]
},
// 404 page must be placed at the end !!!
{ path: '*', redirect: '/404', hidden: true }
]
const createRouter = () => new Router({
// mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes
})
const router = createRouter()
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // reset router
}
export default router
lintOnSave: false,//process.env.NODE_ENV === 'development',
项目中我们会将响应封装成json返回,一般我们会将所有接口的数据格式统一, 使前端(iOS Android, Web)对数据的操作更一致、轻松。
一般情况下,统一返回数据格式没有固定的格式,只要能描述清楚返回的数据状态以及要返回的具体数据就可以。但是一般会包含状态码、返回消息、数据这几部分内容
例如,我们的系统要求返回的基本数据格式如下:
列表:
{
"code": 200,
"message": "成功",
"data": [
{
"id": 2,
"roleName": "系统管理员"
}
],
"ok": true
}
分页:
{
"code": 200,
"message": "成功",
"data": {
"records": [
{
"id": 2,
"roleName": "系统管理员"
},
{
"id": 3,
"name": "普通管理员"
}
],
"total": 10,
"size": 3,
"current": 1,
"orders": [],
"hitCount": false,
"searchCount": true,
"pages": 2
},
"ok": true
}
没有返回数据:
{
"code": 200,
"message": "成功",
"data": null,
"ok": true
}
package com.gdhd.common.result;
import lombok.Data;
@Data
public class Result<T> {
//返回码
private Integer code;
//返回消息
private String message;
//返回数据
private T data;
public Result(){}
// 返回数据
protected static <T> Result<T> build(T data) {
Result<T> result = new Result<T>();
if (data != null)
result.setData(data);
return result;
}
public static <T> Result<T> build(T body, Integer code, String message) {
Result<T> result = build(body);
result.setCode(code);
result.setMessage(message);
return result;
}
public static <T> Result<T> build(T body, ResultCodeEnum resultCodeEnum) {
Result<T> result = build(body);
result.setCode(resultCodeEnum.getCode());
result.setMessage(resultCodeEnum.getMessage());
return result;
}
public static<T> Result<T> ok(){
return Result.ok(null);
}
/**
* 操作成功
* @param data baseCategory1List
* @param
* @return
*/
public static<T> Result<T> ok(T data){
Result<T> result = build(data);
return build(data, ResultCodeEnum.SUCCESS);
}
public static<T> Result<T> fail(){
return Result.fail(null);
}
/**
* 操作失败
* @param data
* @param
* @return
*/
public static<T> Result<T> fail(T data){
Result<T> result = build(data);
return build(data, ResultCodeEnum.FAIL);
}
public Result<T> message(String msg){
this.setMessage(msg);
return this;
}
public Result<T> code(Integer code){
this.setCode(code);
return this;
}
}
package com.gdhd.common.result;
import lombok.Getter;
@Getter
public enum ResultCodeEnum {
SUCCESS(200,"成功"),
FAIL(201, "失败"),
SERVICE_ERROR(2012, "服务异常"),
DATA_ERROR(204, "数据异常"),
LOGIN_AUTH(208, "未登陆"),
PERMISSION(209, "没有权限")
;
private Integer code;
private String message;
private ResultCodeEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
}
文档地址:https://doc.xiaominfo.com/
knife4j是为Java MVC框架集成Swagger生成Api文
前后端分离开发模式中,api文档是最好的沟通方式。
Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。
1、及时性 (接口变更后,能够及时准确地通知相关前后端开发人员)
2、规范性 (并且保证接口的规范性,如接口的地址,请求方式,参数及响应格式和错误信息)
3、一致性 (接口信息一致,不会出现因开发人员拿到的文档版本不一致,而出现分歧)
4、可测性 (直接在接口文档上进行测试,以方便理解业务)
操作模块:service-uitl
<dependency>
<groupId>com.github.xiaoymingroupId>
<artifactId>knife4j-spring-boot-starterartifactId>
dependency>
package com.gdhd.common.config.kinfe4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
import java.util.ArrayList;
import java.util.List;
@Configuration
@EnableSwagger2WebMvc
public class Knife4jConfig {
@Bean
public Docket adminApiConfig(){
List<Parameter> pars = new ArrayList<>();
ParameterBuilder tokenPar = new ParameterBuilder();
tokenPar.name("token")
.description("用户token")
.defaultValue("")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(false)
.build();
pars.add(tokenPar.build());
//添加head参数end
Docket adminApi = new Docket(DocumentationType.SWAGGER_2)
.groupName("adminApi")
.apiInfo(adminApiInfo())
.select()
//只显示admin路径下的页面
.apis(RequestHandlerSelectors.basePackage("com.gdhd"))
.paths(PathSelectors.regex("/admin/.*"))
.build()
.globalOperationParameters(pars);
return adminApi;
}
private ApiInfo adminApiInfo(){
return new ApiInfoBuilder()
.title("后台管理系统-API文档")
.description("本文档描述了后台管理系统微服务接口定义")
.version("1.0")
.contact(new Contact("atguigu", "http://atguigu.com", "[email protected]"))
.build();
}
}
前端中进行配置:vue.config.js
+ 注释掉mock接口配置
+ 配置代理转发请求到目标接口
// before: require('./mock/mock-server.js')
proxy: {
'/dev-api': { // 匹配所有以 '/dev-api'开头的请求路径
target: 'http://localhost:8800',
changeOrigin: true, // 支持跨域
pathRewrite: { // 重写路径: 去掉路径中开头的'/dev-api'
'^/dev-api': ''
}
}
}
const token = store.getters.token
if (token) {
// let each request carry token
// ['X-Token'] is a custom headers key
// please modify it according to the actual situation
// config.headers['X-Token'] = getToken()
config.headers['token'] = token
}
//在这个文件中,修改!==200
if (res.code !== 200) {
Message({
message: res.message || 'Error',
type: 'error',
duration: 5 * 1000
})
return Promise.reject(new Error(res.message || 'Error'))
} else {
return res
}
import request from '@/utils/request'
export function login(data) {
return request({
url: '/admin/system/index/login',
method: 'post',
data
})
}
export function getInfo(token) {
return request({
url: '/admin/system/index/info',
method: 'get',
params: { token }
})
}
export function logout() {
return request({
url: '/admin/system/index/logout',
method: 'post'
})
}
package com.gdhd.auth.controller;
import com.gdhd.common.result.Result;
import io.swagger.annotations.Api;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@Api(tags = "后台登录管理")
@RestController
@RequestMapping("/admin/system/index")
public class IndexController {
/**
* 登录
* @return
*/
@PostMapping("login")
public Result login() {
Map<String, Object> map = new HashMap<>();
map.put("token","admin");
return Result.ok(map);
}
/**
* 获取用户信息
* @return
*/
@GetMapping("info")
public Result info() {
Map<String, Object> map = new HashMap<>();
map.put("roles","[admin]");
map.put("name","admin");
map.put("avatar","https://oss.aliyuncs.com/aliyun_id_photo_bucket/default_handsome.jpg");
return Result.ok(map);
}
/**
* 退出
* @return
*/
@PostMapping("logout")
public Result logout(){
return Result.ok();
}
}
{
path: '/system',
component: Layout,
meta: {
title: '系统管理',
icon: 'el-icon-s-tools'
},
alwaysShow: true,
children: [
{
path: 'sysRole',
component: () => import('@/views/system/sysRole/list'),
meta: {
title: '角色管理',
icon: 'el-icon-s-help'
},
}
]
},
//用于向后端发送请求
import request from '@/utils/request'
const api_name = '/admin/system/sysRole'
export default {
}
//前端页面
<template>
<div class="app-container">
用户列表
<div class="app-container">
<div class="search-div">
<el-form label-width="70px" size="small">
<el-row>
<el-col :span="24">
<el-form-item label="角色名称">
<el-input
style="width: 100%"
v-model="searchObj.roleName"
placeholder="角色名称"
>el-input>
el-form-item>
el-col>
el-row>
<el-row style="display: flex">
<el-button
type="primary"
icon="el-icon-search"
size="mini"
:loading="loading"
@click="fetchData()"
>搜索el-button
>
<el-button icon="el-icon-refresh" size="mini" @click="resetData"
>重置el-button
>
<div class="tools-div">
<el-button
type="success"
icon="el-icon-plus"
size="mini"
@click="add"
>添 加el-button
>
<el-button class="btn-add" size="mini" @click="batchRemove()"
>批量删除el-button
>
div>
el-row>
el-form>
div>
<el-table
v-loading="listLoading"
:data="list"
stripe
border
style="width: 100%; margin-top: 10px"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" />
<el-table-column label="序号" width="70" align="center">
<template slot-scope="scope">
{{ (page - 1) * limit + scope.$index + 1 }}
template>
el-table-column>
<el-table-column prop="roleName" label="角色名称" />
<el-table-column prop="roleCode" label="角色编码" />
<el-table-column prop="createTime" label="创建时间" width="160" />
<el-table-column label="操作" width="200" align="center">
<template slot-scope="scope">
<el-button
type="primary"
icon="el-icon-edit"
size="mini"
@click="edit(scope.row.id)"
title="修改"
/>
<el-button
type="danger"
icon="el-icon-delete"
size="mini"
@click="removeDataById(scope.row.id)"
title="删除"
/>
<el-button
type="warning"
icon="el-icon-baseball"
size="mini"
@click="showAssignAuth(scope.row)"
title="分配权限"
/>
template>
el-table-column>
el-table>
<el-pagination
:current-page="page"
:total="total"
:page-size="limit"
style="padding: 30px 0; text-align: center"
layout="total, prev, pager, next, jumper"
@current-change="fetchData"
/>
<el-dialog title="添加/修改" :visible.sync="dialogVisible" width="40%">
<el-form
ref="dataForm"
:model="sysRole"
label-width="150px"
size="small"
style="padding-right: 40px"
>
<input type="hidden" v-model="sysRole.id" />
<el-form-item label="角色名称">
<el-input v-model="sysRole.roleName" />
el-form-item>
<el-form-item label="角色编码">
<el-input v-model="sysRole.roleCode" />
el-form-item>
el-form>
<span slot="footer" class="dialog-footer">
<el-button
@click="dialogVisible = false"
size="small"
icon="el-icon-refresh-right"
>取 消el-button
>
<el-button
type="primary"
icon="el-icon-check"
@click="saveOrUpdate()"
size="small"
>确 定el-button
>
span>
el-dialog>
div>
div>
template>
<script>
import api from "@/api/system/sysRole";
export default {
// vue代码结构
data() {
return {
list: [], // 列表
total: 0, // 总记录数
page: 1, // 页码
limit: 10, // 每页记录数
searchObj: {}, // 查询条件
multipleSelection: [], // 批量删除选中的记录列表
dialogVisible: false, //是否弹框
sysRole: {
id: null,
roleName: "",
roleCode: "",
},
dialogVisible: false,
selections: [], // 批量删除选中的记录列表
};
},
//在页面渲染之前
};
script>
/*
获取角色分页列表(带搜索)
*/
//page: 页码,limit:每页记录数,searchObj:搜索条件
getPageList(page, limit, searchObj) {
return request({
url: `${api_name}/${page}/${limit}`,
method: 'get',
params: searchObj
})
}
// 页面渲染成功后获取数据
created() {
this.fetchData()
},
// 定义方法
methods: {
//如果没有参数,则代表页码为1
fetchData(current=1) {
this.page = current
// 调用api
api.getPageList(this.page, this.limit, this.searchObj).then(response => {
//拿到数据后,渲染页面
this.list = response.data.records
this.total = response.data.total
})
},
}
@Api("角色功能")
@RestController
@RequestMapping("/admin/system/sysRole")
public class SysRoleController {
@ApiOperation("条件分页查询")
@GetMapping("{page}/{limit}")
//@PathVariable Long page, 当前页
//@PathVariable Long limit,每页x显示记录数
//SysRoleQueryVo 每页条件对象
public Result pageQueryRole(@PathVariable Long page, @PathVariable Long limit, SysRoleQueryVo sysRoleQueryVo ){
//调用Service方法实现
//1. 创建一个Page对象,传递分页相关参数
Page<SysRole> pageParam=new Page<>(page,limit);
//2. 封装条件 ,判断条件是否为空,不为空进行封装
LambdaQueryWrapper<SysRole> wrapper=new LambdaQueryWrapper<>();
String roleName = sysRoleQueryVo.getRoleName();
//如果条件不等于空
if (!StringUtils.isEmpty(roleName)){
//封装
wrapper.like(SysRole::getRoleName,roleName);
}
Page<SysRole> page1 = sysRoleService.page(pageParam, wrapper);
return Result.ok(page1);
}
}
add(){
//弹出添加修改窗口
this.dialogVisible=true
},
//当点击弹出修改窗口的确定按钮,判断有没有id
saveOrUpdate(){
if(!this.sysRole.id){
//如果没有id则代表是添加功能
this.saveData()
}else{
//如果有id则代表是修改功能
this.updateData()
}
},
saveData(){
//调动api
api.save(this.sysRole).then(response=>{
this.$message.success(response.message || '操作成功')
//关闭窗口
this.dialogVisible=false
this.sysRole={}
//重新显示列表
this.fetchData(this.page)
})
},
save(role) {
return request({
url: `${api_name}/save`,
method: 'post',
data: role
})
},
@ApiOperation("添加角色")
@PostMapping("save")
public Result save(@RequestBody SysRole role){
boolean is_success = sysRoleService.save(role);
if (is_success){
return Result.ok();
}else {
return Result.fail();
}
}
//数据回显
edit(id){
//当点击修改的时候,那就根据id进行查询进行数据回显
api.getId(id).then(response=>{
this.dialogVisible=true
this.sysRole=response.data
})
},
//修改
updateData(){
console.log(this.sysRole)
api.update(this.sysRole).then(response=>{
this.$message.success(response.message || '操作成功')
//关闭窗口
this.dialogVisible=false
//重新显示列表
this.fetchData(this.page)
})
},
//根据id查询
getId(id){
return request({
url:`${api_name}/getId/${id}`,
method:'get',
})
},
//修改
update(role){
return request({
url:`${api_name}/update`,
method:'put',
data:role
})
},
@ApiOperation("根据id查询角色")
@GetMapping("getId/{id}")
public Result getId(@PathVariable Long id){
//根据id进行查询
SysRole role = sysRoleService.getById(id);
return Result.ok(role);
}
@ApiOperation("修改角色")
@PutMapping("update")
//put需要从请求头中获取
public Result update(@RequestBody SysRole role){
System.out.println(role);
sysRoleService.updateById(role);
return Result.ok();
}
当点击某条数据的删除功能时,根据该条数据的id进行删除
//根据id删除
removeDataById(id){
if(confirm("你确定要删除吗?")){
api.removeById(id).then(response=>{
alert(this.list)
this.fetchData(this.page)
alert(this.list)
})
}
},
removeById(id){
return request({
url:`${api_name}/removeById/${id}`,
method:'delete'
})
},
@ApiOperation("根据Id进行删除")
@DeleteMapping("removeById/{id}")
public Result removeById(@PathVariable Long id){
sysRoleService.removeById(id);
return Result.ok();
}
当选择多条数据,然后点击批量删除按钮,对选中的数据进行删除。
// 当多选选项发生变化的时候调用
handleSelectionChange(selection) {
this.selections = selection
},
batchRemove(){
if(this.selections.length===0){
alert("请选择要删除的记录")
return
}
if(confirm("确定要删除记录吗?")){
//用于存放数组
var idList=[];
this.selections.forEach(item=>{
idList.push(item.id);
})
api.batchRemove(idList).then(response=>{
this.fetchData()
this.$message.success(response.message)
})
}
},
// 重置表单
resetData() {
console.log('重置查询表单')
this.searchObj = {}
this.fetchData()
},
batchRemove(idList) {
return request({
url: `${api_name}/batchRemove`,
method: `delete`,
data: idList
})
},
@ApiOperation("批量删除")
//数组[1,2,3]
@DeleteMapping("batchRemove")
public Result BatchRemove(@RequestBody List<Long> ids){
boolean is_success = sysRoleService.removeByIds(ids);
if (is_success){
return Result.ok();
}else {
return Result.fail();
}
}
+修改 src/router/index.js 文件
children: [
{
name: 'sysUser',
path: 'sysUser',
component: () => import('@/views/system/sysUser/list'),
meta: {
title: '用户管理',
icon: 'el-icon-s-custom'
},
},
{
path: 'sysRole',
component: () => import('@/views/system/sysRole/list'),
meta: {
title: '角色管理',
icon: 'el-icon-s-help'
}
},
]
创建文件:src/api/system/sysUser.js
import request from '@/utils/request'
const api_name = '/admin/system/sysUser'
export default {
}
创建src/views/system/sysUser/list.vue
<template>
<div class="app-container">
<div class="search-div">
<el-form label-width="70px" size="small">
<el-row>
<el-col :span="8">
<el-form-item label="关 键 字">
<el-input style="width: 95%" v-model="searchObj.keyword" placeholder="用户名/姓名/手机号码"></el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="操作时间">
<el-date-picker
v-model="createTimes"
type="datetimerange"
range-separator="至"
start-placeholder="开始时间"
end-placeholder="结束时间"
value-format="yyyy-MM-dd HH:mm:ss"
style="margin-right: 10px;width: 100%;"
/>
</el-form-item>
</el-col>
</el-row>
<el-row style="display:flex">
<el-button type="primary" icon="el-icon-search" size="mini" :loading="loading" @click="fetchData()">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetData">重置</el-button>
</el-row>
</el-form>
</div>
<!-- 工具条 -->
<div class="tools-div">
<el-button type="success" icon="el-icon-plus" size="mini" @click="add">添 加</el-button>
</div>
<!-- 列表 -->
<el-table
v-loading="listLoading"
:data="list"
stripe
border
style="width: 100%;margin-top: 10px;">
<el-table-column
label="序号"
width="70"
align="center">
<template slot-scope="scope">
{{ (page - 1) * limit + scope.$index + 1 }}
</template>
</el-table-column>
<el-table-column prop="username" label="用户名" width="100"/>
<el-table-column prop="name" label="姓名" width="70"/>
<el-table-column prop="phone" label="手机" width="120"/>
<el-table-column prop="postName" label="岗位" width="100"/>
<el-table-column prop="deptName" label="部门" width="100"/>
<el-table-column label="所属角色" width="130">
<template slot-scope="scope">
<span v-for="item in scope.row.roleList" :key="item.id" style="margin-right: 10px;">{{ item.roleName }}</span>
</template>
</el-table-column>
<el-table-column label="状态" width="80">
<template slot-scope="scope">
<el-switch
v-model="scope.row.status === 1"
@change="switchStatus(scope.row)">
</el-switch>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" width="160"/>
<el-table-column label="操作" width="180" align="center" fixed="right">
<template slot-scope="scope">
<el-button type="primary" icon="el-icon-edit" size="mini" @click="edit(scope.row.id)" title="修改"/>
<el-button type="danger" icon="el-icon-delete" size="mini" @click="removeDataById(scope.row.id)" title="删除" />
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<el-pagination
:current-page="page"
:total="total"
:page-size="limit"
:page-sizes="[5, 10, 20, 30, 40, 50, 100]"
style="padding: 30px 0; text-align: center;"
layout="sizes, prev, pager, next, jumper, ->, total, slot"
@current-change="fetchData"
@size-change="changeSize"
/>
<el-dialog title="添加/修改" :visible.sync="dialogVisible" width="40%" >
<el-form ref="dataForm" :model="sysUser" label-width="100px" size="small" style="padding-right: 40px;">
<el-form-item label="用户名" prop="username">
<el-input v-model="sysUser.username"/>
</el-form-item>
<el-form-item v-if="!sysUser.id" label="密码" prop="password">
<el-input v-model="sysUser.password" type="password"/>
</el-form-item>
<el-form-item label="姓名" prop="name">
<el-input v-model="sysUser.name"/>
</el-form-item>
<el-form-item label="手机" prop="phone">
<el-input v-model="sysUser.phone"/>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false" size="small" icon="el-icon-refresh-right">取 消</el-button>
<el-button type="primary" icon="el-icon-check" @click="saveOrUpdate()" size="small">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import api from '@/api/system/sysUser'
const defaultForm = {
id: '',
username: '',
password: '',
name: '',
phone: '',
status: 1
}
export default {
data() {
return {
listLoading: false, // 数据是否正在加载
list: null, // banner列表
total: 0, // 数据库中的总记录数
page: 1, // 默认页码
limit: 10, // 每页记录数
searchObj: {}, // 查询表单对象
createTimes: [],
dialogVisible: false,
sysUser: defaultForm,
saveBtnDisabled: false,
}
}
}
</script>
在页面渲染前获取数据,然后再渲染在页面上,需要把搜索条件,还有页码,每页记录数进行传递
created(){
this.fetchData()
},
methods:{
// 当页码发生改变的时候
changeSize(size) {
console.log(size)
this.limit = size
this.fetchData(1)
},
fetchData(page=1){
this.page=page
//判断查询条件是否有选择时间
if(this.createTimes && this.createTimes.length ==2) {
this.searchObj.createTimeBegin = this.createTimes[0]
this.searchObj.createTimeEnd = this.createTimes[1]
}
api.getPageList(this.page,this.limit,this.searchObj).then(response=>{
this.list=response.data.records
this.total=response.data.total
// 数据加载并绑定成功
this.listLoading = false
})
}
},
// 重置查询表单
resetData() {
console.log('重置查询表单')
this.searchObj = {}
this.createTimes = []
this.fetchData()
},
getPageList(page, limit, searchObj) {
return request({
url: `${api_name}/${page}/${limit}`,
method: 'get',
params: searchObj // url查询字符串或表单键值对
})
},
@RestController
@RequestMapping("/admin/system/sysUser")
public class SysUserController {
@Autowired
private SysUserService service;
//用户条件分页查询
@ApiOperation("用户条件分页查询")
@GetMapping("{page}/{limit}")
public Result index(@PathVariable Long page,
@PathVariable Long limit,
SysUserQueryVo sysUserQueryVo) {
//创建page对象
Page<SysUser> pageParam = new Page<>(page,limit);
//封装条件,判断条件值不为空
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
//获取条件值
//获取关键字
String username = sysUserQueryVo.getKeyword();
//获取相关时间
String createTimeBegin = sysUserQueryVo.getCreateTimeBegin();
String createTimeEnd = sysUserQueryVo.getCreateTimeEnd();
//判断条件值不为空
//like 模糊查询
if(!StringUtils.isEmpty(username)) {
wrapper.like(SysUser::getUsername,username);
}
//ge 大于等于
if(!StringUtils.isEmpty(createTimeBegin)) {
wrapper.ge(SysUser::getCreateTime,createTimeBegin);
}
//le 小于等于
if(!StringUtils.isEmpty(createTimeEnd)) {
wrapper.le(SysUser::getCreateTime,createTimeEnd);
}
//调用mp的方法实现条件分页查询
IPage<SysUser> pageModel = service.page(pageParam, wrapper);
return Result.ok(pageModel);
}
}
+ 后端其它环境,直接使用代码生成器生成
add() {
this.dialogVisible = true
this.sysUser = Object.assign({}, defaultForm)
},
//当添加修改窗口弹出
saveOrUpdate() {
this.$refs.dataForm.validate(valid => {
if (valid) {
this.saveBtnDisabled = true // 防止表单重复提交
if (!this.sysUser.id) {
this.saveData()
} else {
this.updateData()
}
}
})
},
// 新增
saveData() {
api.save(this.sysUser).then(response => {
this.$message.success('操作成功')
this.dialogVisible = false
this.fetchData(this.page)
})
},
save(role) {
return request({
url: `${api_name}/save`,
method: 'post',
data: role
})
},
@ApiOperation(value = "保存用户")
@PostMapping("save")
public Result save(@RequestBody SysUser user) {
service.save(user);
return Result.ok();
}
//修改
//数据回显
edit(id){
api.findById(id).then(response=>{
this.sysUser=response.data
this.dialogVisible=true
})
},
// 根据id更新记录
updateData() {
api.updateById(this.sysUser).then(response => {
this.$message.success(response.message || '操作成功')
this.dialogVisible = false
this.fetchData(this.page)
})
}
}
findById(id){
return request({
url:`${api_name}/findById/${id}`,
method:'get'
})
},
updateById(role) {
return request({
url: `${api_name}/update`,
method: 'put',
data: role
})
},
@ApiOperation("根据id进行查询")
@GetMapping("findById/{id}")
public Result findById(@PathVariable Long id){
SysUser user = service.getById(id);
System.out.println(user);
return Result.ok(user);
}
@ApiOperation(value = "更新用户")
@PutMapping("update")
public Result updateById(@RequestBody SysUser user) {
service.updateById(user);
return Result.ok();
}
// 根据id删除数据
removeDataById(id) {
// debugger
this.$confirm('此操作将永久删除该记录, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => { // promise
// 点击确定,远程调用ajax
return api.removeById(id)
}).then((response) => {
this.fetchData(this.page)
this.$message.success(response.message || '删除成功')
}).catch(() => {
this.$message.info('取消删除')
})
},
removeById(id) {
return request({
url: `${api_name}/remove/${id}`,
method: 'delete'
})
}
@ApiOperation(value = "删除用户")
@DeleteMapping("remove/{id}")
public Result remove(@PathVariable Long id) {
service.removeById(id);
return Result.ok();
}
1、进入分配页面:获取已分配角色与全部角色,进行页面展示
2、保存分配角色:删除之前分配的角色和保存
src/views/system/sysUser/list.vue
<template>
<div class="app-container">
<div class="search-div">
<el-form label-width="70px" size="small">
<el-row>
<el-col :span="8">
<el-form-item label="关 键 字">
<el-input style="width: 95%" v-model="searchObj.keyword" placeholder="用户名/姓名/手机号码">el-input>
el-form-item>
el-col>
<el-col :span="8">
<el-form-item label="操作时间">
<el-date-picker
v-model="createTimes"
type="datetimerange"
range-separator="至"
start-placeholder="开始时间"
end-placeholder="结束时间"
value-format="yyyy-MM-dd HH:mm:ss"
style="margin-right: 10px;width: 100%;"
/>
el-form-item>
el-col>
el-row>
<el-row style="display:flex">
<el-button type="primary" icon="el-icon-search" size="mini" :loading="loading" @click="fetchData()">搜索el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetData">重置el-button>
el-row>
el-form>
div>
<div class="tools-div">
<el-button type="success" icon="el-icon-plus" size="mini" @click="add">添 加el-button>
div>
<el-table
v-loading="listLoading"
:data="list"
stripe
border
style="width: 100%;margin-top: 10px;">
<el-table-column
label="序号"
width="70"
align="center">
<template slot-scope="scope">
{{ (page - 1) * limit + scope.$index + 1 }}
template>
el-table-column>
<el-table-column prop="username" label="用户名" width="100"/>
<el-table-column prop="name" label="姓名" width="70"/>
<el-table-column prop="phone" label="手机" width="120"/>
<el-table-column prop="postName" label="岗位" width="100"/>
<el-table-column prop="deptName" label="部门" width="100"/>
<el-table-column label="所属角色" width="130">
<template slot-scope="scope">
<span v-for="item in scope.row.roleList" :key="item.id" style="margin-right: 10px;">{{ item.roleName }}span>
template>
el-table-column>
<el-table-column label="状态" width="80">
<template slot-scope="scope">
<el-switch
v-model="scope.row.status === 1"
@change="switchStatus(scope.row)">
el-switch>
template>
el-table-column>
<el-table-column prop="createTime" label="创建时间" width="160"/>
<el-table-column label="操作" width="180" align="center" fixed="right">
<template slot-scope="scope">
<el-button type="primary" icon="el-icon-edit" size="mini" @click="edit(scope.row.id)" title="修改"/>
<el-button type="danger" icon="el-icon-delete" size="mini" @click="removeDataById(scope.row.id)" title="删除" />
<el-button type="warning" icon="el-icon-baseball" size="mini" @click="showAssignRole(scope.row)" title="分配角色"/>
template>
el-table-column>
el-table>
<el-pagination
:current-page="page"
:total="total"
:page-size="limit"
:page-sizes="[5, 10, 20, 30, 40, 50, 100]"
style="padding: 30px 0; text-align: center;"
layout="sizes, prev, pager, next, jumper, ->, total, slot"
@current-change="fetchData"
@size-change="changeSize"
/>
<el-dialog title="添加/修改" :visible.sync="dialogVisible" width="40%" >
<el-form ref="dataForm" :model="sysUser" label-width="100px" size="small" style="padding-right: 40px;">
<el-form-item label="用户名" prop="username">
<el-input v-model="sysUser.username"/>
el-form-item>
<el-form-item v-if="!sysUser.id" label="密码" prop="password">
<el-input v-model="sysUser.password" type="password"/>
el-form-item>
<el-form-item label="姓名" prop="name">
<el-input v-model="sysUser.name"/>
el-form-item>
<el-form-item label="手机" prop="phone">
<el-input v-model="sysUser.phone"/>
el-form-item>
el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false" size="small" icon="el-icon-refresh-right">取 消el-button>
<el-button type="primary" icon="el-icon-check" @click="saveOrUpdate()" size="small">确 定el-button>
span>
el-dialog>
<el-dialog title="分配角色" :visible.sync="dialogRoleVisible">
<el-form label-width="80px">
<el-form-item label="用户名">
<el-input disabled :value="sysUser.username">el-input>
el-form-item>
<el-form-item label="角色列表">
<el-checkbox :indeterminate="isIndeterminate" v-model="checkAll" @change="handleCheckAllChange">全选el-checkbox>
<div style="margin: 15px 0;">div>
<el-checkbox-group v-model="userRoleIds" @change="handleCheckedChange">
<el-checkbox v-for="role in allRoles" :key="role.id" :label="role.id">{{role.roleName}}el-checkbox>
el-checkbox-group>
el-form-item>
el-form>
<div slot="footer">
<el-button type="primary" @click="assignRole" size="small">保存el-button>
<el-button @click="dialogRoleVisible = false" size="small">取消el-button>
div>
el-dialog>
div>
template>
<script>import api from '@/api/system/sysUser'import roleApi from '@/api/system/sysRole'const defaultForm = { id: '', username: '', password: '', name: '', phone: '', status: 1}export default { data() { return { listLoading: true, // 数据是否正在加载 list: null, // banner列表 total: 0, // 数据库中的总记录数 page: 1, // 默认页码 limit: 10, // 每页记录数 searchObj: {}, // 查询表单对象 createTimes: [], dialogVisible: false, sysUser: defaultForm, saveBtnDisabled: false, dialogRoleVisible: false, allRoles: [], // 所有角色列表 userRoleIds: [], // 用户的角色ID的列表 isIndeterminate: false, // 是否是不确定的 checkAll: false // 是否全选 } },
//当点击分配角色
showAssignRole(user){
//当前数据赋值给页面
this.sysUser = user
//弹出分配角色窗口
this.dialogRoleVisible = true
this.getRoles()
},
getRoles () {
//根据Userid进行查询当前用户的角色,和所有角色
roleApi.getRoles(this.sysUser.id).then(response => {
//allRolesList:所有角色
//assginRoleLiest当前用户所属角色
const {allRolesList, assginRoleList} = response.data
this.allRoles = allRolesList
//用户的角色ID的列表
this.userRoleIds = assginRoleList.map(item => item.id)
//是否全选
this.checkAll = allRolesList.length===assginRoleList.length
this.isIndeterminate = assginRoleList.length>0 && assginRoleList.length<allRolesList.length
})
},
/*
全选勾选状态发生改变的监听
*/
handleCheckAllChange (value) {// value 当前勾选状态true/false
// 如果当前全选, userRoleIds就是所有角色id的数组, 否则是空数组
this.userRoleIds = value ? this.allRoles.map(item => item.id) : []
// 如果当前不是全选也不全不选时, 指定为false
this.isIndeterminate = false
},
/*
角色列表选中项发生改变的监听
*/
handleCheckedChange (value) {
const {userRoleIds, allRoles} = this
this.checkAll = userRoleIds.length === allRoles.length && allRoles.length>0
this.isIndeterminate = userRoleIds.length>0 && userRoleIds.length<allRoles.length
},
//当分配角色点击确定窗口时
assignRole () {
let assginRoleVo = {
userId: this.sysUser.id,
roleIdList: this.userRoleIds
}
roleApi.assignRoles(assginRoleVo).then(response => {
this.$message.success(response.message || '分配角色成功')
this.dialogRoleVisible = false
this.fetchData(this.page)
})
},
getRoles(userId){
return request({
url:`${api_name}/getRoles/${userId}`,
method:'get'
})
},
assignRoles(assginRoleVo) {
return request({
url: `${api_name}/doAssign`,
method: 'post',
data: assginRoleVo
})
}
@ApiOperation("根据用户id查询所属角色,和所有角色")
@GetMapping("getRoles/{userId}")
public Result getRoles(@PathVariable Long userId){
Map<String, Object> roleMap = sysRoleService.findRoleByAdminId(userId);
System.out.println(roleMap);
return Result.ok(roleMap);
}
@ApiOperation(value = "根据用户分配角色")
@PostMapping("/doAssign")
public Result doAssign(@RequestBody AssginRoleVo assginRoleVo) {
sysRoleService.doAssign(assginRoleVo);
return Result.ok();
}
@Service
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements SysRoleService {
//需要先搭建sys_user_role数据表环境
@Autowired
private SysUserRoleMapper sysUserRoleMapper;
@Override
public Map<String, Object> findRoleByAdminId(Long userId) {
//查询所有角色
List<SysRole> allRolesList = this.list();
//拥有的角色id
List<Long> existRoleIdList=sysUserRoleMapper.getRoleIds(userId);
// List existUserRoleList = sysUserRoleMapper.selectList(new LambdaQueryWrapper().eq(SysUserRole::getUserId, userId).select(SysUserRole::getRoleId));
// List existRoleIdList = existUserRoleList.stream().map(c->c.getRoleId()).collect(Collectors.toList());
//对角色进行分类
List<SysRole> assginRoleList = new ArrayList<>();
for (SysRole role : allRolesList) {
//已分配
if(existRoleIdList.contains(role.getId())) {
assginRoleList.add(role);
}
}
Map<String, Object> roleMap = new HashMap<>();
roleMap.put("assginRoleList", assginRoleList);
roleMap.put("allRolesList", allRolesList);
return roleMap;
}
@Transactional
@Override
public void doAssign(AssginRoleVo assginRoleVo) {
sysUserRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getUserId, assginRoleVo.getUserId()));
for(Long roleId : assginRoleVo.getRoleIdList()) {
if(StringUtils.isEmpty(roleId)) continue;
SysUserRole userRole = new SysUserRole();
userRole.setUserId(assginRoleVo.getUserId());
userRole.setRoleId(roleId);
sysUserRoleMapper.insert(userRole);
}
}
}
@Mapper
@Repository
public interface SysUserRoleMapper extends BaseMapper<SysUserRole> {
@Select("select role_id from sys_user_role where user_id=#{userId}")
List<Long> getRoleIds(@Param("userId") Long userId);
}
用户状态:状态(1:正常 0:停用),当用户状态为正常时,可以访问后台系统,当用户状态停用后,不可以登录后台系统
switchStatus(row) {
row.status = row.status === 1 ? 0 : 1
api.updateStatus(row.id, row.status).then(response => {
if (response.code) {
this.$message.success(response.message || '操作成功')
this.dialogVisible = false
this.fetchData()
}
})
}
updateStatus(id, status) {
return request({
url: `${api_name}/updateStatus/${id}/${status}`,
method: 'get'
})
}
操作类:SysUserController
@ApiOperation(value = "更新状态")
@GetMapping("updateStatus/{id}/{status}")
public Result updateStatus(@PathVariable Long id, @PathVariable Integer status) {
sysUserService.updateStatus(id, status);
return Result.ok();
}
void updateStatus(Long id, Integer status);
@Transactional
@Override
public void updateStatus(Long id, Integer status) {
SysUser sysUser = this.getById(id);
if(status.intValue() == 1) {
sysUser.setStatus(status);
} else {
sysUser.setStatus(0);
}
this.updateById(sysUser);
}