78 Commits

Author SHA1 Message Date
疯狂的狮子Li
ed43f0a889 😴发布 4.8.2 正式进入维护状态 2023-11-27 11:38:39 +08:00
疯狂的狮子Li
a7daed30d8 fix 修复 OssFactory 并发多创建实例问题 2023-11-24 09:59:26 +08:00
疯狂的狮子Li
f2598e7961 update springboot 2.7.17 => 2.7.18 升级到2.X最终版本(官方停更) 2023-11-24 09:31:03 +08:00
疯狂的狮子Li
4c848cf4cb update 优化 页面关于权限标识符说明 2023-11-23 16:54:09 +08:00
疯狂的狮子Li
4d1bf7c25c !450 demo模块前端的form字段有误
Merge pull request !450 from dhb52/4.X
2023-11-20 08:42:55 +00:00
dhb52
88bd127b67 fix: demo的form字段有误 2023-11-20 16:39:19 +08:00
疯狂的狮子Li
1186b86f73 fix 修复 延迟队列在投递消息未到达时间的时候 服务死机导致重启收不到消息 2023-11-14 17:03:53 +08:00
疯狂的狮子Li
eb946e029d fix 修复 数据权限优化后 update delete 报null问题 2023-11-13 10:43:01 +08:00
疯狂的狮子Li
9b68b27168 fix 修复 五级路由缓存无效问题 2023-11-10 16:35:58 +08:00
疯狂的狮子Li
5dbb255323 fix 修复 oss服务无法连接 导致业务异常问题 查询不应该影响业务 2023-11-10 16:34:52 +08:00
疯狂的狮子Li
cc326e36cc fix 修复 内链iframe没有传递参数问题 2023-11-10 11:40:27 +08:00
疯狂的狮子Li
bacdd8d685 update 优化 数据权限拦截器优先判断方法是否有效 提高性能减少无用sql解析 2023-11-09 18:40:32 +08:00
疯狂的狮子Li
8ce96690fc update 优化 部门数据权限使用默认兜底方案 2023-11-09 12:25:48 +08:00
疯狂的狮子Li
9d0d52a48c update 优化 更改默认日志等级为info 避免日志过多(按需开启debug) 2023-11-08 16:05:50 +08:00
疯狂的狮子Li
8d0c06b5bf fix 修复 外链带端口出现的异常 2023-11-07 13:00:51 +08:00
疯狂的狮子Li
245ade0d79 update 优化 补全代码生成 columnList 接口参数注解缺失 2023-11-06 09:39:26 +08:00
疯狂的狮子Li
8de02e285e fix 修复 普通角色编辑使用内置管理员code越权问题 2023-11-01 16:41:32 +08:00
疯狂的狮子Li
0005710f45 update 优化 操作日志 部门信息完善 vue3页面 2023-10-28 15:53:47 +08:00
疯狂的狮子Li
88e09f2189 update 优化 操作日志 部门信息完善 vue3页面 2023-10-28 15:48:53 +08:00
疯狂的狮子Li
c7e79d2681 update 优化 AddressUtils 兼容linux系统本地ip 2023-10-28 15:41:24 +08:00
柏竹
86223d5a6b !435 操作日志 部门信息完善
* fix 操作日志部门信息完善 , 前端UI优化
2023-10-28 07:33:45 +00:00
疯狂的狮子Li
ce04052da3 fix 修复 代码生成 是否必填与数据库不匹配问题 2023-10-27 17:30:40 +08:00
疯狂的狮子Li
f91f225ba6 update mybatis-plus 3.5.3.2 => 3.5.4 2023-10-27 11:46:35 +08:00
疯狂的狮子Li
2cfdb9bcda fix 降级 poi 版本导致的依赖冲突 2023-10-20 12:09:16 +08:00
疯狂的狮子Li
a06f3255d0 update springboot 2.7.14 => 2.7.17
update poi 5.2.3 => 5.2.4
update satoken 1.36.0 => 1.37.0
update aws-java-sdk-s3 1.12.400 => 1.12.540
2023-10-20 10:19:38 +08:00
疯狂的狮子Li
2affe91837 update 优化 数据权限 减少二次校验查询 2023-10-13 11:25:46 +08:00
疯狂的狮子Li
7544ae318c fix 修复 漏改代码 2023-10-09 12:30:09 +08:00
疯狂的狮子Li
e59f04bfee update 优化 vue3 版本用户初始密码从字典查询 2023-10-09 11:19:11 +08:00
疯狂的狮子Li
e35df6d1d2 update 优化 富文本Editor组件检验图片格式 2023-10-09 10:39:59 +08:00
疯狂的狮子Li
81dcd8b512 fix 修复 HeaderSearch组件跳转query参数丢失问题 2023-10-09 10:39:59 +08:00
疯狂的狮子Li
8ca601dc66 update vue-quill 1.1.0 => 1.2.0 2023-10-09 10:39:59 +08:00
疯狂的狮子Li
5ff7203929 update 优化 操作日志列表新增IP地址查询 2023-10-09 10:39:59 +08:00
疯狂的狮子Li
a6ef8bb938 update 优化 全局数据存储用户编号 2023-10-09 10:39:59 +08:00
疯狂的狮子Li
79fd63599a update 优化 菜单管理类型为按钮状态可选 2023-10-09 10:39:59 +08:00
疯狂的狮子Li
3c89a83d6b fix 修复树结构代码生成新增方法赋值错误
Signed-off-by: 疯狂的狮子Li <15040126243@163.com>
2023-09-30 14:36:17 +00:00
疯狂的狮子Li
d45ae7ffc5 !426 fix 修复树结构代码生成新增方法赋值错误
Merge pull request !426 from 这夏天依然平凡/4.X
2023-09-30 14:31:01 +00:00
这夏天依然平凡
34babcbfa5 fix 修复树结构代码生成新增方法赋值错误
Signed-off-by: 这夏天依然平凡 <1822213252@qq.com>
2023-09-29 09:10:57 +00:00
疯狂的狮子Li
e5661a2cf4 Merge remote-tracking branch 'origin/dev' into 4.X 2023-09-25 10:05:44 +08:00
疯狂的狮子Li
cc40f9c4ac 🎃 发布 4.8.1 稳定性增强 修复部分bug 2023-09-25 10:03:11 +08:00
疯狂的狮子Li
6e38a9e412 update springboot 2.7.15 => 2.7.16
update springboot-admin 2.7.10 => 2.7.11
update satoken 1.35.0.RC => 1.36.0
update lombok 1.18.26 =. 1.18.30
2023-09-25 10:03:11 +08:00
疯狂的狮子Li
481170922e !423 fix 修复重置密码注释参数中文解释错误
Merge pull request !423 from 这夏天依然平凡/dev
2023-09-23 04:33:29 +00:00
这夏天依然平凡
f46b8d0758 fix 修复重置密码注释参数中文解释错误
Signed-off-by: 这夏天依然平凡 <1822213252@qq.com>
2023-09-23 03:56:31 +00:00
疯狂的狮子Li
1d43448423 !421 typo: getTokenActivityTimeout => getTokenActiveTimeout
Merge pull request !421 from dhb52/dev
2023-09-20 14:48:04 +00:00
dhb52
2de97fe45c typo: getTokenActivityTimeout => getTokenActiveTimeout
Signed-off-by: dhb52 <dhb52@126.com>
2023-09-20 14:46:58 +00:00
这夏天依然平凡
c41688831c !419 解决代码生成后 定义 'serialVersionUID' 字段的不可序列化类
* update 补全代码生成vo类序列号导包
* update 优化代码生成vo类的序列化
2023-09-19 02:38:20 +00:00
疯狂的狮子Li
2a36ce4302 fix 修复 自定义字典样式不生效的问题 2023-09-14 17:26:33 +08:00
疯狂的狮子Li
f2200df15a update 优化 !pr417 解决部分问题 支持vue3版本 2023-09-08 17:41:14 +08:00
疯狂的狮子Li
3b498c4939 !417 优化字典标签支持传分隔符分隔的字符串和数组,优化渲染效果
Merge pull request !417 from 抓蛙师/dev
2023-09-08 07:28:18 +00:00
抓蛙师
2cead2c5fd 优化字典标签支持传分隔符分隔的字符串和数组,优化渲染效果 2023-09-08 12:03:42 +08:00
疯狂的狮子Li
23bf840684 update 优化 控制台debuger位置错误问题
update 优化 TopNav 菜单样式
fix 修复 布局配置失效问题
2023-09-01 11:41:12 +08:00
疯狂的狮子Li
f8d551e226 fix 修复 新建用户可能会存在的越权行为 2023-08-30 21:49:19 +08:00
疯狂的狮子Li
2e3b068341 update springboot 2.7.14 => 2.7.15 2023-08-25 12:30:34 +08:00
疯狂的狮子Li
0d9b1c2e82 remove 删除无用配置 2023-08-24 19:46:45 +08:00
疯狂的狮子Li
148c7c588e fix 修复 字典缓存删除方法参数错误问题 2023-08-23 22:21:03 +08:00
疯狂的狮子Li
3a82fb82e8 fix 修复 修复树模板父级编码变量错误 2023-08-19 22:59:27 +08:00
疯狂的狮子Li
c319579c0b fix 修复 有界队列与优先队列 错误使用问题 2023-08-19 22:59:27 +08:00
疯狂的狮子Li
acfa39369e fix 修复 升级 mp 版本导致的问题 2023-08-15 10:57:36 +08:00
疯狂的狮子Li
f71b8110a9 update springboot 2.7.13 => 2.7.14
update mybatis-plus 3.5.3.1 => 3.5.3.2
update easyexcel 3.3.1 => 3.3.2
update hutool 5.8.18 => 5.8.20
2023-08-09 12:47:01 +08:00
疯狂的狮子Li
8311454342 update 优化 全局异常处理器 业务异常不输出具体堆栈信息 减少无用日志存储 2023-08-09 12:37:00 +08:00
疯狂的狮子Li
ed4df16201 update 优化 用户管理 只查询未禁用的部门角色岗位数据 2023-08-07 18:14:11 +08:00
疯狂的狮子Li
2f3dbbfe32 update 优化 岗位如果绑定了用户则不允许禁用 2023-08-07 18:14:03 +08:00
疯狂的狮子Li
dc752ade25 update 优化 部门与角色如果绑定了用户则不允许禁用 2023-08-07 18:13:49 +08:00
疯狂的狮子Li
ebef89be62 update 优化 加密实现 使用 EncryptUtils 统一处理 2023-08-07 12:40:16 +08:00
疯狂的狮子Li
7d209aeb41 update 优化 excel导出字典转下拉框 无需标记index自动处理(感谢一夏coco) 2023-08-01 13:22:00 +08:00
疯狂的狮子Li
e82a031dd0 fix 修复 vue3 版本注册页验证码不显示问题 2023-08-01 11:00:36 +08:00
疯狂的狮子Li
fea9fdaa5f update 优化 excel 导出字典默认转为下拉框 2023-07-28 20:09:03 +08:00
疯狂的狮子Li
952cd0b4d3 update 删除一些跟swagger有关的字眼 避免误解 2023-07-25 12:59:27 +08:00
疯狂的狮子Li
ec6704181f update 删除一些跟swagger有关的字眼 避免误解 2023-07-25 12:58:42 +08:00
疯狂的狮子Li
3389a11933 !396 改了一个字, 错别字看到了总想改了但是又怕改的多了后面同步冲突
Merge pull request !396 from saint/pr
2023-07-17 06:44:46 +00:00
疯狂的狮子Li
db0c882f1b update 优化 角色权限支持仅本人权限查看 解决无法查看自己创建的角色问题 2023-07-17 14:41:28 +08:00
赵志辉
13d468d8cc update 优化 RedisCacheController 注释错误 2023-07-17 14:40:02 +08:00
疯狂的狮子Li
b6daa886e0 Merge remote-tracking branch 'origin/dev' into dev 2023-07-13 19:32:50 +08:00
疯狂的狮子Li
1d10611172 update 优化 xxljob 端口随着主应用端口飘逸 避免集群冲突 2023-07-13 19:32:39 +08:00
疯狂的狮子Li
8a5cc7f76c update 优化 powerjob 端口随着主应用端口飘逸 避免集群冲突 2023-07-13 19:30:54 +08:00
疯狂的狮子Li
d4ab8a8e22 fix 修复 加密模块数据转换异常问题 2023-07-13 19:12:51 +08:00
疯狂的狮子Li
8a86c5bdc5 remove 删除无用注释 2023-07-11 18:13:28 +08:00
疯狂的狮子Li
e9cc7c3058 fix 修复 动态设置 token 有效期不生效问题 2023-07-11 17:38:20 +08:00
疯狂的狮子Li
f7dd7d1e42 fix 修复 token 过期登出无法清理在线用户问题 2023-07-11 17:25:23 +08:00
109 changed files with 661 additions and 420 deletions

View File

@@ -2,7 +2,7 @@
<configuration default="false" name="ruoyi-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker"> <configuration default="false" name="ruoyi-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile"> <deployment type="dockerfile">
<settings> <settings>
<option name="imageTag" value="ruoyi/ruoyi-monitor-admin:4.8.0" /> <option name="imageTag" value="ruoyi/ruoyi-monitor-admin:4.8.2" />
<option name="buildOnly" value="true" /> <option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" /> <option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" />
</settings> </settings>

View File

@@ -2,7 +2,7 @@
<configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker"> <configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile"> <deployment type="dockerfile">
<settings> <settings>
<option name="imageTag" value="ruoyi/ruoyi-server:4.8.0" /> <option name="imageTag" value="ruoyi/ruoyi-server:4.8.2" />
<option name="buildOnly" value="true" /> <option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-admin/Dockerfile" /> <option name="sourceFilePath" value="ruoyi-admin/Dockerfile" />
</settings> </settings>

View File

@@ -2,7 +2,7 @@
<configuration default="false" name="ruoyi-xxl-job-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker"> <configuration default="false" name="ruoyi-xxl-job-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile"> <deployment type="dockerfile">
<settings> <settings>
<option name="imageTag" value="ruoyi/ruoyi-xxl-job-admin:4.8.0" /> <option name="imageTag" value="ruoyi/ruoyi-xxl-job-admin:4.8.2" />
<option name="buildOnly" value="true" /> <option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-xxl-job-admin/Dockerfile" /> <option name="sourceFilePath" value="ruoyi-extend/ruoyi-xxl-job-admin/Dockerfile" />
</settings> </settings>

View File

@@ -3,6 +3,14 @@
- - - - - -
## 版本状态说明
由于 springboot 2.X 与 vue 2.X 官方均宣布停止维护, 故而 框架 4.X 版本 进入维护状态(只处理问题不更新功能)
停止维护时间预计: 2024年6-10月具体根据使用人数动态决定, 此版本已经相当稳定 即便不更新功能也不影响使用
如果依旧选择使用 jdk8 或者 jdk11 可以放心使用此版本, 如果希望使用 jdk17 或者 jdk21 可以选择使用 5.X 分支
## 平台简介 ## 平台简介
[![码云Gitee](https://gitee.com/dromara/RuoYi-Vue-Plus/badge/star.svg?theme=blue)](https://gitee.com/dromara/RuoYi-Vue-Plus) [![码云Gitee](https://gitee.com/dromara/RuoYi-Vue-Plus/badge/star.svg?theme=blue)](https://gitee.com/dromara/RuoYi-Vue-Plus)
@@ -10,7 +18,7 @@
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/master/LICENSE) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/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) [![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
<br> <br>
[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-4.8.0-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus) [![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-4.8.2-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus)
[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-2.7-blue.svg)]() [![Spring Boot](https://img.shields.io/badge/Spring%20Boot-2.7-blue.svg)]()
[![JDK-8+](https://img.shields.io/badge/JDK-8-green.svg)]() [![JDK-8+](https://img.shields.io/badge/JDK-8-green.svg)]()
[![JDK-11](https://img.shields.io/badge/JDK-11-green.svg)]() [![JDK-11](https://img.shields.io/badge/JDK-11-green.svg)]()

34
pom.xml
View File

@@ -6,15 +6,15 @@
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-vue-plus</artifactId> <artifactId>ruoyi-vue-plus</artifactId>
<version>4.8.0</version> <version>4.8.2</version>
<name>RuoYi-Vue-Plus</name> <name>RuoYi-Vue-Plus</name>
<url>https://gitee.com/dromara/RuoYi-Vue-Plus</url> <url>https://gitee.com/dromara/RuoYi-Vue-Plus</url>
<description>RuoYi-Vue-Plus后台管理系统</description> <description>RuoYi-Vue-Plus后台管理系统</description>
<properties> <properties>
<ruoyi-vue-plus.version>4.8.0</ruoyi-vue-plus.version> <ruoyi-vue-plus.version>4.8.2</ruoyi-vue-plus.version>
<spring-boot.version>2.7.13</spring-boot.version> <spring-boot.version>2.7.18</spring-boot.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version> <java.version>1.8</java.version>
@@ -22,29 +22,26 @@
<spring-boot.mybatis>2.2.2</spring-boot.mybatis> <spring-boot.mybatis>2.2.2</spring-boot.mybatis>
<springdoc.version>1.6.15</springdoc.version> <springdoc.version>1.6.15</springdoc.version>
<poi.version>5.2.3</poi.version> <poi.version>5.2.3</poi.version>
<easyexcel.version>3.3.1</easyexcel.version> <easyexcel.version>3.3.2</easyexcel.version>
<velocity.version>2.3</velocity.version> <velocity.version>2.3</velocity.version>
<satoken.version>1.35.0.RC</satoken.version> <satoken.version>1.37.0</satoken.version>
<mybatis-plus.version>3.5.3.1</mybatis-plus.version> <mybatis-plus.version>3.5.4</mybatis-plus.version>
<p6spy.version>3.9.1</p6spy.version> <p6spy.version>3.9.1</p6spy.version>
<hutool.version>5.8.18</hutool.version> <hutool.version>5.8.22</hutool.version>
<okhttp.version>4.10.0</okhttp.version> <okhttp.version>4.10.0</okhttp.version>
<spring-boot-admin.version>2.7.10</spring-boot-admin.version> <spring-boot-admin.version>2.7.11</spring-boot-admin.version>
<redisson.version>3.20.1</redisson.version> <redisson.version>3.20.1</redisson.version>
<lock4j.version>2.2.3</lock4j.version> <lock4j.version>2.2.3</lock4j.version>
<dynamic-ds.version>3.5.2</dynamic-ds.version> <dynamic-ds.version>3.5.2</dynamic-ds.version>
<alibaba-ttl.version>2.14.2</alibaba-ttl.version> <alibaba-ttl.version>2.14.2</alibaba-ttl.version>
<xxl-job.version>2.4.0</xxl-job.version> <xxl-job.version>2.4.0</xxl-job.version>
<lombok.version>1.18.26</lombok.version> <lombok.version>1.18.30</lombok.version>
<bouncycastle.version>1.72</bouncycastle.version> <bouncycastle.version>1.72</bouncycastle.version>
<!-- 离线IP地址定位库 --> <!-- 离线IP地址定位库 -->
<ip2region.version>2.7.0</ip2region.version> <ip2region.version>2.7.0</ip2region.version>
<!-- 临时修复 snakeyaml 漏洞 -->
<snakeyaml.version>1.33</snakeyaml.version>
<!-- OSS 配置 --> <!-- OSS 配置 -->
<aws-java-sdk-s3.version>1.12.400</aws-java-sdk-s3.version> <aws-java-sdk-s3.version>1.12.540</aws-java-sdk-s3.version>
<!-- SMS 配置 --> <!-- SMS 配置 -->
<sms4j.version>2.2.0</sms4j.version> <sms4j.version>2.2.0</sms4j.version>
</properties> </properties>
@@ -55,7 +52,7 @@
<properties> <properties>
<!-- 环境标识,需要与配置文件的名称相对应 --> <!-- 环境标识,需要与配置文件的名称相对应 -->
<profiles.active>local</profiles.active> <profiles.active>local</profiles.active>
<logging.level>debug</logging.level> <logging.level>info</logging.level>
</properties> </properties>
</profile> </profile>
<profile> <profile>
@@ -63,7 +60,7 @@
<properties> <properties>
<!-- 环境标识,需要与配置文件的名称相对应 --> <!-- 环境标识,需要与配置文件的名称相对应 -->
<profiles.active>dev</profiles.active> <profiles.active>dev</profiles.active>
<logging.level>debug</logging.level> <logging.level>info</logging.level>
</properties> </properties>
<activation> <activation>
<!-- 默认环境 --> <!-- 默认环境 -->
@@ -261,13 +258,6 @@
<version>${ip2region.version}</version> <version>${ip2region.version}</version>
</dependency> </dependency>
<!-- 临时修复 snakeyaml 漏洞 -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>${snakeyaml.version}</version>
</dependency>
<!-- 加密包引入 --> <!-- 加密包引入 -->
<dependency> <dependency>
<groupId>org.bouncycastle</groupId> <groupId>org.bouncycastle</groupId>

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>ruoyi-vue-plus</artifactId> <artifactId>ruoyi-vue-plus</artifactId>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<version>4.8.0</version> <version>4.8.2</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging> <packaging>jar</packaging>

View File

@@ -91,9 +91,12 @@ public class SysDeptController extends BaseController {
return R.fail("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在"); return R.fail("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在");
} else if (dept.getParentId().equals(deptId)) { } else if (dept.getParentId().equals(deptId)) {
return R.fail("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己"); return R.fail("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己");
} else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus()) } else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus())) {
&& deptService.selectNormalChildrenDeptById(deptId) > 0) { if (deptService.selectNormalChildrenDeptById(deptId) > 0) {
return R.fail("该部门包含未停用的子部门"); return R.fail("该部门包含未停用的子部门!");
} else if (deptService.checkDeptExistUser(deptId)) {
return R.fail("该部门下存在已分配用户,不能禁用!");
}
} }
return toAjax(deptService.updateDept(dept)); return toAjax(deptService.updateDept(dept));
} }

View File

@@ -88,6 +88,9 @@ public class SysPostController extends BaseController {
return R.fail("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在"); return R.fail("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在");
} else if (!postService.checkPostCodeUnique(post)) { } else if (!postService.checkPostCodeUnique(post)) {
return R.fail("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在"); return R.fail("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在");
} else if (UserConstants.POST_DISABLE.equals(post.getStatus())
&& postService.countUserPostById(post.getPostId()) > 0) {
return R.fail("该岗位下存在已分配用户,不能禁用!");
} }
return toAjax(postService.updatePost(post)); return toAjax(postService.updatePost(post));
} }
@@ -109,7 +112,9 @@ public class SysPostController extends BaseController {
*/ */
@GetMapping("/optionselect") @GetMapping("/optionselect")
public R<List<SysPost>> optionselect() { public R<List<SysPost>> optionselect() {
List<SysPost> posts = postService.selectPostAll(); SysPost post = new SysPost();
post.setStatus(UserConstants.POST_NORMAL);
List<SysPost> posts = postService.selectPostList(post);
return R.ok(posts); return R.ok(posts);
} }
} }

View File

@@ -78,8 +78,8 @@ public class SysProfileController extends BaseController {
/** /**
* 重置密码 * 重置密码
* *
* @param newPassword 密码 * @param newPassword 密码
* @param oldPassword 密码 * @param oldPassword 密码
*/ */
@Log(title = "个人信息", businessType = BusinessType.UPDATE) @Log(title = "个人信息", businessType = BusinessType.UPDATE)
@PutMapping("/updatePwd") @PutMapping("/updatePwd")

View File

@@ -21,6 +21,7 @@ import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.utils.StreamUtils; import com.ruoyi.common.utils.StreamUtils;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.domain.SysPost;
import com.ruoyi.system.domain.vo.SysUserExportVo; import com.ruoyi.system.domain.vo.SysUserExportVo;
import com.ruoyi.system.domain.vo.SysUserImportVo; import com.ruoyi.system.domain.vo.SysUserImportVo;
import com.ruoyi.system.listener.SysUserImportListener; import com.ruoyi.system.listener.SysUserImportListener;
@@ -117,9 +118,13 @@ public class SysUserController extends BaseController {
public R<Map<String, Object>> getInfo(@PathVariable(value = "userId", required = false) Long userId) { public R<Map<String, Object>> getInfo(@PathVariable(value = "userId", required = false) Long userId) {
userService.checkUserDataScope(userId); userService.checkUserDataScope(userId);
Map<String, Object> ajax = new HashMap<>(); Map<String, Object> ajax = new HashMap<>();
List<SysRole> roles = roleService.selectRoleAll(); SysRole role = new SysRole();
role.setStatus(UserConstants.ROLE_NORMAL);
SysPost post = new SysPost();
post.setStatus(UserConstants.POST_NORMAL);
List<SysRole> roles = roleService.selectRoleList(role);
ajax.put("roles", LoginHelper.isAdmin(userId) ? roles : StreamUtils.filter(roles, r -> !r.isAdmin())); ajax.put("roles", LoginHelper.isAdmin(userId) ? roles : StreamUtils.filter(roles, r -> !r.isAdmin()));
ajax.put("posts", postService.selectPostAll()); ajax.put("posts", postService.selectPostList(post));
if (ObjectUtil.isNotNull(userId)) { if (ObjectUtil.isNotNull(userId)) {
SysUser sysUser = userService.selectUserById(userId); SysUser sysUser = userService.selectUserById(userId);
ajax.put("user", sysUser); ajax.put("user", sysUser);
@@ -136,6 +141,7 @@ public class SysUserController extends BaseController {
@Log(title = "用户管理", businessType = BusinessType.INSERT) @Log(title = "用户管理", businessType = BusinessType.INSERT)
@PostMapping @PostMapping
public R<Void> add(@Validated @RequestBody SysUser user) { public R<Void> add(@Validated @RequestBody SysUser user) {
deptService.checkDeptDataScope(user.getDeptId());
if (!userService.checkUserNameUnique(user)) { if (!userService.checkUserNameUnique(user)) {
return R.fail("新增用户'" + user.getUserName() + "'失败,登录账号已存在"); return R.fail("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
} else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) { } else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
@@ -156,6 +162,7 @@ public class SysUserController extends BaseController {
public R<Void> edit(@Validated @RequestBody SysUser user) { public R<Void> edit(@Validated @RequestBody SysUser user) {
userService.checkUserAllowed(user); userService.checkUserAllowed(user);
userService.checkUserDataScope(user.getUserId()); userService.checkUserDataScope(user.getUserId());
deptService.checkDeptDataScope(user.getDeptId());
if (!userService.checkUserNameUnique(user)) { if (!userService.checkUserNameUnique(user)) {
return R.fail("修改用户'" + user.getUserName() + "'失败,登录账号已存在"); return R.fail("修改用户'" + user.getUserName() + "'失败,登录账号已存在");
} else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) { } else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {

View File

@@ -19,8 +19,8 @@ xxl.job:
executor: executor:
# 执行器AppName执行器心跳注册分组依据为空则关闭自动注册 # 执行器AppName执行器心跳注册分组依据为空则关闭自动注册
appname: xxl-job-executor appname: xxl-job-executor
# 执行器端口号 执行器从9101开始往后写 # 28080 端口 随着主应用端口飘逸 避免集群冲突
port: 9101 port: 2${server.port}
# 执行器注册默认IP:PORT # 执行器注册默认IP:PORT
address: address:
# 执行器IP默认自动获取IP # 执行器IP默认自动获取IP

View File

@@ -22,8 +22,8 @@ xxl.job:
executor: executor:
# 执行器AppName执行器心跳注册分组依据为空则关闭自动注册 # 执行器AppName执行器心跳注册分组依据为空则关闭自动注册
appname: xxl-job-executor appname: xxl-job-executor
# 执行器端口号 执行器从9101开始往后写 # 28080 端口 随着主应用端口飘逸 避免集群冲突
port: 9101 port: 2${server.port}
# 执行器注册默认IP:PORT # 执行器注册默认IP:PORT
address: address:
# 执行器IP默认自动获取IP # 执行器IP默认自动获取IP

View File

@@ -5,11 +5,7 @@ ruoyi:
# 版本 # 版本
version: ${ruoyi-vue-plus.version} version: ${ruoyi-vue-plus.version}
# 版权年份 # 版权年份
copyrightYear: 2022 copyrightYear: 2023
# 实例演示开关
demoEnabled: true
# 获取ip地址开关
addressEnabled: true
# 缓存懒加载 # 缓存懒加载
cacheLazy: false cacheLazy: false
@@ -107,6 +103,8 @@ sa-token:
# 多端不同 token 有效期 可查看 LoginHelper.loginByDevice 方法自定义 # 多端不同 token 有效期 可查看 LoginHelper.loginByDevice 方法自定义
# token最低活跃时间 (指定时间无操作就过期) 单位: 秒 # token最低活跃时间 (指定时间无操作就过期) 单位: 秒
active-timeout: 1800 active-timeout: 1800
# 允许动态设置 token 有效期
dynamic-active-timeout: true
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录) # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
is-concurrent: true is-concurrent: true
# 在多人登录同一账号时是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token) # 在多人登录同一账号时是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
@@ -197,8 +195,13 @@ mybatis-encryptor:
publicKey: publicKey:
privateKey: privateKey:
# Swagger配置 springdoc:
swagger: api-docs:
# 是否开启接口文档
enabled: true
# swagger-ui:
# # 持久化认证数据
# persistAuthorization: true
info: info:
# 标题 # 标题
title: '标题:${ruoyi.name}后台管理系统_接口文档' title: '标题:${ruoyi.name}后台管理系统_接口文档'
@@ -218,14 +221,6 @@ swagger:
type: APIKEY type: APIKEY
in: HEADER in: HEADER
name: ${sa-token.token-name} name: ${sa-token.token-name}
springdoc:
api-docs:
# 是否开启接口文档
enabled: true
swagger-ui:
# 持久化认证数据
persistAuthorization: true
#这里定义了两个分组,可定义多个,也可以不定义 #这里定义了两个分组,可定义多个,也可以不定义
group-configs: group-configs:
- group: 1.演示模块 - group: 1.演示模块

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>ruoyi-vue-plus</artifactId> <artifactId>ruoyi-vue-plus</artifactId>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<version>4.8.0</version> <version>4.8.2</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -1,7 +1,6 @@
package com.ruoyi.common.config; package com.ruoyi.common.config;
import lombok.Data; import lombok.Data;
import lombok.Getter;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -31,24 +30,9 @@ public class RuoYiConfig {
*/ */
private String copyrightYear; private String copyrightYear;
/**
* 实例演示开关
*/
private boolean demoEnabled;
/** /**
* 缓存懒加载 * 缓存懒加载
*/ */
private boolean cacheLazy; private boolean cacheLazy;
/**
* 获取地址开关
*/
@Getter
private static boolean addressEnabled;
public void setAddressEnabled(boolean addressEnabled) {
RuoYiConfig.addressEnabled = addressEnabled;
}
} }

View File

@@ -52,6 +52,16 @@ public interface UserConstants {
*/ */
String DEPT_DISABLE = "1"; String DEPT_DISABLE = "1";
/**
* 岗位正常状态
*/
String POST_NORMAL = "0";
/**
* 岗位停用状态
*/
String POST_DISABLE = "1";
/** /**
* 字典正常状态 * 字典正常状态
*/ */

View File

@@ -180,12 +180,12 @@ public interface BaseMapperPlus<M, T, V> extends BaseMapper<T> {
* 分页查询VO * 分页查询VO
*/ */
default <C, P extends IPage<C>> P selectVoPage(IPage<T> page, Wrapper<T> wrapper, Class<C> voClass) { default <C, P extends IPage<C>> P selectVoPage(IPage<T> page, Wrapper<T> wrapper, Class<C> voClass) {
IPage<T> pageData = this.selectPage(page, wrapper); List<T> list = this.selectList(page, wrapper);
IPage<C> voPage = new Page<>(pageData.getCurrent(), pageData.getSize(), pageData.getTotal()); IPage<C> voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
if (CollUtil.isEmpty(pageData.getRecords())) { if (CollUtil.isEmpty(list)) {
return (P) voPage; return (P) voPage;
} }
voPage.setRecords(BeanCopyUtils.copyList(pageData.getRecords(), voClass)); voPage.setRecords(BeanCopyUtils.copyList(list, voClass));
return (P) voPage; return (P) voPage;
} }

View File

@@ -1,14 +1,9 @@
package com.ruoyi.common.encrypt.encryptor; package com.ruoyi.common.encrypt.encryptor;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import com.ruoyi.common.encrypt.EncryptContext; import com.ruoyi.common.encrypt.EncryptContext;
import com.ruoyi.common.enums.AlgorithmType; import com.ruoyi.common.enums.AlgorithmType;
import com.ruoyi.common.enums.EncodeType; import com.ruoyi.common.enums.EncodeType;
import com.ruoyi.common.utils.EncryptUtils;
import java.nio.charset.StandardCharsets;
/** /**
* AES算法实现 * AES算法实现
@@ -18,20 +13,11 @@ import java.nio.charset.StandardCharsets;
*/ */
public class AesEncryptor extends AbstractEncryptor { public class AesEncryptor extends AbstractEncryptor {
private final AES aes; private final EncryptContext context;
public AesEncryptor(EncryptContext context) { public AesEncryptor(EncryptContext context) {
super(context); super(context);
String password = context.getPassword(); this.context = context;
if (StrUtil.isBlank(password)) {
throw new IllegalArgumentException("AES没有获得秘钥信息");
}
// aes算法的秘钥要求是16位、24位、32位
int[] array = {16, 24, 32};
if (!ArrayUtil.contains(array, password.length())) {
throw new IllegalArgumentException("AES秘钥长度应该为16位、24位、32位实际为" + password.length() + "");
}
aes = SecureUtil.aes(context.getPassword().getBytes(StandardCharsets.UTF_8));
} }
/** /**
@@ -51,9 +37,9 @@ public class AesEncryptor extends AbstractEncryptor {
@Override @Override
public String encrypt(String value, EncodeType encodeType) { public String encrypt(String value, EncodeType encodeType) {
if (encodeType == EncodeType.HEX) { if (encodeType == EncodeType.HEX) {
return aes.encryptHex(value); return EncryptUtils.encryptByAesHex(value, context.getPassword());
} else { } else {
return aes.encryptBase64(value); return EncryptUtils.encryptByAes(value, context.getPassword());
} }
} }
@@ -64,6 +50,6 @@ public class AesEncryptor extends AbstractEncryptor {
*/ */
@Override @Override
public String decrypt(String value) { public String decrypt(String value) {
return this.aes.decryptStr(value); return EncryptUtils.decryptByAes(value, context.getPassword());
} }
} }

View File

@@ -1,9 +1,9 @@
package com.ruoyi.common.encrypt.encryptor; package com.ruoyi.common.encrypt.encryptor;
import cn.hutool.core.codec.Base64;
import com.ruoyi.common.encrypt.EncryptContext; import com.ruoyi.common.encrypt.EncryptContext;
import com.ruoyi.common.enums.AlgorithmType; import com.ruoyi.common.enums.AlgorithmType;
import com.ruoyi.common.enums.EncodeType; import com.ruoyi.common.enums.EncodeType;
import com.ruoyi.common.utils.EncryptUtils;
/** /**
* Base64算法实现 * Base64算法实现
@@ -33,7 +33,7 @@ public class Base64Encryptor extends AbstractEncryptor {
*/ */
@Override @Override
public String encrypt(String value, EncodeType encodeType) { public String encrypt(String value, EncodeType encodeType) {
return Base64.encode(value); return EncryptUtils.encryptByBase64(value);
} }
/** /**
@@ -43,6 +43,6 @@ public class Base64Encryptor extends AbstractEncryptor {
*/ */
@Override @Override
public String decrypt(String value) { public String decrypt(String value) {
return Base64.decodeStr(value); return EncryptUtils.decryptByBase64(value);
} }
} }

View File

@@ -1,12 +1,9 @@
package com.ruoyi.common.encrypt.encryptor; package com.ruoyi.common.encrypt.encryptor;
import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import com.ruoyi.common.encrypt.EncryptContext; import com.ruoyi.common.encrypt.EncryptContext;
import com.ruoyi.common.enums.AlgorithmType; import com.ruoyi.common.enums.AlgorithmType;
import com.ruoyi.common.enums.EncodeType; import com.ruoyi.common.enums.EncodeType;
import com.ruoyi.common.utils.EncryptUtils;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
@@ -18,7 +15,7 @@ import com.ruoyi.common.utils.StringUtils;
*/ */
public class RsaEncryptor extends AbstractEncryptor { public class RsaEncryptor extends AbstractEncryptor {
private final RSA rsa; private final EncryptContext context;
public RsaEncryptor(EncryptContext context) { public RsaEncryptor(EncryptContext context) {
super(context); super(context);
@@ -27,7 +24,7 @@ public class RsaEncryptor extends AbstractEncryptor {
if (StringUtils.isAnyEmpty(privateKey, publicKey)) { if (StringUtils.isAnyEmpty(privateKey, publicKey)) {
throw new IllegalArgumentException("RSA公私钥均需要提供公钥加密私钥解密。"); throw new IllegalArgumentException("RSA公私钥均需要提供公钥加密私钥解密。");
} }
this.rsa = SecureUtil.rsa(Base64.decode(privateKey), Base64.decode(publicKey)); this.context = context;
} }
/** /**
@@ -47,9 +44,9 @@ public class RsaEncryptor extends AbstractEncryptor {
@Override @Override
public String encrypt(String value, EncodeType encodeType) { public String encrypt(String value, EncodeType encodeType) {
if (encodeType == EncodeType.HEX) { if (encodeType == EncodeType.HEX) {
return rsa.encryptHex(value, KeyType.PublicKey); return EncryptUtils.encryptByRsaHex(value, context.getPublicKey());
} else { } else {
return rsa.encryptBase64(value, KeyType.PublicKey); return EncryptUtils.encryptByRsa(value, context.getPublicKey());
} }
} }
@@ -60,6 +57,6 @@ public class RsaEncryptor extends AbstractEncryptor {
*/ */
@Override @Override
public String decrypt(String value) { public String decrypt(String value) {
return this.rsa.decryptStr(value, KeyType.PrivateKey); return EncryptUtils.decryptByRsa(value, context.getPrivateKey());
} }
} }

View File

@@ -1,13 +1,10 @@
package com.ruoyi.common.encrypt.encryptor; package com.ruoyi.common.encrypt.encryptor;
import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import com.ruoyi.common.encrypt.EncryptContext; import com.ruoyi.common.encrypt.EncryptContext;
import com.ruoyi.common.enums.AlgorithmType; import com.ruoyi.common.enums.AlgorithmType;
import com.ruoyi.common.enums.EncodeType; import com.ruoyi.common.enums.EncodeType;
import com.ruoyi.common.utils.EncryptUtils;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
/** /**
@@ -18,7 +15,7 @@ import com.ruoyi.common.utils.StringUtils;
*/ */
public class Sm2Encryptor extends AbstractEncryptor { public class Sm2Encryptor extends AbstractEncryptor {
private final SM2 sm2; private final EncryptContext context;
public Sm2Encryptor(EncryptContext context) { public Sm2Encryptor(EncryptContext context) {
super(context); super(context);
@@ -27,7 +24,7 @@ public class Sm2Encryptor extends AbstractEncryptor {
if (StringUtils.isAnyEmpty(privateKey, publicKey)) { if (StringUtils.isAnyEmpty(privateKey, publicKey)) {
throw new IllegalArgumentException("SM2公私钥均需要提供公钥加密私钥解密。"); throw new IllegalArgumentException("SM2公私钥均需要提供公钥加密私钥解密。");
} }
this.sm2 = SmUtil.sm2(Base64.decode(privateKey), Base64.decode(publicKey)); this.context = context;
} }
/** /**
@@ -47,9 +44,9 @@ public class Sm2Encryptor extends AbstractEncryptor {
@Override @Override
public String encrypt(String value, EncodeType encodeType) { public String encrypt(String value, EncodeType encodeType) {
if (encodeType == EncodeType.HEX) { if (encodeType == EncodeType.HEX) {
return sm2.encryptHex(value, KeyType.PublicKey); return EncryptUtils.encryptBySm2Hex(value, context.getPublicKey());
} else { } else {
return sm2.encryptBase64(value, KeyType.PublicKey); return EncryptUtils.encryptBySm2(value, context.getPublicKey());
} }
} }
@@ -60,6 +57,6 @@ public class Sm2Encryptor extends AbstractEncryptor {
*/ */
@Override @Override
public String decrypt(String value) { public String decrypt(String value) {
return this.sm2.decryptStr(value, KeyType.PrivateKey); return EncryptUtils.decryptBySm2(value, context.getPrivateKey());
} }
} }

View File

@@ -1,13 +1,9 @@
package com.ruoyi.common.encrypt.encryptor; package com.ruoyi.common.encrypt.encryptor;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.symmetric.SM4;
import com.ruoyi.common.encrypt.EncryptContext; import com.ruoyi.common.encrypt.EncryptContext;
import com.ruoyi.common.enums.AlgorithmType; import com.ruoyi.common.enums.AlgorithmType;
import com.ruoyi.common.enums.EncodeType; import com.ruoyi.common.enums.EncodeType;
import com.ruoyi.common.utils.EncryptUtils;
import java.nio.charset.StandardCharsets;
/** /**
* sm4算法实现 * sm4算法实现
@@ -17,19 +13,11 @@ import java.nio.charset.StandardCharsets;
*/ */
public class Sm4Encryptor extends AbstractEncryptor { public class Sm4Encryptor extends AbstractEncryptor {
private final SM4 sm4; private final EncryptContext context;
public Sm4Encryptor(EncryptContext context) { public Sm4Encryptor(EncryptContext context) {
super(context); super(context);
String password = context.getPassword(); this.context = context;
if (StrUtil.isBlank(password)) {
throw new IllegalArgumentException("SM4没有获得秘钥信息");
}
// sm4算法的秘钥要求是16位长度
if (16 != password.length()) {
throw new IllegalArgumentException("SM4秘钥长度应该为16位实际为" + password.length() + "");
}
this.sm4 = SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8));
} }
/** /**
@@ -49,9 +37,9 @@ public class Sm4Encryptor extends AbstractEncryptor {
@Override @Override
public String encrypt(String value, EncodeType encodeType) { public String encrypt(String value, EncodeType encodeType) {
if (encodeType == EncodeType.HEX) { if (encodeType == EncodeType.HEX) {
return sm4.encryptHex(value); return EncryptUtils.encryptBySm4Hex(value, context.getPassword());
} else { } else {
return sm4.encryptBase64(value); return EncryptUtils.encryptBySm4(value, context.getPassword());
} }
} }
@@ -62,6 +50,6 @@ public class Sm4Encryptor extends AbstractEncryptor {
*/ */
@Override @Override
public String decrypt(String value) { public String decrypt(String value) {
return this.sm4.decryptStr(value); return EncryptUtils.decryptBySm4(value, context.getPassword());
} }
} }

View File

@@ -29,17 +29,17 @@ public enum DataScopeType {
/** /**
* 自定数据权限 * 自定数据权限
*/ */
CUSTOM("2", " #{#deptName} IN ( #{@sdss.getRoleCustom( #user.roleId )} ) ", ""), CUSTOM("2", " #{#deptName} IN ( #{@sdss.getRoleCustom( #user.roleId )} ) ", " 1 = 0 "),
/** /**
* 部门数据权限 * 部门数据权限
*/ */
DEPT("3", " #{#deptName} = #{#user.deptId} ", ""), DEPT("3", " #{#deptName} = #{#user.deptId} ", " 1 = 0 "),
/** /**
* 部门及以下数据权限 * 部门及以下数据权限
*/ */
DEPT_AND_CHILD("4", " #{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )} )", ""), DEPT_AND_CHILD("4", " #{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )} )", " 1 = 0 "),
/** /**
* 仅本人数据权限 * 仅本人数据权限

View File

@@ -5,7 +5,9 @@ import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.EnumUtil; import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.metadata.FieldCache;
import com.alibaba.excel.metadata.FieldWrapper;
import com.alibaba.excel.util.ClassUtils;
import com.alibaba.excel.write.handler.SheetWriteHandler; import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
@@ -83,16 +85,18 @@ public class ExcelDownHandler implements SheetWriteHandler {
Sheet sheet = writeSheetHolder.getSheet(); Sheet sheet = writeSheetHolder.getSheet();
// 开始设置下拉框 HSSFWorkbook // 开始设置下拉框 HSSFWorkbook
DataValidationHelper helper = sheet.getDataValidationHelper(); DataValidationHelper helper = sheet.getDataValidationHelper();
Field[] fields = writeWorkbookHolder.getClazz().getDeclaredFields();
Workbook workbook = writeWorkbookHolder.getWorkbook(); Workbook workbook = writeWorkbookHolder.getWorkbook();
int length = fields.length; FieldCache fieldCache = ClassUtils.declaredFields(writeWorkbookHolder.getClazz(), writeWorkbookHolder);
for (int i = 0; i < length; i++) { for (Map.Entry<Integer, FieldWrapper> entry : fieldCache.getSortedFieldMap().entrySet()) {
Integer index = entry.getKey();
FieldWrapper wrapper = entry.getValue();
Field field = wrapper.getField();
// 循环实体中的每个属性 // 循环实体中的每个属性
// 可选的下拉值 // 可选的下拉值
List<String> options = new ArrayList<>(); List<String> options = new ArrayList<>();
if (fields[i].isAnnotationPresent(ExcelDictFormat.class)) { if (field.isAnnotationPresent(ExcelDictFormat.class)) {
// 如果指定了@ExcelDictFormat则使用字典的逻辑 // 如果指定了@ExcelDictFormat则使用字典的逻辑
ExcelDictFormat format = fields[i].getDeclaredAnnotation(ExcelDictFormat.class); ExcelDictFormat format = field.getDeclaredAnnotation(ExcelDictFormat.class);
String dictType = format.dictType(); String dictType = format.dictType();
String converterExp = format.readConverterExp(); String converterExp = format.readConverterExp();
if (StrUtil.isNotBlank(dictType)) { if (StrUtil.isNotBlank(dictType)) {
@@ -105,20 +109,14 @@ public class ExcelDownHandler implements SheetWriteHandler {
// 如果指定了确切的值,则直接解析确切的值 // 如果指定了确切的值,则直接解析确切的值
options = StrUtil.split(converterExp, format.separator(), true, true); options = StrUtil.split(converterExp, format.separator(), true, true);
} }
} else if (fields[i].isAnnotationPresent(ExcelEnumFormat.class)) { } else if (field.isAnnotationPresent(ExcelEnumFormat.class)) {
// 否则如果指定了@ExcelEnumFormat则使用枚举的逻辑 // 否则如果指定了@ExcelEnumFormat则使用枚举的逻辑
ExcelEnumFormat format = fields[i].getDeclaredAnnotation(ExcelEnumFormat.class); ExcelEnumFormat format = field.getDeclaredAnnotation(ExcelEnumFormat.class);
List<Object> values = EnumUtil.getFieldValues(format.enumClass(), format.textField()); List<Object> values = EnumUtil.getFieldValues(format.enumClass(), format.textField());
options = StreamUtils.toList(values, String::valueOf); options = StreamUtils.toList(values, String::valueOf);
} }
if (ObjectUtil.isNotEmpty(options)) { if (ObjectUtil.isNotEmpty(options)) {
// 仅当下拉可选项不为空时执行 // 仅当下拉可选项不为空时执行
// 获取列下标,默认为当前循环次数
int index = i;
if (fields[i].isAnnotationPresent(ExcelProperty.class)) {
// 如果指定了列下标,以指定的为主
index = fields[i].getDeclaredAnnotation(ExcelProperty.class).index();
}
if (options.size() > 20) { if (options.size() > 20) {
// 这里限制如果可选项大于20则使用额外表形式 // 这里限制如果可选项大于20则使用额外表形式
dropDownWithSheet(helper, workbook, sheet, index, options); dropDownWithSheet(helper, workbook, sheet, index, options);
@@ -128,6 +126,9 @@ public class ExcelDownHandler implements SheetWriteHandler {
} }
} }
} }
if (CollUtil.isEmpty(dropDownOptions)) {
return;
}
dropDownOptions.forEach(everyOptions -> { dropDownOptions.forEach(everyOptions -> {
// 如果传递了下拉框选择器参数 // 如果传递了下拉框选择器参数
if (!everyOptions.getNextOptions().isEmpty()) { if (!everyOptions.getNextOptions().isEmpty()) {

View File

@@ -67,6 +67,25 @@ public class EncryptUtils {
return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8); return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8);
} }
/**
* AES加密
*
* @param data 待解密数据
* @param password 秘钥字符串
* @return 加密后字符串, 采用Hex编码
*/
public static String encryptByAesHex(String data, String password) {
if (StrUtil.isBlank(password)) {
throw new IllegalArgumentException("AES需要传入秘钥信息");
}
// aes算法的秘钥要求是16位、24位、32位
int[] array = {16, 24, 32};
if (!ArrayUtil.contains(array, password.length())) {
throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位");
}
return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).encryptHex(data, StandardCharsets.UTF_8);
}
/** /**
* AES解密 * AES解密
* *
@@ -105,6 +124,25 @@ public class EncryptUtils {
return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8); return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8);
} }
/**
* sm4加密
*
* @param data 待加密数据
* @param password 秘钥字符串
* @return 加密后字符串, 采用Base64编码
*/
public static String encryptBySm4Hex(String data, String password) {
if (StrUtil.isBlank(password)) {
throw new IllegalArgumentException("SM4需要传入秘钥信息");
}
// sm4算法的秘钥要求是16位长度
int sm4PasswordLength = 16;
if (sm4PasswordLength != password.length()) {
throw new IllegalArgumentException("SM4秘钥长度要求为16位");
}
return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).encryptHex(data, StandardCharsets.UTF_8);
}
/** /**
* sm4解密 * sm4解密
* *
@@ -152,6 +190,21 @@ public class EncryptUtils {
return sm2.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey); return sm2.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey);
} }
/**
* sm2公钥加密
*
* @param data 待加密数据
* @param publicKey 公钥
* @return 加密后字符串, 采用Hex编码
*/
public static String encryptBySm2Hex(String data, String publicKey) {
if (StrUtil.isBlank(publicKey)) {
throw new IllegalArgumentException("SM2需要传入公钥进行加密");
}
SM2 sm2 = SmUtil.sm2(null, publicKey);
return sm2.encryptHex(data, StandardCharsets.UTF_8, KeyType.PublicKey);
}
/** /**
* sm2私钥解密 * sm2私钥解密
* *
@@ -195,6 +248,21 @@ public class EncryptUtils {
return rsa.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey); return rsa.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey);
} }
/**
* rsa公钥加密
*
* @param data 待加密数据
* @param publicKey 公钥
* @return 加密后字符串, 采用Hex编码
*/
public static String encryptByRsaHex(String data, String publicKey) {
if (StrUtil.isBlank(publicKey)) {
throw new IllegalArgumentException("RSA需要传入公钥进行加密");
}
RSA rsa = SecureUtil.rsa(null, publicKey);
return rsa.encryptHex(data, StandardCharsets.UTF_8, KeyType.PublicKey);
}
/** /**
* rsa私钥解密 * rsa私钥解密
* *

View File

@@ -30,7 +30,6 @@ public class StreamUtils {
if (CollUtil.isEmpty(collection)) { if (CollUtil.isEmpty(collection)) {
return CollUtil.newArrayList(); return CollUtil.newArrayList();
} }
// 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题
return collection.stream().filter(function).collect(Collectors.toList()); return collection.stream().filter(function).collect(Collectors.toList());
} }
@@ -71,7 +70,6 @@ public class StreamUtils {
if (CollUtil.isEmpty(collection)) { if (CollUtil.isEmpty(collection)) {
return CollUtil.newArrayList(); return CollUtil.newArrayList();
} }
// 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题
return collection.stream().filter(Objects::nonNull).sorted(comparing).collect(Collectors.toList()); return collection.stream().filter(Objects::nonNull).sorted(comparing).collect(Collectors.toList());
} }
@@ -190,7 +188,6 @@ public class StreamUtils {
.stream() .stream()
.map(function) .map(function)
.filter(Objects::nonNull) .filter(Objects::nonNull)
// 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题
.collect(Collectors.toList()); .collect(Collectors.toList());
} }

View File

@@ -24,7 +24,7 @@ public class AddressUtils {
return UNKNOWN; return UNKNOWN;
} }
// 内网不查询 // 内网不查询
ip = "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip); ip = StringUtils.contains(ip, "0:0:0:0:0:0:0:1") ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip);
if (NetUtil.isInnerIP(ip)) { if (NetUtil.isInnerIP(ip)) {
return "内网IP"; return "内网IP";
} }

View File

@@ -196,10 +196,8 @@ public class ExcelUtil {
// 合并处理器 // 合并处理器
builder.registerWriteHandler(new CellMergeStrategy(list, true)); builder.registerWriteHandler(new CellMergeStrategy(list, true));
} }
if (CollUtil.isNotEmpty(options)) { // 添加下拉框操作
// 添加下拉框操作 builder.registerWriteHandler(new ExcelDownHandler(options));
builder.registerWriteHandler(new ExcelDownHandler(options));
}
builder.doWrite(list); builder.doWrite(list);
} }

View File

@@ -131,6 +131,32 @@ public class QueueUtils {
return priorityBlockingQueue.offer(data); return priorityBlockingQueue.offer(data);
} }
/**
* 优先队列获取一个队列数据 没有数据返回 null(不支持延迟队列)
*
* @param queueName 队列名
*/
public static <T> T getPriorityQueueObject(String queueName) {
RPriorityBlockingQueue<T> queue = CLIENT.getPriorityBlockingQueue(queueName);
return queue.poll();
}
/**
* 优先队列删除队列数据(不支持延迟队列)
*/
public static <T> boolean removePriorityQueueObject(String queueName, T data) {
RPriorityBlockingQueue<T> queue = CLIENT.getPriorityBlockingQueue(queueName);
return queue.remove(data);
}
/**
* 优先队列销毁队列 所有阻塞监听 报错(不支持延迟队列)
*/
public static <T> boolean destroyPriorityQueue(String queueName) {
RPriorityBlockingQueue<T> queue = CLIENT.getPriorityBlockingQueue(queueName);
return queue.delete();
}
/** /**
* 尝试设置 有界队列 容量 用于限制数量 * 尝试设置 有界队列 容量 用于限制数量
* *
@@ -169,11 +195,41 @@ public class QueueUtils {
return boundedBlockingQueue.offer(data); return boundedBlockingQueue.offer(data);
} }
/**
* 有界队列获取一个队列数据 没有数据返回 null(不支持延迟队列)
*
* @param queueName 队列名
*/
public static <T> T getBoundedQueueObject(String queueName) {
RBoundedBlockingQueue<T> queue = CLIENT.getBoundedBlockingQueue(queueName);
return queue.poll();
}
/**
* 有界队列删除队列数据(不支持延迟队列)
*/
public static <T> boolean removeBoundedQueueObject(String queueName, T data) {
RBoundedBlockingQueue<T> queue = CLIENT.getBoundedBlockingQueue(queueName);
return queue.remove(data);
}
/**
* 有界队列销毁队列 所有阻塞监听 报错(不支持延迟队列)
*/
public static <T> boolean destroyBoundedQueue(String queueName) {
RBoundedBlockingQueue<T> queue = CLIENT.getBoundedBlockingQueue(queueName);
return queue.delete();
}
/** /**
* 订阅阻塞队列(可订阅所有实现类 例如: 延迟 优先 有界 等) * 订阅阻塞队列(可订阅所有实现类 例如: 延迟 优先 有界 等)
*/ */
public static <T> void subscribeBlockingQueue(String queueName, Consumer<T> consumer) { public static <T> void subscribeBlockingQueue(String queueName, Consumer<T> consumer, boolean isDelayed) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName); RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
if (isDelayed) {
// 订阅延迟队列
CLIENT.getDelayedQueue(queue);
}
queue.subscribeOnElements(consumer); queue.subscribeOnElements(consumer);
} }

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>ruoyi-vue-plus</artifactId> <artifactId>ruoyi-vue-plus</artifactId>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<version>4.8.0</version> <version>4.8.2</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -34,7 +34,7 @@ public class RedisCacheController {
* 如果没有,就调用方法,然后把结果缓存起来 * 如果没有,就调用方法,然后把结果缓存起来
* 这个注解「一般用在查询方法上」 * 这个注解「一般用在查询方法上」
* <p> * <p>
* 重点说明: 缓存注解严与其他筛选数据功能一起使用 * 重点说明: 缓存注解严与其他筛选数据功能一起使用
* 例如: 数据权限注解 会造成 缓存击穿 与 数据不一致问题 * 例如: 数据权限注解 会造成 缓存击穿 与 数据不一致问题
* <p> * <p>
* cacheNames 命名规则 查看 {@link CacheNames} 注释 支持多参数 * cacheNames 命名规则 查看 {@link CacheNames} 注释 支持多参数

View File

@@ -35,7 +35,7 @@ public class BoundedQueueController {
@GetMapping("/add") @GetMapping("/add")
public R<Void> add(String queueName, int capacity) { public R<Void> add(String queueName, int capacity) {
// 用完了一定要销毁 否则会一直存在 // 用完了一定要销毁 否则会一直存在
boolean b = QueueUtils.destroyQueue(queueName); boolean b = QueueUtils.destroyBoundedQueue(queueName);
log.info("通道: {} , 删除: {}", queueName, b); log.info("通道: {} , 删除: {}", queueName, b);
// 初始化设置一次即可 // 初始化设置一次即可
if (QueueUtils.trySetBoundedQueueCapacity(queueName, capacity)) { if (QueueUtils.trySetBoundedQueueCapacity(queueName, capacity)) {
@@ -64,7 +64,7 @@ public class BoundedQueueController {
@GetMapping("/remove") @GetMapping("/remove")
public R<Void> remove(String queueName) { public R<Void> remove(String queueName) {
String data = "data-" + 5; String data = "data-" + 5;
if (QueueUtils.removeQueueObject(queueName, data)) { if (QueueUtils.removeBoundedQueueObject(queueName, data)) {
log.info("通道: {} , 删除数据: {}", queueName, data); log.info("通道: {} , 删除数据: {}", queueName, data);
} else { } else {
return R.fail("操作失败"); return R.fail("操作失败");
@@ -81,7 +81,7 @@ public class BoundedQueueController {
public R<Void> get(String queueName) { public R<Void> get(String queueName) {
String data; String data;
do { do {
data = QueueUtils.getQueueObject(queueName); data = QueueUtils.getBoundedQueueObject(queueName);
log.info("通道: {} , 获取数据: {}", queueName, data); log.info("通道: {} , 获取数据: {}", queueName, data);
} while (data != null); } while (data != null);
return R.ok("操作成功"); return R.ok("操作成功");

View File

@@ -40,7 +40,7 @@ public class DelayedQueueController {
QueueUtils.subscribeBlockingQueue(queueName, (String orderNum) -> { QueueUtils.subscribeBlockingQueue(queueName, (String orderNum) -> {
// 观察接收时间 // 观察接收时间
log.info("通道: {}, 收到数据: {}", queueName, orderNum); log.info("通道: {}, 收到数据: {}", queueName, orderNum);
}); }, true);
return R.ok("操作成功"); return R.ok("操作成功");
} }

View File

@@ -34,7 +34,7 @@ public class PriorityQueueController {
@GetMapping("/add") @GetMapping("/add")
public R<Void> add(String queueName) { public R<Void> add(String queueName) {
// 用完了一定要销毁 否则会一直存在 // 用完了一定要销毁 否则会一直存在
boolean b = QueueUtils.destroyQueue(queueName); boolean b = QueueUtils.destroyPriorityQueue(queueName);
log.info("通道: {} , 删除: {}", queueName, b); log.info("通道: {} , 删除: {}", queueName, b);
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
@@ -63,7 +63,7 @@ public class PriorityQueueController {
PriorityDemo data = new PriorityDemo(); PriorityDemo data = new PriorityDemo();
data.setName(name); data.setName(name);
data.setOrderNum(orderNum); data.setOrderNum(orderNum);
if (QueueUtils.removeQueueObject(queueName, data)) { if (QueueUtils.removePriorityQueueObject(queueName, data)) {
log.info("通道: {} , 删除数据: {}", queueName, data); log.info("通道: {} , 删除数据: {}", queueName, data);
} else { } else {
return R.fail("操作失败"); return R.fail("操作失败");
@@ -80,7 +80,7 @@ public class PriorityQueueController {
public R<Void> get(String queueName) { public R<Void> get(String queueName) {
PriorityDemo data; PriorityDemo data;
do { do {
data = QueueUtils.getQueueObject(queueName); data = QueueUtils.getPriorityQueueObject(queueName);
log.info("通道: {} , 获取数据: {}", queueName, data); log.info("通道: {} , 获取数据: {}", queueName, data);
} while (data != null); } while (data != null);
return R.ok("操作成功"); return R.ok("操作成功");

View File

@@ -33,7 +33,7 @@ public interface TestDemoMapper extends BaseMapperPlus<TestDemoMapper, TestDemo,
@DataColumn(key = "deptName", value = "dept_id"), @DataColumn(key = "deptName", value = "dept_id"),
@DataColumn(key = "userName", value = "user_id") @DataColumn(key = "userName", value = "user_id")
}) })
<P extends IPage<TestDemo>> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<TestDemo> queryWrapper); List<TestDemo> selectList(IPage<TestDemo> page, @Param(Constants.WRAPPER) Wrapper<TestDemo> queryWrapper);
@Override @Override
@DataPermission({ @DataPermission({

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>ruoyi-vue-plus</artifactId> <artifactId>ruoyi-vue-plus</artifactId>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<version>4.8.0</version> <version>4.8.2</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-extend</artifactId> <artifactId>ruoyi-extend</artifactId>

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>ruoyi-extend</artifactId> <artifactId>ruoyi-extend</artifactId>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<version>4.8.0</version> <version>4.8.2</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging> <packaging>jar</packaging>

View File

@@ -4,7 +4,7 @@
<parent> <parent>
<artifactId>ruoyi-extend</artifactId> <artifactId>ruoyi-extend</artifactId>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<version>4.8.0</version> <version>4.8.2</version>
</parent> </parent>
<artifactId>ruoyi-xxl-job-admin</artifactId> <artifactId>ruoyi-xxl-job-admin</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>ruoyi-vue-plus</artifactId> <artifactId>ruoyi-vue-plus</artifactId>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<version>4.8.0</version> <version>4.8.2</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -6,6 +6,7 @@ import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.ruoyi.common.annotation.Log; import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.domain.event.OperLogEvent; import com.ruoyi.common.core.domain.event.OperLogEvent;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.BusinessStatus; import com.ruoyi.common.enums.BusinessStatus;
import com.ruoyi.common.enums.HttpMethod; import com.ruoyi.common.enums.HttpMethod;
import com.ruoyi.common.helper.LoginHelper; import com.ruoyi.common.helper.LoginHelper;
@@ -74,7 +75,9 @@ public class LogAspect {
String ip = ServletUtils.getClientIP(); String ip = ServletUtils.getClientIP();
operLog.setOperIp(ip); operLog.setOperIp(ip);
operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255)); operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));
operLog.setOperName(LoginHelper.getUsername()); LoginUser loginUser = LoginHelper.getLoginUser();
operLog.setOperName(loginUser.getUsername());
operLog.setDeptName(loginUser.getDeptName());
if (e != null) { if (e != null) {
operLog.setStatus(BusinessStatus.FAIL.ordinal()); operLog.setStatus(BusinessStatus.FAIL.ordinal());

View File

@@ -50,8 +50,8 @@ public class SaTokenConfig implements WebMvcConfigurer {
// 有效率影响 用于临时测试 // 有效率影响 用于临时测试
// if (log.isDebugEnabled()) { // if (log.isDebugEnabled()) {
// log.debug("剩余有效时间: {}", StpUtil.getTokenTimeout()); // log.info("剩余有效时间: {}", StpUtil.getTokenTimeout());
// log.debug("临时有效时间: {}", StpUtil.getTokenActivityTimeout()); // log.info("临时有效时间: {}", StpUtil.getTokenActiveTimeout());
// } // }
}); });

View File

@@ -1,7 +1,7 @@
package com.ruoyi.framework.config; package com.ruoyi.framework.config;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.config.properties.SwaggerProperties; import com.ruoyi.framework.config.properties.SpringDocProperties;
import com.ruoyi.framework.handler.OpenApiHandler; import com.ruoyi.framework.handler.OpenApiHandler;
import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Paths; import io.swagger.v3.oas.models.Paths;
@@ -34,25 +34,24 @@ import java.util.Set;
@Configuration @Configuration
@AutoConfigureBefore(SpringDocConfiguration.class) @AutoConfigureBefore(SpringDocConfiguration.class)
@ConditionalOnProperty(name = "springdoc.api-docs.enabled", havingValue = "true", matchIfMissing = true) @ConditionalOnProperty(name = "springdoc.api-docs.enabled", havingValue = "true", matchIfMissing = true)
public class SwaggerConfig { public class SpringDocConfig {
private final SwaggerProperties swaggerProperties;
private final ServerProperties serverProperties; private final ServerProperties serverProperties;
@Bean @Bean
@ConditionalOnMissingBean(OpenAPI.class) @ConditionalOnMissingBean(OpenAPI.class)
public OpenAPI openApi() { public OpenAPI openApi(SpringDocProperties properties) {
OpenAPI openApi = new OpenAPI(); OpenAPI openApi = new OpenAPI();
// 文档基本信息 // 文档基本信息
SwaggerProperties.InfoProperties infoProperties = swaggerProperties.getInfo(); SpringDocProperties.InfoProperties infoProperties = properties.getInfo();
Info info = convertInfo(infoProperties); Info info = convertInfo(infoProperties);
openApi.info(info); openApi.info(info);
// 扩展文档信息 // 扩展文档信息
openApi.externalDocs(swaggerProperties.getExternalDocs()); openApi.externalDocs(properties.getExternalDocs());
openApi.tags(swaggerProperties.getTags()); openApi.tags(properties.getTags());
openApi.paths(swaggerProperties.getPaths()); openApi.paths(properties.getPaths());
openApi.components(swaggerProperties.getComponents()); openApi.components(properties.getComponents());
Set<String> keySet = swaggerProperties.getComponents().getSecuritySchemes().keySet(); Set<String> keySet = properties.getComponents().getSecuritySchemes().keySet();
List<SecurityRequirement> list = new ArrayList<>(); List<SecurityRequirement> list = new ArrayList<>();
SecurityRequirement securityRequirement = new SecurityRequirement(); SecurityRequirement securityRequirement = new SecurityRequirement();
keySet.forEach(securityRequirement::addList); keySet.forEach(securityRequirement::addList);
@@ -62,7 +61,7 @@ public class SwaggerConfig {
return openApi; return openApi;
} }
private Info convertInfo(SwaggerProperties.InfoProperties infoProperties) { private Info convertInfo(SpringDocProperties.InfoProperties infoProperties) {
Info info = new Info(); Info info = new Info();
info.setTitle(infoProperties.getTitle()); info.setTitle(infoProperties.getTitle());
info.setDescription(infoProperties.getDescription()); info.setDescription(infoProperties.getDescription());

View File

@@ -20,8 +20,8 @@ import java.util.List;
*/ */
@Data @Data
@Component @Component
@ConfigurationProperties(prefix = "swagger") @ConfigurationProperties(prefix = "springdoc")
public class SwaggerProperties { public class SpringDocProperties {
/** /**
* 文档基本信息 * 文档基本信息

View File

@@ -1,6 +1,7 @@
package com.ruoyi.framework.encrypt; package com.ruoyi.framework.encrypt;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.ruoyi.common.annotation.EncryptField; import com.ruoyi.common.annotation.EncryptField;
import com.ruoyi.common.encrypt.EncryptContext; import com.ruoyi.common.encrypt.EncryptContext;
@@ -76,7 +77,7 @@ public class MybatisDecryptInterceptor implements Interceptor {
Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass()); Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass());
try { try {
for (Field field : fields) { for (Field field : fields) {
field.set(sourceObject, this.decryptField(String.valueOf(field.get(sourceObject)), field)); field.set(sourceObject, this.decryptField(Convert.toStr(field.get(sourceObject)), field));
} }
} catch (Exception e) { } catch (Exception e) {
log.error("处理解密字段时出错", e); log.error("处理解密字段时出错", e);

View File

@@ -1,6 +1,7 @@
package com.ruoyi.framework.encrypt; package com.ruoyi.framework.encrypt;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.ruoyi.common.annotation.EncryptField; import com.ruoyi.common.annotation.EncryptField;
import com.ruoyi.common.encrypt.EncryptContext; import com.ruoyi.common.encrypt.EncryptContext;
@@ -86,7 +87,7 @@ public class MybatisEncryptInterceptor implements Interceptor {
Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass()); Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass());
try { try {
for (Field field : fields) { for (Field field : fields) {
field.set(sourceObject, this.encryptField(String.valueOf(field.get(sourceObject)), field)); field.set(sourceObject, this.encryptField(Convert.toStr(field.get(sourceObject)), field));
} }
} catch (Exception e) { } catch (Exception e) {
log.error("处理加密字段时出错", e); log.error("处理加密字段时出错", e);

View File

@@ -2,8 +2,6 @@ package com.ruoyi.framework.handler;
import cn.hutool.core.annotation.AnnotationUtil; import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ConcurrentHashSet;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ClassUtil; import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.ruoyi.common.annotation.DataColumn; import com.ruoyi.common.annotation.DataColumn;
@@ -51,11 +49,6 @@ public class PlusDataPermissionHandler {
*/ */
private final Map<String, DataPermission> dataPermissionCacheMap = new ConcurrentHashMap<>(); private final Map<String, DataPermission> dataPermissionCacheMap = new ConcurrentHashMap<>();
/**
* 无效注解方法缓存用于快速返回
*/
private final Set<String> invalidCacheSet = new ConcurrentHashSet<>();
/** /**
* spel 解析器 * spel 解析器
*/ */
@@ -69,10 +62,6 @@ public class PlusDataPermissionHandler {
public Expression getSqlSegment(Expression where, String mappedStatementId, boolean isSelect) { public Expression getSqlSegment(Expression where, String mappedStatementId, boolean isSelect) {
DataColumn[] dataColumns = findAnnotation(mappedStatementId); DataColumn[] dataColumns = findAnnotation(mappedStatementId);
if (ArrayUtil.isEmpty(dataColumns)) {
invalidCacheSet.add(mappedStatementId);
return where;
}
LoginUser currentUser = DataPermissionHelper.getVariable("user"); LoginUser currentUser = DataPermissionHelper.getVariable("user");
if (ObjectUtil.isNull(currentUser)) { if (ObjectUtil.isNull(currentUser)) {
currentUser = LoginHelper.getLoginUser(); currentUser = LoginHelper.getLoginUser();
@@ -156,7 +145,7 @@ public class PlusDataPermissionHandler {
return ""; return "";
} }
private DataColumn[] findAnnotation(String mappedStatementId) { public DataColumn[] findAnnotation(String mappedStatementId) {
StringBuilder sb = new StringBuilder(mappedStatementId); StringBuilder sb = new StringBuilder(mappedStatementId);
int index = sb.lastIndexOf("."); int index = sb.lastIndexOf(".");
String clazzName = sb.substring(0, index); String clazzName = sb.substring(0, index);
@@ -190,10 +179,4 @@ public class PlusDataPermissionHandler {
return null; return null;
} }
/**
* 是否为无效方法 无数据权限
*/
public boolean isInvalid(String mappedStatementId) {
return invalidCacheSet.contains(mappedStatementId);
}
} }

View File

@@ -1,9 +1,12 @@
package com.ruoyi.framework.interceptor; package com.ruoyi.framework.interceptor;
import cn.hutool.core.collection.ConcurrentHashSet;
import cn.hutool.core.util.ArrayUtil;
import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils; import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport; import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.ruoyi.common.annotation.DataColumn;
import com.ruoyi.framework.handler.PlusDataPermissionHandler; import com.ruoyi.framework.handler.PlusDataPermissionHandler;
import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.statement.delete.Delete; import net.sf.jsqlparser.statement.delete.Delete;
@@ -23,6 +26,7 @@ import org.apache.ibatis.session.RowBounds;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List; import java.util.List;
import java.util.Set;
/** /**
* 数据权限拦截器 * 数据权限拦截器
@@ -33,6 +37,11 @@ import java.util.List;
public class PlusDataPermissionInterceptor extends JsqlParserSupport implements InnerInterceptor { public class PlusDataPermissionInterceptor extends JsqlParserSupport implements InnerInterceptor {
private final PlusDataPermissionHandler dataPermissionHandler = new PlusDataPermissionHandler(); private final PlusDataPermissionHandler dataPermissionHandler = new PlusDataPermissionHandler();
/**
* 无效注解方法缓存用于快速返回
*/
private final Set<String> invalidCacheSet = new ConcurrentHashSet<>();
@Override @Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
@@ -41,7 +50,12 @@ public class PlusDataPermissionInterceptor extends JsqlParserSupport implements
return; return;
} }
// 检查是否无效 无数据权限注解 // 检查是否无效 无数据权限注解
if (dataPermissionHandler.isInvalid(ms.getId())) { if (invalidCacheSet.contains(ms.getId())) {
return;
}
DataColumn[] dataColumns = dataPermissionHandler.findAnnotation(ms.getId());
if (ArrayUtil.isEmpty(dataColumns)) {
invalidCacheSet.add(ms.getId());
return; return;
} }
// 解析 sql 分配对应方法 // 解析 sql 分配对应方法
@@ -58,6 +72,15 @@ public class PlusDataPermissionInterceptor extends JsqlParserSupport implements
if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) { if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) {
return; return;
} }
// 检查是否无效 无数据权限注解
if (invalidCacheSet.contains(ms.getId())) {
return;
}
DataColumn[] dataColumns = dataPermissionHandler.findAnnotation(ms.getId());
if (ArrayUtil.isEmpty(dataColumns)) {
invalidCacheSet.add(ms.getId());
return;
}
PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql(); PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
mpBs.sql(parserMulti(mpBs.sql(), ms.getId())); mpBs.sql(parserMulti(mpBs.sql(), ms.getId()));
} }

View File

@@ -44,14 +44,14 @@ public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor {
BufferedReader reader = request.getReader(); BufferedReader reader = request.getReader();
jsonParam = IoUtil.read(reader); jsonParam = IoUtil.read(reader);
} }
log.debug("[PLUS]开始请求 => URL[{}],参数类型[json],参数:[{}]", url, jsonParam); log.info("[PLUS]开始请求 => URL[{}],参数类型[json],参数:[{}]", url, jsonParam);
} else { } else {
Map<String, String[]> parameterMap = request.getParameterMap(); Map<String, String[]> parameterMap = request.getParameterMap();
if (MapUtil.isNotEmpty(parameterMap)) { if (MapUtil.isNotEmpty(parameterMap)) {
String parameters = JsonUtils.toJsonString(parameterMap); String parameters = JsonUtils.toJsonString(parameterMap);
log.debug("[PLUS]开始请求 => URL[{}],参数类型[param],参数:[{}]", url, parameters); log.info("[PLUS]开始请求 => URL[{}],参数类型[param],参数:[{}]", url, parameters);
} else { } else {
log.debug("[PLUS]开始请求 => URL[{}],无参数", url); log.info("[PLUS]开始请求 => URL[{}],无参数", url);
} }
} }
@@ -72,7 +72,7 @@ public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor {
if (!prodProfile.equals(SpringUtils.getActiveProfile())) { if (!prodProfile.equals(SpringUtils.getActiveProfile())) {
StopWatch stopWatch = invokeTimeTL.get(); StopWatch stopWatch = invokeTimeTL.get();
stopWatch.stop(); stopWatch.stop();
log.debug("[PLUS]结束请求 => URL[{}],耗时:[{}]毫秒", request.getMethod() + " " + request.getRequestURI(), stopWatch.getTime()); log.info("[PLUS]结束请求 => URL[{}],耗时:[{}]毫秒", request.getMethod() + " " + request.getRequestURI(), stopWatch.getTime());
invokeTimeTL.remove(); invokeTimeTL.remove();
} }
} }

View File

@@ -8,6 +8,7 @@ import cn.hutool.http.HttpStatus;
import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.exception.DemoModeException; import com.ruoyi.common.exception.DemoModeException;
import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.StreamUtils; import com.ruoyi.common.utils.StreamUtils;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.MyBatisSystemException; import org.mybatis.spring.MyBatisSystemException;
@@ -105,18 +106,27 @@ public class GlobalExceptionHandler {
*/ */
@ExceptionHandler(ServiceException.class) @ExceptionHandler(ServiceException.class)
public R<Void> handleServiceException(ServiceException e, HttpServletRequest request) { public R<Void> handleServiceException(ServiceException e, HttpServletRequest request) {
log.error(e.getMessage(), e); log.error(e.getMessage());
Integer code = e.getCode(); Integer code = e.getCode();
return ObjectUtil.isNotNull(code) ? R.fail(code, e.getMessage()) : R.fail(e.getMessage()); return ObjectUtil.isNotNull(code) ? R.fail(code, e.getMessage()) : R.fail(e.getMessage());
} }
/**
* 业务异常
*/
@ExceptionHandler(BaseException.class)
public R<Void> handleBaseException(BaseException e, HttpServletRequest request) {
log.error(e.getMessage());
return R.fail(e.getMessage());
}
/** /**
* 请求路径中缺少必需的路径变量 * 请求路径中缺少必需的路径变量
*/ */
@ExceptionHandler(MissingPathVariableException.class) @ExceptionHandler(MissingPathVariableException.class)
public R<Void> handleMissingPathVariableException(MissingPathVariableException e, HttpServletRequest request) { public R<Void> handleMissingPathVariableException(MissingPathVariableException e, HttpServletRequest request) {
String requestURI = request.getRequestURI(); String requestURI = request.getRequestURI();
log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI, e); log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI);
return R.fail(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName())); return R.fail(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName()));
} }
@@ -126,7 +136,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentTypeMismatchException.class) @ExceptionHandler(MethodArgumentTypeMismatchException.class)
public R<Void> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request) { public R<Void> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request) {
String requestURI = request.getRequestURI(); String requestURI = request.getRequestURI();
log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI, e); log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI);
return R.fail(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getName(), e.getRequiredType().getName(), e.getValue())); return R.fail(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getName(), e.getRequiredType().getName(), e.getValue()));
} }
@@ -155,7 +165,7 @@ public class GlobalExceptionHandler {
*/ */
@ExceptionHandler(BindException.class) @ExceptionHandler(BindException.class)
public R<Void> handleBindException(BindException e) { public R<Void> handleBindException(BindException e) {
log.error(e.getMessage(), e); log.error(e.getMessage());
String message = StreamUtils.join(e.getAllErrors(), DefaultMessageSourceResolvable::getDefaultMessage, ", "); String message = StreamUtils.join(e.getAllErrors(), DefaultMessageSourceResolvable::getDefaultMessage, ", ");
return R.fail(message); return R.fail(message);
} }
@@ -165,7 +175,7 @@ public class GlobalExceptionHandler {
*/ */
@ExceptionHandler(ConstraintViolationException.class) @ExceptionHandler(ConstraintViolationException.class)
public R<Void> constraintViolationException(ConstraintViolationException e) { public R<Void> constraintViolationException(ConstraintViolationException e) {
log.error(e.getMessage(), e); log.error(e.getMessage());
String message = StreamUtils.join(e.getConstraintViolations(), ConstraintViolation::getMessage, ", "); String message = StreamUtils.join(e.getConstraintViolations(), ConstraintViolation::getMessage, ", ");
return R.fail(message); return R.fail(message);
} }
@@ -175,7 +185,7 @@ public class GlobalExceptionHandler {
*/ */
@ExceptionHandler(MethodArgumentNotValidException.class) @ExceptionHandler(MethodArgumentNotValidException.class)
public R<Void> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { public R<Void> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
log.error(e.getMessage(), e); log.error(e.getMessage());
String message = e.getBindingResult().getFieldError().getDefaultMessage(); String message = e.getBindingResult().getFieldError().getDefaultMessage();
return R.fail(message); return R.fail(message);
} }

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>ruoyi-vue-plus</artifactId> <artifactId>ruoyi-vue-plus</artifactId>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<version>4.8.0</version> <version>4.8.2</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -78,7 +78,7 @@ public class GenController extends BaseController {
*/ */
@SaCheckPermission("tool:gen:list") @SaCheckPermission("tool:gen:list")
@GetMapping(value = "/column/{tableId}") @GetMapping(value = "/column/{tableId}")
public TableDataInfo<GenTableColumn> columnList(Long tableId) { public TableDataInfo<GenTableColumn> columnList(@PathVariable("tableId") Long tableId) {
TableDataInfo<GenTableColumn> dataInfo = new TableDataInfo<>(); TableDataInfo<GenTableColumn> dataInfo = new TableDataInfo<>();
List<GenTableColumn> list = genTableService.selectGenTableColumnListByTableId(tableId); List<GenTableColumn> list = genTableService.selectGenTableColumnListByTableId(tableId);
dataInfo.setRows(list); dataInfo.setRows(list);

View File

@@ -32,7 +32,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<select id="selectDbTableColumnsByName" parameterType="String" resultMap="GenTableColumnResult"> <select id="selectDbTableColumnsByName" parameterType="String" resultMap="GenTableColumnResult">
<if test="@com.ruoyi.common.helper.DataBaseHelper@isMySql()"> <if test="@com.ruoyi.common.helper.DataBaseHelper@isMySql()">
select column_name, select column_name,
(case when (is_nullable = 'no' <![CDATA[ && ]]> column_key != 'PRI') then '1' else null end) as is_required, (case when (is_nullable = 'no' <![CDATA[ && ]]> column_key != 'PRI') then '1' else '0' end) as is_required,
(case when column_key = 'PRI' then '1' else '0' end) as is_pk, (case when column_key = 'PRI' then '1' else '0' end) as is_pk,
ordinal_position as sort, ordinal_position as sort,
column_comment, column_comment,
@@ -43,7 +43,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</if> </if>
<if test="@com.ruoyi.common.helper.DataBaseHelper@isOracle()"> <if test="@com.ruoyi.common.helper.DataBaseHelper@isOracle()">
select lower(temp.column_name) as column_name, select lower(temp.column_name) as column_name,
(case when (temp.nullable = 'N' and temp.constraint_type != 'P') then '1' else null end) as is_required, (case when (temp.nullable = 'N' and temp.constraint_type != 'P') then '1' else '0' end) as is_required,
(case when temp.constraint_type = 'P' then '1' else '0' end) as is_pk, (case when temp.constraint_type = 'P' then '1' else '0' end) as is_pk,
temp.column_id as sort, temp.column_id as sort,
temp.comments as column_comment, temp.comments as column_comment,

View File

@@ -10,7 +10,7 @@ import com.ruoyi.common.convert.ExcelDictConvert;
import lombok.Data; import lombok.Data;
import java.util.Date; import java.util.Date;
import java.io.Serializable;
/** /**
* ${functionName}视图对象 ${tableName} * ${functionName}视图对象 ${tableName}
@@ -20,7 +20,7 @@ import java.util.Date;
*/ */
@Data @Data
@ExcelIgnoreUnannotated @ExcelIgnoreUnannotated
public class ${ClassName}Vo { public class ${ClassName}Vo implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

View File

@@ -455,7 +455,7 @@ export default {
this.reset(); this.reset();
this.getTreeselect(); this.getTreeselect();
if (row != null) { if (row != null) {
this.form.${treeParentCode} = row.${treeCode}; this.form.${treeParentCode} = row.${treeParentCode};
} }
get${BusinessName}(row.${pkColumn.javaField}).then(response => { get${BusinessName}(row.${pkColumn.javaField}).then(response => {
this.loading = false; this.loading = false;

View File

@@ -421,7 +421,7 @@ async function handleUpdate(row) {
reset(); reset();
await getTreeselect(); await getTreeselect();
if (row != null) { if (row != null) {
form.value.${treeParentCode} = row.${treeCode}; form.value.${treeParentCode} = row.${treeParentCode};
} }
get${BusinessName}(row.${pkColumn.javaField}).then(response => { get${BusinessName}(row.${pkColumn.javaField}).then(response => {
loading.value = false; loading.value = false;

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>ruoyi-vue-plus</artifactId> <artifactId>ruoyi-vue-plus</artifactId>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<version>4.8.0</version> <version>4.8.2</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging> <packaging>jar</packaging>

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>ruoyi-vue-plus</artifactId> <artifactId>ruoyi-vue-plus</artifactId>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<version>4.8.0</version> <version>4.8.2</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -39,7 +39,7 @@ public class OssFactory {
/** /**
* 根据类型获取实例 * 根据类型获取实例
*/ */
public static OssClient instance(String configKey) { public static synchronized OssClient instance(String configKey) {
String json = CacheUtils.get(CacheNames.SYS_OSS_CONFIG, configKey); String json = CacheUtils.get(CacheNames.SYS_OSS_CONFIG, configKey);
if (json == null) { if (json == null) {
throw new OssException("系统异常, '" + configKey + "'配置信息不存在!"); throw new OssException("系统异常, '" + configKey + "'配置信息不存在!");

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>ruoyi-vue-plus</artifactId> <artifactId>ruoyi-vue-plus</artifactId>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<version>4.8.0</version> <version>4.8.2</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>ruoyi-vue-plus</artifactId> <artifactId>ruoyi-vue-plus</artifactId>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<version>4.8.0</version> <version>4.8.2</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -19,7 +19,8 @@ import java.util.List;
public interface SysRoleMapper extends BaseMapperPlus<SysRoleMapper, SysRole, SysRole> { public interface SysRoleMapper extends BaseMapperPlus<SysRoleMapper, SysRole, SysRole> {
@DataPermission({ @DataPermission({
@DataColumn(key = "deptName", value = "d.dept_id") @DataColumn(key = "deptName", value = "d.dept_id"),
@DataColumn(key = "userName", value = "us.user_id")
}) })
Page<SysRole> selectPageRoleList(@Param("page") Page<SysRole> page, @Param(Constants.WRAPPER) Wrapper<SysRole> queryWrapper); Page<SysRole> selectPageRoleList(@Param("page") Page<SysRole> page, @Param(Constants.WRAPPER) Wrapper<SysRole> queryWrapper);
@@ -30,7 +31,8 @@ public interface SysRoleMapper extends BaseMapperPlus<SysRoleMapper, SysRole, Sy
* @return 角色数据集合信息 * @return 角色数据集合信息
*/ */
@DataPermission({ @DataPermission({
@DataColumn(key = "deptName", value = "d.dept_id") @DataColumn(key = "deptName", value = "d.dept_id"),
@DataColumn(key = "userName", value = "us.user_id")
}) })
List<SysRole> selectRoleList(@Param(Constants.WRAPPER) Wrapper<SysRole> queryWrapper); List<SysRole> selectRoleList(@Param(Constants.WRAPPER) Wrapper<SysRole> queryWrapper);

View File

@@ -143,9 +143,13 @@ public class SysLoginService {
public void logout() { public void logout() {
try { try {
LoginUser loginUser = LoginHelper.getLoginUser(); LoginUser loginUser = LoginHelper.getLoginUser();
StpUtil.logout();
recordLogininfor(loginUser.getUsername(), Constants.LOGOUT, MessageUtils.message("user.logout.success")); recordLogininfor(loginUser.getUsername(), Constants.LOGOUT, MessageUtils.message("user.logout.success"));
} catch (NotLoginException ignored) { } catch (NotLoginException ignored) {
} finally {
try {
StpUtil.logout();
} catch (NotLoginException ignored) {
}
} }
} }

View File

@@ -49,11 +49,8 @@ public class SysDataScopeServiceImpl implements ISysDataScopeService {
.apply(DataBaseHelper.findInSet(deptId, "ancestors"))); .apply(DataBaseHelper.findInSet(deptId, "ancestors")));
List<Long> ids = StreamUtils.toList(deptList, SysDept::getDeptId); List<Long> ids = StreamUtils.toList(deptList, SysDept::getDeptId);
ids.add(deptId); ids.add(deptId);
List<SysDept> list = deptMapper.selectList(new LambdaQueryWrapper<SysDept>() if (CollUtil.isNotEmpty(ids)) {
.select(SysDept::getDeptId) return StreamUtils.join(ids, Convert::toStr);
.in(SysDept::getDeptId, ids));
if (CollUtil.isNotEmpty(list)) {
return StreamUtils.join(list, d -> Convert.toStr(d.getDeptId()));
} }
return null; return null;
} }

View File

@@ -72,6 +72,8 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService {
*/ */
@Override @Override
public List<Tree<Long>> selectDeptTreeList(SysDept dept) { public List<Tree<Long>> selectDeptTreeList(SysDept dept) {
// 只查询未禁用部门
dept.setStatus(UserConstants.DEPT_NORMAL);
List<SysDept> depts = this.selectDeptList(dept); List<SysDept> depts = this.selectDeptList(dept);
return buildDeptTreeSelect(depts); return buildDeptTreeSelect(depts);
} }

View File

@@ -437,7 +437,7 @@ public class SysMenuServiceImpl implements ISysMenuService {
* 内链域名特殊字符替换 * 内链域名特殊字符替换
*/ */
public String innerLinkReplaceEach(String path) { public String innerLinkReplaceEach(String path) {
return StringUtils.replaceEach(path, new String[]{Constants.HTTP, Constants.HTTPS, Constants.WWW, "."}, return StringUtils.replaceEach(path, new String[]{Constants.HTTP, Constants.HTTPS, Constants.WWW, ".", ":"},
new String[]{"", "", "", "/"}); new String[]{"", "", "", "/", "/"});
} }
} }

View File

@@ -51,6 +51,7 @@ public class SysOperLogServiceImpl implements ISysOperLogService {
public TableDataInfo<SysOperLog> selectPageOperLogList(SysOperLog operLog, PageQuery pageQuery) { public TableDataInfo<SysOperLog> selectPageOperLogList(SysOperLog operLog, PageQuery pageQuery) {
Map<String, Object> params = operLog.getParams(); Map<String, Object> params = operLog.getParams();
LambdaQueryWrapper<SysOperLog> lqw = new LambdaQueryWrapper<SysOperLog>() LambdaQueryWrapper<SysOperLog> lqw = new LambdaQueryWrapper<SysOperLog>()
.like(StringUtils.isNotBlank(operLog.getOperIp()), SysOperLog::getOperIp, operLog.getOperIp())
.like(StringUtils.isNotBlank(operLog.getTitle()), SysOperLog::getTitle, operLog.getTitle()) .like(StringUtils.isNotBlank(operLog.getTitle()), SysOperLog::getTitle, operLog.getTitle())
.eq(operLog.getBusinessType() != null && operLog.getBusinessType() > 0, .eq(operLog.getBusinessType() != null && operLog.getBusinessType() > 0,
SysOperLog::getBusinessType, operLog.getBusinessType()) SysOperLog::getBusinessType, operLog.getBusinessType())
@@ -93,6 +94,7 @@ public class SysOperLogServiceImpl implements ISysOperLogService {
public List<SysOperLog> selectOperLogList(SysOperLog operLog) { public List<SysOperLog> selectOperLogList(SysOperLog operLog) {
Map<String, Object> params = operLog.getParams(); Map<String, Object> params = operLog.getParams();
return baseMapper.selectList(new LambdaQueryWrapper<SysOperLog>() return baseMapper.selectList(new LambdaQueryWrapper<SysOperLog>()
.like(StringUtils.isNotBlank(operLog.getOperIp()), SysOperLog::getOperIp, operLog.getOperIp())
.like(StringUtils.isNotBlank(operLog.getTitle()), SysOperLog::getTitle, operLog.getTitle()) .like(StringUtils.isNotBlank(operLog.getTitle()), SysOperLog::getTitle, operLog.getTitle())
.eq(operLog.getBusinessType() != null && operLog.getBusinessType() > 0, .eq(operLog.getBusinessType() != null && operLog.getBusinessType() > 0,
SysOperLog::getBusinessType, operLog.getBusinessType()) SysOperLog::getBusinessType, operLog.getBusinessType())

View File

@@ -66,8 +66,12 @@ public class SysOssServiceImpl implements ISysOssService, OssService {
for (Long id : ossIds) { for (Long id : ossIds) {
SysOssVo vo = SpringUtils.getAopProxy(this).getById(id); SysOssVo vo = SpringUtils.getAopProxy(this).getById(id);
if (ObjectUtil.isNotNull(vo)) { if (ObjectUtil.isNotNull(vo)) {
list.add(this.matchingUrl(vo)); try {
} list.add(this.matchingUrl(vo));
} catch (Exception ignored) {
// 如果oss异常无法连接则将数据直接返回
list.add(vo);
} }
} }
return list; return list;
} }
@@ -78,7 +82,12 @@ public class SysOssServiceImpl implements ISysOssService, OssService {
for (Long id : StringUtils.splitTo(ossIds, Convert::toLong)) { for (Long id : StringUtils.splitTo(ossIds, Convert::toLong)) {
SysOssVo vo = SpringUtils.getAopProxy(this).getById(id); SysOssVo vo = SpringUtils.getAopProxy(this).getById(id);
if (ObjectUtil.isNotNull(vo)) { if (ObjectUtil.isNotNull(vo)) {
list.add(this.matchingUrl(vo).getUrl()); try {
list.add(this.matchingUrl(vo).getUrl());
} catch (Exception ignored) {
// 如果oss异常无法连接则将数据直接返回
list.add(vo.getUrl());
}
} }
} }
return String.join(StringUtils.SEPARATOR, list); return String.join(StringUtils.SEPARATOR, list);

View File

@@ -147,7 +147,7 @@ public class SysPostServiceImpl implements ISysPostService {
for (Long postId : postIds) { for (Long postId : postIds) {
SysPost post = selectPostById(postId); SysPost post = selectPostById(postId);
if (countUserPostById(postId) > 0) { if (countUserPostById(postId) > 0) {
throw new ServiceException(String.format("%1$s已分配,不能删除", post.getPostName())); throw new ServiceException(String.format("%1$s已分配不能删除!", post.getPostName()));
} }
} }
return baseMapper.deleteBatchIds(Arrays.asList(postIds)); return baseMapper.deleteBatchIds(Arrays.asList(postIds));

View File

@@ -195,9 +195,12 @@ public class SysRoleServiceImpl implements ISysRoleService {
if (ObjectUtil.isNotNull(role.getRoleId())) { if (ObjectUtil.isNotNull(role.getRoleId())) {
SysRole sysRole = baseMapper.selectById(role.getRoleId()); SysRole sysRole = baseMapper.selectById(role.getRoleId());
// 如果标识符不相等 判断为修改了管理员标识符 // 如果标识符不相等 判断为修改了管理员标识符
if (!StringUtils.equals(sysRole.getRoleKey(), role.getRoleKey()) if (!StringUtils.equals(sysRole.getRoleKey(), role.getRoleKey())) {
&& StringUtils.equals(sysRole.getRoleKey(), UserConstants.ADMIN_ROLE_KEY)) { if (StringUtils.equals(sysRole.getRoleKey(), UserConstants.ADMIN_ROLE_KEY)) {
throw new ServiceException("不允许修改系统内置管理员角色标识符!"); throw new ServiceException("不允许修改系统内置管理员角色标识符!");
} else if (StringUtils.equals(role.getRoleKey(), UserConstants.ADMIN_ROLE_KEY)) {
throw new ServiceException("不允许使用系统内置管理员角色标识符!");
}
} }
} }
} }
@@ -268,6 +271,9 @@ public class SysRoleServiceImpl implements ISysRoleService {
*/ */
@Override @Override
public int updateRoleStatus(SysRole role) { public int updateRoleStatus(SysRole role) {
if (UserConstants.ROLE_DISABLE.equals(role.getStatus()) && this.countUserRoleByRoleId(role.getRoleId()) > 0) {
throw new ServiceException("角色已分配,不能禁用!");
}
return baseMapper.updateById(role); return baseMapper.updateById(role);
} }
@@ -360,7 +366,7 @@ public class SysRoleServiceImpl implements ISysRoleService {
checkRoleAllowed(role); checkRoleAllowed(role);
checkRoleDataScope(roleId); checkRoleDataScope(roleId);
if (countUserRoleByRoleId(roleId) > 0) { if (countUserRoleByRoleId(roleId) > 0) {
throw new ServiceException(String.format("%1$s已分配,不能删除", role.getRoleName())); throw new ServiceException(String.format("%1$s已分配不能删除!", role.getRoleName()));
} }
} }
List<Long> ids = Arrays.asList(roleIds); List<Long> ids = Arrays.asList(roleIds);

View File

@@ -36,6 +36,7 @@
from sys_role r from sys_role r
left join sys_user_role sur on sur.role_id = r.role_id left join sys_user_role sur on sur.role_id = r.role_id
left join sys_user u on u.user_id = sur.user_id left join sys_user u on u.user_id = sur.user_id
left join sys_user us on us.user_name = r.create_by
left join sys_dept d on u.dept_id = d.dept_id left join sys_dept d on u.dept_id = d.dept_id
</sql> </sql>

View File

@@ -1,6 +1,6 @@
{ {
"name": "ruoyi-vue-plus", "name": "ruoyi-vue-plus",
"version": "4.8.0", "version": "4.8.2",
"description": "RuoYi-Vue-Plus后台管理系统", "description": "RuoYi-Vue-Plus后台管理系统",
"author": "LionLi", "author": "LionLi",
"license": "MIT", "license": "MIT",
@@ -15,7 +15,7 @@
}, },
"dependencies": { "dependencies": {
"@element-plus/icons-vue": "2.0.10", "@element-plus/icons-vue": "2.0.10",
"@vueup/vue-quill": "1.1.0", "@vueup/vue-quill": "1.2.0",
"@vueuse/core": "9.5.0", "@vueuse/core": "9.5.0",
"axios": "0.27.2", "axios": "0.27.2",
"echarts": "5.4.0", "echarts": "5.4.0",
@@ -38,6 +38,6 @@
"vite": "3.2.3", "vite": "3.2.3",
"vite-plugin-compression": "0.5.1", "vite-plugin-compression": "0.5.1",
"vite-plugin-svg-icons": "2.0.1", "vite-plugin-svg-icons": "2.0.1",
"vite-plugin-vue-setup-extend": "0.4.0" "unplugin-vue-setup-extend-plus": "0.4.9"
} }
} }

View File

@@ -3,7 +3,7 @@
<template v-for="(item, index) in options"> <template v-for="(item, index) in options">
<template v-if="values.includes(item.value)"> <template v-if="values.includes(item.value)">
<span <span
v-if="item.elTagType == 'default' || item.elTagType == ''" v-if="(item.elTagType == 'default' || item.elTagType == '') && (item.elTagClass == '' || item.elTagClass == null)"
:key="item.value" :key="item.value"
:index="index" :index="index"
:class="item.elTagClass" :class="item.elTagClass"
@@ -41,34 +41,30 @@ const props = defineProps({
type: Boolean, type: Boolean,
default: true, default: true,
}, },
separator: {
type: String,
default: ",",
}
}); });
const values = computed(() => { const values = computed(() => {
if (props.value !== null && typeof props.value !== 'undefined') { if (props.value === null || typeof props.value === 'undefined' || props.value === '') return [];
return Array.isArray(props.value) ? props.value : [String(props.value)]; return Array.isArray(props.value) ? props.value.map(item => '' + item) : String(props.value).split(props.separator);
} else {
return [];
}
}); });
const unmatch = computed(() => { const unmatch = computed(() => {
unmatchArray.value = []; unmatchArray.value = [];
if (props.value !== null && typeof props.value !== "undefined") {
// 传入值为非数组
if (!Array.isArray(props.value)) {
if (props.options.some((v) => v.value == props.value)) return false;
unmatchArray.value.push(props.value);
return true;
}
// 传入值为Array
props.value.forEach((item) => {
if (!props.options.some((v) => v.value == item))
unmatchArray.value.push(item);
});
return true;
}
// 没有value不显示 // 没有value不显示
return false; if (props.value === null || typeof props.value === 'undefined' || props.value === '' || props.options.length === 0) return false
// 传入值为数组
let unmatch = false // 添加一个标志来判断是否有未匹配项
values.value.forEach(item => {
if (!props.options.some(v => v.value === item)) {
unmatchArray.value.push(item)
unmatch = true // 如果有未匹配项将标志设置为true
}
})
return unmatch // 返回标志的值
}); });
function handleArray(array) { function handleArray(array) {

View File

@@ -9,14 +9,13 @@
name="file" name="file"
:show-file-list="false" :show-file-list="false"
:headers="headers" :headers="headers"
style="display: none"
ref="uploadRef" ref="uploadRef"
v-if="type == 'url'" v-if="type == 'url'"
> >
</el-upload> </el-upload>
<div class="editor"> <div class="editor">
<quill-editor <quill-editor
ref="myQuillEditor" ref="quillEditorRef"
v-model:content="content" v-model:content="content"
contentType="html" contentType="html"
@textChange="(e) => $emit('update:modelValue', content)" @textChange="(e) => $emit('update:modelValue', content)"
@@ -68,7 +67,7 @@ const { proxy } = getCurrentInstance();
// 上传的图片服务器地址 // 上传的图片服务器地址
const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/system/oss/upload"); const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/system/oss/upload");
const headers = ref({ Authorization: "Bearer " + getToken() }); const headers = ref({ Authorization: "Bearer " + getToken() });
const myQuillEditor = ref(); const quillEditorRef = ref();
const options = ref({ const options = ref({
theme: "snow", theme: "snow",
@@ -101,7 +100,7 @@ const options = ref({
}, },
} }
}, },
placeholder: '请输入内容', placeholder: "请输入内容",
readOnly: props.readOnly, readOnly: props.readOnly,
}); });
@@ -114,7 +113,7 @@ const styles = computed(() => {
style.height = `${props.height}px`; style.height = `${props.height}px`;
} }
return style; return style;
}) });
const content = ref(""); const content = ref("");
watch(() => props.modelValue, (v) => { watch(() => props.modelValue, (v) => {
@@ -125,10 +124,10 @@ watch(() => props.modelValue, (v) => {
// 图片上传成功返回图片地址 // 图片上传成功返回图片地址
function handleUploadSuccess(res, file) { function handleUploadSuccess(res, file) {
// 获取富文本实例
let quill = toRaw(myQuillEditor.value).getQuill();
// 如果上传成功 // 如果上传成功
if (res.code == 200) { if (res.code == 200) {
// 获取富文本实例
let quill = toRaw(quillEditorRef.value).getQuill();
// 获取光标位置 // 获取光标位置
let length = quill.selection.savedRange.index; let length = quill.selection.savedRange.index;
// 插入图片res为服务器返回的图片链接地址 // 插入图片res为服务器返回的图片链接地址
@@ -144,6 +143,13 @@ function handleUploadSuccess(res, file) {
// 图片上传前拦截 // 图片上传前拦截
function handleBeforeUpload(file) { function handleBeforeUpload(file) {
const type = ["image/jpeg", "image/jpg", "image/png", "image/svg"];
const isJPG = type.includes(file.type);
//检验文件格式
if (!isJPG) {
proxy.$modal.msgError(`图片格式错误!`);
return false;
}
// 校检文件大小 // 校检文件大小
if (props.fileSize) { if (props.fileSize) {
const isLt = file.size / 1024 / 1024 < props.fileSize; const isLt = file.size / 1024 / 1024 < props.fileSize;
@@ -164,6 +170,9 @@ function handleUploadError(err) {
</script> </script>
<style> <style>
.editor-img-uploader {
display: none;
}
.editor, .ql-toolbar { .editor, .ql-toolbar {
white-space: pre-wrap !important; white-space: pre-wrap !important;
line-height: normal !important; line-height: normal !important;

View File

@@ -45,12 +45,17 @@ function close() {
} }
function change(val) { function change(val) {
const path = val.path; const path = val.path;
const query = val.query;
if (isHttp(path)) { if (isHttp(path)) {
// http(s):// 路径新窗口打开 // http(s):// 路径新窗口打开
const pindex = path.indexOf("http"); const pindex = path.indexOf("http");
window.open(path.substr(pindex, path.length), "_blank"); window.open(path.substr(pindex, path.length), "_blank");
} else { } else {
router.push(path) if (query) {
router.push({ path: path, query: JSON.parse(query) });
} else {
router.push(path)
}
} }
search.value = '' search.value = ''
@@ -77,7 +82,7 @@ function initFuse(list) {
} }
// Filter out the routes that can be displayed in the sidebar // Filter out the routes that can be displayed in the sidebar
// And generate the internationalized title // And generate the internationalized title
function generateRoutes(routes, basePath = '', prefixTitle = []) { function generateRoutes(routes, basePath = '', prefixTitle = [], query = {}) {
let res = [] let res = []
for (const r of routes) { for (const r of routes) {
@@ -99,9 +104,13 @@ function generateRoutes(routes, basePath = '', prefixTitle = []) {
} }
} }
if (r.query) {
data.query = r.query
}
// recursive child routes // recursive child routes
if (r.children) { if (r.children) {
const tempRoutes = generateRoutes(r.children, data.path, data.title) const tempRoutes = generateRoutes(r.children, data.path, data.title, data.query)
if (tempRoutes.length >= 1) { if (tempRoutes.length >= 1) {
res = [...res, ...tempRoutes] res = [...res, ...tempRoutes]
} }
@@ -176,4 +185,4 @@ watch(searchPool, (list) => {
} }
} }
} }
</style> </style>

View File

@@ -6,10 +6,12 @@
:ellipsis="false" :ellipsis="false"
> >
<template v-for="(item, index) in topMenus"> <template v-for="(item, index) in topMenus">
<el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber" <el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber">
><svg-icon :icon-class="item.meta.icon" /> <svg-icon
{{ item.meta.title }}</el-menu-item v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
> :icon-class="item.meta.icon"/>
{{ item.meta.title }}
</el-menu-item>
</template> </template>
<!-- 顶部菜单超出数量折叠 --> <!-- 顶部菜单超出数量折叠 -->
@@ -19,10 +21,12 @@
<el-menu-item <el-menu-item
:index="item.path" :index="item.path"
:key="index" :key="index"
v-if="index >= visibleNumber" v-if="index >= visibleNumber">
><svg-icon :icon-class="item.meta.icon" /> <svg-icon
{{ item.meta.title }}</el-menu-item v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
> :icon-class="item.meta.icon"/>
{{ item.meta.title }}
</el-menu-item>
</template> </template>
</el-sub-menu> </el-sub-menu>
</el-menu> </el-menu>
@@ -189,4 +193,22 @@ onMounted(() => {
padding: 0 5px !important; padding: 0 5px !important;
margin: 0 10px !important; margin: 0 10px !important;
} }
/* 背景色隐藏 */
.topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):focus, .topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):hover, .topmenu-container.el-menu--horizontal>.el-submenu .el-submenu__title:hover {
background-color: #ffffff !important;
}
/* 图标右间距 */
.topmenu-container .svg-icon {
margin-right: 4px;
}
/* topmenu more arrow */
.topmenu-container .el-sub-menu .el-sub-menu__icon-arrow {
position: static;
vertical-align: middle;
margin-left: 8px;
margin-top: 0px;
}
</style> </style>

View File

@@ -5,15 +5,23 @@
:key="item.path" :key="item.path"
:iframeId="'iframe' + index" :iframeId="'iframe' + index"
v-show="route.path === item.path" v-show="route.path === item.path"
:src="item.meta.link" :src="iframeUrl(item.meta.link, item.query)"
></inner-link> ></inner-link>
</transition-group> </transition-group>
</template> </template>
<script setup> <script setup>
import InnerLink from "../InnerLink/index" import InnerLink from "../InnerLink/index";
import useTagsViewStore from '@/store/modules/tagsView' import useTagsViewStore from "@/store/modules/tagsView";
const route = useRoute(); const route = useRoute();
const tagsViewStore = useTagsViewStore() const tagsViewStore = useTagsViewStore();
function iframeUrl(url, query) {
if (Object.keys(query).length > 0) {
let params = Object.keys(query).map((key) => key + "=" + query[key]).join("&");
return url + "?" + params;
}
return url;
}
</script> </script>

View File

@@ -33,7 +33,7 @@
<router-link to="/user/profile"> <router-link to="/user/profile">
<el-dropdown-item>个人中心</el-dropdown-item> <el-dropdown-item>个人中心</el-dropdown-item>
</router-link> </router-link>
<el-dropdown-item command="setLayout"> <el-dropdown-item command="setLayout" v-if="settingsStore.showSettings">
<span>布局设置</span> <span>布局设置</span>
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item divided command="logout"> <el-dropdown-item divided command="logout">

View File

@@ -280,7 +280,7 @@ function handleScroll() {
height: 8px; height: 8px;
border-radius: 50%; border-radius: 50%;
position: relative; position: relative;
margin-right: 2px; margin-right: 5px;
} }
} }
} }
@@ -335,4 +335,4 @@ function handleScroll() {
} }
} }
} }
</style> </style>

View File

@@ -10,7 +10,7 @@ export default {
/** /**
* 是否系统布局配置 * 是否系统布局配置
*/ */
showSettings: false, showSettings: true,
/** /**
* 是否显示顶部导航 * 是否显示顶部导航

View File

@@ -101,6 +101,10 @@ function filterChildren(childrenMap, lastRouter = false) {
} }
if (lastRouter) { if (lastRouter) {
el.path = lastRouter.path + '/' + el.path el.path = lastRouter.path + '/' + el.path
if (el.children && el.children.length) {
children = children.concat(filterChildren(el.children, el))
return
}
} }
children = children.concat(el) children = children.concat(el)
}) })

View File

@@ -7,6 +7,7 @@ const useUserStore = defineStore(
{ {
state: () => ({ state: () => ({
token: getToken(), token: getToken(),
id: '',
name: '', name: '',
avatar: '', avatar: '',
roles: [], roles: [],
@@ -42,8 +43,9 @@ const useUserStore = defineStore(
} else { } else {
this.roles = ['ROLE_DEFAULT'] this.roles = ['ROLE_DEFAULT']
} }
this.id = user.userId
this.name = user.userName this.name = user.userName
this.avatar = avatar; this.avatar = avatar
resolve(res) resolve(res)
}).catch(error => { }).catch(error => {
reject(error) reject(error)

View File

@@ -336,7 +336,7 @@ function submitForm() {
proxy.$refs["demoRef"].validate(valid => { proxy.$refs["demoRef"].validate(valid => {
if (valid) { if (valid) {
buttonLoading.value = true; buttonLoading.value = true;
if (form.value.ossConfigId != null) { if (form.value.id != null) {
updateDemo(form.value).then(response => { updateDemo(form.value).then(response => {
proxy.$modal.msgSuccess("修改成功"); proxy.$modal.msgSuccess("修改成功");
open.value = false; open.value = false;

View File

@@ -243,7 +243,7 @@ function submitForm() {
proxy.$refs["treeRef"].validate(valid => { proxy.$refs["treeRef"].validate(valid => {
if (valid) { if (valid) {
buttonLoading.value = true; buttonLoading.value = true;
if (form.value.ossConfigId != null) { if (form.value.id != null) {
updateTree(form.value).then(response => { updateTree(form.value).then(response => {
proxy.$modal.msgSuccess("修改成功"); proxy.$modal.msgSuccess("修改成功");
open.value = false; open.value = false;

View File

@@ -103,7 +103,7 @@
</template> </template>
<script setup name="Index"> <script setup name="Index">
const version = ref('4.8.0') const version = ref('4.8.2')
function goTarget(url) { function goTarget(url) {
window.open(url, '__blank') window.open(url, '__blank')

View File

@@ -1,7 +1,16 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px"> <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="系统模块" prop="title"> <el-form-item label="操作地址" prop="operIp">
<el-input
v-model="queryParams.operIp"
placeholder="请输入操作地址"
clearable
style="width: 240px;"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="系统模块" prop="title">
<el-input <el-input
v-model="queryParams.title" v-model="queryParams.title"
placeholder="请输入系统模块" placeholder="请输入系统模块"
@@ -107,9 +116,12 @@
<dict-tag :options="sys_oper_type" :value="scope.row.businessType" /> <dict-tag :options="sys_oper_type" :value="scope.row.businessType" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="请求方式" align="center" prop="requestMethod" />
<el-table-column label="操作人员" align="center" width="110" prop="operName" :show-overflow-tooltip="true" sortable="custom" :sort-orders="['descending', 'ascending']" /> <el-table-column label="操作人员" align="center" width="110" prop="operName" :show-overflow-tooltip="true" sortable="custom" :sort-orders="['descending', 'ascending']" />
<el-table-column label="主机" align="center" prop="operIp" width="130" :show-overflow-tooltip="true" /> <el-table-column label="部门" align="center" prop="deptName" width="130" :show-overflow-tooltip="true" />
<el-table-column label="操作状态" align="center" prop="status"> <el-table-column label="操作地址" align="center" prop="operIp" width="130" :show-overflow-tooltip="true" />
<el-table-column label="操作地点" align="center" prop="operLocation" :show-overflow-tooltip="true" />
<el-table-column label="操作状态" align="center" prop="status">
<template #default="scope"> <template #default="scope">
<dict-tag :options="sys_common_status" :value="scope.row.status" /> <dict-tag :options="sys_common_status" :value="scope.row.status" />
</template> </template>
@@ -143,15 +155,16 @@
<el-dialog title="操作日志详细" v-model="open" width="700px" append-to-body> <el-dialog title="操作日志详细" v-model="open" width="700px" append-to-body>
<el-form :model="form" label-width="100px"> <el-form :model="form" label-width="100px">
<el-row> <el-row>
<el-col :span="12"> <el-col :span="24">
<el-form-item label="操作模块:">{{ form.title }} / {{ typeFormat(form) }}</el-form-item>
<el-form-item <el-form-item
label="登录信息:" label="登录信息:"
>{{ form.operName }} / {{ form.operIp }} / {{ form.operLocation }}</el-form-item> >{{ form.operName }} / {{form.deptName}} / {{ form.operIp }} / {{ form.operLocation }}</el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="请求地址">{{ form.operUrl }}</el-form-item> <el-form-item label="请求信息">{{ form.requestMethod }} {{form.operUrl }}</el-form-item>
<el-form-item label="请求方式:">{{ form.requestMethod }}</el-form-item> </el-col>
<el-col :span="12">
<el-form-item label="操作模块:">{{ form.title }} / {{ typeFormat(form) }}</el-form-item>
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="操作方法:">{{ form.method }}</el-form-item> <el-form-item label="操作方法:">{{ form.method }}</el-form-item>
@@ -211,6 +224,7 @@ const data = reactive({
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
operIp: undefined,
title: undefined, title: undefined,
operName: undefined, operName: undefined,
businessType: undefined, businessType: undefined,

View File

@@ -3,11 +3,11 @@
<el-form ref="registerRef" :model="registerForm" :rules="registerRules" class="register-form"> <el-form ref="registerRef" :model="registerForm" :rules="registerRules" class="register-form">
<h3 class="title">RuoYi-Vue-Plus后台管理系统</h3> <h3 class="title">RuoYi-Vue-Plus后台管理系统</h3>
<el-form-item prop="username"> <el-form-item prop="username">
<el-input <el-input
v-model="registerForm.username" v-model="registerForm.username"
type="text" type="text"
size="large" size="large"
auto-complete="off" auto-complete="off"
placeholder="账号" placeholder="账号"
> >
<template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template> <template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template>
@@ -17,7 +17,7 @@
<el-input <el-input
v-model="registerForm.password" v-model="registerForm.password"
type="password" type="password"
size="large" size="large"
auto-complete="off" auto-complete="off"
placeholder="密码" placeholder="密码"
@keyup.enter="handleRegister" @keyup.enter="handleRegister"
@@ -29,7 +29,7 @@
<el-input <el-input
v-model="registerForm.confirmPassword" v-model="registerForm.confirmPassword"
type="password" type="password"
size="large" size="large"
auto-complete="off" auto-complete="off"
placeholder="确认密码" placeholder="确认密码"
@keyup.enter="handleRegister" @keyup.enter="handleRegister"
@@ -39,7 +39,7 @@
</el-form-item> </el-form-item>
<el-form-item prop="code" v-if="captchaEnabled"> <el-form-item prop="code" v-if="captchaEnabled">
<el-input <el-input
size="large" size="large"
v-model="registerForm.code" v-model="registerForm.code"
auto-complete="off" auto-complete="off"
placeholder="验证码" placeholder="验证码"
@@ -55,7 +55,7 @@
<el-form-item style="width:100%;"> <el-form-item style="width:100%;">
<el-button <el-button
:loading="loading" :loading="loading"
size="large" size="large"
type="primary" type="primary"
style="width:100%;" style="width:100%;"
@click.prevent="handleRegister" @click.prevent="handleRegister"
@@ -143,10 +143,10 @@ function handleRegister() {
function getCode() { function getCode() {
getCodeImg().then(res => { getCodeImg().then(res => {
captchaEnabled.value = res.captchaEnabled === undefined ? true : res.captchaEnabled; captchaEnabled.value = res.data.captchaEnabled === undefined ? true : res.data.captchaEnabled;
if (captchaEnabled.value) { if (captchaEnabled.value) {
codeUrl.value = "data:image/gif;base64," + res.img; codeUrl.value = "data:image/gif;base64," + res.data.img;
registerForm.value.uuid = res.uuid; registerForm.value.uuid = res.data.uuid;
} }
}); });
} }

View File

@@ -91,8 +91,8 @@
<el-table-column label="字典编码" align="center" prop="dictCode" /> <el-table-column label="字典编码" align="center" prop="dictCode" />
<el-table-column label="字典标签" align="center" prop="dictLabel"> <el-table-column label="字典标签" align="center" prop="dictLabel">
<template #default="scope"> <template #default="scope">
<span v-if="scope.row.listClass == '' || scope.row.listClass == 'default'">{{ scope.row.dictLabel }}</span> <span v-if="(scope.row.listClass == '' || scope.row.listClass == 'default') && (scope.row.cssClass == '' || scope.row.cssClass == null)">{{ scope.row.dictLabel }}</span>
<el-tag v-else :type="scope.row.listClass == 'primary' ? '' : scope.row.listClass">{{ scope.row.dictLabel }}</el-tag> <el-tag v-else :type="scope.row.listClass == 'primary' ? '' : scope.row.listClass" :class="scope.row.cssClass">{{ scope.row.dictLabel }}</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="字典键值" align="center" prop="dictValue" /> <el-table-column label="字典键值" align="center" prop="dictValue" />
@@ -197,8 +197,8 @@ const typeOptions = ref([]);
const route = useRoute(); const route = useRoute();
// 数据标签回显样式 // 数据标签回显样式
const listClassOptions = ref([ const listClassOptions = ref([
{ value: "default", label: "默认" }, { value: "default", label: "默认" },
{ value: "primary", label: "主要" }, { value: "primary", label: "主要" },
{ value: "success", label: "成功" }, { value: "success", label: "成功" },
{ value: "info", label: "信息" }, { value: "info", label: "信息" },
{ value: "warning", label: "警告" }, { value: "warning", label: "警告" },

View File

@@ -246,7 +246,7 @@
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12" v-if="form.menuType != 'F'"> <el-col :span="12">
<el-form-item> <el-form-item>
<template #label> <template #label>
<span> <span>

View File

@@ -149,7 +149,7 @@
<el-form-item prop="roleKey"> <el-form-item prop="roleKey">
<template #label> <template #label>
<span> <span>
<el-tooltip content="控制器中定义的权限字符,如:@PreAuthorize(`@ss.hasRole('admin')`)" placement="top"> <el-tooltip content="控制器中定义的权限字符,如:@SaCheckRole('admin')" placement="top">
<el-icon><question-filled /></el-icon> <el-icon><question-filled /></el-icon>
</el-tooltip> </el-tooltip>
权限字符 权限字符

View File

@@ -605,4 +605,7 @@ function submitForm() {
getDeptTree(); getDeptTree();
getList(); getList();
proxy.getConfigKey("sys.user.initPassword").then(response => {
initPassword.value = response.msg;
});
</script> </script>

View File

@@ -10,7 +10,7 @@
</template> </template>
<div> <div>
<div class="text-center"> <div class="text-center">
<userAvatar :user="state.user" /> <userAvatar />
</div> </div>
<ul class="list-group list-group-striped"> <ul class="list-group list-group-striped">
<li class="list-group-item"> <li class="list-group-item">

View File

@@ -1,5 +1,5 @@
import setupExtend from 'vite-plugin-vue-setup-extend' import setupExtend from 'unplugin-vue-setup-extend-plus/vite'
export default function createSetupExtend() { export default function createSetupExtend() {
return setupExtend() return setupExtend({})
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "ruoyi-vue-plus", "name": "ruoyi-vue-plus",
"version": "4.8.0", "version": "4.8.2",
"description": "RuoYi-Vue-Plus后台管理系统", "description": "RuoYi-Vue-Plus后台管理系统",
"author": "LionLi", "author": "LionLi",
"license": "MIT", "license": "MIT",

View File

@@ -3,7 +3,7 @@
<template v-for="(item, index) in options"> <template v-for="(item, index) in options">
<template v-if="values.includes(item.value)"> <template v-if="values.includes(item.value)">
<span <span
v-if="item.raw.listClass == 'default' || item.raw.listClass == ''" v-if="(item.raw.listClass == 'default' || item.raw.listClass == '') && (item.raw.cssClass == '' || item.raw.cssClass == null)"
:key="item.value" :key="item.value"
:index="index" :index="index"
:class="item.raw.cssClass" :class="item.raw.cssClass"
@@ -40,6 +40,10 @@ export default {
showValue: { showValue: {
type: Boolean, type: Boolean,
default: true, default: true,
},
separator: {
type: String,
default: ","
} }
}, },
data() { data() {
@@ -49,35 +53,28 @@ export default {
}, },
computed: { computed: {
values() { values() {
if (this.value !== null && typeof this.value !== 'undefined') { if (this.value === null || typeof this.value === 'undefined' || this.value === '') return []
return Array.isArray(this.value) ? this.value : [String(this.value)]; return Array.isArray(this.value) ? this.value.map(item => '' + item) : String(this.value).split(this.separator)
} else {
return [];
}
}, },
unmatch(){ unmatch() {
this.unmatchArray = []; this.unmatchArray = []
if (this.value !== null && typeof this.value !== 'undefined') {
// 传入值为非数组
if(!Array.isArray(this.value)){
if(this.options.some(v=> v.value == this.value )) return false;
this.unmatchArray.push(this.value);
return true;
}
// 传入值为Array
this.value.forEach(item => {
if (!this.options.some(v=> v.value == item )) this.unmatchArray.push(item)
});
return true;
}
// 没有value不显示 // 没有value不显示
return false; if (this.value === null || typeof this.value === 'undefined' || this.value === '' || this.options.length === 0) return false
// 传入值为数组
let unmatch = false // 添加一个标志来判断是否有未匹配项
this.values.forEach(item => {
if (!this.options.some(v => v.value === item)) {
this.unmatchArray.push(item)
unmatch = true // 如果有未匹配项将标志设置为true
}
})
return unmatch // 返回标志的值
}, },
}, },
filters: { filters: {
handleArray(array) { handleArray(array) {
if(array.length===0) return ''; if (array.length === 0) return '';
return array.reduce((pre, cur) => { return array.reduce((pre, cur) => {
return pre + ' ' + cur; return pre + ' ' + cur;
}) })

View File

@@ -47,7 +47,7 @@ export default {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
// 上传文件大小限制(MB) /* 上传文件大小限制(MB) */
fileSize: { fileSize: {
type: Number, type: Number,
default: 5, default: 5,
@@ -129,7 +129,6 @@ export default {
if (this.type == 'url') { if (this.type == 'url') {
let toolbar = this.Quill.getModule("toolbar"); let toolbar = this.Quill.getModule("toolbar");
toolbar.addHandler("image", (value) => { toolbar.addHandler("image", (value) => {
this.uploadType = "image";
if (value) { if (value) {
this.$refs.upload.$children[0].$refs.input.click(); this.$refs.upload.$children[0].$refs.input.click();
} else { } else {
@@ -158,6 +157,13 @@ export default {
}, },
// 上传前校检格式和大小 // 上传前校检格式和大小
handleBeforeUpload(file) { handleBeforeUpload(file) {
const type = ["image/jpeg", "image/jpg", "image/png", "image/svg"];
const isJPG = type.includes(file.type);
// 检验文件格式
if (!isJPG) {
this.$message.error(`图片格式错误!`);
return false;
}
// 校检文件大小 // 校检文件大小
if (this.fileSize) { if (this.fileSize) {
const isLt = file.size / 1024 / 1024 < this.fileSize; const isLt = file.size / 1024 / 1024 < this.fileSize;
@@ -169,10 +175,10 @@ export default {
return true; return true;
}, },
handleUploadSuccess(res, file) { handleUploadSuccess(res, file) {
// 获取富文本组件实例
let quill = this.Quill;
// 如果上传成功 // 如果上传成功
if (res.code == 200) { if (res.code == 200) {
// 获取富文本组件实例
let quill = this.Quill;
// 获取光标所在位置 // 获取光标所在位置
let length = quill.getSelection().index; let length = quill.getSelection().index;
// 插入图片 res.url为服务器返回的图片地址 // 插入图片 res.url为服务器返回的图片地址

View File

@@ -71,12 +71,17 @@ export default {
}, },
change(val) { change(val) {
const path = val.path; const path = val.path;
const query = val.query;
if(this.ishttp(val.path)) { if(this.ishttp(val.path)) {
// http(s):// 路径新窗口打开 // http(s):// 路径新窗口打开
const pindex = path.indexOf("http"); const pindex = path.indexOf("http");
window.open(path.substr(pindex, path.length), "_blank"); window.open(path.substr(pindex, path.length), "_blank");
} else { } else {
this.$router.push(val.path) if (query) {
this.$router.push({ path: path, query: JSON.parse(query) });
} else {
this.$router.push(path)
}
} }
this.search = '' this.search = ''
this.options = [] this.options = []
@@ -102,7 +107,7 @@ export default {
}, },
// Filter out the routes that can be displayed in the sidebar // Filter out the routes that can be displayed in the sidebar
// And generate the internationalized title // And generate the internationalized title
generateRoutes(routes, basePath = '/', prefixTitle = []) { generateRoutes(routes, basePath = '/', prefixTitle = [], query = {}) {
let res = [] let res = []
for (const router of routes) { for (const router of routes) {
@@ -124,9 +129,13 @@ export default {
} }
} }
if (router.query) {
data.query = router.query
}
// recursive child routes // recursive child routes
if (router.children) { if (router.children) {
const tempRoutes = this.generateRoutes(router.children, data.path, data.title) const tempRoutes = this.generateRoutes(router.children, data.path, data.title, data.query)
if (tempRoutes.length >= 1) { if (tempRoutes.length >= 1) {
res = [...res, ...tempRoutes] res = [...res, ...tempRoutes]
} }

View File

@@ -5,10 +5,12 @@
@select="handleSelect" @select="handleSelect"
> >
<template v-for="(item, index) in topMenus"> <template v-for="(item, index) in topMenus">
<el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber" <el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber">
><svg-icon :icon-class="item.meta.icon" /> <svg-icon
{{ item.meta.title }}</el-menu-item v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
> :icon-class="item.meta.icon"/>
{{ item.meta.title }}
</el-menu-item>
</template> </template>
<!-- 顶部菜单超出数量折叠 --> <!-- 顶部菜单超出数量折叠 -->
@@ -18,10 +20,12 @@
<el-menu-item <el-menu-item
:index="item.path" :index="item.path"
:key="index" :key="index"
v-if="index >= visibleNumber" v-if="index >= visibleNumber">
><svg-icon :icon-class="item.meta.icon" /> <svg-icon
{{ item.meta.title }}</el-menu-item v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
> :icon-class="item.meta.icon"/>
{{ item.meta.title }}
</el-menu-item>
</template> </template>
</el-submenu> </el-submenu>
</el-menu> </el-menu>

View File

@@ -1,24 +1,33 @@
<template> <template>
<transition-group name="fade-transform" mode="out-in"> <transition-group name="fade-transform" mode="out-in">
<inner-link <inner-link
v-for="(item, index) in iframeViews" v-for="(item, index) in iframeViews"
:key="item.path" :key="item.path"
:iframeId="'iframe' + index" :iframeId="'iframe' + index"
v-show="$route.path === item.path" v-show="$route.path === item.path"
:src="item.meta.link" :src="iframeUrl(item.meta.link, item.query)"
></inner-link> ></inner-link>
</transition-group> </transition-group>
</template> </template>
<script> <script>
import InnerLink from "../InnerLink/index" import InnerLink from "../InnerLink/index";
export default { export default {
components: { InnerLink }, components: { InnerLink },
computed: { computed: {
iframeViews() { iframeViews() {
return this.$store.state.tagsView.iframeViews return this.$store.state.tagsView.iframeViews;
} }
} },
} methods: {
</script> iframeUrl(url, query) {
if (Object.keys(query).length > 0) {
let params = Object.keys(query).map((key) => key + "=" + query[key]).join("&");
return url + "?" + params;
}
return url;
}
}
}
</script>

View File

@@ -14,7 +14,7 @@ const mutations = {
try { try {
for (let i = 0; i < state.dict.length; i++) { for (let i = 0; i < state.dict.length; i++) {
if (state.dict[i].key == key) { if (state.dict[i].key == key) {
state.dict.splice(i, i) state.dict.splice(i, 1)
return true return true
} }
} }

View File

@@ -98,6 +98,10 @@ function filterChildren(childrenMap, lastRouter = false) {
} }
if (lastRouter) { if (lastRouter) {
el.path = lastRouter.path + '/' + el.path el.path = lastRouter.path + '/' + el.path
if (el.children && el.children.length) {
children = children.concat(filterChildren(el.children, el))
return
}
} }
children = children.concat(el) children = children.concat(el)
}) })

Some files were not shown because too many files have changed in this diff Show More