commit
5632278688
21
README.md
21
README.md
|
@ -4,12 +4,17 @@
|
|||
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/blob/master/LICENSE)
|
||||
[![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
|
||||
<br>
|
||||
[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-2.4.0-success.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus)
|
||||
[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-2.5.0-success.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus)
|
||||
[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-2.4-blue.svg)]()
|
||||
[![JDK-8+](https://img.shields.io/badge/JDK-8+-green.svg)]()
|
||||
[![JDK-11](https://img.shields.io/badge/JDK-11-green.svg)]()
|
||||
|
||||
基于 RuoYi-Vue 集成 Mybatis-Plus Lombok Hutool 等便捷开发工具 适配重写相关业务 便于开发 定期与 RuoYi-Vue 同步
|
||||
RuoYi-Vue-Plus 是基于 RuoYi-Vue 针对 `分布式集群` 场景升级 定期与 RuoYi-Vue 同步
|
||||
|
||||
集成 Lock4j dynamic-datasource 等分布式场景解决方案
|
||||
|
||||
集成 Mybatis-Plus Lombok Hutool 等便捷开发工具 适配重写相关业务 便于开发
|
||||
|
||||
* 前端开发框架 Vue、Element UI
|
||||
* 后端开发框架 Spring Boot、Redis
|
||||
* 容器框架 Undertow 基于 Netty 的高性能容器
|
||||
|
@ -27,6 +32,7 @@
|
|||
* 多数据源框架 dynamic-datasource 支持主从与多种类数据库异构
|
||||
* Redis客户端 采用 Redisson 性能更强
|
||||
* 分布式锁 Lock4j 注解锁、工具锁 多种多样
|
||||
* 部署方式 Docker 容器编排 一键部署业务集群
|
||||
|
||||
## 参考文档
|
||||
|
||||
|
@ -34,6 +40,8 @@
|
|||
<br>
|
||||
>[初始化项目 必看](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/关于初始化项目?sort_id=4164117)
|
||||
>
|
||||
>[部署项目 必看](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/关于应用部署?sort_id=4219382)
|
||||
>
|
||||
>[参考文档 Wiki](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages)
|
||||
|
||||
## 提问四部曲
|
||||
|
@ -58,6 +66,12 @@
|
|||
### 四、加群
|
||||
以上三点已经能解决大家绝大部分问题了,如果还有问题没能通过这几种方式解决,那么加群,大家一起在群里探讨一下
|
||||
|
||||
## 贡献代码
|
||||
|
||||
欢迎各路英雄豪杰 `PR` 代码 请提交到 `dev` 开发分支 统一测试发版
|
||||
|
||||
框架定位为 `通用后台管理系统(分布式集群强化)` 原则上不接受业务 `PR`
|
||||
|
||||
## 修改RuoYi功能
|
||||
### 依赖改动
|
||||
|
||||
|
@ -74,6 +88,7 @@
|
|||
* 移除 fastjson 统一使用 jackson 序列化
|
||||
* 集成 dynamic-datasource 多数据源(默认支持MySQL,其他种类需自行适配)
|
||||
* 集成 Lock4j 实现分布式 注解锁、工具锁 多种多样
|
||||
* 增加 Docker 容器编排 打包插件与部署脚本
|
||||
|
||||
### 代码改动
|
||||
|
||||
|
@ -90,7 +105,7 @@
|
|||
|
||||
### 其他
|
||||
|
||||
* 同步升级 RuoYi-Vue 3.5.0
|
||||
* 同步升级 RuoYi-Vue
|
||||
* GitHub 地址 [RuoYi-Vue-Plus-github](https://github.com/JavaLionLi/RuoYi-Vue-Plus)
|
||||
* 单模块 fast 分支 [RuoYi-Vue-Plus-fast](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/fast/)
|
||||
* Oracle 模块 oracle 分支 [RuoYi-Vue-Plus-oracle](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/oracle/)
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
#!/bin/bash
|
||||
|
||||
#使用说明,用来提示输入参数
|
||||
usage() {
|
||||
echo "Usage: sh 执行脚本.sh [port|mount|monitor|base|start|stop|stopall|rm|rmiNoneTag]"
|
||||
exit 1
|
||||
}
|
||||
|
||||
#开启所需端口
|
||||
port(){
|
||||
firewall-cmd --add-port=3306/tcp --permanent
|
||||
firewall-cmd --add-port=6379/tcp --permanent
|
||||
service firewalld restart
|
||||
}
|
||||
|
||||
##放置挂载文件
|
||||
mount(){
|
||||
#挂载配置文件
|
||||
if test ! -f "/docker/nginx/conf/nginx.conf" ;then
|
||||
mkdir -p /docker/nginx/conf
|
||||
cp nginx/nginx.conf /docker/nginx/conf/nginx.conf
|
||||
fi
|
||||
}
|
||||
|
||||
#启动基础模块
|
||||
base(){
|
||||
docker-compose up -d mysql nginx-web redis
|
||||
}
|
||||
|
||||
#启动基础模块
|
||||
monitor(){
|
||||
docker-compose up -d ruoyi-monitor-admin
|
||||
}
|
||||
|
||||
#启动程序模块
|
||||
start(){
|
||||
docker-compose up -d ruoyi-server1 ruoyi-server2
|
||||
}
|
||||
|
||||
#停止程序模块
|
||||
stop(){
|
||||
docker-compose stop ruoyi-server1 ruoyi-server2
|
||||
}
|
||||
|
||||
#关闭所有模块
|
||||
stopall(){
|
||||
docker-compose stop
|
||||
}
|
||||
|
||||
#删除所有模块
|
||||
rm(){
|
||||
docker-compose rm
|
||||
}
|
||||
|
||||
#删除Tag为空的镜像
|
||||
rmiNoneTag(){
|
||||
docker images|grep none|awk '{print $3}'|xargs docker rmi -f
|
||||
}
|
||||
|
||||
#根据输入参数,选择执行对应方法,不输入则执行使用说明
|
||||
case "$1" in
|
||||
"port")
|
||||
port
|
||||
;;
|
||||
"mount")
|
||||
mount
|
||||
;;
|
||||
"base")
|
||||
base
|
||||
;;
|
||||
"monitor")
|
||||
monitor
|
||||
;;
|
||||
"start")
|
||||
start
|
||||
;;
|
||||
"stop")
|
||||
stop
|
||||
;;
|
||||
"stopall")
|
||||
stopall
|
||||
;;
|
||||
"rm")
|
||||
rm
|
||||
;;
|
||||
"rmiNoneTag")
|
||||
rmiNoneTag
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
|
@ -0,0 +1,119 @@
|
|||
version: '3'
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:8.0.24
|
||||
container_name: mysql
|
||||
environment:
|
||||
# 时区上海
|
||||
TZ: Asia/Shanghai
|
||||
# root 密码
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
# 初始化数据库(后续的初始化sql会在这个库执行)
|
||||
MYSQL_DATABASE: ry-vue
|
||||
ports:
|
||||
- 3306:3306
|
||||
volumes:
|
||||
# 数据挂载
|
||||
- /docker/mysql/data/:/var/lib/mysql/
|
||||
# 配置挂载
|
||||
- /docker/mysql/conf/:/etc/mysql/conf.d/
|
||||
command:
|
||||
# 将mysql8.0默认密码策略 修改为 原先 策略 (mysql8.0对其默认策略做了更改 会导致密码无法匹配)
|
||||
--default-authentication-plugin=mysql_native_password
|
||||
--character-set-server=utf8mb4
|
||||
--collation-server=utf8mb4_general_ci
|
||||
--explicit_defaults_for_timestamp=true
|
||||
--lower_case_table_names=1
|
||||
privileged: true
|
||||
restart: always
|
||||
networks:
|
||||
ruoyi_net:
|
||||
ipv4_address: 172.30.0.36
|
||||
|
||||
nginx-web:
|
||||
# 如果需要指定版本 就把 latest 换成版本号
|
||||
image: nginx:latest
|
||||
container_name: nginx-web
|
||||
ports:
|
||||
- 80:80
|
||||
- 443:443
|
||||
volumes:
|
||||
# 证书映射
|
||||
- /docker/nginx/cert:/etc/nginx/cert
|
||||
# 配置文件映射
|
||||
- /docker/nginx/conf/nginx.conf:/etc/nginx/nginx.conf
|
||||
# 页面目录
|
||||
- /docker/nginx/html:/usr/share/nginx/html
|
||||
# 日志目录
|
||||
- /docker/nginx/log:/var/log/nginx
|
||||
# 主机本机时间文件映射 与本机时间同步
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
privileged: true
|
||||
restart: always
|
||||
networks:
|
||||
- ruoyi_net
|
||||
|
||||
redis:
|
||||
image: redis:6.2.1
|
||||
container_name: redis
|
||||
ports:
|
||||
- 6379:6379
|
||||
environment:
|
||||
# 设置环境变量 时区上海 编码UTF-8
|
||||
TZ: Asia/Shanghai
|
||||
LANG: en_US.UTF-8
|
||||
volumes:
|
||||
# 配置文件
|
||||
- /docker/redis/conf/redis.conf:/redis.conf:rw
|
||||
# 数据文件
|
||||
- /docker/redis/data:/data:rw
|
||||
command: "redis-server --appendonly yes"
|
||||
privileged: true
|
||||
restart: always
|
||||
networks:
|
||||
ruoyi_net:
|
||||
ipv4_address: 172.30.0.48
|
||||
|
||||
ruoyi-server1:
|
||||
image: "ruoyi/ruoyi-server:2.5.0"
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
volumes:
|
||||
# 配置文件
|
||||
- /docker/server1/logs/:/ruoyi/server/logs/
|
||||
privileged: true
|
||||
restart: always
|
||||
networks:
|
||||
ruoyi_net:
|
||||
ipv4_address: 172.30.0.60
|
||||
|
||||
ruoyi-server2:
|
||||
image: "ruoyi/ruoyi-server:2.5.0"
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
volumes:
|
||||
# 配置文件
|
||||
- /docker/server2/logs/:/ruoyi/server/logs/
|
||||
privileged: true
|
||||
restart: always
|
||||
networks:
|
||||
ruoyi_net:
|
||||
ipv4_address: 172.30.0.61
|
||||
|
||||
ruoyi-monitor-admin:
|
||||
image: "ruoyi/ruoyi-monitor-admin:2.5.0"
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
privileged: true
|
||||
restart: always
|
||||
networks:
|
||||
ruoyi_net:
|
||||
ipv4_address: 172.30.0.90
|
||||
|
||||
networks:
|
||||
ruoyi_net:
|
||||
driver: bridge
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 172.30.0.0/16
|
|
@ -0,0 +1,77 @@
|
|||
worker_processes 1;
|
||||
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include mime.types;
|
||||
default_type application/octet-stream;
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
# 限制body大小
|
||||
client_max_body_size 100m;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
upstream server {
|
||||
server 172.30.0.60:8080;
|
||||
server 172.30.0.61:8080;
|
||||
}
|
||||
|
||||
upstream monitor-admin {
|
||||
server 172.30.0.90:9090;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
# https配置参考 start
|
||||
#listen 443 ssl;
|
||||
|
||||
# 证书直接存放 /docker/nginx/cert/ 目录下即可 更改证书名称即可 无需更改证书路径
|
||||
#ssl on;
|
||||
#ssl_certificate /etc/nginx/cert/xxx.local.crt; # /etc/nginx/cert/ 为docker映射路径 不允许更改
|
||||
#ssl_certificate_key /etc/nginx/cert/xxx.local.key; # /etc/nginx/cert/ 为docker映射路径 不允许更改
|
||||
#ssl_session_timeout 5m;
|
||||
#ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
|
||||
#ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
#ssl_prefer_server_ciphers on;
|
||||
# https配置参考 end
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
try_files $uri $uri/ /index.html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
|
||||
location /prod-api/ {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header REMOTE-HOST $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_pass http://server/;
|
||||
}
|
||||
|
||||
location /admin/ {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header REMOTE-HOST $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_pass http://monitor-admin/admin/;
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root html;
|
||||
}
|
||||
}
|
||||
}
|
21
pom.xml
21
pom.xml
|
@ -6,32 +6,38 @@
|
|||
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-vue-plus</artifactId>
|
||||
<version>${ruoyi-vue-plus.version}</version>
|
||||
<version>2.5.0</version>
|
||||
|
||||
<name>RuoYi-Vue-Plus</name>
|
||||
<url>https://gitee.com/JavaLionLi/RuoYi-Vue-Plus</url>
|
||||
<description>RuoYi-Vue-Plus后台管理系统</description>
|
||||
|
||||
<properties>
|
||||
<ruoyi-vue-plus.version>2.4.0</ruoyi-vue-plus.version>
|
||||
<spring-boot.version>2.4.7</spring-boot.version>
|
||||
<ruoyi-vue-plus.version>2.5.0</ruoyi-vue-plus.version>
|
||||
<spring-boot.version>2.4.8</spring-boot.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<java.version>1.8</java.version>
|
||||
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
|
||||
<druid.version>1.2.6</druid.version>
|
||||
<knife4j.version>3.0.2</knife4j.version>
|
||||
<knife4j.version>3.0.3</knife4j.version>
|
||||
<poi.version>4.1.2</poi.version>
|
||||
<velocity.version>1.7</velocity.version>
|
||||
<jwt.version>0.9.1</jwt.version>
|
||||
<mybatis-plus.version>3.4.3</mybatis-plus.version>
|
||||
<hutool.version>5.7.2</hutool.version>
|
||||
<hutool.version>5.7.4</hutool.version>
|
||||
<feign.version>3.0.3</feign.version>
|
||||
<feign-okhttp.version>11.0</feign-okhttp.version>
|
||||
<spring-boot-admin.version>2.4.1</spring-boot-admin.version>
|
||||
<redisson.version>3.15.2</redisson.version>
|
||||
<spring-boot-admin.version>2.4.3</spring-boot-admin.version>
|
||||
<redisson.version>3.16.0</redisson.version>
|
||||
<lock4j.version>2.2.1</lock4j.version>
|
||||
<datasource.version>3.4.0</datasource.version>
|
||||
|
||||
<!-- docker 配置 -->
|
||||
<docker.registry.url>localhost</docker.registry.url>
|
||||
<docker.registry.host>http://${docker.registry.url}:2375</docker.registry.host>
|
||||
<docker.namespace>ruoyi</docker.namespace>
|
||||
<docker.plugin.version>1.2.0</docker.plugin.version>
|
||||
</properties>
|
||||
|
||||
<!-- 依赖声明 -->
|
||||
|
@ -192,6 +198,7 @@
|
|||
<module>ruoyi-generator</module>
|
||||
<module>ruoyi-common</module>
|
||||
<module>ruoyi-demo</module>
|
||||
<module>ruoyi-extend</module>
|
||||
</modules>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
FROM anapsix/alpine-java:8_server-jre_unlimited
|
||||
|
||||
MAINTAINER Lion Li
|
||||
|
||||
RUN mkdir -p /ruoyi/server
|
||||
RUN mkdir -p /ruoyi/server/logs
|
||||
|
||||
WORKDIR /ruoyi/server
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
ADD ./target/ruoyi-admin.jar ./app.jar
|
||||
|
||||
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"]
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>ruoyi-vue-plus</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>${ruoyi-vue-plus.version}</version>
|
||||
<version>2.5.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<packaging>jar</packaging>
|
||||
|
@ -83,6 +83,25 @@
|
|||
<warName>${project.artifactId}</warName>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.spotify</groupId>
|
||||
<artifactId>docker-maven-plugin</artifactId>
|
||||
<version>${docker.plugin.version}</version>
|
||||
<configuration>
|
||||
<imageName>${docker.namespace}/ruoyi-server:${project.version}</imageName>
|
||||
<dockerDirectory>${project.basedir}</dockerDirectory>
|
||||
<dockerHost>${docker.registry.host}</dockerHost>
|
||||
<registryUrl>${docker.registry.url}</registryUrl>
|
||||
<serverId>${docker.registry.url}</serverId>
|
||||
<resources>
|
||||
<resource>
|
||||
<targetPath>/</targetPath>
|
||||
<directory>${project.build.directory}</directory>
|
||||
<include>${project.build.finalName}.jar</include>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package com.ruoyi.web.controller.system;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.ruoyi.common.config.RuoYiConfig;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 首页
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@RestController
|
||||
public class SysIndexController
|
||||
{
|
||||
/** 系统基础配置 */
|
||||
@Autowired
|
||||
private RuoYiConfig ruoyiConfig;
|
||||
|
||||
/**
|
||||
* 访问首页,提示语
|
||||
*/
|
||||
@RequestMapping("/")
|
||||
public String index()
|
||||
{
|
||||
return StrUtil.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion());
|
||||
}
|
||||
}
|
|
@ -1,8 +1,6 @@
|
|||
package com.ruoyi.web.controller.system;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.constant.Constants;
|
||||
import com.ruoyi.common.constant.UserConstants;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
|
@ -11,6 +9,7 @@ import com.ruoyi.common.core.domain.model.LoginUser;
|
|||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.common.utils.ServletUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.framework.web.service.TokenService;
|
||||
import com.ruoyi.system.service.ISysMenuService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -98,8 +97,7 @@ public class SysMenuController extends BaseController
|
|||
{
|
||||
return AjaxResult.error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
|
||||
}
|
||||
else if (UserConstants.YES_FRAME.equals(menu.getIsFrame())
|
||||
&& !StrUtil.startWithAny(menu.getPath(), Constants.HTTP, Constants.HTTPS))
|
||||
else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath()))
|
||||
{
|
||||
return AjaxResult.error("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
|
||||
}
|
||||
|
@ -119,8 +117,7 @@ public class SysMenuController extends BaseController
|
|||
{
|
||||
return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
|
||||
}
|
||||
else if (UserConstants.YES_FRAME.equals(menu.getIsFrame())
|
||||
&& !StrUtil.startWithAny(menu.getPath(), Constants.HTTP, Constants.HTTPS))
|
||||
else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath()))
|
||||
{
|
||||
return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import com.ruoyi.common.constant.UserConstants;
|
|||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.domain.entity.SysRole;
|
||||
import com.ruoyi.common.core.domain.entity.SysUser;
|
||||
import com.ruoyi.common.core.domain.model.LoginUser;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
|
@ -14,6 +15,7 @@ import com.ruoyi.common.utils.ServletUtils;
|
|||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||
import com.ruoyi.framework.web.service.SysPermissionService;
|
||||
import com.ruoyi.framework.web.service.TokenService;
|
||||
import com.ruoyi.system.domain.SysUserRole;
|
||||
import com.ruoyi.system.service.ISysRoleService;
|
||||
import com.ruoyi.system.service.ISysUserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -171,4 +173,57 @@ public class SysRoleController extends BaseController
|
|||
{
|
||||
return AjaxResult.success(roleService.selectRoleAll());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询已分配用户角色列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:role:list')")
|
||||
@GetMapping("/authUser/allocatedList")
|
||||
public TableDataInfo allocatedList(SysUser user)
|
||||
{
|
||||
return userService.selectAllocatedList(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询未分配用户角色列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:role:list')")
|
||||
@GetMapping("/authUser/unallocatedList")
|
||||
public TableDataInfo unallocatedList(SysUser user)
|
||||
{
|
||||
return userService.selectUnallocatedList(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消授权用户
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:role:edit')")
|
||||
@Log(title = "角色管理", businessType = BusinessType.GRANT)
|
||||
@PutMapping("/authUser/cancel")
|
||||
public AjaxResult cancelAuthUser(@RequestBody SysUserRole userRole)
|
||||
{
|
||||
return toAjax(roleService.deleteAuthUser(userRole));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量取消授权用户
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:role:edit')")
|
||||
@Log(title = "角色管理", businessType = BusinessType.GRANT)
|
||||
@PutMapping("/authUser/cancelAll")
|
||||
public AjaxResult cancelAuthUserAll(Long roleId, Long[] userIds)
|
||||
{
|
||||
return toAjax(roleService.deleteAuthUsers(roleId, userIds));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量选择用户授权
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:role:edit')")
|
||||
@Log(title = "角色管理", businessType = BusinessType.GRANT)
|
||||
@PutMapping("/authUser/selectAll")
|
||||
public AjaxResult selectAuthUserAll(Long roleId, Long[] userIds)
|
||||
{
|
||||
return toAjax(roleService.insertAuthUsers(roleId, userIds));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -196,4 +196,31 @@ public class SysUserController extends BaseController
|
|||
user.setUpdateBy(SecurityUtils.getUsername());
|
||||
return toAjax(userService.updateUserStatus(user));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户编号获取授权角色
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:user:query')")
|
||||
@GetMapping("/authRole/{userId}")
|
||||
public AjaxResult authRole(@PathVariable("userId") Long userId)
|
||||
{
|
||||
SysUser user = userService.selectUserById(userId);
|
||||
List<SysRole> roles = roleService.selectRolesByUserId(userId);
|
||||
Map<String, Object> ajax = new HashMap<>();
|
||||
ajax.put("user", user);
|
||||
ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
|
||||
return AjaxResult.success(ajax);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户授权角色
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:user:edit')")
|
||||
@Log(title = "用户管理", businessType = BusinessType.GRANT)
|
||||
@PutMapping("/authRole")
|
||||
public AjaxResult insertAuthRole(Long userId, Long[] roleIds)
|
||||
{
|
||||
userService.insertUserAuth(userId, roleIds);
|
||||
return success();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -110,3 +110,30 @@ redisson:
|
|||
subscriptionsPerConnection: 5
|
||||
# DNS监测时间间隔,单位:毫秒
|
||||
dnsMonitoringInterval: 5000
|
||||
|
||||
--- # 监控配置
|
||||
spring:
|
||||
boot:
|
||||
admin:
|
||||
# Spring Boot Admin Client 客户端的相关配置
|
||||
client:
|
||||
# 设置 Spring Boot Admin Server 地址
|
||||
url: http://localhost:9090/admin
|
||||
instance:
|
||||
prefer-ip: true # 注册实例时,优先使用 IP
|
||||
username: ruoyi
|
||||
password: 123456
|
||||
|
||||
# Actuator 监控端点的配置项
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
# Actuator 提供的 API 接口的根目录。默认为 /actuator
|
||||
base-path: /actuator
|
||||
exposure:
|
||||
# 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
|
||||
# 生产环境不建议放开所有 根据项目需求放开即可
|
||||
include: '*'
|
||||
endpoint:
|
||||
logfile:
|
||||
external-file: ./logs/sys-console.log
|
||||
|
|
|
@ -12,7 +12,7 @@ spring:
|
|||
# 主库数据源
|
||||
master:
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true
|
||||
url: jdbc:mysql://172.30.0.36:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true
|
||||
username: root
|
||||
password: root
|
||||
# 从库数据源
|
||||
|
@ -66,7 +66,7 @@ spring:
|
|||
# redis 配置
|
||||
redis:
|
||||
# 地址
|
||||
host: localhost
|
||||
host: 172.30.0.48
|
||||
# 端口,默认为6379
|
||||
port: 6379
|
||||
# 数据库索引
|
||||
|
@ -110,3 +110,30 @@ redisson:
|
|||
subscriptionsPerConnection: 5
|
||||
# DNS监测时间间隔,单位:毫秒
|
||||
dnsMonitoringInterval: 5000
|
||||
|
||||
--- # 监控配置
|
||||
spring:
|
||||
boot:
|
||||
admin:
|
||||
# Spring Boot Admin Client 客户端的相关配置
|
||||
client:
|
||||
# 设置 Spring Boot Admin Server 地址
|
||||
url: http://172.30.0.90:9090/admin
|
||||
instance:
|
||||
prefer-ip: true # 注册实例时,优先使用 IP
|
||||
username: ruoyi
|
||||
password: 123456
|
||||
|
||||
# Actuator 监控端点的配置项
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
# Actuator 提供的 API 接口的根目录。默认为 /actuator
|
||||
base-path: /actuator
|
||||
exposure:
|
||||
# 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
|
||||
# 生产环境不建议放开所有 根据项目需求放开即可
|
||||
include: health,info
|
||||
endpoint:
|
||||
logfile:
|
||||
external-file: ./logs/sys-console.log
|
||||
|
|
|
@ -64,6 +64,8 @@ logging:
|
|||
|
||||
# Spring配置
|
||||
spring:
|
||||
application:
|
||||
name: ${ruoyi.name}
|
||||
# 资源信息
|
||||
messages:
|
||||
# 国际化资源文件路径
|
||||
|
@ -110,6 +112,8 @@ token:
|
|||
# MyBatisPlus配置
|
||||
# https://baomidou.com/config/
|
||||
mybatis-plus:
|
||||
# 不支持多包, 如有需要可在注解配置 或 提升扫包等级
|
||||
# 例如 com.**.**.mapper
|
||||
mapperPackage: com.ruoyi.**.mapper
|
||||
# 对应的 XML 文件位置
|
||||
mapperLocations: classpath*:mapper/**/*Mapper.xml
|
||||
|
@ -156,7 +160,9 @@ mybatis-plus:
|
|||
# STATEMENT 关闭一级缓存
|
||||
localCacheScope: SESSION
|
||||
# 开启Mybatis二级缓存,默认为 true
|
||||
cacheEnabled: true
|
||||
cacheEnabled: false
|
||||
# 更详细的日志输出 会有性能损耗
|
||||
# logImpl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
global-config:
|
||||
# 是否打印 Logo banner
|
||||
banner: true
|
||||
|
@ -203,7 +209,7 @@ swagger:
|
|||
# 请求前缀
|
||||
pathMapping: /dev-api
|
||||
# 标题
|
||||
title: '标题:RuoYi-Vue-Plus后台管理系统_接口文档'
|
||||
title: '标题:${ruoyi.name}后台管理系统_接口文档'
|
||||
# 描述
|
||||
description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...'
|
||||
# 版本
|
||||
|
@ -244,6 +250,8 @@ thread-pool:
|
|||
|
||||
# feign 相关配置
|
||||
feign:
|
||||
# 不支持多包, 如有需要可在注解配置 或 提升扫包等级
|
||||
# 例如 com.**.**.feign
|
||||
package: com.ruoyi.**.feign
|
||||
# 开启压缩
|
||||
compression:
|
||||
|
@ -256,6 +264,21 @@ feign:
|
|||
circuitbreaker:
|
||||
enabled: true
|
||||
|
||||
--- # redisson 缓存配置
|
||||
redisson:
|
||||
cacheGroup:
|
||||
# 用例: @Cacheable(cacheNames="groupId", key="#XXX") 方可使用缓存组配置
|
||||
- groupId: redissonCacheMap
|
||||
# 组过期时间(脚本监控)
|
||||
ttl: 60000
|
||||
# 组最大空闲时间(脚本监控)
|
||||
maxIdleTime: 60000
|
||||
# 组最大长度
|
||||
maxSize: 0
|
||||
- groupId: testCache
|
||||
ttl: 1000
|
||||
maxIdleTime: 500
|
||||
|
||||
--- # 分布式锁 lock4j 全局配置
|
||||
lock4j:
|
||||
# 获取分布式锁超时时间,默认为 3000 毫秒
|
||||
|
@ -293,31 +316,3 @@ spring:
|
|||
tablePrefix: QRTZ_
|
||||
# sqlserver 启用
|
||||
# selectWithLockSQL: SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?
|
||||
|
||||
--- # 监控配置
|
||||
spring:
|
||||
application:
|
||||
name: ruoyi-vue-plus
|
||||
boot:
|
||||
admin:
|
||||
# Spring Boot Admin Client 客户端的相关配置
|
||||
client:
|
||||
# 设置 Spring Boot Admin Server 地址
|
||||
url: http://localhost:${server.port}${spring.boot.admin.context-path}
|
||||
instance:
|
||||
prefer-ip: true # 注册实例时,优先使用 IP
|
||||
# Spring Boot Admin Server 服务端的相关配置
|
||||
context-path: /admin # 配置 Spring
|
||||
|
||||
# Actuator 监控端点的配置项
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
# Actuator 提供的 API 接口的根目录。默认为 /actuator
|
||||
base-path: /actuator
|
||||
exposure:
|
||||
# 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
|
||||
include: '*'
|
||||
endpoint:
|
||||
logfile:
|
||||
external-file: ./logs/sys-console.log
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
#错误消息
|
||||
not.null=
|
||||
user.jcaptcha.error=
|
||||
user.jcaptcha.expire=
|
||||
user.not.exists=
|
||||
user.password.not.match=
|
||||
user.password.retry.limit.count=
|
||||
user.password.retry.limit.exceed=
|
||||
user.password.delete=
|
||||
user.blocked=
|
||||
role.blocked=
|
||||
user.logout.success=
|
||||
length.not.valid=
|
||||
user.username.not.valid=
|
||||
user.password.not.valid=
|
||||
user.email.not.valid=
|
||||
user.mobile.phone.number.not.valid=
|
||||
user.login.success=
|
||||
user.notfound=
|
||||
user.forcelogout=
|
||||
user.unknown.error=
|
||||
|
||||
##文件上传消息
|
||||
upload.exceed.maxSize=
|
||||
upload.filename.exceed.length=
|
||||
|
||||
##权限
|
||||
no.permission=
|
||||
no.create.permission=
|
||||
no.update.permission=
|
||||
no.delete.permission=
|
||||
no.export.permission=
|
||||
no.view.permission=
|
|
@ -0,0 +1,36 @@
|
|||
#错误消息
|
||||
not.null=* 必须填写
|
||||
user.jcaptcha.error=验证码错误
|
||||
user.jcaptcha.expire=验证码已失效
|
||||
user.not.exists=用户不存在/密码错误
|
||||
user.password.not.match=用户不存在/密码错误
|
||||
user.password.retry.limit.count=密码输入错误{0}次
|
||||
user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定10分钟
|
||||
user.password.delete=对不起,您的账号已被删除
|
||||
user.blocked=用户已封禁,请联系管理员
|
||||
role.blocked=角色已封禁,请联系管理员
|
||||
user.logout.success=退出成功
|
||||
|
||||
length.not.valid=长度必须在{min}到{max}个字符之间
|
||||
|
||||
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
|
||||
user.password.not.valid=* 5-50个字符
|
||||
|
||||
user.email.not.valid=邮箱格式错误
|
||||
user.mobile.phone.number.not.valid=手机号格式错误
|
||||
user.login.success=登录成功
|
||||
user.notfound=请重新登录
|
||||
user.forcelogout=管理员强制退出,请重新登录
|
||||
user.unknown.error=未知错误,请重新登录
|
||||
|
||||
##文件上传消息
|
||||
upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB!
|
||||
upload.filename.exceed.length=上传的文件名最长{0}个字符
|
||||
|
||||
##权限
|
||||
no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]
|
||||
no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]
|
||||
no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]
|
||||
no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
|
||||
no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
|
||||
no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>ruoyi-vue-plus</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>${ruoyi-vue-plus.version}</version>
|
||||
<version>2.5.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -116,10 +116,6 @@
|
|||
<artifactId>feign-okhttp</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>de.codecentric</groupId>
|
||||
<artifactId>spring-boot-admin-starter-server</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.codecentric</groupId>
|
||||
<artifactId>spring-boot-admin-starter-client</artifactId>
|
||||
|
|
|
@ -57,6 +57,9 @@ public class UserConstants
|
|||
/** ParentView组件标识 */
|
||||
public final static String PARENT_VIEW = "ParentView";
|
||||
|
||||
/** InnerLink组件标识 */
|
||||
public final static String INNER_LINK = "InnerLink";
|
||||
|
||||
/** 校验返回结果码 */
|
||||
public final static String UNIQUE = "0";
|
||||
public final static String NOT_UNIQUE = "1";
|
||||
|
|
|
@ -148,6 +148,10 @@ public class SysUser implements Serializable
|
|||
@TableField(exist = false)
|
||||
private Long[] postIds;
|
||||
|
||||
/** 角色ID */
|
||||
@TableField(exist = false)
|
||||
private Long roleId;
|
||||
|
||||
public SysUser(Long userId)
|
||||
{
|
||||
this.userId = userId;
|
||||
|
|
|
@ -15,6 +15,9 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
|
|||
/**
|
||||
* mybatis-redis 二级缓存
|
||||
*
|
||||
* 使用方法 配置文件开启 mybatis-plus 二级缓存
|
||||
* 在 XxxMapper.java 类上添加注解 @CacheNamespace(implementation = MybatisPlusRedisCache.class, eviction = MybatisPlusRedisCache.class)
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Slf4j
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
package com.ruoyi.common.core.mybatisplus.methods;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.core.enums.SqlMethod;
|
||||
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfo;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
||||
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
|
||||
import org.apache.ibatis.executor.keygen.KeyGenerator;
|
||||
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.mapping.SqlSource;
|
||||
|
||||
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfo;
|
||||
|
||||
/**
|
||||
* 单sql批量插入
|
||||
*
|
||||
|
@ -20,9 +24,28 @@ public class InsertAll extends AbstractMethod {
|
|||
final String sql = "<script>insert into %s %s values %s</script>";
|
||||
final String fieldSql = prepareFieldSql(tableInfo);
|
||||
final String valueSql = prepareValuesSqlForMysqlBatch(tableInfo);
|
||||
KeyGenerator keyGenerator = new NoKeyGenerator();
|
||||
SqlMethod sqlMethod = SqlMethod.INSERT_ONE;
|
||||
String keyProperty = null;
|
||||
String keyColumn = null;
|
||||
// 表包含主键处理逻辑,如果不包含主键当普通字段处理
|
||||
if (StrUtil.isNotBlank(tableInfo.getKeyProperty())) {
|
||||
if (tableInfo.getIdType() == IdType.AUTO) {
|
||||
/** 自增主键 */
|
||||
keyGenerator = new Jdbc3KeyGenerator();
|
||||
keyProperty = tableInfo.getKeyProperty();
|
||||
keyColumn = tableInfo.getKeyColumn();
|
||||
} else {
|
||||
if (null != tableInfo.getKeySequence()) {
|
||||
keyGenerator = TableInfoHelper.genKeyGenerator(getMethod(sqlMethod), tableInfo, builderAssistant);
|
||||
keyProperty = tableInfo.getKeyProperty();
|
||||
keyColumn = tableInfo.getKeyColumn();
|
||||
}
|
||||
}
|
||||
}
|
||||
final String sqlResult = String.format(sql, tableInfo.getTableName(), fieldSql, valueSql);
|
||||
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);
|
||||
return this.addInsertMappedStatement(mapperClass, modelClass, "insertAll", sqlSource, new NoKeyGenerator(), null, null);
|
||||
return this.addInsertMappedStatement(mapperClass, modelClass, "insertAll", sqlSource, keyGenerator, keyProperty, keyColumn);
|
||||
}
|
||||
|
||||
private String prepareFieldSql(TableInfo tableInfo) {
|
||||
|
|
|
@ -205,9 +205,9 @@ public class RedisCache {
|
|||
* @param hKeys Hash键集合
|
||||
* @return Hash对象集合
|
||||
*/
|
||||
public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) {
|
||||
RListMultimap rListMultimap = redissonClient.getListMultimap(key);
|
||||
return rListMultimap.getAll(hKeys);
|
||||
public <K,V> Map<K,V> getMultiCacheMapValue(final String key, final Set<K> hKeys) {
|
||||
RMap<K,V> rMap = redissonClient.getMap(key);
|
||||
return rMap.getAll(hKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -88,7 +88,7 @@ public class DictUtils
|
|||
StringBuilder propertyString = new StringBuilder();
|
||||
List<SysDictData> datas = getDictCache(dictType);
|
||||
|
||||
if (StrUtil.containsAny(separator, dictValue) && CollUtil.isNotEmpty(datas))
|
||||
if (StrUtil.containsAny(dictValue, separator) && CollUtil.isNotEmpty(datas))
|
||||
{
|
||||
for (SysDictData dict : datas)
|
||||
{
|
||||
|
@ -128,7 +128,7 @@ public class DictUtils
|
|||
StringBuilder propertyString = new StringBuilder();
|
||||
List<SysDictData> datas = getDictCache(dictType);
|
||||
|
||||
if (StrUtil.containsAny(separator, dictLabel) && CollUtil.isNotEmpty(datas))
|
||||
if (StrUtil.containsAny(dictLabel, separator) && CollUtil.isNotEmpty(datas))
|
||||
{
|
||||
for (SysDictData dict : datas)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package com.ruoyi.common.utils;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.ruoyi.common.constant.Constants;
|
||||
|
||||
/**
|
||||
* 字符串工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class StringUtils extends org.apache.commons.lang3.StringUtils {
|
||||
/** 空字符串 */
|
||||
private static final String NULLSTR = "";
|
||||
|
||||
/** 下划线 */
|
||||
private static final char SEPARATOR = '_';
|
||||
|
||||
/**
|
||||
* 是否为http(s)://开头
|
||||
*
|
||||
* @param link 链接
|
||||
* @return 结果
|
||||
*/
|
||||
public static boolean ishttp(String link) {
|
||||
return StrUtil.startWithAny(link, Constants.HTTP, Constants.HTTPS);
|
||||
}
|
||||
|
||||
}
|
|
@ -716,7 +716,7 @@ public class ExcelUtil<T>
|
|||
for (String item : convertSource)
|
||||
{
|
||||
String[] itemArray = item.split("=");
|
||||
if (StrUtil.containsAny(separator, propertyValue))
|
||||
if (StrUtil.containsAny(propertyValue, separator))
|
||||
{
|
||||
for (String value : propertyValue.split(separator))
|
||||
{
|
||||
|
@ -753,7 +753,7 @@ public class ExcelUtil<T>
|
|||
for (String item : convertSource)
|
||||
{
|
||||
String[] itemArray = item.split("=");
|
||||
if (StrUtil.containsAny(separator, propertyValue))
|
||||
if (StrUtil.containsAny(propertyValue, separator))
|
||||
{
|
||||
for (String value : propertyValue.split(separator))
|
||||
{
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>ruoyi-vue-plus</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>${ruoyi-vue-plus.version}</version>
|
||||
<version>2.5.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
package com.ruoyi.demo.controller;
|
||||
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.CachePut;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* spring-cache 演示案例
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
// 类级别 缓存统一配置
|
||||
//@CacheConfig(cacheNames = "redissonCacheMap")
|
||||
@RequiredArgsConstructor(onConstructor_ = @Autowired)
|
||||
@RestController
|
||||
@RequestMapping("/demo/cache")
|
||||
public class RedisCacheController {
|
||||
|
||||
/**
|
||||
* 测试 @Cacheable
|
||||
*
|
||||
* 表示这个方法有了缓存的功能,方法的返回值会被缓存下来
|
||||
* 下一次调用该方法前,会去检查是否缓存中已经有值
|
||||
* 如果有就直接返回,不调用方法
|
||||
* 如果没有,就调用方法,然后把结果缓存起来
|
||||
* 这个注解「一般用在查询方法上」
|
||||
*
|
||||
* cacheNames 为配置文件内 groupId
|
||||
*/
|
||||
@Cacheable(cacheNames = "redissonCacheMap", key = "#key", condition = "#key != null")
|
||||
@GetMapping("/test1")
|
||||
public AjaxResult<String> test1(String key, String value){
|
||||
return AjaxResult.success("操作成功", value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 @CachePut
|
||||
*
|
||||
* 加了@CachePut注解的方法,会把方法的返回值put到缓存里面缓存起来,供其它地方使用
|
||||
* 它「通常用在新增方法上」
|
||||
*
|
||||
* cacheNames 为 配置文件内 groupId
|
||||
*/
|
||||
@CachePut(cacheNames = "redissonCacheMap", key = "#key", condition = "#key != null")
|
||||
@GetMapping("/test2")
|
||||
public AjaxResult<String> test2(String key, String value){
|
||||
return AjaxResult.success("操作成功", value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 @CacheEvict
|
||||
*
|
||||
* 使用了CacheEvict注解的方法,会清空指定缓存
|
||||
* 「一般用在更新或者删除的方法上」
|
||||
*
|
||||
* cacheNames 为 配置文件内 groupId
|
||||
*/
|
||||
@CacheEvict(cacheNames = "redissonCacheMap", key = "#key", condition = "#key != null")
|
||||
@GetMapping("/test3")
|
||||
public AjaxResult<String> test3(String key, String value){
|
||||
return AjaxResult.success("操作成功", value);
|
||||
}
|
||||
|
||||
}
|
|
@ -7,7 +7,10 @@ import org.springframework.stereotype.Component;
|
|||
|
||||
/**
|
||||
* feign测试fallback
|
||||
* 自定义封装结构体熔断
|
||||
* 需重写解码器 根据自定义实体 自行解析熔断
|
||||
*
|
||||
* @see {com.ruoyi.framework.config.FeignConfig#errorDecoder()}
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Slf4j
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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">
|
||||
<parent>
|
||||
<artifactId>ruoyi-vue-plus</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>2.5.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>ruoyi-extend</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>ruoyi-monitor-admin</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,13 @@
|
|||
FROM anapsix/alpine-java:8_server-jre_unlimited
|
||||
|
||||
MAINTAINER Lion Li
|
||||
|
||||
RUN mkdir -p /ruoyi/monitor
|
||||
|
||||
WORKDIR /ruoyi/monitor
|
||||
|
||||
EXPOSE 9090
|
||||
|
||||
ADD ./target/ruoyi-monitor-admin.jar ./app.jar
|
||||
|
||||
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"]
|
|
@ -0,0 +1,73 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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">
|
||||
<parent>
|
||||
<artifactId>ruoyi-extend</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>2.5.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<packaging>jar</packaging>
|
||||
<artifactId>ruoyi-monitor-admin</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<!-- SpringWeb模块 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- spring security 安全认证 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>de.codecentric</groupId>
|
||||
<artifactId>spring-boot-admin-starter-server</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<configuration>
|
||||
<fork>true</fork> <!-- 如果没有该配置,devtools不会生效 -->
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.spotify</groupId>
|
||||
<artifactId>docker-maven-plugin</artifactId>
|
||||
<version>${docker.plugin.version}</version>
|
||||
<configuration>
|
||||
<imageName>${docker.namespace}/${project.artifactId}:${project.version}</imageName>
|
||||
<dockerDirectory>${project.basedir}</dockerDirectory>
|
||||
<dockerHost>${docker.registry.host}</dockerHost>
|
||||
<registryUrl>${docker.registry.url}</registryUrl>
|
||||
<serverId>${docker.registry.url}</serverId>
|
||||
<resources>
|
||||
<resource>
|
||||
<targetPath>/</targetPath>
|
||||
<directory>${project.build.directory}</directory>
|
||||
<include>${project.build.finalName}.jar</include>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,19 @@
|
|||
package com.ruoyi.monitor.admin;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* Admin 监控启动程序
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class MonitorAdminApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(MonitorAdminApplication.class, args);
|
||||
System.out.println("Admin 监控启动成功" );
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package com.ruoyi.monitor.admin.config;
|
||||
|
||||
import de.codecentric.boot.admin.server.config.EnableAdminServer;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
|
||||
import org.springframework.boot.task.TaskExecutorBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* springboot-admin server配置类
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Configuration
|
||||
@EnableAdminServer
|
||||
public class AdminServerConfig {
|
||||
|
||||
@Lazy
|
||||
@Bean(name = TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME)
|
||||
@ConditionalOnMissingBean(Executor.class)
|
||||
public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package com.ruoyi.monitor.admin.config;
|
||||
|
||||
import de.codecentric.boot.admin.server.config.AdminServerProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
||||
|
||||
/**
|
||||
* spring security配置
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, proxyTargetClass = true)
|
||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
private final String adminContextPath;
|
||||
|
||||
public SecurityConfig(AdminServerProperties adminServerProperties) {
|
||||
this.adminContextPath = adminServerProperties.getContextPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity httpSecurity) throws Exception {
|
||||
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
|
||||
successHandler.setTargetUrlParameter("redirectTo");
|
||||
successHandler.setDefaultTargetUrl(adminContextPath + "/");
|
||||
|
||||
httpSecurity.authorizeRequests()
|
||||
//授予对所有静态资产和登录页面的公共访问权限。
|
||||
.antMatchers(adminContextPath + "/assets/**").permitAll()
|
||||
.antMatchers(adminContextPath + "/login").permitAll()
|
||||
//必须对每个其他请求进行身份验证
|
||||
.anyRequest().authenticated().and()
|
||||
//配置登录和注销
|
||||
.formLogin().loginPage(adminContextPath + "/login")
|
||||
.successHandler(successHandler).and()
|
||||
.logout().logoutUrl(adminContextPath + "/logout").and()
|
||||
//启用HTTP-Basic支持。这是Spring Boot Admin Client注册所必需的
|
||||
.httpBasic().and().csrf().disable()
|
||||
.headers().frameOptions().disable();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
server:
|
||||
port: 9090
|
||||
|
||||
spring:
|
||||
security:
|
||||
user:
|
||||
name: ruoyi
|
||||
password: 123456
|
||||
boot:
|
||||
admin:
|
||||
context-path: /admin
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>ruoyi-vue-plus</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>${ruoyi-vue-plus.version}</version>
|
||||
<version>2.5.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
package com.ruoyi.framework.config;
|
||||
|
||||
import de.codecentric.boot.admin.server.config.EnableAdminServer;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties;
|
||||
import org.springframework.boot.task.TaskExecutorBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.thymeleaf.dialect.IDialect;
|
||||
import org.thymeleaf.spring5.ISpringTemplateEngine;
|
||||
import org.thymeleaf.spring5.SpringTemplateEngine;
|
||||
import org.thymeleaf.templateresolver.ITemplateResolver;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* springboot-admin server配置类
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Configuration
|
||||
@EnableAdminServer
|
||||
public class AdminServerConfig {
|
||||
|
||||
@Lazy
|
||||
@Bean(name = TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME)
|
||||
@ConditionalOnMissingBean(Executor.class)
|
||||
public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解决 admin 与 项目 页面的交叉引用 将 admin 的路由放到最后
|
||||
* @param properties
|
||||
* @param templateResolvers
|
||||
* @param dialects
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ISpringTemplateEngine.class)
|
||||
SpringTemplateEngine templateEngine(ThymeleafProperties properties,
|
||||
ObjectProvider<ITemplateResolver> templateResolvers, ObjectProvider<IDialect> dialects) {
|
||||
SpringTemplateEngine engine = new SpringTemplateEngine();
|
||||
engine.setEnableSpringELCompiler(properties.isEnableSpringElCompiler());
|
||||
engine.setRenderHiddenMarkersBeforeCheckboxes(properties.isRenderHiddenMarkersBeforeCheckboxes());
|
||||
templateResolvers.orderedStream().forEach(engine::addTemplateResolver);
|
||||
dialects.orderedStream().forEach(engine::addDialect);
|
||||
Set<ITemplateResolver> templateResolvers1 = engine.getTemplateResolvers();
|
||||
templateResolvers1 = templateResolvers1.stream()
|
||||
.sorted(Comparator.comparing(ITemplateResolver::getOrder))
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
engine.setTemplateResolvers(templateResolvers1);
|
||||
return engine;
|
||||
}
|
||||
}
|
|
@ -54,4 +54,40 @@ public class FeignConfig {
|
|||
return new Retryer.Default();
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 自定义异常解码器
|
||||
// * 用于自定义返回体异常熔断
|
||||
// */
|
||||
// @Bean
|
||||
// public ErrorDecoder errorDecoder() {
|
||||
// return new CustomErrorDecoder();
|
||||
// }
|
||||
//
|
||||
//
|
||||
// /**
|
||||
// * 自定义返回体解码器
|
||||
// */
|
||||
// @Slf4j
|
||||
// public static class CustomErrorDecoder implements ErrorDecoder {
|
||||
//
|
||||
// @Override
|
||||
// public Exception decode(String methodKey, Response response) {
|
||||
// Exception exception = null;
|
||||
// try {
|
||||
// // 获取原始的返回内容
|
||||
// String json = JsonUtils.toJsonString(response.body().asReader(StandardCharsets.UTF_8));
|
||||
// exception = new RuntimeException(json);
|
||||
// // 将返回内容反序列化为Result,这里应根据自身项目作修改
|
||||
// AjaxResult result = JsonUtils.parseObject(json, AjaxResult.class);
|
||||
// // 业务异常抛出简单的 RuntimeException,保留原来错误信息
|
||||
// if (result.getCode() != 200) {
|
||||
// exception = new RuntimeException(result.getMsg());
|
||||
// }
|
||||
// } catch (IOException e) {
|
||||
// log.error(e.getMessage(), e);
|
||||
// }
|
||||
// return exception;
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import org.springframework.context.annotation.Configuration;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
|
@ -78,8 +79,13 @@ public class RedisConfig extends CachingConfigurerSupport {
|
|||
*/
|
||||
@Bean
|
||||
public CacheManager cacheManager(RedissonClient redissonClient) {
|
||||
List<RedissonProperties.CacheGroup> cacheGroup = redissonProperties.getCacheGroup();
|
||||
Map<String, CacheConfig> config = new HashMap<>();
|
||||
config.put("redissonCacheMap", new CacheConfig(30*60*1000, 10*60*1000));
|
||||
for (RedissonProperties.CacheGroup group : cacheGroup) {
|
||||
CacheConfig cacheConfig = new CacheConfig(group.getTtl(), group.getMaxIdleTime());
|
||||
cacheConfig.setMaxSize(group.getMaxSize());
|
||||
config.put(group.getGroupId(), cacheConfig);
|
||||
}
|
||||
return new RedissonSpringCacheManager(redissonClient, config, JsonJacksonCodec.INSTANCE);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ package com.ruoyi.framework.config;
|
|||
import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter;
|
||||
import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl;
|
||||
import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl;
|
||||
import de.codecentric.boot.admin.server.config.AdminServerProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
@ -57,9 +56,6 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
|
|||
@Autowired
|
||||
private CorsFilter corsFilter;
|
||||
|
||||
@Autowired
|
||||
private AdminServerProperties adminServerProperties;
|
||||
|
||||
/**
|
||||
* 解决 无法直接注入 AuthenticationManager
|
||||
*
|
||||
|
@ -104,12 +100,13 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
|
|||
.antMatchers("/login", "/captchaImage").anonymous()
|
||||
.antMatchers(
|
||||
HttpMethod.GET,
|
||||
"/",
|
||||
"/*.html",
|
||||
"/**/*.html",
|
||||
"/**/*.css",
|
||||
"/**/*.js"
|
||||
"/**/*.js",
|
||||
"/profile/**"
|
||||
).permitAll()
|
||||
.antMatchers("/profile/**").anonymous()
|
||||
.antMatchers("/common/download**").anonymous()
|
||||
.antMatchers("/common/download/resource**").anonymous()
|
||||
.antMatchers("/doc.html").anonymous()
|
||||
|
@ -117,9 +114,6 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
|
|||
.antMatchers("/webjars/**").anonymous()
|
||||
.antMatchers("/*/api-docs").anonymous()
|
||||
.antMatchers("/druid/**").anonymous()
|
||||
// Spring Boot Admin Server 的安全配置
|
||||
.antMatchers(adminServerProperties.getContextPath()).anonymous()
|
||||
.antMatchers(adminServerProperties.getContextPath() + "/**").anonymous()
|
||||
// Spring Boot Actuator 的安全配置
|
||||
.antMatchers("/actuator").anonymous()
|
||||
.antMatchers("/actuator/**").anonymous()
|
||||
|
|
|
@ -2,11 +2,12 @@ package com.ruoyi.framework.config.properties;
|
|||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.redisson.client.codec.Codec;
|
||||
import org.redisson.config.TransportMode;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Redisson 配置属性
|
||||
*
|
||||
|
@ -37,6 +38,11 @@ public class RedissonProperties {
|
|||
*/
|
||||
private SingleServerConfig singleServerConfig;
|
||||
|
||||
/**
|
||||
* 缓存组
|
||||
*/
|
||||
private List<CacheGroup> cacheGroup;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class SingleServerConfig {
|
||||
|
@ -98,4 +104,30 @@ public class RedissonProperties {
|
|||
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class CacheGroup {
|
||||
|
||||
/**
|
||||
* 组id
|
||||
*/
|
||||
private String groupId;
|
||||
|
||||
/**
|
||||
* 组过期时间
|
||||
*/
|
||||
private long ttl;
|
||||
|
||||
/**
|
||||
* 组最大空闲时间
|
||||
*/
|
||||
private long maxIdleTime;
|
||||
|
||||
/**
|
||||
* 组最大长度
|
||||
*/
|
||||
private int maxSize;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package com.ruoyi.framework.mybatisplus;
|
||||
|
||||
import cn.hutool.http.HttpStatus;
|
||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||
import com.ruoyi.common.exception.CustomException;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
|
||||
|
@ -8,6 +10,7 @@ import java.util.Date;
|
|||
|
||||
/**
|
||||
* MP注入处理器
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2021/4/25
|
||||
*/
|
||||
|
@ -15,6 +18,7 @@ public class CreateAndUpdateMetaObjectHandler implements MetaObjectHandler {
|
|||
|
||||
@Override
|
||||
public void insertFill(MetaObject metaObject) {
|
||||
try {
|
||||
//根据属性名字设置要填充的值
|
||||
if (metaObject.hasGetter("createTime")) {
|
||||
if (metaObject.getValue("createTime") == null) {
|
||||
|
@ -26,10 +30,14 @@ public class CreateAndUpdateMetaObjectHandler implements MetaObjectHandler {
|
|||
this.setFieldValByName("createBy", SecurityUtils.getUsername(), metaObject);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new CustomException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFill(MetaObject metaObject) {
|
||||
try {
|
||||
if (metaObject.hasGetter("updateBy")) {
|
||||
if (metaObject.getValue("updateBy") == null) {
|
||||
this.setFieldValByName("updateBy", SecurityUtils.getUsername(), metaObject);
|
||||
|
@ -40,6 +48,9 @@ public class CreateAndUpdateMetaObjectHandler implements MetaObjectHandler {
|
|||
this.setFieldValByName("updateTime", new Date(), metaObject);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new CustomException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>ruoyi-vue-plus</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>${ruoyi-vue-plus.version}</version>
|
||||
<version>2.5.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
|
|
@ -182,8 +182,8 @@ public class GenTableServiceImpl extends ServicePlusImpl<GenTableMapper, GenTabl
|
|||
List<GenTableColumn> genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName);
|
||||
for (GenTableColumn column : genTableColumns) {
|
||||
GenUtils.initColumnField(column, table);
|
||||
genTableColumnMapper.insert(column);
|
||||
}
|
||||
genTableColumnMapper.insertAll(genTableColumns);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
@ -258,7 +258,7 @@ public class GenTableServiceImpl extends ServicePlusImpl<GenTableMapper, GenTabl
|
|||
// 获取模板列表
|
||||
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
|
||||
for (String template : templates) {
|
||||
if (!StrUtil.containsAny(template, "sql.vm", "api.js.vm", "index.vue.vm", "index-tree.vue.vm")) {
|
||||
if (!StrUtil.containsAny("sql.vm", "api.js.vm", "index.vue.vm", "index-tree.vue.vm", template)) {
|
||||
// 渲染模板
|
||||
StringWriter sw = new StringWriter();
|
||||
Template tpl = Velocity.getTemplate(template, Constants.UTF8);
|
||||
|
@ -290,9 +290,9 @@ public class GenTableServiceImpl extends ServicePlusImpl<GenTableMapper, GenTabl
|
|||
dbTableColumns.forEach(column -> {
|
||||
if (!tableColumnNames.contains(column.getColumnName())) {
|
||||
GenUtils.initColumnField(column, table);
|
||||
genTableColumnMapper.insert(column);
|
||||
}
|
||||
});
|
||||
genTableColumnMapper.insertAll(tableColumns);
|
||||
|
||||
List<GenTableColumn> delColumns = tableColumns.stream().filter(column -> !dbTableColumnNames.contains(column.getColumnName())).collect(Collectors.toList());
|
||||
if (CollUtil.isNotEmpty(delColumns)) {
|
||||
|
|
|
@ -11,8 +11,6 @@ import org.apache.ibatis.annotations.CacheNamespace;
|
|||
* @author ${author}
|
||||
* @date ${datetime}
|
||||
*/
|
||||
// 如使需切换数据源 请勿使用缓存 会造成数据不一致现象
|
||||
@CacheNamespace(implementation = MybatisPlusRedisCache.class, eviction = MybatisPlusRedisCache.class)
|
||||
public interface ${ClassName}Mapper extends BaseMapperPlus<${ClassName}> {
|
||||
|
||||
}
|
||||
|
|
|
@ -256,46 +256,10 @@
|
|||
import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName}, export${BusinessName} } from "@/api/${moduleName}/${businessName}";
|
||||
import Treeselect from "@riophae/vue-treeselect";
|
||||
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
|
||||
#foreach($column in $columns)
|
||||
#if($column.insert && !$column.pk && $column.htmlType == "imageUpload")
|
||||
import ImageUpload from '@/components/ImageUpload';
|
||||
#break
|
||||
#end
|
||||
#end
|
||||
#foreach($column in $columns)
|
||||
#if($column.insert && !$column.pk && $column.htmlType == "fileUpload")
|
||||
import FileUpload from '@/components/FileUpload';
|
||||
#break
|
||||
#end
|
||||
#end
|
||||
#foreach($column in $columns)
|
||||
#if($column.insert && !$column.pk && $column.htmlType == "editor")
|
||||
import Editor from '@/components/Editor';
|
||||
#break
|
||||
#end
|
||||
#end
|
||||
|
||||
export default {
|
||||
name: "${BusinessName}",
|
||||
components: {
|
||||
#foreach($column in $columns)
|
||||
#if($column.insert && !$column.pk && $column.htmlType == "imageUpload")
|
||||
ImageUpload,
|
||||
#break
|
||||
#end
|
||||
#end
|
||||
#foreach($column in $columns)
|
||||
#if($column.insert && !$column.pk && $column.htmlType == "fileUpload")
|
||||
FileUpload,
|
||||
#break
|
||||
#end
|
||||
#end
|
||||
#foreach($column in $columns)
|
||||
#if($column.insert && !$column.pk && $column.htmlType == "editor")
|
||||
Editor,
|
||||
#break
|
||||
#end
|
||||
#end
|
||||
Treeselect
|
||||
},
|
||||
data() {
|
||||
|
@ -510,17 +474,19 @@ export default {
|
|||
#end
|
||||
if (this.form.${pkColumn.javaField} != null) {
|
||||
update${BusinessName}(this.form).then(response => {
|
||||
this.buttonLoading = false;
|
||||
this.msgSuccess("修改成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
} else {
|
||||
add${BusinessName}(this.form).then(response => {
|
||||
this.buttonLoading = false;
|
||||
this.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -308,47 +308,9 @@
|
|||
|
||||
<script>
|
||||
import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName}, export${BusinessName} } from "@/api/${moduleName}/${businessName}";
|
||||
#foreach($column in $columns)
|
||||
#if($column.insert && !$column.pk && $column.htmlType == "imageUpload")
|
||||
import ImageUpload from '@/components/ImageUpload';
|
||||
#break
|
||||
#end
|
||||
#end
|
||||
#foreach($column in $columns)
|
||||
#if($column.insert && !$column.pk && $column.htmlType == "fileUpload")
|
||||
import FileUpload from '@/components/FileUpload';
|
||||
#break
|
||||
#end
|
||||
#end
|
||||
#foreach($column in $columns)
|
||||
#if($column.insert && !$column.pk && $column.htmlType == "editor")
|
||||
import Editor from '@/components/Editor';
|
||||
#break
|
||||
#end
|
||||
#end
|
||||
|
||||
export default {
|
||||
name: "${BusinessName}",
|
||||
components: {
|
||||
#foreach($column in $columns)
|
||||
#if($column.insert && !$column.pk && $column.htmlType == "imageUpload")
|
||||
ImageUpload,
|
||||
#break
|
||||
#end
|
||||
#end
|
||||
#foreach($column in $columns)
|
||||
#if($column.insert && !$column.pk && $column.htmlType == "fileUpload")
|
||||
FileUpload,
|
||||
#break
|
||||
#end
|
||||
#end
|
||||
#foreach($column in $columns)
|
||||
#if($column.insert && !$column.pk && $column.htmlType == "editor")
|
||||
Editor,
|
||||
#break
|
||||
#end
|
||||
#end
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 按钮loading
|
||||
|
@ -567,17 +529,19 @@ export default {
|
|||
#end
|
||||
if (this.form.${pkColumn.javaField} != null) {
|
||||
update${BusinessName}(this.form).then(response => {
|
||||
this.buttonLoading = false;
|
||||
this.msgSuccess("修改成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
} else {
|
||||
add${BusinessName}(this.form).then(response => {
|
||||
this.buttonLoading = false;
|
||||
this.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>ruoyi-vue-plus</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>${ruoyi-vue-plus.version}</version>
|
||||
<version>2.5.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>ruoyi-vue-plus</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>${ruoyi-vue-plus.version}</version>
|
||||
<version>2.5.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package com.ruoyi.system.domain.vo;
|
||||
|
||||
import lombok.*;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
|
@ -28,6 +30,11 @@ public class MetaVo {
|
|||
*/
|
||||
private boolean noCache;
|
||||
|
||||
/**
|
||||
* 内链地址(http(s)://开头)
|
||||
*/
|
||||
private String link;
|
||||
|
||||
public MetaVo(String title, String icon) {
|
||||
this.title = title;
|
||||
this.icon = icon;
|
||||
|
@ -39,4 +46,19 @@ public class MetaVo {
|
|||
this.noCache = noCache;
|
||||
}
|
||||
|
||||
public MetaVo(String title, String icon, String link) {
|
||||
this.title = title;
|
||||
this.icon = icon;
|
||||
this.link = link;
|
||||
}
|
||||
|
||||
public MetaVo(String title, String icon, boolean noCache, String link) {
|
||||
this.title = title;
|
||||
this.icon = icon;
|
||||
this.noCache = noCache;
|
||||
if (StringUtils.ishttp(link)) {
|
||||
this.link = link;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,6 +24,22 @@ public interface SysUserMapper extends BaseMapperPlus<SysUser> {
|
|||
*/
|
||||
public List<SysUser> selectUserList(SysUser sysUser);
|
||||
|
||||
/**
|
||||
* 根据条件分页查询未已配用户角色列表
|
||||
*
|
||||
* @param user 用户信息
|
||||
* @return 用户信息集合信息
|
||||
*/
|
||||
public Page<SysUser> selectAllocatedList(@Param("page") Page<SysUser> page, @Param("user") SysUser user);
|
||||
|
||||
/**
|
||||
* 根据条件分页查询未分配用户角色列表
|
||||
*
|
||||
* @param user 用户信息
|
||||
* @return 用户信息集合信息
|
||||
*/
|
||||
public Page<SysUser> selectUnallocatedList(@Param("page") Page<SysUser> page, @Param("user") SysUser user);
|
||||
|
||||
/**
|
||||
* 通过用户名查询用户
|
||||
*
|
||||
|
|
|
@ -3,6 +3,8 @@ package com.ruoyi.system.service;
|
|||
import com.ruoyi.common.core.domain.entity.SysRole;
|
||||
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.core.domain.entity.SysRole;
|
||||
import com.ruoyi.system.domain.SysUserRole;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
@ -26,7 +28,15 @@ public interface ISysRoleService extends IServicePlus<SysRole> {
|
|||
public List<SysRole> selectRoleList(SysRole role);
|
||||
|
||||
/**
|
||||
* 根据用户ID查询角色
|
||||
* 根据用户ID查询角色列表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 角色列表
|
||||
*/
|
||||
public List<SysRole> selectRolesByUserId(Long userId);
|
||||
|
||||
/**
|
||||
* 根据用户ID查询角色权限
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 权限列表
|
||||
|
@ -134,4 +144,30 @@ public interface ISysRoleService extends IServicePlus<SysRole> {
|
|||
* @return 结果
|
||||
*/
|
||||
public int deleteRoleByIds(Long[] roleIds);
|
||||
|
||||
/**
|
||||
* 取消授权用户角色
|
||||
*
|
||||
* @param userRole 用户和角色关联信息
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteAuthUser(SysUserRole userRole);
|
||||
|
||||
/**
|
||||
* 批量取消授权用户角色
|
||||
*
|
||||
* @param roleId 角色ID
|
||||
* @param userIds 需要取消授权的用户数据ID
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteAuthUsers(Long roleId, Long[] userIds);
|
||||
|
||||
/**
|
||||
* 批量选择授权用户角色
|
||||
*
|
||||
* @param roleId 角色ID
|
||||
* @param userIds 需要删除的用户数据ID
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertAuthUsers(Long roleId, Long[] userIds);
|
||||
}
|
||||
|
|
|
@ -24,6 +24,22 @@ public interface ISysUserService extends IServicePlus<SysUser> {
|
|||
*/
|
||||
public List<SysUser> selectUserList(SysUser user);
|
||||
|
||||
/**
|
||||
* 根据条件分页查询已分配用户角色列表
|
||||
*
|
||||
* @param user 用户信息
|
||||
* @return 用户信息集合信息
|
||||
*/
|
||||
public TableDataInfo<SysUser> selectAllocatedList(SysUser user);
|
||||
|
||||
/**
|
||||
* 根据条件分页查询未分配用户角色列表
|
||||
*
|
||||
* @param user 用户信息
|
||||
* @return 用户信息集合信息
|
||||
*/
|
||||
public TableDataInfo<SysUser> selectUnallocatedList(SysUser user);
|
||||
|
||||
/**
|
||||
* 通过用户名查询用户
|
||||
*
|
||||
|
@ -103,6 +119,14 @@ public interface ISysUserService extends IServicePlus<SysUser> {
|
|||
*/
|
||||
public int updateUser(SysUser user);
|
||||
|
||||
/**
|
||||
* 用户授权角色
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param roleIds 角色组
|
||||
*/
|
||||
public void insertUserAuth(Long userId, Long[] roleIds);
|
||||
|
||||
/**
|
||||
* 修改用户状态
|
||||
*
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.ruoyi.system.service.impl;
|
|||
import cn.hutool.core.lang.Validator;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.ruoyi.common.constant.Constants;
|
||||
import com.ruoyi.common.constant.UserConstants;
|
||||
import com.ruoyi.common.core.domain.TreeSelect;
|
||||
import com.ruoyi.common.core.domain.entity.SysMenu;
|
||||
|
@ -10,6 +11,7 @@ import com.ruoyi.common.core.domain.entity.SysRole;
|
|||
import com.ruoyi.common.core.domain.entity.SysUser;
|
||||
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.system.domain.SysRoleMenu;
|
||||
import com.ruoyi.system.domain.vo.MetaVo;
|
||||
import com.ruoyi.system.domain.vo.RouterVo;
|
||||
|
@ -135,7 +137,7 @@ public class SysMenuServiceImpl extends ServicePlusImpl<SysMenuMapper, SysMenu>
|
|||
router.setName(getRouteName(menu));
|
||||
router.setPath(getRouterPath(menu));
|
||||
router.setComponent(getComponent(menu));
|
||||
router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StrUtil.equals("1", menu.getIsCache())));
|
||||
router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StrUtil.equals("1", menu.getIsCache()), menu.getPath()));
|
||||
List<SysMenu> cMenus = menu.getChildren();
|
||||
if (!cMenus.isEmpty() && UserConstants.TYPE_DIR.equals(menu.getMenuType())) {
|
||||
router.setAlwaysShow(true);
|
||||
|
@ -148,7 +150,19 @@ public class SysMenuServiceImpl extends ServicePlusImpl<SysMenuMapper, SysMenu>
|
|||
children.setPath(menu.getPath());
|
||||
children.setComponent(menu.getComponent());
|
||||
children.setName(StrUtil.upperFirst(menu.getPath()));
|
||||
children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StrUtil.equals("1", menu.getIsCache())));
|
||||
children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StrUtil.equals("1", menu.getIsCache()), menu.getPath()));
|
||||
childrenList.add(children);
|
||||
router.setChildren(childrenList);
|
||||
} else if (menu.getParentId().intValue() == 0 && isInnerLink(menu)) {
|
||||
router.setMeta(null);
|
||||
router.setPath("/inner");
|
||||
List<RouterVo> childrenList = new ArrayList<RouterVo>();
|
||||
RouterVo children = new RouterVo();
|
||||
String routerPath = StringUtils.replaceEach(menu.getPath(), new String[] { Constants.HTTP, Constants.HTTPS }, new String[] { "", "" });
|
||||
children.setPath(routerPath);
|
||||
children.setComponent(UserConstants.INNER_LINK);
|
||||
children.setName(StringUtils.capitalize(routerPath));
|
||||
children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), menu.getPath()));
|
||||
childrenList.add(children);
|
||||
router.setChildren(childrenList);
|
||||
}
|
||||
|
@ -305,6 +319,10 @@ public class SysMenuServiceImpl extends ServicePlusImpl<SysMenuMapper, SysMenu>
|
|||
*/
|
||||
public String getRouterPath(SysMenu menu) {
|
||||
String routerPath = menu.getPath();
|
||||
// 内链打开外网方式
|
||||
if (menu.getParentId().intValue() != 0 && isInnerLink(menu)) {
|
||||
routerPath = StringUtils.replaceEach(routerPath, new String[] { Constants.HTTP, Constants.HTTPS }, new String[] { "", "" });
|
||||
}
|
||||
// 非外链并且是一级目录(类型为目录)
|
||||
if (0 == menu.getParentId().intValue() && UserConstants.TYPE_DIR.equals(menu.getMenuType())
|
||||
&& UserConstants.NO_FRAME.equals(menu.getIsFrame())) {
|
||||
|
@ -327,6 +345,8 @@ public class SysMenuServiceImpl extends ServicePlusImpl<SysMenuMapper, SysMenu>
|
|||
String component = UserConstants.LAYOUT;
|
||||
if (StrUtil.isNotEmpty(menu.getComponent()) && !isMenuFrame(menu)) {
|
||||
component = menu.getComponent();
|
||||
} else if (StrUtil.isEmpty(menu.getComponent()) && menu.getParentId().intValue() != 0 && isInnerLink(menu)) {
|
||||
component = UserConstants.INNER_LINK;
|
||||
} else if (StrUtil.isEmpty(menu.getComponent()) && isParentView(menu)) {
|
||||
component = UserConstants.PARENT_VIEW;
|
||||
}
|
||||
|
@ -344,6 +364,16 @@ public class SysMenuServiceImpl extends ServicePlusImpl<SysMenuMapper, SysMenu>
|
|||
&& menu.getIsFrame().equals(UserConstants.NO_FRAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为内链组件
|
||||
*
|
||||
* @param menu 菜单信息
|
||||
* @return 结果
|
||||
*/
|
||||
public boolean isInnerLink(SysMenu menu) {
|
||||
return menu.getIsFrame().equals(UserConstants.NO_FRAME) && StringUtils.ishttp(menu.getPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为parent_view组件
|
||||
*
|
||||
|
|
|
@ -59,6 +59,27 @@ public class SysRoleServiceImpl extends ServicePlusImpl<SysRoleMapper, SysRole>
|
|||
return baseMapper.selectRoleList(role);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户ID查询角色
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 角色列表
|
||||
*/
|
||||
@Override
|
||||
public List<SysRole> selectRolesByUserId(Long userId) {
|
||||
List<SysRole> userRoles = baseMapper.selectRolePermissionByUserId(userId);
|
||||
List<SysRole> roles = selectRoleAll();
|
||||
for (SysRole role : roles) {
|
||||
for (SysRole userRole : userRoles) {
|
||||
if (role.getRoleId().longValue() == userRole.getRoleId().longValue()) {
|
||||
role.setFlag(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户ID查询权限
|
||||
*
|
||||
|
@ -305,4 +326,51 @@ public class SysRoleServiceImpl extends ServicePlusImpl<SysRoleMapper, SysRole>
|
|||
roleDeptMapper.delete(new LambdaQueryWrapper<SysRoleDept>().in(SysRoleDept::getRoleId, ids));
|
||||
return baseMapper.deleteBatchIds(ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消授权用户角色
|
||||
*
|
||||
* @param userRole 用户和角色关联信息
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deleteAuthUser(SysUserRole userRole) {
|
||||
return userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>()
|
||||
.eq(SysUserRole::getRoleId, userRole.getRoleId())
|
||||
.eq(SysUserRole::getUserId, userRole.getUserId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量取消授权用户角色
|
||||
*
|
||||
* @param roleId 角色ID
|
||||
* @param userIds 需要取消授权的用户数据ID
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deleteAuthUsers(Long roleId, Long[] userIds) {
|
||||
return userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>()
|
||||
.eq(SysUserRole::getRoleId, roleId)
|
||||
.in(SysUserRole::getUserId, Arrays.asList(userIds)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量选择授权用户角色
|
||||
*
|
||||
* @param roleId 角色ID
|
||||
* @param userIds 需要删除的用户数据ID
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int insertAuthUsers(Long roleId, Long[] userIds) {
|
||||
// 新增用户与角色管理
|
||||
List<SysUserRole> list = new ArrayList<SysUserRole>();
|
||||
for (Long userId : userIds) {
|
||||
SysUserRole ur = new SysUserRole();
|
||||
ur.setUserId(userId);
|
||||
ur.setRoleId(roleId);
|
||||
list.add(ur);
|
||||
}
|
||||
return userRoleMapper.insertAll(list);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,6 +69,30 @@ public class SysUserServiceImpl extends ServicePlusImpl<SysUserMapper, SysUser>
|
|||
return baseMapper.selectUserList(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件分页查询已分配用户角色列表
|
||||
*
|
||||
* @param user 用户信息
|
||||
* @return 用户信息集合信息
|
||||
*/
|
||||
@Override
|
||||
@DataScope(deptAlias = "d", userAlias = "u", isUser = true)
|
||||
public TableDataInfo<SysUser> selectAllocatedList(SysUser user) {
|
||||
return PageUtils.buildDataInfo(baseMapper.selectAllocatedList(PageUtils.buildPage(), user));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件分页查询未分配用户角色列表
|
||||
*
|
||||
* @param user 用户信息
|
||||
* @return 用户信息集合信息
|
||||
*/
|
||||
@Override
|
||||
@DataScope(deptAlias = "d", userAlias = "u", isUser = true)
|
||||
public TableDataInfo<SysUser> selectUnallocatedList(SysUser user) {
|
||||
return PageUtils.buildDataInfo(baseMapper.selectUnallocatedList(PageUtils.buildPage(), user));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过用户名查询用户
|
||||
*
|
||||
|
@ -231,6 +255,21 @@ public class SysUserServiceImpl extends ServicePlusImpl<SysUserMapper, SysUser>
|
|||
return baseMapper.updateById(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户授权角色
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param roleIds 角色组
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public void insertUserAuth(Long userId, Long[] roleIds)
|
||||
{
|
||||
userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>()
|
||||
.eq(SysUserRole::getUserId, userId));
|
||||
insertUserRole(userId, roleIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改用户状态
|
||||
*
|
||||
|
@ -338,6 +377,28 @@ public class SysUserServiceImpl extends ServicePlusImpl<SysUserMapper, SysUser>
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增用户角色信息
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param roleIds 角色组
|
||||
*/
|
||||
public void insertUserRole(Long userId, Long[] roleIds) {
|
||||
if (Validator.isNotNull(roleIds)) {
|
||||
// 新增用户与角色管理
|
||||
List<SysUserRole> list = new ArrayList<SysUserRole>();
|
||||
for (Long roleId : roleIds) {
|
||||
SysUserRole ur = new SysUserRole();
|
||||
ur.setUserId(userId);
|
||||
ur.setRoleId(roleId);
|
||||
list.add(ur);
|
||||
}
|
||||
if (list.size() > 0) {
|
||||
userRoleMapper.insertAll(list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过用户ID删除用户
|
||||
*
|
||||
|
|
|
@ -142,6 +142,45 @@
|
|||
</if>
|
||||
</select>
|
||||
|
||||
<select id="selectAllocatedList" parameterType="SysUser" resultMap="SysUserResult">
|
||||
select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time
|
||||
from sys_user u
|
||||
left join sys_dept d on u.dept_id = d.dept_id
|
||||
left join sys_user_role ur on u.user_id = ur.user_id
|
||||
left join sys_role r on r.role_id = ur.role_id
|
||||
where u.del_flag = '0' and r.role_id = #{user.roleId}
|
||||
<if test="user.userName != null and user.userName != ''">
|
||||
AND u.user_name like concat('%', #{user.userName}, '%')
|
||||
</if>
|
||||
<if test="user.phonenumber != null and user.phonenumber != ''">
|
||||
AND u.phonenumber like concat('%', #{user.phonenumber}, '%')
|
||||
</if>
|
||||
<!-- 数据范围过滤 -->
|
||||
<if test="user.params.dataScope != null and user.params.dataScope != ''">
|
||||
AND ( ${user.params.dataScope} )
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<select id="selectUnallocatedList" parameterType="SysUser" resultMap="SysUserResult">
|
||||
select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time
|
||||
from sys_user u
|
||||
left join sys_dept d on u.dept_id = d.dept_id
|
||||
left join sys_user_role ur on u.user_id = ur.user_id
|
||||
left join sys_role r on r.role_id = ur.role_id
|
||||
where u.del_flag = '0' and (r.role_id != #{user.roleId} or r.role_id IS NULL)
|
||||
and u.user_id not in (select u.user_id from sys_user u inner join sys_user_role ur on u.user_id = ur.user_id and ur.role_id = #{user.roleId})
|
||||
<if test="user.userName != null and user.userName != ''">
|
||||
AND u.user_name like concat('%', #{user.userName}, '%')
|
||||
</if>
|
||||
<if test="user.phonenumber != null and user.phonenumber != ''">
|
||||
AND u.phonenumber like concat('%', #{user.phonenumber}, '%')
|
||||
</if>
|
||||
<!-- 数据范围过滤 -->
|
||||
<if test="user.params.dataScope != null and user.params.dataScope != ''">
|
||||
AND ( ${user.params.dataScope} )
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<select id="selectUserByUserName" parameterType="String" resultMap="SysUserResult">
|
||||
<include refid="selectUserVo"/>
|
||||
where u.user_name = #{userName}
|
||||
|
|
|
@ -7,5 +7,8 @@ ENV = 'development'
|
|||
# 若依管理系统/开发环境
|
||||
VUE_APP_BASE_API = '/dev-api'
|
||||
|
||||
# 监控地址
|
||||
VUE_APP_MONITRO_ADMIN = 'http://localhost:9090/admin/login'
|
||||
|
||||
# 路由懒加载
|
||||
VUE_CLI_BABEL_TRANSPILE_MODULES = true
|
||||
|
|
|
@ -4,5 +4,8 @@ VUE_APP_TITLE = RuoYi-Vue-Plus后台管理系统
|
|||
# 生产环境配置
|
||||
ENV = 'production'
|
||||
|
||||
# 监控地址
|
||||
VUE_APP_MONITRO_ADMIN = '/admin/login'
|
||||
|
||||
# 若依管理系统/生产环境
|
||||
VUE_APP_BASE_API = '/prod-api'
|
||||
|
|
|
@ -6,5 +6,8 @@ NODE_ENV = production
|
|||
# 测试环境配置
|
||||
ENV = 'staging'
|
||||
|
||||
# 监控地址
|
||||
VUE_APP_MONITRO_ADMIN = '/admin/login'
|
||||
|
||||
# 若依管理系统/测试环境
|
||||
VUE_APP_BASE_API = '/stage-api'
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "ruoyi-vue-plus",
|
||||
"version": "2.4.0",
|
||||
"version": "2.5.0",
|
||||
"description": "RuoYi-Vue-Plus后台管理系统",
|
||||
"author": "LionLi",
|
||||
"license": "MIT",
|
||||
|
|
|
@ -73,3 +73,49 @@ export function exportRole(query) {
|
|||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 查询角色已授权用户列表
|
||||
export function allocatedUserList(query) {
|
||||
return request({
|
||||
url: '/system/role/authUser/allocatedList',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询角色未授权用户列表
|
||||
export function unallocatedUserList(query) {
|
||||
return request({
|
||||
url: '/system/role/authUser/unallocatedList',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 取消用户授权角色
|
||||
export function authUserCancel(data) {
|
||||
return request({
|
||||
url: '/system/role/authUser/cancel',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 批量取消用户授权角色
|
||||
export function authUserCancelAll(data) {
|
||||
return request({
|
||||
url: '/system/role/authUser/cancelAll',
|
||||
method: 'put',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 授权用户选择
|
||||
export function authUserSelectAll(data) {
|
||||
return request({
|
||||
url: '/system/role/authUser/selectAll',
|
||||
method: 'put',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
|
|
@ -125,3 +125,20 @@ export function importTemplate() {
|
|||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询授权角色
|
||||
export function getAuthRole(userId) {
|
||||
return request({
|
||||
url: '/system/user/authRole/' + userId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 保存授权角色
|
||||
export function updateAuthRole(data) {
|
||||
return request({
|
||||
url: '/system/user/authRole',
|
||||
method: 'put',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
|
|
@ -53,6 +53,13 @@
|
|||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
|
||||
font-family: inherit;
|
||||
font-weight: 500;
|
||||
line-height: 1.1;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.el-dialog:not(.is-fullscreen){
|
||||
margin-top: 6vh !important;
|
||||
}
|
||||
|
@ -120,6 +127,17 @@
|
|||
width: inherit;
|
||||
}
|
||||
|
||||
/** 表格更多操作下拉样式 */
|
||||
.el-table .el-dropdown-link {
|
||||
cursor: pointer;
|
||||
color: #1890ff;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.el-table .el-dropdown, .el-icon-arrow-down {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.el-tree-node__content > .el-checkbox {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
:headers="headers"
|
||||
style="display: none"
|
||||
ref="upload"
|
||||
v-if="this.uploadUrl"
|
||||
v-if="this.type == 'url'"
|
||||
>
|
||||
</el-upload>
|
||||
<div class="editor" ref="editor" :style="styles"></div>
|
||||
|
@ -46,14 +46,15 @@ export default {
|
|||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
/* 上传地址 */
|
||||
uploadUrl: {
|
||||
/* 类型(base64格式、url格式) */
|
||||
type: {
|
||||
type: String,
|
||||
default: "",
|
||||
default: "url",
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
uploadUrl: process.env.VUE_APP_BASE_API + "/common/upload", // 上传的图片服务器地址
|
||||
headers: {
|
||||
Authorization: "Bearer " + getToken()
|
||||
},
|
||||
|
@ -119,7 +120,7 @@ export default {
|
|||
const editor = this.$refs.editor;
|
||||
this.Quill = new Quill(editor, this.options);
|
||||
// 如果设置了上传地址则自定义图片上传事件
|
||||
if (this.uploadUrl) {
|
||||
if (this.type == 'url') {
|
||||
let toolbar = this.Quill.getModule("toolbar");
|
||||
toolbar.addHandler("image", (value) => {
|
||||
this.uploadType = "image";
|
||||
|
@ -165,7 +166,7 @@ export default {
|
|||
// 获取光标所在位置
|
||||
let length = quill.getSelection().index;
|
||||
// 插入图片 res.url为服务器返回的图片地址
|
||||
quill.insertEmbed(length, "image", res.url);
|
||||
quill.insertEmbed(length, "image", process.env.VUE_APP_BASE_API + res.data.fileName);
|
||||
// 调整光标到最后
|
||||
quill.setSelection(length + 1);
|
||||
} else {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
:action="uploadFileUrl"
|
||||
:before-upload="handleBeforeUpload"
|
||||
:file-list="fileList"
|
||||
:limit="1"
|
||||
:limit="limit"
|
||||
:on-error="handleUploadError"
|
||||
:on-exceed="handleExceed"
|
||||
:on-success="handleUploadSuccess"
|
||||
|
@ -26,8 +26,8 @@
|
|||
|
||||
<!-- 文件列表 -->
|
||||
<transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
|
||||
<li :key="file.uid" class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in list">
|
||||
<el-link :href="file.url" :underline="false" target="_blank">
|
||||
<li :key="file.uid" class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in fileList">
|
||||
<el-link :href="`${baseUrl}${file.url}`" :underline="false" target="_blank">
|
||||
<span class="el-icon-document"> {{ getFileName(file.name) }} </span>
|
||||
</el-link>
|
||||
<div class="ele-upload-list__item-content-action">
|
||||
|
@ -42,9 +42,15 @@
|
|||
import { getToken } from "@/utils/auth";
|
||||
|
||||
export default {
|
||||
name: "FileUpload",
|
||||
props: {
|
||||
// 值
|
||||
value: [String, Object, Array],
|
||||
// 数量限制
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 5,
|
||||
},
|
||||
// 大小限制(MB)
|
||||
fileSize: {
|
||||
type: Number,
|
||||
|
@ -63,6 +69,7 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
baseUrl: process.env.VUE_APP_BASE_API,
|
||||
uploadFileUrl: process.env.VUE_APP_BASE_API + "/common/upload", // 上传的图片服务器地址
|
||||
headers: {
|
||||
Authorization: "Bearer " + getToken(),
|
||||
|
@ -70,19 +77,15 @@ export default {
|
|||
fileList: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 是否显示提示
|
||||
showTip() {
|
||||
return this.isShowTip && (this.fileType || this.fileSize);
|
||||
},
|
||||
// 列表
|
||||
list() {
|
||||
watch: {
|
||||
value: {
|
||||
handler(val) {
|
||||
if (val) {
|
||||
let temp = 1;
|
||||
if (this.value) {
|
||||
// 首先将值转为数组
|
||||
const list = Array.isArray(this.value) ? this.value : [this.value];
|
||||
const list = Array.isArray(val) ? val : this.value.split(',');
|
||||
// 然后将数组转为对象数组
|
||||
return list.map((item) => {
|
||||
this.fileList = list.map(item => {
|
||||
if (typeof item === "string") {
|
||||
item = { name: item, url: item };
|
||||
}
|
||||
|
@ -94,6 +97,15 @@ export default {
|
|||
return [];
|
||||
}
|
||||
},
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 是否显示提示
|
||||
showTip() {
|
||||
return this.isShowTip && (this.fileType || this.fileSize);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// 上传前校检格式和大小
|
||||
|
@ -126,7 +138,7 @@ export default {
|
|||
},
|
||||
// 文件个数超出
|
||||
handleExceed() {
|
||||
this.$message.error(`只允许上传单个文件`);
|
||||
this.$message.error(`上传文件数量不能超过 ${this.limit} 个!`);
|
||||
},
|
||||
// 上传失败
|
||||
handleUploadError(err) {
|
||||
|
@ -135,12 +147,13 @@ export default {
|
|||
// 上传成功回调
|
||||
handleUploadSuccess(res, file) {
|
||||
this.$message.success("上传成功");
|
||||
this.$emit("input", res.data.url);
|
||||
this.fileList.push({ name: res.data.fileName, url: res.data.fileName });
|
||||
this.$emit("input", this.listToString(this.fileList));
|
||||
},
|
||||
// 删除文件
|
||||
handleDelete(index) {
|
||||
this.fileList.splice(index, 1);
|
||||
this.$emit("input", '');
|
||||
this.$emit("input", this.listToString(this.fileList));
|
||||
},
|
||||
// 获取文件名称
|
||||
getFileName(name) {
|
||||
|
@ -149,11 +162,17 @@ export default {
|
|||
} else {
|
||||
return "";
|
||||
}
|
||||
},
|
||||
// 对象转成指定字符串分隔
|
||||
listToString(list, separator) {
|
||||
let strs = "";
|
||||
separator = separator || ",";
|
||||
for (let i in list) {
|
||||
strs += list[i].url + separator;
|
||||
}
|
||||
return strs != '' ? strs.substr(0, strs.length - 1) : '';
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fileList = this.list;
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
@ -70,9 +70,11 @@ export default {
|
|||
this.show = false
|
||||
},
|
||||
change(val) {
|
||||
const path = val.path;
|
||||
if(this.ishttp(val.path)) {
|
||||
// http(s):// 路径新窗口打开
|
||||
window.open(val.path, "_blank");
|
||||
const pindex = path.indexOf("http");
|
||||
window.open(path.substr(pindex, path.length), "_blank");
|
||||
} else {
|
||||
this.$router.push(val.path)
|
||||
}
|
||||
|
|
|
@ -5,33 +5,38 @@
|
|||
list-type="picture-card"
|
||||
:on-success="handleUploadSuccess"
|
||||
:before-upload="handleBeforeUpload"
|
||||
:limit="limit"
|
||||
:on-error="handleUploadError"
|
||||
:on-exceed="handleExceed"
|
||||
name="file"
|
||||
:show-file-list="false"
|
||||
:on-remove="handleRemove"
|
||||
:show-file-list="true"
|
||||
:headers="headers"
|
||||
style="display: inline-block; vertical-align: top"
|
||||
:file-list="fileList"
|
||||
:on-preview="handlePictureCardPreview"
|
||||
:class="{hide: this.fileList.length >= this.limit}"
|
||||
>
|
||||
<el-image v-if="!value" :src="value">
|
||||
<div slot="error" class="image-slot">
|
||||
<i class="el-icon-plus" />
|
||||
</div>
|
||||
</el-image>
|
||||
<div v-else class="image">
|
||||
<el-image :src="value" :style="`width:150px;height:150px;`" fit="fill"/>
|
||||
<div class="mask">
|
||||
<div class="actions">
|
||||
<span title="预览" @click.stop="dialogVisible = true">
|
||||
<i class="el-icon-zoom-in" />
|
||||
</span>
|
||||
<span title="移除" @click.stop="removeImage">
|
||||
<i class="el-icon-delete" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<i class="el-icon-plus"></i>
|
||||
</el-upload>
|
||||
<el-dialog :visible.sync="dialogVisible" title="预览" width="800" append-to-body>
|
||||
<img :src="value" style="display: block; max-width: 100%; margin: 0 auto;">
|
||||
|
||||
<!-- 上传提示 -->
|
||||
<div class="el-upload__tip" slot="tip" v-if="showTip">
|
||||
请上传
|
||||
<template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> </template>
|
||||
<template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>
|
||||
的文件
|
||||
</div>
|
||||
|
||||
<el-dialog
|
||||
:visible.sync="dialogVisible"
|
||||
title="预览"
|
||||
width="800"
|
||||
append-to-body
|
||||
>
|
||||
<img
|
||||
:src="dialogImageUrl"
|
||||
style="display: block; max-width: 100%; margin: 0 auto"
|
||||
/>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -40,36 +45,128 @@
|
|||
import { getToken } from "@/utils/auth";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
value: [String, Object, Array],
|
||||
// 图片数量限制
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 5,
|
||||
},
|
||||
// 大小限制(MB)
|
||||
fileSize: {
|
||||
type: Number,
|
||||
default: 5,
|
||||
},
|
||||
// 文件类型, 例如['png', 'jpg', 'jpeg']
|
||||
fileType: {
|
||||
type: Array,
|
||||
default: () => ["png", "jpg", "jpeg"],
|
||||
},
|
||||
// 是否显示提示
|
||||
isShowTip: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialogImageUrl: "",
|
||||
dialogVisible: false,
|
||||
hideUpload: false,
|
||||
baseUrl: process.env.VUE_APP_BASE_API,
|
||||
uploadImgUrl: process.env.VUE_APP_BASE_API + "/common/upload", // 上传的图片服务器地址
|
||||
headers: {
|
||||
Authorization: "Bearer " + getToken(),
|
||||
},
|
||||
fileList: []
|
||||
};
|
||||
},
|
||||
props: {
|
||||
watch: {
|
||||
value: {
|
||||
type: String,
|
||||
default: "",
|
||||
handler(val) {
|
||||
if (val) {
|
||||
// 首先将值转为数组
|
||||
const list = Array.isArray(val) ? val : this.value.split(',');
|
||||
// 然后将数组转为对象数组
|
||||
this.fileList = list.map(item => {
|
||||
if (typeof item === "string") {
|
||||
if (item.indexOf(this.baseUrl) === -1) {
|
||||
item = { name: this.baseUrl + item, url: this.baseUrl + item };
|
||||
} else {
|
||||
item = { name: item, url: item };
|
||||
}
|
||||
}
|
||||
return item;
|
||||
});
|
||||
} else {
|
||||
this.fileList = [];
|
||||
return [];
|
||||
}
|
||||
},
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 是否显示提示
|
||||
showTip() {
|
||||
return this.isShowTip && (this.fileType || this.fileSize);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
removeImage() {
|
||||
this.$emit("input", "");
|
||||
// 删除图片
|
||||
handleRemove(file, fileList) {
|
||||
const findex = this.fileList.indexOf(file.name);
|
||||
this.fileList.splice(findex, 1);
|
||||
this.$emit("input", this.listToString(this.fileList));
|
||||
},
|
||||
// 上传成功回调
|
||||
handleUploadSuccess(res) {
|
||||
this.$emit("input", res.data.url);
|
||||
this.fileList.push({ name: res.data.fileName, url: res.data.fileName });
|
||||
this.$emit("input", this.listToString(this.fileList));
|
||||
this.loading.close();
|
||||
},
|
||||
handleBeforeUpload() {
|
||||
// 上传前loading加载
|
||||
handleBeforeUpload(file) {
|
||||
let isImg = false;
|
||||
if (this.fileType.length) {
|
||||
let fileExtension = "";
|
||||
if (file.name.lastIndexOf(".") > -1) {
|
||||
fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
|
||||
}
|
||||
isImg = this.fileType.some(type => {
|
||||
if (file.type.indexOf(type) > -1) return true;
|
||||
if (fileExtension && fileExtension.indexOf(type) > -1) return true;
|
||||
return false;
|
||||
});
|
||||
} else {
|
||||
isImg = file.type.indexOf("image") > -1;
|
||||
}
|
||||
|
||||
if (!isImg) {
|
||||
this.$message.error(
|
||||
`文件格式不正确, 请上传${this.fileType.join("/")}图片格式文件!`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (this.fileSize) {
|
||||
const isLt = file.size / 1024 / 1024 < this.fileSize;
|
||||
if (!isLt) {
|
||||
this.$message.error(`上传头像图片大小不能超过 ${this.fileSize} MB!`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
this.loading = this.$loading({
|
||||
lock: true,
|
||||
text: "上传中",
|
||||
background: "rgba(0, 0, 0, 0.7)",
|
||||
});
|
||||
},
|
||||
// 文件个数超出
|
||||
handleExceed() {
|
||||
this.$message.error(`上传文件数量不能超过 ${this.limit} 个!`);
|
||||
},
|
||||
// 上传失败
|
||||
handleUploadError() {
|
||||
this.$message({
|
||||
type: "error",
|
||||
|
@ -77,24 +174,37 @@ export default {
|
|||
});
|
||||
this.loading.close();
|
||||
},
|
||||
// 预览
|
||||
handlePictureCardPreview(file) {
|
||||
this.dialogImageUrl = file.url;
|
||||
this.dialogVisible = true;
|
||||
},
|
||||
watch: {},
|
||||
// 对象转成指定字符串分隔
|
||||
listToString(list, separator) {
|
||||
let strs = "";
|
||||
separator = separator || ",";
|
||||
for (let i in list) {
|
||||
strs += list[i].url + separator;
|
||||
}
|
||||
return strs != '' ? strs.substr(0, strs.length - 1) : '';
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.image {
|
||||
position: relative;
|
||||
.mask {
|
||||
// .el-upload--picture-card 控制加号部分
|
||||
::v-deep.hide .el-upload--picture-card {
|
||||
display: none;
|
||||
}
|
||||
// 去掉动画效果
|
||||
::v-deep .el-list-enter-active,
|
||||
::v-deep .el-list-leave-active {
|
||||
transition: all 0s;
|
||||
}
|
||||
|
||||
::v-deep .el-list-enter, .el-list-leave-active {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
&:hover .mask {
|
||||
opacity: 1;
|
||||
}
|
||||
transform: translateY(0);
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/**
|
||||
* v-dialogDrag 弹窗拖拽
|
||||
* Copyright (c) 2019 ruoyi
|
||||
*/
|
||||
|
||||
export default {
|
||||
bind(el, binding, vnode, oldVnode) {
|
||||
const value = binding.value
|
||||
if (value == false) return
|
||||
// 获取拖拽内容头部
|
||||
const dialogHeaderEl = el.querySelector('.el-dialog__header');
|
||||
const dragDom = el.querySelector('.el-dialog');
|
||||
dialogHeaderEl.style.cursor = 'move';
|
||||
// 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
|
||||
const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null);
|
||||
dragDom.style.position = 'absolute';
|
||||
dragDom.style.marginTop = 0;
|
||||
let width = dragDom.style.width;
|
||||
if (width.includes('%')) {
|
||||
width = +document.body.clientWidth * (+width.replace(/\%/g, '') / 100);
|
||||
} else {
|
||||
width = +width.replace(/\px/g, '');
|
||||
}
|
||||
dragDom.style.left = `${(document.body.clientWidth - width) / 2}px`;
|
||||
// 鼠标按下事件
|
||||
dialogHeaderEl.onmousedown = (e) => {
|
||||
// 鼠标按下,计算当前元素距离可视区的距离 (鼠标点击位置距离可视窗口的距离)
|
||||
const disX = e.clientX - dialogHeaderEl.offsetLeft;
|
||||
const disY = e.clientY - dialogHeaderEl.offsetTop;
|
||||
|
||||
// 获取到的值带px 正则匹配替换
|
||||
let styL, styT;
|
||||
|
||||
// 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
|
||||
if (sty.left.includes('%')) {
|
||||
styL = +document.body.clientWidth * (+sty.left.replace(/\%/g, '') / 100);
|
||||
styT = +document.body.clientHeight * (+sty.top.replace(/\%/g, '') / 100);
|
||||
} else {
|
||||
styL = +sty.left.replace(/\px/g, '');
|
||||
styT = +sty.top.replace(/\px/g, '');
|
||||
};
|
||||
|
||||
// 鼠标拖拽事件
|
||||
document.onmousemove = function (e) {
|
||||
// 通过事件委托,计算移动的距离 (开始拖拽至结束拖拽的距离)
|
||||
const l = e.clientX - disX;
|
||||
const t = e.clientY - disY;
|
||||
|
||||
let finallyL = l + styL
|
||||
let finallyT = t + styT
|
||||
|
||||
// 移动当前元素
|
||||
dragDom.style.left = `${finallyL}px`;
|
||||
dragDom.style.top = `${finallyT}px`;
|
||||
|
||||
};
|
||||
|
||||
document.onmouseup = function (e) {
|
||||
document.onmousemove = null;
|
||||
document.onmouseup = null;
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
|
@ -1,14 +1,17 @@
|
|||
import hasRole from './hasRole'
|
||||
import hasPermi from './hasPermi'
|
||||
import hasRole from './permission/hasRole'
|
||||
import hasPermi from './permission/hasPermi'
|
||||
import dialogDrag from './dialog/drag'
|
||||
|
||||
const install = function(Vue) {
|
||||
Vue.directive('hasRole', hasRole)
|
||||
Vue.directive('hasPermi', hasPermi)
|
||||
Vue.directive('dialogDrag', dialogDrag)
|
||||
}
|
||||
|
||||
if (window.Vue) {
|
||||
window['hasRole'] = hasRole
|
||||
window['hasPermi'] = hasPermi
|
||||
window['dialogDrag'] = dialogDrag
|
||||
Vue.use(install); // eslint-disable-line
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* 操作权限处理
|
||||
* v-hasPermi 操作权限处理
|
||||
* Copyright (c) 2019 ruoyi
|
||||
*/
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* 角色权限处理
|
||||
* v-hasRole 角色权限处理
|
||||
* Copyright (c) 2019 ruoyi
|
||||
*/
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ export default {
|
|||
// fix css style bug in open el-dialog
|
||||
.el-popup-parent--hidden {
|
||||
.fixed-header {
|
||||
padding-right: 15px;
|
||||
padding-right: 17px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
render() {
|
||||
const { $route: { meta: { link } }, } = this;
|
||||
if ({ link }.link === "") {
|
||||
return "404";
|
||||
}
|
||||
let url = { link }.link;
|
||||
const height = document.documentElement.clientHeight - 94.5 + "px";
|
||||
const style = { height: height };
|
||||
|
||||
return (
|
||||
<div style={style}>
|
||||
<iframe
|
||||
src={url}
|
||||
frameborder="no"
|
||||
style="width: 100%; height: 100%"
|
||||
scrolling="auto"
|
||||
></iframe>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -10,7 +10,7 @@ import '@/assets/styles/ruoyi.scss' // ruoyi css
|
|||
import App from './App'
|
||||
import store from './store'
|
||||
import router from './router'
|
||||
import permission from './directive/permission'
|
||||
import directive from './directive' //directive
|
||||
|
||||
import './assets/icons' // icon
|
||||
import './permission' // permission control
|
||||
|
@ -20,6 +20,12 @@ import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels,
|
|||
import Pagination from "@/components/Pagination";
|
||||
// 自定义表格工具组件
|
||||
import RightToolbar from "@/components/RightToolbar"
|
||||
// 富文本组件
|
||||
import Editor from "@/components/Editor"
|
||||
// 文件上传组件
|
||||
import FileUpload from "@/components/FileUpload"
|
||||
// 图片上传组件
|
||||
import ImageUpload from "@/components/ImageUpload"
|
||||
// 字典标签组件
|
||||
import DictTag from '@/components/DictTag'
|
||||
// 头部标签组件
|
||||
|
@ -52,8 +58,11 @@ Vue.prototype.msgInfo = function (msg) {
|
|||
Vue.component('DictTag', DictTag)
|
||||
Vue.component('Pagination', Pagination)
|
||||
Vue.component('RightToolbar', RightToolbar)
|
||||
Vue.component('Editor', Editor)
|
||||
Vue.component('FileUpload', FileUpload)
|
||||
Vue.component('ImageUpload', ImageUpload)
|
||||
|
||||
Vue.use(permission)
|
||||
Vue.use(directive)
|
||||
Vue.use(VueMeta)
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,6 +6,7 @@ Vue.use(Router)
|
|||
/* Layout */
|
||||
import Layout from '@/layout'
|
||||
import ParentView from '@/components/ParentView';
|
||||
import InnerLink from '@/layout/components/InnerLink'
|
||||
|
||||
/**
|
||||
* Note: 路由配置项
|
||||
|
@ -80,6 +81,32 @@ export const constantRoutes = [
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/auth',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
children: [
|
||||
{
|
||||
path: 'role/:userId(\\d+)',
|
||||
component: (resolve) => require(['@/views/system/user/authRole'], resolve),
|
||||
name: 'AuthRole',
|
||||
meta: { title: '分配角色'}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/auth',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
children: [
|
||||
{
|
||||
path: 'user/:roleId(\\d+)',
|
||||
component: (resolve) => require(['@/views/system/role/authUser'], resolve),
|
||||
name: 'AuthUser',
|
||||
meta: { title: '分配用户'}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/dict',
|
||||
component: Layout,
|
||||
|
|
|
@ -2,6 +2,7 @@ import { constantRoutes } from '@/router'
|
|||
import { getRouters } from '@/api/menu'
|
||||
import Layout from '@/layout/index'
|
||||
import ParentView from '@/components/ParentView';
|
||||
import InnerLink from '@/layout/components/InnerLink'
|
||||
|
||||
const permission = {
|
||||
state: {
|
||||
|
@ -65,6 +66,8 @@ function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
|
|||
route.component = Layout
|
||||
} else if (route.component === 'ParentView') {
|
||||
route.component = ParentView
|
||||
} else if (route.component === 'InnerLink') {
|
||||
route.component = InnerLink
|
||||
} else {
|
||||
route.component = loadView(route.component)
|
||||
}
|
||||
|
|
|
@ -304,17 +304,19 @@ export default {
|
|||
this.buttonLoading = true;
|
||||
if (this.form.id != null) {
|
||||
updateDemo(this.form).then(response => {
|
||||
this.buttonLoading = false;
|
||||
this.msgSuccess("修改成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
} else {
|
||||
addDemo(this.form).then(response => {
|
||||
this.buttonLoading = false;
|
||||
this.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -255,17 +255,19 @@ export default {
|
|||
this.buttonLoading = true;
|
||||
if (this.form.id != null) {
|
||||
updateTree(this.form).then(response => {
|
||||
this.buttonLoading = false;
|
||||
this.msgSuccess("修改成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
} else {
|
||||
addTree(this.form).then(response => {
|
||||
this.buttonLoading = false;
|
||||
this.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,6 +91,32 @@
|
|||
<span>更新日志</span>
|
||||
</div>
|
||||
<el-collapse accordion>
|
||||
<el-collapse-item title="v2.5.0 - 2021-7-12">
|
||||
<ol>
|
||||
<li>update springboot 2.4.7 => 2.4.8</li>
|
||||
<li>update knife4j 3.0.2 => 3.0.3</li>
|
||||
<li>update hutool 5.7.2 => 5.7.4</li>
|
||||
<li>update spring-boot-admin 2.4.1 => 2.4.3</li>
|
||||
<li>update redisson 3.15.2 => 3.16.0</li>
|
||||
<li>add 增加 docker 编排 与 shell 脚本</li>
|
||||
<li>add 增加 feign 熔断 自定义结构体解析方法 与 demo 注释</li>
|
||||
<li>add 用户管理新增分配角色功能</li>
|
||||
<li>add 角色管理新增分配用户功能</li>
|
||||
<li>add 增加spring-cache演示案例</li>
|
||||
<li>update 独立 springboot-admin 监控到扩展模块项目</li>
|
||||
<li>update springboot-admin 监控 增加用户登录权限管理</li>
|
||||
<li>update 优化代码生成器 批量导入</li>
|
||||
<li>update 优化 增加MP注入异常拦截</li>
|
||||
<li>update 关闭默认二级缓存 推荐使用 spring-cache 注解手动缓存</li>
|
||||
<li>update FileUpload ImageUpload组件 支持多图片上传</li>
|
||||
<li>update 优化中英文语言配置</li>
|
||||
<li>update 规范maven写法</li>
|
||||
<li>fix redis获取map属性bug修复。</li>
|
||||
<li>fix 修复 按钮loading 后端500卡死问题</li>
|
||||
<li>fix 相对路径下载问题</li>
|
||||
<li>fix 修复 hutool 工具返回结果不一致问题</li>
|
||||
</ol>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="v2.4.0 - 2021-6-24">
|
||||
<ol>
|
||||
<li>update springboot 2.3.11 => 2.4.7</li>
|
||||
|
|
|
@ -1,26 +1,16 @@
|
|||
<template>
|
||||
<div v-loading="loading" :style="'height:'+ height">
|
||||
<iframe :src="src" frameborder="no" style="width: 100%;height: 100%" scrolling="auto" />
|
||||
</div>
|
||||
<i-frame :src="url" />
|
||||
</template>
|
||||
<script>
|
||||
import iFrame from "@/components/iFrame/index";
|
||||
export default {
|
||||
name: "Admin",
|
||||
components: { iFrame },
|
||||
data() {
|
||||
console.log(process.env)
|
||||
return {
|
||||
src: process.env.VUE_APP_BASE_API + "/admin",
|
||||
height: document.documentElement.clientHeight - 94.5 + "px;",
|
||||
loading: true
|
||||
url: process.env.VUE_APP_MONITRO_ADMIN
|
||||
};
|
||||
},
|
||||
mounted: function() {
|
||||
setTimeout(() => {
|
||||
this.loading = false;
|
||||
}, 230);
|
||||
const that = this;
|
||||
window.onresize = function temp() {
|
||||
that.height = document.documentElement.clientHeight - 94.5 + "px;";
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -177,13 +177,9 @@
|
|||
|
||||
<script>
|
||||
import { listNotice, getNotice, delNotice, addNotice, updateNotice } from "@/api/system/notice";
|
||||
import Editor from '@/components/Editor';
|
||||
|
||||
export default {
|
||||
name: "Notice",
|
||||
components: {
|
||||
Editor
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" v-show="showSearch" :inline="true">
|
||||
<el-form-item label="用户名称" prop="userName">
|
||||
<el-input
|
||||
v-model="queryParams.userName"
|
||||
placeholder="请输入用户名称"
|
||||
clearable
|
||||
size="small"
|
||||
style="width: 240px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="phonenumber">
|
||||
<el-input
|
||||
v-model="queryParams.phonenumber"
|
||||
placeholder="请输入手机号码"
|
||||
clearable
|
||||
size="small"
|
||||
style="width: 240px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
@click="openSelectUser"
|
||||
v-hasPermi="['system:role:add']"
|
||||
>添加用户</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="el-icon-circle-close"
|
||||
size="mini"
|
||||
:disabled="multiple"
|
||||
@click="cancelAuthUserAll"
|
||||
v-hasPermi="['system:role:remove']"
|
||||
>批量取消授权</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="el-icon-close"
|
||||
size="mini"
|
||||
@click="handleClose"
|
||||
>关闭</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="用户名称" prop="userName" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="用户昵称" prop="nickName" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="邮箱" prop="email" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="手机" prop="phonenumber" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="statusOptions" :value="scope.row.status"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-circle-close"
|
||||
@click="cancelAuthUser(scope.row)"
|
||||
v-hasPermi="['system:role:remove']"
|
||||
>取消授权</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
<select-user ref="select" :roleId="queryParams.roleId" @ok="handleQuery" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { allocatedUserList, authUserCancel, authUserCancelAll } from "@/api/system/role";
|
||||
import selectUser from "./selectUser";
|
||||
|
||||
export default {
|
||||
name: "AuthUser",
|
||||
components: { selectUser },
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 选中用户组
|
||||
userIds: [],
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 用户表格数据
|
||||
userList: [],
|
||||
// 状态数据字典
|
||||
statusOptions: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
roleId: undefined,
|
||||
userName: undefined,
|
||||
phonenumber: undefined
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
const roleId = this.$route.params && this.$route.params.roleId;
|
||||
if (roleId) {
|
||||
this.queryParams.roleId = roleId;
|
||||
this.getList();
|
||||
this.getDicts("sys_normal_disable").then(response => {
|
||||
this.statusOptions = response.data;
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 查询授权用户列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
allocatedUserList(this.queryParams).then(response => {
|
||||
this.userList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
}
|
||||
);
|
||||
},
|
||||
// 返回按钮
|
||||
handleClose() {
|
||||
this.$store.dispatch("tagsView/delView", this.$route);
|
||||
this.$router.push({ path: "/system/role" });
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
// 多选框选中数据
|
||||
handleSelectionChange(selection) {
|
||||
this.userIds = selection.map(item => item.userId)
|
||||
this.multiple = !selection.length
|
||||
},
|
||||
/** 打开授权用户表弹窗 */
|
||||
openSelectUser() {
|
||||
this.$refs.select.show();
|
||||
},
|
||||
/** 取消授权按钮操作 */
|
||||
cancelAuthUser(row) {
|
||||
const roleId = this.queryParams.roleId;
|
||||
this.$confirm('确认要取消该用户"' + row.userName + '"角色吗?', "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(function() {
|
||||
return authUserCancel({ userId: row.userId, roleId: roleId });
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.msgSuccess("取消授权成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
/** 批量取消授权按钮操作 */
|
||||
cancelAuthUserAll(row) {
|
||||
const roleId = this.queryParams.roleId;
|
||||
const userIds = this.userIds.join(",");
|
||||
this.$confirm('是否取消选中用户授权数据项?', "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
return authUserCancelAll({ roleId: roleId, userIds: userIds });
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.msgSuccess("取消授权成功");
|
||||
}).catch(() => {});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -124,7 +124,7 @@
|
|||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template slot-scope="scope" v-if="scope.row.roleId !== 1">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
|
@ -132,13 +132,6 @@
|
|||
@click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['system:role:edit']"
|
||||
>修改</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-circle-check"
|
||||
@click="handleDataScope(scope.row)"
|
||||
v-hasPermi="['system:role:edit']"
|
||||
>数据权限</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
|
@ -146,6 +139,17 @@
|
|||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['system:role:remove']"
|
||||
>删除</el-button>
|
||||
<el-dropdown size="mini" @command="(command) => handleCommand(command, scope.row)">
|
||||
<span class="el-dropdown-link">
|
||||
<i class="el-icon-d-arrow-right el-icon--right"></i>更多
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="handleDataScope" icon="el-icon-circle-check"
|
||||
v-hasPermi="['system:role:edit']">数据权限</el-dropdown-item>
|
||||
<el-dropdown-item command="handleAuthUser" icon="el-icon-user"
|
||||
v-hasPermi="['system:role:edit']">分配用户</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
@ -469,6 +473,19 @@ export default {
|
|||
this.single = selection.length!=1
|
||||
this.multiple = !selection.length
|
||||
},
|
||||
// 更多操作触发
|
||||
handleCommand(command, row) {
|
||||
switch (command) {
|
||||
case "handleDataScope":
|
||||
this.handleDataScope(row);
|
||||
break;
|
||||
case "handleAuthUser":
|
||||
this.handleAuthUser(row);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
// 树权限(展开/折叠)
|
||||
handleCheckedTreeExpand(value, type) {
|
||||
if (type == 'menu') {
|
||||
|
@ -548,6 +565,11 @@ export default {
|
|||
this.title = "分配数据权限";
|
||||
});
|
||||
},
|
||||
/** 分配用户操作 */
|
||||
handleAuthUser: function(row) {
|
||||
const roleId = row.roleId;
|
||||
this.$router.push("/auth/user/" + roleId);
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm: function() {
|
||||
this.$refs["form"].validate(valid => {
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
<template>
|
||||
<!-- 授权用户 -->
|
||||
<el-dialog title="选择用户" :visible.sync="visible" width="800px" top="5vh" append-to-body>
|
||||
<el-form :model="queryParams" ref="queryForm" :inline="true">
|
||||
<el-form-item label="用户名称" prop="userName">
|
||||
<el-input
|
||||
v-model="queryParams.userName"
|
||||
placeholder="请输入用户名称"
|
||||
clearable
|
||||
size="small"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="phonenumber">
|
||||
<el-input
|
||||
v-model="queryParams.phonenumber"
|
||||
placeholder="请输入手机号码"
|
||||
clearable
|
||||
size="small"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-row>
|
||||
<el-table @row-click="clickRow" ref="table" :data="userList" @selection-change="handleSelectionChange" height="260px">
|
||||
<el-table-column type="selection" width="55"></el-table-column>
|
||||
<el-table-column label="用户名称" prop="userName" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="用户昵称" prop="nickName" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="邮箱" prop="email" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="手机" prop="phonenumber" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="statusOptions" :value="scope.row.status"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</el-row>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="handleSelectUser">确 定</el-button>
|
||||
<el-button @click="visible = false">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { unallocatedUserList, authUserSelectAll } from "@/api/system/role";
|
||||
export default {
|
||||
props: {
|
||||
// 角色编号
|
||||
roleId: {
|
||||
type: Number
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
visible: false,
|
||||
// 选中数组值
|
||||
userIds: [],
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 未授权用户数据
|
||||
userList: [],
|
||||
// 状态数据字典
|
||||
statusOptions: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
roleId: undefined,
|
||||
userName: undefined,
|
||||
phonenumber: undefined
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getDicts("sys_normal_disable").then(response => {
|
||||
this.statusOptions = response.data;
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
// 显示弹框
|
||||
show() {
|
||||
this.queryParams.roleId = this.roleId;
|
||||
this.getList();
|
||||
this.visible = true;
|
||||
},
|
||||
clickRow(row) {
|
||||
this.$refs.table.toggleRowSelection(row);
|
||||
},
|
||||
// 多选框选中数据
|
||||
handleSelectionChange(selection) {
|
||||
this.userIds = selection.map(item => item.userId);
|
||||
},
|
||||
// 查询表数据
|
||||
getList() {
|
||||
unallocatedUserList(this.queryParams).then(res => {
|
||||
this.userList = res.rows;
|
||||
this.total = res.total;
|
||||
});
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 选择授权用户操作 */
|
||||
handleSelectUser() {
|
||||
const roleId = this.queryParams.roleId;
|
||||
const userIds = this.userIds.join(",");
|
||||
authUserSelectAll({ roleId: roleId, userIds: userIds }).then(res => {
|
||||
this.msgSuccess(res.msg);
|
||||
if (res.code === 200) {
|
||||
this.visible = false;
|
||||
this.$emit("ok");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,117 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<h4 class="form-header h4">基本信息</h4>
|
||||
<el-form ref="form" :model="form" label-width="80px">
|
||||
<el-row>
|
||||
<el-col :span="8" :offset="2">
|
||||
<el-form-item label="用户昵称" prop="nickName">
|
||||
<el-input v-model="form.nickName" disabled />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" :offset="2">
|
||||
<el-form-item label="登录账号" prop="phonenumber">
|
||||
<el-input v-model="form.userName" disabled />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<h4 class="form-header h4">角色信息</h4>
|
||||
<el-table v-loading="loading" :row-key="getRowKey" @row-click="clickRow" ref="table" @selection-change="handleSelectionChange" :data="roles.slice((pageNum-1)*pageSize,pageNum*pageSize)">
|
||||
<el-table-column label="序号" type="index" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span>{{(pageNum - 1) * pageSize + scope.$index + 1}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column type="selection" :reserve-selection="true" width="55"></el-table-column>
|
||||
<el-table-column label="角色编号" align="center" prop="roleId" />
|
||||
<el-table-column label="角色名称" align="center" prop="roleName" />
|
||||
<el-table-column label="权限字符" align="center" prop="roleKey" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="pageNum" :limit.sync="pageSize" />
|
||||
|
||||
<el-form label-width="100px">
|
||||
<el-form-item style="text-align: center;margin-left:-120px;margin-top:30px;">
|
||||
<el-button type="primary" @click="submitForm()">提交</el-button>
|
||||
<el-button @click="close()">返回</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getAuthRole, updateAuthRole } from "@/api/system/user";
|
||||
|
||||
export default {
|
||||
name: "AuthRole",
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 分页信息
|
||||
total: 0,
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
// 选中角色编号
|
||||
roleIds:[],
|
||||
// 角色信息
|
||||
roles: [],
|
||||
// 用户信息
|
||||
form: {}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
const userId = this.$route.params && this.$route.params.userId;
|
||||
if (userId) {
|
||||
this.loading = true;
|
||||
getAuthRole(userId).then((response) => {
|
||||
this.form = response.data.user;
|
||||
this.roles = response.data.roles;
|
||||
this.total = this.roles.length;
|
||||
this.$nextTick(() => {
|
||||
this.roles.forEach((row) => {
|
||||
if (row.flag) {
|
||||
this.$refs.table.toggleRowSelection(row);
|
||||
}
|
||||
});
|
||||
});
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 单击选中行数据 */
|
||||
clickRow(row) {
|
||||
this.$refs.table.toggleRowSelection(row);
|
||||
},
|
||||
// 多选框选中数据
|
||||
handleSelectionChange(selection) {
|
||||
this.roleIds = selection.map((item) => item.roleId);
|
||||
},
|
||||
// 保存选中的数据编号
|
||||
getRowKey(row) {
|
||||
return row.roleId;
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
const userId = this.form.userId;
|
||||
const roleIds = this.roleIds.join(",");
|
||||
updateAuthRole({ userId: userId, roleIds: roleIds }).then((response) => {
|
||||
this.msgSuccess("授权成功");
|
||||
this.close();
|
||||
});
|
||||
},
|
||||
/** 关闭按钮 */
|
||||
close() {
|
||||
this.$store.dispatch("tagsView/delView", this.$route);
|
||||
this.$router.push({ path: "/system/user" });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -167,7 +167,7 @@
|
|||
width="160"
|
||||
class-name="small-padding fixed-width"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<template slot-scope="scope" v-if="scope.row.userId !== 1">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
|
@ -176,20 +176,23 @@
|
|||
v-hasPermi="['system:user:edit']"
|
||||
>修改</el-button>
|
||||
<el-button
|
||||
v-if="scope.row.userId !== 1"
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['system:user:remove']"
|
||||
>删除</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-key"
|
||||
@click="handleResetPwd(scope.row)"
|
||||
v-hasPermi="['system:user:resetPwd']"
|
||||
>重置</el-button>
|
||||
<el-dropdown size="mini" @command="(command) => handleCommand(command, scope.row)">
|
||||
<span class="el-dropdown-link">
|
||||
<i class="el-icon-d-arrow-right el-icon--right"></i>更多
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="handleResetPwd" icon="el-icon-key"
|
||||
v-hasPermi="['system:user:resetPwd']">重置密码</el-dropdown-item>
|
||||
<el-dropdown-item command="handleAuthRole" icon="el-icon-circle-check"
|
||||
v-hasPermi="['system:user:edit']">分配角色</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
@ -561,6 +564,19 @@ export default {
|
|||
this.single = selection.length != 1;
|
||||
this.multiple = !selection.length;
|
||||
},
|
||||
// 更多操作触发
|
||||
handleCommand(command, row) {
|
||||
switch (command) {
|
||||
case "handleResetPwd":
|
||||
this.handleResetPwd(row);
|
||||
break;
|
||||
case "handleAuthRole":
|
||||
this.handleAuthRole(row);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset();
|
||||
|
@ -603,6 +619,11 @@ export default {
|
|||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
/** 分配角色操作 */
|
||||
handleAuthRole: function(row) {
|
||||
const userId = row.userId;
|
||||
this.$router.push("/auth/role/" + userId);
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm: function() {
|
||||
this.$refs["form"].validate(valid => {
|
||||
|
|
|
@ -109,7 +109,7 @@ module.exports = {
|
|||
config.optimization.runtimeChunk('single'),
|
||||
{
|
||||
from: path.resolve(__dirname, './public/robots.txt'), //防爬虫文件
|
||||
to: './', //到根目录下
|
||||
to: './' //到根目录下
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -159,7 +159,7 @@ create table sys_menu (
|
|||
insert into sys_menu values('1', '系统管理', '0', '1', 'system', null, 1, 0, 'M', '0', '0', '', 'system', 'admin', sysdate(), '', null, '系统管理目录');
|
||||
insert into sys_menu values('2', '系统监控', '0', '2', 'monitor', null, 1, 0, 'M', '0', '0', '', 'monitor', 'admin', sysdate(), '', null, '系统监控目录');
|
||||
insert into sys_menu values('3', '系统工具', '0', '3', 'tool', null, 1, 0, 'M', '0', '0', '', 'tool', 'admin', sysdate(), '', null, '系统工具目录');
|
||||
insert into sys_menu values('4', '若依官网', '0', '4', 'http://ruoyi.vip', null , 0, 0, 'M', '0', '0', '', 'guide', 'admin', sysdate(), '', null, '若依官网地址');
|
||||
insert into sys_menu values('4', 'PLUS官网', '0', '4', 'https://gitee.com/JavaLionLi/RuoYi-Vue-Plus', null , 0, 0, 'M', '0', '0', '', 'guide', 'admin', sysdate(), '', null, 'RuoYi-Vue-Plus官网地址');
|
||||
-- 二级菜单
|
||||
insert into sys_menu values('100', '用户管理', '1', '1', 'user', 'system/user/index', 1, 0, 'C', '0', '0', 'system:user:list', 'user', 'admin', sysdate(), '', null, '用户管理菜单');
|
||||
insert into sys_menu values('101', '角色管理', '1', '2', 'role', 'system/role/index', 1, 0, 'C', '0', '0', 'system:role:list', 'peoples', 'admin', sysdate(), '', null, '角色管理菜单');
|
||||
|
|
Loading…
Reference in New Issue