150 Commits

Author SHA1 Message Date
6a35245aaf 修正UserAgent解析逻辑,正确设置浏览器和操作系统字段 2025-12-31 09:29:10 +08:00
b2cd0b51c2 修正UserAgent解析逻辑,正确设置浏览器和操作系统字段 2025-12-21 10:51:25 +08:00
a0be25948d 优化topbar顶部菜单样式 2025-12-18 14:00:02 +08:00
ab1bbf21ed 若依 3.9.1 2025-12-18 09:06:24 +08:00
2c82a847eb 默认固定头部 2025-12-18 09:05:51 +08:00
2b3b29a4a3 优化字典组件值宽松匹配 2025-12-16 16:41:56 +08:00
f57dea5332 菜单导航设置支持纯顶部 2025-12-16 13:02:05 +08:00
fab921a1d2 升级quartz到最新版本2.5.2 2025-12-12 11:04:33 +08:00
336a47660f 升级spring-boot到最新版本3.5.8 2025-12-11 14:40:16 +08:00
575b1b727f 升级spring-boot-mybatis到最新版3.0.5 2025-12-11 14:37:58 +08:00
542979e703 升级springdoc到最新版本2.8.14 2025-12-11 14:37:19 +08:00
c2202fee27 升级druid到最新版本1.2.27 2025-12-11 14:34:31 +08:00
17a726942f 升级oshi到最新版本6.9.1 2025-12-11 14:34:14 +08:00
362db4a071 升级commons.io到最新版本2.21.0 2025-12-11 14:33:38 +08:00
771b9af7c0 升级fastjson到最新版2.0.60 2025-12-11 14:33:14 +08:00
7a787a919e update sqlkeyword 2025-12-11 14:33:05 +08:00
8fdbd05ce7 使用yauaa代替bitwalker 2025-12-09 14:29:23 +08:00
970275755d 优化用户密码字段序列化配置 2025-12-05 14:59:52 +08:00
516116f125 优化数据权限控制逻辑,放开permission限制 2025-12-04 17:33:15 +08:00
d3493e94d6 支持Excel导出对象的多个子列表 2025-12-04 16:32:48 +08:00
3beb3ae3f9 优化表单构建关闭页签销毁复制插件 2025-12-04 13:53:57 +08:00
f77c0ec97d 修复优雅停机出现的冲突问题 2025-12-04 13:51:36 +08:00
55854ec195 忽略用户密码字段的JSON序列化 2025-12-03 14:38:31 +08:00
088e9afbc9 优化代码 2025-12-03 11:48:40 +08:00
456e1534ee 优化生成代码下载的zip文件名 2025-12-03 10:27:04 +08:00
352ed6eee7 网页标题设置新增SET_TITLE方法 2025-12-02 19:30:01 +08:00
0cb12f5440 支持Excel导出对象的多个子列表 2025-12-02 19:13:23 +08:00
169ca65ed3 登录/注册页面底部版权信息修改为读取配置 2025-12-02 15:29:35 +08:00
cd6c2fc5f1 修复v3时间控件between选择后清空报错问题 2025-12-02 14:57:45 +08:00
e24918b054 修复表单构建移除所有控件后切换路由回来空白问题 2025-12-02 13:08:27 +08:00
2d36603c46 修复comboReadDict属性下多个sheet出现的报错(ICWQ8E) 2025-11-13 11:34:45 +08:00
964569f715 update ruoyi-admin pom.xml 2025-10-14 12:14:12 +08:00
e37b8ff9b8 添加新群号:174569686 2025-10-05 20:11:22 +08:00
5896895bab 升级fastjson到最新版2.0.58 2025-09-05 09:17:39 +08:00
0c8e9ad74e 修复固定头部时出现的导航栏偏移问题(ICV9OH) 2025-09-04 19:58:58 +08:00
f0346b3def 支持防盗链功能 2025-09-02 11:31:18 +08:00
d1cdbbd22c 优化代码 2025-09-02 08:53:31 +08:00
e2badb7c68 升级oshi到最新版本6.8.3 2025-08-28 13:38:19 +08:00
40f54f9700 优化代码 2025-08-28 13:37:57 +08:00
534bd7b8cd 优化代码 2025-08-27 15:34:47 +08:00
6c811c88c2 用户导入添加验证提示 2025-08-23 11:14:45 +08:00
eed3ae8f80 优化布局设置显示 2025-08-23 11:14:36 +08:00
5aa5f42042 修复用户归属部门无法修改为空问题 2025-08-21 14:48:00 +08:00
29e4eae4e2 columns default value 2025-08-09 16:12:51 +08:00
84e1f8a210 显示列信息支持对象格式 2025-08-09 13:23:50 +08:00
2eceac52cf 自动识别json对象白名单配置范围缩小 2025-08-09 10:57:40 +08:00
624c52b568 升级spring-boot到最新版本3.5.4 2025-08-07 15:12:17 +08:00
0e698bcd5b 升级pagehelper到最新版2.1.1 2025-08-07 15:11:35 +08:00
c044ec0bb8 升级oshi到最新版本6.8.2 2025-08-07 15:10:41 +08:00
c6d9a03f9f 添加新群号:191164766 2025-06-20 11:39:46 +08:00
3030c3fa11 优化定时任务包名白名单匹配方式 2025-06-20 11:34:35 +08:00
9dfea3c77f 优化Excel统计行数值的单元格样式显示 2025-06-19 14:48:04 +08:00
ada072c767 用户头像更换后移除旧头像文件 2025-06-06 14:58:29 +08:00
c8a47f8b39 若依 3.9.0 2025-05-28 09:04:44 +08:00
92f8a44f67 注册账号设置默认密码最后更新时间 2025-05-26 10:58:19 +08:00
e0bfca5bdb 升级fastjson到最新版2.0.57 2025-05-26 09:00:13 +08:00
3f753cd801 添加底部版权信息及开关 2025-05-24 14:24:46 +08:00
f3bdf8c7e8 添加页签图标显示开关功能 2025-05-23 14:57:21 +08:00
9a37444e68 update pwdUpdateDate 2025-05-23 10:45:05 +08:00
e43efd179c 账号密码支持自定义更新周期 2025-05-23 09:06:07 +08:00
99e9f21253 初始密码支持自定义修改策略 2025-05-22 23:07:18 +08:00
6ee698b1ab 升级oshi到最新版本6.8.1 2025-05-15 09:14:42 +08:00
992537f343 升级commons.io到最新版本2.19.0 2025-05-15 09:14:23 +08:00
fbd5052f99 delete eslint&vue-meta 2025-05-15 09:14:02 +08:00
f8a918c62c 优化导航栏显示昵称&设置 2025-05-09 13:53:05 +08:00
df0954d5df 菜单搜索支持键盘选择&悬浮主题背景 2025-05-07 13:23:35 +08:00
8af2aa929a 图片上传组件新增disabled属性 2025-05-06 19:13:58 +08:00
fb7743de1c add columnName Drag 2025-05-06 14:53:17 +08:00
eefe15e36b 修复上传组件被多次引用拖动仅对第一个有效的问题 2025-05-06 13:10:37 +08:00
b6874d4320 update icon 2025-05-06 11:08:15 +08:00
6d00fc8603 上传组件新增拖动排序属性 2025-04-30 10:28:30 +08:00
00b2eeb4c3 优化Excel匹配数值型.0结尾 2025-04-28 11:24:35 +08:00
838fc115ca update editor index 2025-04-27 14:03:01 +08:00
62630a5a95 remove all semicolons 2025-04-27 10:05:55 +08:00
f30e737be2 Excel导入导出支持多图片 2025-04-25 10:09:02 +08:00
5069654cb7 富文本复制粘贴图片上传至url 2025-04-24 14:24:20 +08:00
fc6b8eefae update package.json 2025-04-24 11:08:59 +08:00
d6c22b8a42 优化低版本node无法启动的问题 2025-04-22 12:06:28 +08:00
33303f13d9 优化代码 2025-04-22 12:06:12 +08:00
0154365cc0 显隐列组件支持全选/全不选 2025-04-21 15:22:19 +08:00
2cbae4126d 优化菜单搜索查询页 2025-04-21 13:22:49 +08:00
a8ba5b1b0e 支持文件&图片组件自定义地址&参数 2025-04-18 12:57:34 +08:00
cf59bb007d 添加新群号:287842588 2025-04-18 12:57:09 +08:00
fd2568b0e7 springdoc proxy 2025-04-17 15:26:23 +08:00
7ab047745f 优化角色禁用不允许分配 2025-04-17 15:24:35 +08:00
3d09ea31fc update status name 2025-04-17 15:24:08 +08:00
0188c81853 添加新群号:287842588 2025-04-01 19:14:31 +08:00
e39629472c remove dev runjs 2025-03-18 16:06:40 +08:00
df911695f0 登录页和注册页表头使用VUE_APP_TITLE配置值 2025-03-18 16:05:14 +08:00
13b2f5149b 优化代码 2025-03-14 16:11:22 +08:00
495e640888 菜单管理新增路由名称 2025-03-06 11:03:43 +08:00
3a224461d8 优化代码 2025-03-04 20:16:53 +08:00
5fdd49582b 优化顶部菜单搜索栏为多层级显示(IBESXH) 2025-03-03 12:08:36 +08:00
e763e67c61 优化导出Excel日期格式双击离开后与设定的格式不一致问题 2025-03-01 15:33:49 +08:00
9962534193 优化代码 2025-03-01 15:33:07 +08:00
dd1f6a7b9e pagination更换成flex布局 2025-03-01 15:29:58 +08:00
7eb86458cc 优化前端处理路由函数代码 2025-03-01 15:29:42 +08:00
d3de651360 优化前端树结构性能问题 2025-03-01 15:29:13 +08:00
637a09cfa9 修复代码生成主子表校验必填失效问题 2025-02-28 21:53:15 +08:00
499648c90e 代码生成列表支持按时间排序 2025-02-28 19:44:41 +08:00
896c0a23de 文件上传组件新增类型 2025-02-28 19:44:23 +08:00
50e1a727d4 优化空指针异常时无法获取错误信息问题 2025-02-28 19:44:04 +08:00
af80841751 文件上传组件新增disabled属性 2025-02-28 13:03:50 +08:00
4bbe3f3b56 优化代码 2025-02-28 13:03:33 +08:00
57293a2ecf copyright 2025 2025-01-07 10:53:33 +08:00
98cdcae01f 若依 3.8.9 2024-12-30 08:54:15 +08:00
927ad037a2 代码生成新增配置是否允许文件覆盖到本地 2024-12-25 16:00:25 +08:00
8a672152f8 优化导入带标题文件关闭清理 2024-12-25 16:00:06 +08:00
1883a40dcf update sqlkeyword 2024-12-25 00:07:36 +08:00
8b63f468b4 优化特殊字符密码修改失败问题 2024-12-17 14:35:30 +08:00
bfc6fe5ae1 优化TopNav内链菜单点击没有高亮(IB8WHJ) 2024-12-17 14:35:13 +08:00
45e8c51751 优化菜单管理切换Mini布局错乱问题 2024-12-17 14:35:02 +08:00
353476b88c update README 2024-12-17 14:34:48 +08:00
db2f438279 用户管理过滤掉已禁用部门(IB5H7F) 2024-12-11 11:48:44 +08:00
739a1c262d 修改主题样式本地读取 2024-12-10 16:41:20 +08:00
88f620c44d 白名单支持对通配符路径匹配 2024-12-07 14:41:58 +08:00
aec1a93e16 Excel注解支持wrapText是否允许内容换行 2024-12-07 14:41:35 +08:00
ed96bf80e7 修复导出子列表对象只能在最后的问题 2024-12-07 14:41:17 +08:00
4a9011a727 修复默认关闭Tags-Views时,内链页面打不开 2024-11-27 19:59:11 +08:00
8e99b826fa 修复TopNav无法正确获取active的问题 2024-11-27 19:58:58 +08:00
c5615001c8 菜单面包屑导航支持多层级显示 2024-11-25 22:35:42 +08:00
b376ab792a 优化代码 2024-11-25 22:35:19 +08:00
0ca6619a80 分栏参数微调 2024-11-22 14:51:08 +08:00
a691f28f30 用户管理支持分栏拖动 2024-11-22 14:10:36 +08:00
32f0636d46 用户头像http(s)链接支持 2024-11-22 14:10:15 +08:00
4c013a4f73 update .env.staging 2024-11-22 14:09:34 +08:00
a69a436159 支持自定义显示Excel属性列 2024-11-07 22:48:40 +08:00
8ba8524250 升级spring-boot到最新版本3.3.5 2024-11-06 21:40:02 +08:00
bb74a620ba 升级springdoc到最新版本2.6.0 2024-11-06 21:39:47 +08:00
ff8ded2704 升级oshi到最新版本6.6.5 2024-11-05 16:40:36 +08:00
fa8b909408 优化无用户编号不校验数据权限 2024-11-05 16:39:55 +08:00
433f588747 校检文件名是否包含特殊字符 2024-11-05 16:39:14 +08:00
35c1f02392 优化身份证脱敏正则 2024-10-21 16:47:53 +08:00
1a7bcc10bd 优化权限更新后同步缓存 2024-10-21 16:47:44 +08:00
4008d1f30d 优化上传图片带域名不增加前缀 2024-10-21 16:44:57 +08:00
27e07c6a80 升级quill到最新版本2.0.2 2024-10-21 16:44:20 +08:00
587858abe3 升级axios到最新版本0.28.1 2024-10-21 16:43:52 +08:00
0adc70d510 操作日志记录DELETE请求参数(IAMV6F) 2024-10-17 13:09:23 +08:00
4e1ec5e98e 升级fastjson到最新版2.0.53 2024-10-17 13:09:09 +08:00
7d0724b9bf 修复码生成上级菜单显示问题(I9CTIJ) 2024-10-17 13:08:04 +08:00
0bbb8b7c06 修复角色禁用权限不失效问题(IAA8ZX) 2024-09-21 11:30:57 +08:00
19d7e347e4 优化代码 2024-09-08 10:50:55 +08:00
9369590f57 升级oshi到最新版本6.6.3 2024-08-30 21:49:32 +08:00
16af81344c update sqlkeyword 2024-08-30 21:49:23 +08:00
6fc05bc616 修改时间范围日期格式 2024-07-08 16:55:23 +08:00
b84d5554c2 remove sub resultType 2024-07-08 16:55:06 +08:00
583f057de6 若依 3.8.8 2024-06-30 08:11:13 +08:00
232d228e52 菜单管理新增路由名称 2024-06-29 19:01:48 +08:00
92ea278242 删除多余的依赖 2024-06-27 17:34:19 +08:00
534f7d13d3 update springboot3 2024-06-27 16:03:30 +08:00
99 changed files with 1578 additions and 1104 deletions

View File

@ -1,11 +1,11 @@
<p align="center"> <p align="center">
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-d3d0a9303e11d522a06cd263f3079027715.png"> <img alt="logo" src="https://oscimg.oschina.net/oscnet/up-d3d0a9303e11d522a06cd263f3079027715.png">
</p> </p>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.9.0</h1> <h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.9.1</h1>
<h4 align="center">基于SpringBoot+Vue前后端分离的Java快速开发框架</h4> <h4 align="center">基于SpringBoot+Vue前后端分离的Java快速开发框架</h4>
<p align="center"> <p align="center">
<a href="https://gitee.com/y_project/RuoYi-Vue/stargazers"><img src="https://gitee.com/y_project/RuoYi-Vue/badge/star.svg?theme=dark"></a> <a href="https://gitee.com/y_project/RuoYi-Vue/stargazers"><img src="https://gitee.com/y_project/RuoYi-Vue/badge/star.svg?theme=dark"></a>
<a href="https://gitee.com/y_project/RuoYi-Vue"><img src="https://img.shields.io/badge/RuoYi-v3.9.0-brightgreen.svg"></a> <a href="https://gitee.com/y_project/RuoYi-Vue"><img src="https://img.shields.io/badge/RuoYi-v3.9.1-brightgreen.svg"></a>
<a href="https://gitee.com/y_project/RuoYi-Vue/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a> <a href="https://gitee.com/y_project/RuoYi-Vue/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
</p> </p>

143
pom.xml
View File

@ -6,112 +6,62 @@
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId> <artifactId>ruoyi</artifactId>
<version>3.9.0</version> <version>3.9.1</version>
<name>ruoyi</name> <name>ruoyi</name>
<url>http://www.ruoyi.vip</url> <url>http://www.ruoyi.vip</url>
<description>若依管理系统</description> <description>若依管理系统</description>
<properties> <properties>
<ruoyi.version>3.9.0</ruoyi.version> <ruoyi.version>3.9.1</ruoyi.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>17</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version> <maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<spring-boot.version>2.5.15</spring-boot.version> <mybatis-spring-boot.version>3.0.5</mybatis-spring-boot.version>
<druid.version>1.2.23</druid.version> <druid.version>1.2.27</druid.version>
<bitwalker.version>1.21</bitwalker.version> <yauaa.version>7.32.0</yauaa.version>
<swagger.version>3.0.0</swagger.version> <swagger.version>3.0.0</swagger.version>
<kaptcha.version>2.3.3</kaptcha.version> <kaptcha.version>2.3.3</kaptcha.version>
<pagehelper.boot.version>1.4.7</pagehelper.boot.version> <pagehelper.boot.version>2.1.1</pagehelper.boot.version>
<fastjson.version>2.0.58</fastjson.version> <fastjson.version>2.0.60</fastjson.version>
<oshi.version>6.8.3</oshi.version> <oshi.version>6.9.1</oshi.version>
<commons.io.version>2.19.0</commons.io.version> <commons.io.version>2.21.0</commons.io.version>
<poi.version>4.1.2</poi.version> <poi.version>4.1.2</poi.version>
<velocity.version>2.3</velocity.version> <velocity.version>2.3</velocity.version>
<jwt.version>0.9.1</jwt.version> <jwt.version>0.9.1</jwt.version>
<!-- override dependency version --> <quartz.version>2.5.2</quartz.version>
<tomcat.version>9.0.112</tomcat.version> <mysql.version>8.2.0</mysql.version>
<logback.version>1.2.13</logback.version> <jaxb-api.version>2.3.1</jaxb-api.version>
<spring-security.version>5.7.14</spring-security.version> <jakarta.version>6.0.0</jakarta.version>
<spring-framework.version>5.3.39</spring-framework.version> <springdoc.version>2.8.14</springdoc.version>
</properties> </properties>
<!-- 依赖声明 --> <!-- 依赖声明 -->
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
<!-- 覆盖SpringFramework的依赖配置-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>${spring-framework.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 覆盖SpringSecurity的依赖配置-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-bom</artifactId>
<version>${spring-security.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- SpringBoot的依赖配置--> <!-- SpringBoot的依赖配置-->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId> <artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version> <version>3.5.8</version>
<type>pom</type> <type>pom</type>
<scope>import</scope> <scope>import</scope>
</dependency> </dependency>
<!-- 覆盖logback的依赖配置-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<!-- 覆盖tomcat的依赖配置-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-el</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-websocket</artifactId>
<version>${tomcat.version}</version>
</dependency>
<!-- 阿里数据库连接池 --> <!-- 阿里数据库连接池 -->
<dependency> <dependency>
<groupId>com.alibaba</groupId> <groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId> <artifactId>druid-spring-boot-3-starter</artifactId>
<version>${druid.version}</version> <version>${druid.version}</version>
</dependency> </dependency>
<!-- 解析客户端操作系统、浏览器等 --> <!-- 解析客户端操作系统、浏览器等 -->
<dependency> <dependency>
<groupId>eu.bitwalker</groupId> <groupId>nl.basjes.parse.useragent</groupId>
<artifactId>UserAgentUtils</artifactId> <artifactId>yauaa</artifactId>
<version>${bitwalker.version}</version> <version>${yauaa.version}</version>
</dependency> </dependency>
<!-- pagehelper 分页插件 --> <!-- pagehelper 分页插件 -->
@ -121,6 +71,30 @@
<version>${pagehelper.boot.version}</version> <version>${pagehelper.boot.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-spring-boot.version}</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>${jaxb-api.version}</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>${jakarta.version}</version>
</dependency>
<!-- 获取系统信息 --> <!-- 获取系统信息 -->
<dependency> <dependency>
<groupId>com.github.oshi</groupId> <groupId>com.github.oshi</groupId>
@ -128,17 +102,11 @@
<version>${oshi.version}</version> <version>${oshi.version}</version>
</dependency> </dependency>
<!-- Swagger3依赖 --> <!-- spring-doc -->
<dependency> <dependency>
<groupId>io.springfox</groupId> <groupId>org.springdoc</groupId>
<artifactId>springfox-boot-starter</artifactId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${swagger.version}</version> <version>${springdoc.version}</version>
<exclusions>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<!-- io常用工具类 --> <!-- io常用工具类 -->
@ -162,6 +130,13 @@
<version>${velocity.version}</version> <version>${velocity.version}</version>
</dependency> </dependency>
<!-- 定时任务 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>${quartz.version}</version>
</dependency>
<!-- 阿里JSON解析器 --> <!-- 阿里JSON解析器 -->
<dependency> <dependency>
<groupId>com.alibaba.fastjson2</groupId> <groupId>com.alibaba.fastjson2</groupId>
@ -236,13 +211,19 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version> <version>3.13.0</version>
<configuration> <configuration>
<parameters>true</parameters>
<source>${java.version}</source> <source>${java.version}</source>
<target>${java.version}</target> <target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding> <encoding>${project.build.sourceEncoding}</encoding>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.3.0</version>
</plugin>
</plugins> </plugins>
</build> </build>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>ruoyi</artifactId> <artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<version>3.9.0</version> <version>3.9.1</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging> <packaging>jar</packaging>
@ -24,23 +24,16 @@
<optional>true</optional> <!-- 表示依赖不会传递 --> <optional>true</optional> <!-- 表示依赖不会传递 -->
</dependency> </dependency>
<!-- swagger3--> <!-- spring-doc -->
<dependency> <dependency>
<groupId>io.springfox</groupId> <groupId>org.springdoc</groupId>
<artifactId>springfox-boot-starter</artifactId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
</dependency>
<!-- 防止进入swagger页面报类型转换错误排除3.0.0中的引用手动增加1.6.2版本 -->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.6.2</version>
</dependency> </dependency>
<!-- Mysql驱动包 --> <!-- Mysql驱动包 -->
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>com.mysql</groupId>
<artifactId>mysql-connector-java</artifactId> <artifactId>mysql-connector-j</artifactId>
</dependency> </dependency>
<!-- 核心模块--> <!-- 核心模块-->
@ -68,9 +61,9 @@
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>
<version>2.5.15</version> <version>3.5.4</version>
<configuration> <configuration>
<fork>true</fork> <!-- 如果没有该配置devtools不会生效 --> <addResources>true</addResources>
</configuration> </configuration>
<executions> <executions>
<execution> <execution>

View File

@ -3,9 +3,9 @@ package com.ruoyi.web.controller.common;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.annotation.Resource; import jakarta.annotation.Resource;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.FastByteArrayOutputStream; import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;

View File

@ -2,8 +2,8 @@ package com.ruoyi.web.controller.common;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;

View File

@ -45,6 +45,7 @@ public class CacheController
caches.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数")); caches.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数"));
} }
@SuppressWarnings("deprecation")
@PreAuthorize("@ss.hasPermi('monitor:cache:list')") @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@GetMapping() @GetMapping()
public AjaxResult getInfo() throws Exception public AjaxResult getInfo() throws Exception

View File

@ -1,7 +1,7 @@
package com.ruoyi.web.controller.monitor; package com.ruoyi.web.controller.monitor;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.DeleteMapping;

View File

@ -1,7 +1,7 @@
package com.ruoyi.web.controller.monitor; package com.ruoyi.web.controller.monitor;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.DeleteMapping;

View File

@ -1,7 +1,7 @@
package com.ruoyi.web.controller.system; package com.ruoyi.web.controller.system;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;

View File

@ -2,7 +2,7 @@ package com.ruoyi.web.controller.system;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;

View File

@ -1,7 +1,7 @@
package com.ruoyi.web.controller.system; package com.ruoyi.web.controller.system;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;

View File

@ -1,7 +1,7 @@
package com.ruoyi.web.controller.system; package com.ruoyi.web.controller.system;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;

View File

@ -96,7 +96,8 @@ public class SysProfileController extends BaseController
String newPassword = params.get("newPassword"); String newPassword = params.get("newPassword");
LoginUser loginUser = getLoginUser(); LoginUser loginUser = getLoginUser();
Long userId = loginUser.getUserId(); Long userId = loginUser.getUserId();
String password = loginUser.getPassword(); SysUser user = userService.selectUserById(userId);
String password = user.getPassword();
if (!SecurityUtils.matchesPassword(oldPassword, password)) if (!SecurityUtils.matchesPassword(oldPassword, password))
{ {
return error("修改密码失败,旧密码错误"); return error("修改密码失败,旧密码错误");

View File

@ -1,7 +1,7 @@
package com.ruoyi.web.controller.system; package com.ruoyi.web.controller.system;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;

View File

@ -2,7 +2,7 @@ package com.ruoyi.web.controller.system;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;

View File

@ -15,19 +15,16 @@ import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
import io.swagger.annotations.Api; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.annotations.ApiImplicitParam; import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.annotations.ApiImplicitParams; import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
/** /**
* swagger 用户测试方法 * swagger 用户测试方法
* *
* @author ruoyi * @author ruoyi
*/ */
@Api("用户信息管理") @Tag(name = "用户信息管理")
@RestController @RestController
@RequestMapping("/test/user") @RequestMapping("/test/user")
public class TestController extends BaseController public class TestController extends BaseController
@ -37,19 +34,19 @@ public class TestController extends BaseController
users.put(1, new UserEntity(1, "admin", "admin123", "15888888888")); users.put(1, new UserEntity(1, "admin", "admin123", "15888888888"));
users.put(2, new UserEntity(2, "ry", "admin123", "15666666666")); users.put(2, new UserEntity(2, "ry", "admin123", "15666666666"));
} }
@ApiOperation("获取用户列表") @Operation(summary = "获取用户列表")
@GetMapping("/list") @GetMapping("/list")
public R<List<UserEntity>> userList() public R<List<UserEntity>> userList()
{ {
List<UserEntity> userList = new ArrayList<UserEntity>(users.values()); List<UserEntity> userList = new ArrayList<UserEntity>(users.values());
return R.ok(userList); return R.ok(userList);
} }
@ApiOperation("获取用户详细") @Operation(summary = "获取用户详细")
@ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class)
@GetMapping("/{userId}") @GetMapping("/{userId}")
public R<UserEntity> getUser(@PathVariable Integer userId) public R<UserEntity> getUser(@PathVariable(name = "userId")
Integer userId)
{ {
if (!users.isEmpty() && users.containsKey(userId)) if (!users.isEmpty() && users.containsKey(userId))
{ {
@ -60,14 +57,8 @@ public class TestController extends BaseController
return R.fail("用户不存在"); return R.fail("用户不存在");
} }
} }
@ApiOperation("新增用户") @Operation(summary = "新增用户")
@ApiImplicitParams({
@ApiImplicitParam(name = "userId", value = "用户id", dataType = "Integer", dataTypeClass = Integer.class),
@ApiImplicitParam(name = "username", value = "用户名称", dataType = "String", dataTypeClass = String.class),
@ApiImplicitParam(name = "password", value = "用户密码", dataType = "String", dataTypeClass = String.class),
@ApiImplicitParam(name = "mobile", value = "用户手机", dataType = "String", dataTypeClass = String.class)
})
@PostMapping("/save") @PostMapping("/save")
public R<String> save(UserEntity user) public R<String> save(UserEntity user)
{ {
@ -78,10 +69,11 @@ public class TestController extends BaseController
users.put(user.getUserId(), user); users.put(user.getUserId(), user);
return R.ok(); return R.ok();
} }
@ApiOperation("更新用户") @Operation(summary = "更新用户")
@PutMapping("/update") @PutMapping("/update")
public R<String> update(@RequestBody UserEntity user) public R<String> update(@RequestBody
UserEntity user)
{ {
if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())) if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId()))
{ {
@ -95,11 +87,11 @@ public class TestController extends BaseController
users.put(user.getUserId(), user); users.put(user.getUserId(), user);
return R.ok(); return R.ok();
} }
@ApiOperation("删除用户信息") @Operation(summary = "删除用户信息")
@ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class)
@DeleteMapping("/{userId}") @DeleteMapping("/{userId}")
public R<String> delete(@PathVariable Integer userId) public R<String> delete(@PathVariable(name = "userId")
Integer userId)
{ {
if (!users.isEmpty() && users.containsKey(userId)) if (!users.isEmpty() && users.containsKey(userId))
{ {
@ -113,26 +105,26 @@ public class TestController extends BaseController
} }
} }
@ApiModel(value = "UserEntity", description = "用户实体") @Schema(description = "用户实体")
class UserEntity class UserEntity
{ {
@ApiModelProperty("用户ID") @Schema(title = "用户ID")
private Integer userId; private Integer userId;
@ApiModelProperty("用户名称") @Schema(title = "用户名称")
private String username; private String username;
@ApiModelProperty("用户密码") @Schema(title = "用户密码")
private String password; private String password;
@ApiModelProperty("用户手机") @Schema(title = "用户手机")
private String mobile; private String mobile;
public UserEntity() public UserEntity()
{ {
} }
public UserEntity(Integer userId, String username, String password, String mobile) public UserEntity(Integer userId, String username, String password, String mobile)
{ {
this.userId = userId; this.userId = userId;
@ -140,42 +132,42 @@ class UserEntity
this.password = password; this.password = password;
this.mobile = mobile; this.mobile = mobile;
} }
public Integer getUserId() public Integer getUserId()
{ {
return userId; return userId;
} }
public void setUserId(Integer userId) public void setUserId(Integer userId)
{ {
this.userId = userId; this.userId = userId;
} }
public String getUsername() public String getUsername()
{ {
return username; return username;
} }
public void setUsername(String username) public void setUsername(String username)
{ {
this.username = username; this.username = username;
} }
public String getPassword() public String getPassword()
{ {
return password; return password;
} }
public void setPassword(String password) public void setPassword(String password)
{ {
this.password = password; this.password = password;
} }
public String getMobile() public String getMobile()
{ {
return mobile; return mobile;
} }
public void setMobile(String mobile) public void setMobile(String mobile)
{ {
this.mobile = mobile; this.mobile = mobile;

View File

@ -1,26 +1,15 @@
package com.ruoyi.web.core.config; package com.ruoyi.web.core.config;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import com.ruoyi.common.config.RuoYiConfig; import com.ruoyi.common.config.RuoYiConfig;
import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.models.Components;
import io.swagger.models.auth.In; import io.swagger.v3.oas.models.OpenAPI;
import springfox.documentation.builders.ApiInfoBuilder; import io.swagger.v3.oas.models.info.Contact;
import springfox.documentation.builders.PathSelectors; import io.swagger.v3.oas.models.info.Info;
import springfox.documentation.builders.RequestHandlerSelectors; import io.swagger.v3.oas.models.security.SecurityRequirement;
import springfox.documentation.service.ApiInfo; import io.swagger.v3.oas.models.security.SecurityScheme;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.Contact;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.service.SecurityScheme;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
/** /**
* Swagger2的接口配置 * Swagger2的接口配置
@ -33,93 +22,43 @@ public class SwaggerConfig
/** 系统基础配置 */ /** 系统基础配置 */
@Autowired @Autowired
private RuoYiConfig ruoyiConfig; private RuoYiConfig ruoyiConfig;
/** 是否开启swagger */
@Value("${swagger.enabled}")
private boolean enabled;
/** 设置请求的统一前缀 */
@Value("${swagger.pathMapping}")
private String pathMapping;
/** /**
* 创建API * 自定义的 OpenAPI 对象
*/ */
@Bean @Bean
public Docket createRestApi() public OpenAPI customOpenApi()
{ {
return new Docket(DocumentationType.OAS_30) return new OpenAPI().components(new Components()
// 是否启用Swagger // 设置认证的请求头
.enable(enabled) .addSecuritySchemes("apikey", securityScheme()))
// 用来创建该API的基本信息展示在文档的页面中自定义展示的信息 .addSecurityItem(new SecurityRequirement().addList("apikey"))
.apiInfo(apiInfo()) .info(getApiInfo());
// 设置哪些接口暴露给Swagger展示
.select()
// 扫描所有有注解的api用这种方式更灵活
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
// 扫描指定包中的swagger注解
// .apis(RequestHandlerSelectors.basePackage("com.ruoyi.project.tool.swagger"))
// 扫描所有 .apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
/* 设置安全模式swagger可以设置访问token */
.securitySchemes(securitySchemes())
.securityContexts(securityContexts())
.pathMapping(pathMapping);
} }
/** @Bean
* 安全模式这里指定token通过Authorization头请求头传递 public SecurityScheme securityScheme()
*/
private List<SecurityScheme> securitySchemes()
{ {
List<SecurityScheme> apiKeyList = new ArrayList<SecurityScheme>(); return new SecurityScheme()
apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue())); .type(SecurityScheme.Type.APIKEY)
return apiKeyList; .name("Authorization")
.in(SecurityScheme.In.HEADER)
.scheme("Bearer");
} }
/**
* 安全上下文
*/
private List<SecurityContext> securityContexts()
{
List<SecurityContext> securityContexts = new ArrayList<>();
securityContexts.add(
SecurityContext.builder()
.securityReferences(defaultAuth())
.operationSelector(o -> o.requestMappingPattern().matches("/.*"))
.build());
return securityContexts;
}
/**
* 默认的安全上引用
*/
private List<SecurityReference> defaultAuth()
{
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
List<SecurityReference> securityReferences = new ArrayList<>();
securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
return securityReferences;
}
/** /**
* 添加摘要信息 * 添加摘要信息
*/ */
private ApiInfo apiInfo() public Info getApiInfo()
{ {
// 用ApiInfoBuilder进行定制 return new Info()
return new ApiInfoBuilder() // 设置标题
// 设置标题 .title("标题若依管理系统_接口文档")
.title("标题若依管理系统_接口文档") // 描述
// 描述 .description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...")
.description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...") // 作者信息
// 作者信息 .contact(new Contact().name(ruoyiConfig.getName()))
.contact(new Contact(ruoyiConfig.getName(), null, null)) // 版本
// 版本 .version("版本号:" + ruoyiConfig.getVersion());
.version("版本号:" + ruoyiConfig.getVersion())
.build();
} }
} }

View File

@ -3,7 +3,7 @@ ruoyi:
# 名称 # 名称
name: RuoYi name: RuoYi
# 版本 # 版本
version: 3.9.0 version: 3.9.1
# 版权年份 # 版权年份
copyrightYear: 2025 copyrightYear: 2025
# 文件路径 示例( Windows配置D:/ruoyi/uploadPathLinux配置 /home/ruoyi/uploadPath # 文件路径 示例( Windows配置D:/ruoyi/uploadPathLinux配置 /home/ruoyi/uploadPath
@ -65,28 +65,29 @@ spring:
restart: restart:
# 热部署开关 # 热部署开关
enabled: true enabled: true
# redis 配置 data:
redis: # redis 配置
# 地址 redis:
host: localhost # 地址
# 端口默认为6379 host: localhost
port: 6379 # 端口,默认为6379
# 数据库索引 port: 6379
database: 0 # 数据库索引
# 密码 database: 0
password: # 密码
# 连接超时时间 password:
timeout: 10s # 连接超时时间
lettuce: timeout: 10s
pool: lettuce:
# 连接池中的最小空闲连接 pool:
min-idle: 0 # 连接池中的最小空闲连接
# 连接池中的最大空闲连接 min-idle: 0
max-idle: 8 # 连接池中的最大空闲连接
# 连接池的最大数据库连接数 max-idle: 8
max-active: 8 # 连接池的最大数据库连接数
# #连接池最大阻塞等待时间(使用负值表示没有限制) max-active: 8
max-wait: -1ms # #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# token配置 # token配置
token: token:
@ -112,12 +113,19 @@ pagehelper:
supportMethodsArguments: true supportMethodsArguments: true
params: count=countSql params: count=countSql
# Swagger配置 # Springdoc配置
swagger: springdoc:
# 是否开启swagger api-docs:
enabled: true path: /v3/api-docs
# 请求前缀 swagger-ui:
pathMapping: /dev-api enabled: true
path: /swagger-ui.html
tags-sorter: alpha
group-configs:
- group: 'default'
display-name: '测试模块'
paths-to-match: '/**'
packages-to-scan: com.ruoyi.web.controller.tool
# 防盗链配置 # 防盗链配置
referer: referer:

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>ruoyi</artifactId> <artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<version>3.9.0</version> <version>3.9.1</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@ -77,12 +77,6 @@
<artifactId>poi-ooxml</artifactId> <artifactId>poi-ooxml</artifactId>
</dependency> </dependency>
<!-- yml解析器 -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</dependency>
<!-- Token生成与解析--> <!-- Token生成与解析-->
<dependency> <dependency>
<groupId>io.jsonwebtoken</groupId> <groupId>io.jsonwebtoken</groupId>
@ -109,14 +103,14 @@
<!-- 解析客户端操作系统、浏览器等 --> <!-- 解析客户端操作系统、浏览器等 -->
<dependency> <dependency>
<groupId>eu.bitwalker</groupId> <groupId>nl.basjes.parse.useragent</groupId>
<artifactId>UserAgentUtils</artifactId> <artifactId>yauaa</artifactId>
</dependency> </dependency>
<!-- servlet包 --> <!-- servlet包 -->
<dependency> <dependency>
<groupId>javax.servlet</groupId> <groupId>jakarta.servlet</groupId>
<artifactId>javax.servlet-api</artifactId> <artifactId>jakarta.servlet-api</artifactId>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -56,6 +56,7 @@ public @interface Excel
/** /**
* BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN * BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN
*/ */
@SuppressWarnings("deprecation")
public int roundingMode() default BigDecimal.ROUND_HALF_EVEN; public int roundingMode() default BigDecimal.ROUND_HALF_EVEN;
/** /**

View File

@ -2,10 +2,10 @@ package com.ruoyi.common.core.domain.entity;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.validation.constraints.Email; import jakarta.validation.constraints.Email;
import javax.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import javax.validation.constraints.Size; import jakarta.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.core.domain.BaseEntity; import com.ruoyi.common.core.domain.BaseEntity;

View File

@ -1,7 +1,7 @@
package com.ruoyi.common.core.domain.entity; package com.ruoyi.common.core.domain.entity;
import javax.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import javax.validation.constraints.Size; import jakarta.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel; import com.ruoyi.common.annotation.Excel;

View File

@ -1,8 +1,8 @@
package com.ruoyi.common.core.domain.entity; package com.ruoyi.common.core.domain.entity;
import javax.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern; import jakarta.validation.constraints.Pattern;
import javax.validation.constraints.Size; import jakarta.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel; import com.ruoyi.common.annotation.Excel;

View File

@ -2,9 +2,9 @@ package com.ruoyi.common.core.domain.entity;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import javax.validation.constraints.Size; import jakarta.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.core.domain.BaseEntity; import com.ruoyi.common.core.domain.BaseEntity;

View File

@ -1,9 +1,9 @@
package com.ruoyi.common.core.domain.entity; package com.ruoyi.common.core.domain.entity;
import java.util.Set; import java.util.Set;
import javax.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import javax.validation.constraints.Size; import jakarta.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel; import com.ruoyi.common.annotation.Excel;

View File

@ -2,9 +2,10 @@ package com.ruoyi.common.core.domain.entity;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import javax.validation.constraints.*; import jakarta.validation.constraints.*;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.ruoyi.common.annotation.Excel; import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.annotation.Excel.ColumnType; import com.ruoyi.common.annotation.Excel.ColumnType;
import com.ruoyi.common.annotation.Excel.Type; import com.ruoyi.common.annotation.Excel.Type;
@ -200,6 +201,7 @@ public class SysUser extends BaseEntity
this.avatar = avatar; this.avatar = avatar;
} }
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
public String getPassword() public String getPassword()
{ {
return password; return password;

View File

@ -3,7 +3,7 @@ package com.ruoyi.common.exception.file;
import java.util.Arrays; import java.util.Arrays;
/** /**
* 文件上传异常类 * 文件上传无效扩展名异常类
* *
* @author ruoyi * @author ruoyi
*/ */

View File

@ -3,14 +3,14 @@ package com.ruoyi.common.filter;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import javax.servlet.Filter; import jakarta.servlet.Filter;
import javax.servlet.FilterChain; import jakarta.servlet.FilterChain;
import javax.servlet.FilterConfig; import jakarta.servlet.FilterConfig;
import javax.servlet.ServletException; import jakarta.servlet.ServletException;
import javax.servlet.ServletRequest; import jakarta.servlet.ServletRequest;
import javax.servlet.ServletResponse; import jakarta.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
/** /**
* 防盗链过滤器 * 防盗链过滤器

View File

@ -1,13 +1,13 @@
package com.ruoyi.common.filter; package com.ruoyi.common.filter;
import java.io.IOException; import java.io.IOException;
import javax.servlet.Filter; import jakarta.servlet.Filter;
import javax.servlet.FilterChain; import jakarta.servlet.FilterChain;
import javax.servlet.FilterConfig; import jakarta.servlet.FilterConfig;
import javax.servlet.ServletException; import jakarta.servlet.ServletException;
import javax.servlet.ServletRequest; import jakarta.servlet.ServletRequest;
import javax.servlet.ServletResponse; import jakarta.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;

View File

@ -4,11 +4,11 @@ import java.io.BufferedReader;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import javax.servlet.ReadListener; import jakarta.servlet.ReadListener;
import javax.servlet.ServletInputStream; import jakarta.servlet.ServletInputStream;
import javax.servlet.ServletResponse; import jakarta.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper; import jakarta.servlet.http.HttpServletRequestWrapper;
import com.ruoyi.common.utils.http.HttpHelper; import com.ruoyi.common.utils.http.HttpHelper;
import com.ruoyi.common.constant.Constants; import com.ruoyi.common.constant.Constants;

View File

@ -3,14 +3,14 @@ package com.ruoyi.common.filter;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.servlet.Filter; import jakarta.servlet.Filter;
import javax.servlet.FilterChain; import jakarta.servlet.FilterChain;
import javax.servlet.FilterConfig; import jakarta.servlet.FilterConfig;
import javax.servlet.ServletException; import jakarta.servlet.ServletException;
import javax.servlet.ServletRequest; import jakarta.servlet.ServletRequest;
import javax.servlet.ServletResponse; import jakarta.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.enums.HttpMethod; import com.ruoyi.common.enums.HttpMethod;

View File

@ -2,10 +2,10 @@ package com.ruoyi.common.filter;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import javax.servlet.ReadListener; import jakarta.servlet.ReadListener;
import javax.servlet.ServletInputStream; import jakarta.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper; import jakarta.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;

View File

@ -16,6 +16,7 @@ import org.apache.commons.lang3.time.DateFormatUtils;
* *
* @author ruoyi * @author ruoyi
*/ */
@SuppressWarnings("deprecation")
public class DateUtils extends org.apache.commons.lang3.time.DateUtils public class DateUtils extends org.apache.commons.lang3.time.DateUtils
{ {
public static String YYYY = "yyyy"; public static String YYYY = "yyyy";

View File

@ -7,10 +7,10 @@ import java.net.URLEncoder;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javax.servlet.ServletRequest; import jakarta.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession; import jakarta.servlet.http.HttpSession;
import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.context.request.ServletRequestAttributes;

View File

@ -15,6 +15,7 @@ import com.ruoyi.common.core.text.StrFormatter;
* *
* @author ruoyi * @author ruoyi
*/ */
@SuppressWarnings("deprecation")
public class StringUtils extends org.apache.commons.lang3.StringUtils public class StringUtils extends org.apache.commons.lang3.StringUtils
{ {
/** 空字符串 */ /** 空字符串 */

View File

@ -1,9 +1,9 @@
package com.ruoyi.common.utils.bean; package com.ruoyi.common.utils.bean;
import java.util.Set; import java.util.Set;
import javax.validation.ConstraintViolation; import jakarta.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException; import jakarta.validation.ConstraintViolationException;
import javax.validation.Validator; import jakarta.validation.Validator;
/** /**
* bean对象属性验证 * bean对象属性验证

View File

@ -9,8 +9,8 @@ import java.io.OutputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import javax.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;

View File

@ -5,7 +5,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import javax.servlet.ServletRequest; import jakarta.servlet.ServletRequest;
import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@ -0,0 +1,254 @@
package com.ruoyi.common.utils.http;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.ruoyi.common.utils.StringUtils;
import nl.basjes.parse.useragent.UserAgent;
import nl.basjes.parse.useragent.UserAgentAnalyzer;
/**
* UserAgent解析工具类
*
* @author ruoyi
*/
public class UserAgentUtils
{
public static final String UNKNOWN = "";
// 浏览器正则表达式模式
private static final Pattern CHROME_PATTERN = Pattern.compile("Chrome/(\\d+)(?:\\.\\d+)*");
private static final Pattern FIREFOX_PATTERN = Pattern.compile("Firefox/(\\d+)(?:\\.\\d+)*");
private static final Pattern EDGE_PATTERN = Pattern.compile("Edg(?:e)?/(\\d+)(?:\\.\\d+)*");
private static final Pattern SAFARI_PATTERN = Pattern.compile("Version/(\\d+)(?:\\.\\d+)*");
private static final Pattern OPERA_PATTERN = Pattern.compile("Opera/(\\d+)(?:\\.\\d+)*");
private static final Pattern IE_PATTERN = Pattern.compile("(?:MSIE |Trident/.*rv:)(\\d+)(?:\\.\\d+)*");
private static final Pattern SAMSUNG_PATTERN = Pattern.compile("SamsungBrowser/(\\d+)(?:\\.\\d+)*");
private static final Pattern UC_PATTERN = Pattern.compile("UCBrowser/(\\d+)(?:\\.\\d+)*");
private static final Pattern QQ_PATTERN = Pattern.compile("QQBrowser/(\\d+)(?:\\.\\d+)*");
private static final Pattern WECHAT_PATTERN = Pattern.compile("MicroMessenger/(\\d+)(?:\\.\\d+)*");
private static final Pattern BAIDU_PATTERN = Pattern.compile("baidubrowser/(\\d+)(?:\\.\\d+)*");
// 操作系统正则表达式模式
private static final Pattern WINDOWS_PATTERN = Pattern.compile("Windows NT (\\d+\\.\\d+)");
private static final Pattern MACOS_PATTERN = Pattern.compile("Mac OS X (\\d+[_\\d]*)");
private static final Pattern ANDROID_PATTERN = Pattern.compile("Android (\\d+)(?:\\.\\d+)*");
private static final Pattern IOS_PATTERN = Pattern.compile("OS[\\s_](\\d+)(?:_\\d+)*");
private static final Pattern LINUX_PATTERN = Pattern.compile("Linux");
private static final Pattern CHROMEOS_PATTERN = Pattern.compile("CrOS");
private static final UserAgentAnalyzer userAgentAnalyzer = UserAgentAnalyzer
.newBuilder().hideMatcherLoadStats()
.withCache(5000)
.showMinimalVersion()
.withField(UserAgent.AGENT_NAME_VERSION)
.withField(UserAgent.OPERATING_SYSTEM_NAME_VERSION)
.build();
/**
* 获取客户端浏览器
*/
public static String getBrowser(String userAgent)
{
UserAgent.ImmutableUserAgent iua = userAgentAnalyzer.parse(userAgent);
String agentNameVersion = iua.get(UserAgent.AGENT_NAME_VERSION).getValue();
if (StringUtils.isBlank(agentNameVersion) || agentNameVersion.contains("??"))
{
return formatBrowser(userAgent);
}
return agentNameVersion;
}
/**
* 获取客户端操作系统
*/
public static String getOperatingSystem(String userAgent)
{
UserAgent.ImmutableUserAgent iua = userAgentAnalyzer.parse(userAgent);
String operatingSystemNameVersion = iua.get(UserAgent.OPERATING_SYSTEM_NAME_VERSION).getValue();
if (StringUtils.isBlank(operatingSystemNameVersion) || operatingSystemNameVersion.contains("??"))
{
return formatOperatingSystem(userAgent);
}
return operatingSystemNameVersion;
}
/**
* 全面浏览器检测
*/
private static String formatBrowser(String browser)
{
// Chrome系列浏览器
Matcher chromeMatcher = CHROME_PATTERN.matcher(browser);
if (chromeMatcher.find() && (browser.contains("Chrome") || browser.contains("CriOS")))
{
return "Chrome" + chromeMatcher.group(1);
}
// Firefox
Matcher firefoxMatcher = FIREFOX_PATTERN.matcher(browser);
if (firefoxMatcher.find())
{
return "Firefox" + firefoxMatcher.group(1);
}
// Edge浏览器
Matcher edgeMatcher = EDGE_PATTERN.matcher(browser);
if (edgeMatcher.find())
{
return "Edge" + edgeMatcher.group(1);
}
// Safari浏览器需排除Chrome
Matcher safariMatcher = SAFARI_PATTERN.matcher(browser);
if (safariMatcher.find() && !browser.contains("Chrome"))
{
return "Safari" + safariMatcher.group(1);
}
// 微信内置浏览器
Matcher wechatMatcher = WECHAT_PATTERN.matcher(browser);
if (wechatMatcher.find())
{
return "WeChat" + wechatMatcher.group(1);
}
// UC浏览器
Matcher ucMatcher = UC_PATTERN.matcher(browser);
if (ucMatcher.find())
{
return "UC Browser" + ucMatcher.group(1);
}
// QQ浏览器
Matcher qqMatcher = QQ_PATTERN.matcher(browser);
if (qqMatcher.find())
{
return "QQ Browser" + qqMatcher.group(1);
}
// 百度浏览器
Matcher baiduMatcher = BAIDU_PATTERN.matcher(browser);
if (baiduMatcher.find())
{
return "Baidu Browser" + baiduMatcher.group(1);
}
// Samsung浏览器
Matcher samsungMatcher = SAMSUNG_PATTERN.matcher(browser);
if (samsungMatcher.find())
{
return "Samsung Browser" + samsungMatcher.group(1);
}
// Opera浏览器
Matcher operaMatcher = OPERA_PATTERN.matcher(browser);
if (operaMatcher.find())
{
return "Opera" + operaMatcher.group(1);
}
// IE浏览器
Matcher ieMatcher = IE_PATTERN.matcher(browser);
if (ieMatcher.find())
{
return "Internet Explorer" + ieMatcher.group(1);
}
return UNKNOWN;
}
/**
* 检测操作系统
*/
private static String formatOperatingSystem(String operatingSystem)
{
// Windows系统
Matcher windowsMatcher = WINDOWS_PATTERN.matcher(operatingSystem);
if (windowsMatcher.find())
{
return "Windows" + getWindowsVersionDisplay(windowsMatcher.group(1));
}
// macOS系统
Matcher macMatcher = MACOS_PATTERN.matcher(operatingSystem);
if (macMatcher.find())
{
String version = macMatcher.group(1).replace("_", ".");
return "macOS" + extractMajorVersion(version);
}
// Android系统
Matcher androidMatcher = ANDROID_PATTERN.matcher(operatingSystem);
if (androidMatcher.find())
{
return "Android" + extractMajorVersion(androidMatcher.group(1));
}
// iOS系统
Matcher iosMatcher = IOS_PATTERN.matcher(operatingSystem);
if (iosMatcher.find() && (operatingSystem.contains("iPhone") || operatingSystem.contains("iPad")))
{
return "iOS" + extractMajorVersion(iosMatcher.group(1));
}
// Linux系统
if (LINUX_PATTERN.matcher(operatingSystem).find() && !operatingSystem.contains("Android"))
{
return "Linux";
}
// Chrome OS
if (CHROMEOS_PATTERN.matcher(operatingSystem).find())
{
return "Chrome OS";
}
return UNKNOWN;
}
/**
* 提取优化的主版本号
*/
private static String extractMajorVersion(String fullVersion)
{
if (StringUtils.isEmpty(fullVersion))
{
return StringUtils.EMPTY;
}
try
{
// 清理版本号中的非数字字符
String cleanVersion = fullVersion.replaceAll("[^0-9.]", "");
String[] parts = cleanVersion.split("\\.");
if (parts.length > 0)
{
String firstPart = parts[0];
if (firstPart.matches("\\d+"))
{
int version = Integer.parseInt(firstPart);
// 处理三位数版本号如142 -> 14
if (version >= 100)
{
return String.valueOf(version / 10);
}
return firstPart;
}
}
}
catch (NumberFormatException e)
{
// 版本号解析失败,返回原始值
}
return fullVersion;
}
/**
* Windows版本号显示优化
*/
private static String getWindowsVersionDisplay(String version)
{
switch (version)
{
case "10.0":
return "10";
case "6.3":
return "8.1";
case "6.2":
return "8";
case "6.1":
return "7";
case "6.0":
return "Vista";
case "5.1":
return "XP";
case "5.0":
return "2000";
default:
return extractMajorVersion(version);
}
}
}

View File

@ -2,7 +2,7 @@ package com.ruoyi.common.utils.ip;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import javax.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import com.ruoyi.common.utils.ServletUtils; import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;

View File

@ -23,7 +23,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.RegExUtils; import org.apache.commons.lang3.RegExUtils;
import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.commons.lang3.reflect.FieldUtils;
@ -174,12 +174,12 @@ public class ExcelUtil<T>
/** /**
* 对象的子列表方法 * 对象的子列表方法
*/ */
private Map<String, Method> subMethods = new HashMap<>(); private Map<String, Method> subMethods;
/** /**
* 对象的子列表属性 * 对象的子列表属性
*/ */
private Map<String, List<Field>> subFieldsMap = new HashMap<>(); private Map<String, List<Field>> subFieldsMap;
/** /**
* 统计列表 * 统计列表
@ -252,7 +252,10 @@ public class ExcelUtil<T>
int titleLastCol = this.fields.size() - 1; int titleLastCol = this.fields.size() - 1;
if (isSubList()) if (isSubList())
{ {
titleLastCol = titleLastCol + subFieldsMap.values().size() - 1; for (List<Field> currentSubFields : subFieldsMap.values())
{
titleLastCol = titleLastCol + currentSubFields.size() - 1;
}
} }
Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0); Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0);
titleRow.setHeightInPoints(30); titleRow.setHeightInPoints(30);
@ -415,7 +418,7 @@ public class ExcelUtil<T>
Object val = this.getCellValue(row, entry.getKey()); Object val = this.getCellValue(row, entry.getKey());
// 如果不存在实例则新建. // 如果不存在实例则新建.
entity = (entity == null ? clazz.newInstance() : entity); entity = (entity == null ? clazz.getDeclaredConstructor().newInstance() : entity);
// 从map中得到对应列的field. // 从map中得到对应列的field.
Field field = (Field) entry.getValue()[0]; Field field = (Field) entry.getValue()[0];
Excel attr = (Excel) entry.getValue()[1]; Excel attr = (Excel) entry.getValue()[1];
@ -722,7 +725,6 @@ public class ExcelUtil<T>
* 填充excel数据 * 填充excel数据
* *
* @param index 序号 * @param index 序号
* @param row 单元格行
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void fillExcelData(int index) public void fillExcelData(int index)
@ -746,10 +748,10 @@ public class ExcelUtil<T>
try try
{ {
Collection<?> subList = (Collection<?>) getTargetValue(vo, field, excel); Collection<?> subList = (Collection<?>) getTargetValue(vo, field, excel);
List<Field> currentSubFields = subFieldsMap.get(field.getName());
if (subList != null && !subList.isEmpty()) if (subList != null && !subList.isEmpty())
{ {
int subIndex = 0; int subIndex = 0;
List<Field> currentSubFields = subFieldsMap.get(field.getName());
for (Object subVo : subList) for (Object subVo : subList)
{ {
Row subRow = sheet.getRow(currentRowNum + subIndex); Row subRow = sheet.getRow(currentRowNum + subIndex);
@ -766,8 +768,8 @@ public class ExcelUtil<T>
} }
subIndex++; subIndex++;
} }
column += currentSubFields.size();
} }
column += currentSubFields.size();
} }
catch (Exception e) catch (Exception e)
{ {
@ -1122,7 +1124,8 @@ public class ExcelUtil<T>
/** /**
* 添加单元格 * 添加单元格
*/ */
public Cell addCell(Excel attr, Row row, T vo, Field field, int column) @SuppressWarnings("deprecation")
public Cell addCell(Excel attr, Row row, T vo, Field field, int column)
{ {
Cell cell = null; Cell cell = null;
try try
@ -1408,7 +1411,7 @@ public class ExcelUtil<T>
{ {
try try
{ {
Object instance = excel.handler().newInstance(); Object instance = excel.handler().getDeclaredConstructor().newInstance();
Method formatMethod = excel.handler().getMethod("format", new Class[] { Object.class, String[].class, Cell.class, Workbook.class }); Method formatMethod = excel.handler().getMethod("format", new Class[] { Object.class, String[].class, Cell.class, Workbook.class });
value = formatMethod.invoke(instance, value, excel.args(), cell, this.wb); value = formatMethod.invoke(instance, value, excel.args(), cell, this.wb);
} }
@ -1558,6 +1561,8 @@ public class ExcelUtil<T>
{ {
List<Object[]> fields = new ArrayList<Object[]>(); List<Object[]> fields = new ArrayList<Object[]>();
List<Field> tempFields = new ArrayList<>(); List<Field> tempFields = new ArrayList<>();
subFieldsMap = new HashMap<>();
subMethods = new HashMap<>();
tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields())); tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
tempFields.addAll(Arrays.asList(clazz.getDeclaredFields())); tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
if (StringUtils.isNotEmpty(includeFields)) if (StringUtils.isNotEmpty(includeFields))

View File

@ -310,6 +310,7 @@ public class ReflectUtils
/** /**
* 改变private/protected的方法为public尽量不调用实际改动的语句避免JDK的SecurityManager抱怨。 * 改变private/protected的方法为public尽量不调用实际改动的语句避免JDK的SecurityManager抱怨。
*/ */
@SuppressWarnings("deprecation")
public static void makeAccessible(Method method) public static void makeAccessible(Method method)
{ {
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
@ -322,6 +323,7 @@ public class ReflectUtils
/** /**
* 改变private/protected的成员变量为public尽量不调用实际改动的语句避免JDK的SecurityManager抱怨。 * 改变private/protected的成员变量为public尽量不调用实际改动的语句避免JDK的SecurityManager抱怨。
*/ */
@SuppressWarnings("deprecation")
public static void makeAccessible(Field field) public static void makeAccessible(Field field)
{ {
if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())

View File

@ -13,7 +13,7 @@ public class SqlUtil
/** /**
* 定义常用的 sql关键字 * 定义常用的 sql关键字
*/ */
public static String SQL_REGEX = "\u000B|and |extractvalue|updatexml|sleep|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()"; public static String SQL_REGEX = "\u000B|and |extractvalue|updatexml|sleep|information_schema|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()";
/** /**
* 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)

View File

@ -1,7 +1,7 @@
package com.ruoyi.common.xss; package com.ruoyi.common.xss;
import javax.validation.Constraint; import jakarta.validation.Constraint;
import javax.validation.Payload; import jakarta.validation.Payload;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;

View File

@ -1,8 +1,8 @@
package com.ruoyi.common.xss; package com.ruoyi.common.xss;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
import javax.validation.ConstraintValidator; import jakarta.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext; import jakarta.validation.ConstraintValidatorContext;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>ruoyi</artifactId> <artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<version>3.9.0</version> <version>3.9.1</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@ -32,7 +32,7 @@
<!-- 阿里数据库连接池 --> <!-- 阿里数据库连接池 -->
<dependency> <dependency>
<groupId>com.alibaba</groupId> <groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId> <artifactId>druid-spring-boot-3-starter</artifactId>
</dependency> </dependency>
<!-- 验证码 --> <!-- 验证码 -->

View File

@ -94,7 +94,7 @@ public class DataScopeAspect
List<String> conditions = new ArrayList<String>(); List<String> conditions = new ArrayList<String>();
List<String> scopeCustomIds = new ArrayList<String>(); List<String> scopeCustomIds = new ArrayList<String>();
user.getRoles().forEach(role -> { user.getRoles().forEach(role -> {
if (DATA_SCOPE_CUSTOM.equals(role.getDataScope()) && StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL) && StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))) if (DATA_SCOPE_CUSTOM.equals(role.getDataScope()) && StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL) && (StringUtils.isEmpty(permission) || StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))))
{ {
scopeCustomIds.add(Convert.toStr(role.getRoleId())); scopeCustomIds.add(Convert.toStr(role.getRoleId()));
} }
@ -107,7 +107,7 @@ public class DataScopeAspect
{ {
continue; continue;
} }
if (!StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))) if (StringUtils.isNotEmpty(permission) && !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
{ {
continue; continue;
} }

View File

@ -2,8 +2,8 @@ package com.ruoyi.framework.aspectj;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterReturning;
@ -112,7 +112,7 @@ public class LogAspect
if (e != null) if (e != null)
{ {
operLog.setStatus(BusinessStatus.FAIL.ordinal()); operLog.setStatus(BusinessStatus.FAIL.ordinal());
operLog.setErrorMsg(StringUtils.substring(Convert.toStr(e.getMessage(), ExceptionUtil.getExceptionMessage(e)), 0, PARAM_MAX_LENGTH)); operLog.setErrorMsg(StringUtils.substring(Convert.toStr(e.getMessage(), ExceptionUtil.getExceptionMessage(e)), 0, 2000));
} }
// 设置方法名称 // 设置方法名称
String className = joinPoint.getTarget().getClass().getName(); String className = joinPoint.getTarget().getClass().getName();
@ -163,7 +163,7 @@ public class LogAspect
// 是否需要保存response参数和值 // 是否需要保存response参数和值
if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult)) if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult))
{ {
operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, PARAM_MAX_LENGTH)); operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000));
} }
} }

View File

@ -3,11 +3,6 @@ package com.ruoyi.framework.config;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
@ -16,13 +11,18 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceBuilder;
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties; import com.alibaba.druid.spring.boot3.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.util.Utils; import com.alibaba.druid.util.Utils;
import com.ruoyi.common.enums.DataSourceType; import com.ruoyi.common.enums.DataSourceType;
import com.ruoyi.common.utils.spring.SpringUtils; import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.framework.config.properties.DruidProperties; import com.ruoyi.framework.config.properties.DruidProperties;
import com.ruoyi.framework.datasource.DynamicDataSource; import com.ruoyi.framework.datasource.DynamicDataSource;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
/** /**
* druid 配置多数据源 * druid 配置多数据源
@ -96,7 +96,7 @@ public class DruidConfig
Filter filter = new Filter() Filter filter = new Filter()
{ {
@Override @Override
public void init(javax.servlet.FilterConfig filterConfig) throws ServletException public void init(jakarta.servlet.FilterConfig filterConfig) throws ServletException
{ {
} }
@Override @Override

View File

@ -2,7 +2,7 @@ package com.ruoyi.framework.config;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javax.servlet.DispatcherType; import jakarta.servlet.DispatcherType;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.FilterRegistrationBean;

View File

@ -14,6 +14,7 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
* *
* @author ruoyi * @author ruoyi
*/ */
@SuppressWarnings("deprecation")
@Configuration @Configuration
@EnableCaching @EnableCaching
public class RedisConfig extends CachingConfigurerSupport public class RedisConfig extends CachingConfigurerSupport

View File

@ -5,12 +5,10 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@ -30,12 +28,6 @@ import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl;
@Configuration @Configuration
public class SecurityConfig public class SecurityConfig
{ {
/**
* 自定义用户认证逻辑
*/
@Autowired
private UserDetailsService userDetailsService;
/** /**
* 认证失败处理类 * 认证失败处理类
*/ */
@ -66,17 +58,14 @@ public class SecurityConfig
@Autowired @Autowired
private PermitAllUrlProperties permitAllUrl; private PermitAllUrlProperties permitAllUrl;
/** /**
* 身份验证实现 * 身份验证实现
*/ */
@Bean @Bean
public AuthenticationManager authenticationManager() public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception
{ {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider(); return authenticationConfiguration.getAuthenticationManager();
daoAuthenticationProvider.setUserDetailsService(userDetailsService); }
daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder());
return new ProviderManager(daoAuthenticationProvider);
}
/** /**
* anyRequest | 匹配所有请求路径 * anyRequest | 匹配所有请求路径
@ -109,12 +98,12 @@ public class SecurityConfig
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
// 注解标记允许匿名访问的url // 注解标记允许匿名访问的url
.authorizeHttpRequests((requests) -> { .authorizeHttpRequests((requests) -> {
permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll()); permitAllUrl.getUrls().forEach(url -> requests.requestMatchers(url).permitAll());
// 对于登录login 注册register 验证码captchaImage 允许匿名访问 // 对于登录login 注册register 验证码captchaImage 允许匿名访问
requests.antMatchers("/login", "/register", "/captchaImage").permitAll() requests.requestMatchers("/login", "/register", "/captchaImage").permitAll()
// 静态资源,可匿名访问 // 静态资源,可匿名访问
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll() .requestMatchers(HttpMethod.GET, "/", "/*.html", "/**.html", "/**.css", "/**.js", "/profile/**").permitAll()
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll() .requestMatchers("/swagger-ui.html", "/v3/api-docs/**", "/swagger-ui/**", "/druid/**").permitAll()
// 除上面外的所有请求全部需要鉴权认证 // 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated(); .anyRequest().authenticated();
}) })

View File

@ -1,6 +1,6 @@
package com.ruoyi.framework.config; package com.ruoyi.framework.config;
import javax.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.ruoyi.common.utils.ServletUtils; import com.ruoyi.common.utils.ServletUtils;

View File

@ -37,7 +37,7 @@ public class PermitAllUrlProperties implements InitializingBean, ApplicationCont
@Override @Override
public void afterPropertiesSet() public void afterPropertiesSet()
{ {
RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class); RequestMappingHandlerMapping mapping = applicationContext.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);
Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods(); Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
map.keySet().forEach(info -> { map.keySet().forEach(info -> {
@ -45,12 +45,12 @@ public class PermitAllUrlProperties implements InitializingBean, ApplicationCont
// 获取方法上边的注解 替代path variable 为 * // 获取方法上边的注解 替代path variable 为 *
Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class); Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class);
Optional.ofNullable(method).ifPresent(anonymous -> Objects.requireNonNull(info.getPatternsCondition().getPatterns()) Optional.ofNullable(method).ifPresent(anonymous -> Objects.requireNonNull(info.getPathPatternsCondition().getPatternValues()) //
.forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK))));
// 获取类上边的注解, 替代path variable 为 * // 获取类上边的注解, 替代path variable 为 *
Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class); Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class);
Optional.ofNullable(controller).ifPresent(anonymous -> Objects.requireNonNull(info.getPatternsCondition().getPatterns()) Optional.ofNullable(controller).ifPresent(anonymous -> Objects.requireNonNull(info.getPathPatternsCondition().getPatternValues())
.forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK))));
}); });
} }

View File

@ -1,8 +1,8 @@
package com.ruoyi.framework.interceptor; package com.ruoyi.framework.interceptor;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.HandlerInterceptor;

View File

@ -3,7 +3,7 @@ package com.ruoyi.framework.interceptor.impl;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;

View File

@ -3,7 +3,7 @@ package com.ruoyi.framework.manager;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy; import jakarta.annotation.PreDestroy;
/** /**
* 确保应用退出时能关闭后台线程 * 确保应用退出时能关闭后台线程

View File

@ -7,6 +7,7 @@ import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.LogUtils; import com.ruoyi.common.utils.LogUtils;
import com.ruoyi.common.utils.ServletUtils; import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.http.UserAgentUtils;
import com.ruoyi.common.utils.ip.AddressUtils; import com.ruoyi.common.utils.ip.AddressUtils;
import com.ruoyi.common.utils.ip.IpUtils; import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.common.utils.spring.SpringUtils; import com.ruoyi.common.utils.spring.SpringUtils;
@ -14,7 +15,6 @@ import com.ruoyi.system.domain.SysLogininfor;
import com.ruoyi.system.domain.SysOperLog; import com.ruoyi.system.domain.SysOperLog;
import com.ruoyi.system.service.ISysLogininforService; import com.ruoyi.system.service.ISysLogininforService;
import com.ruoyi.system.service.ISysOperLogService; import com.ruoyi.system.service.ISysOperLogService;
import eu.bitwalker.useragentutils.UserAgent;
/** /**
* 异步工厂(产生任务用) * 异步工厂(产生任务用)
@ -37,7 +37,7 @@ public class AsyncFactory
public static TimerTask recordLogininfor(final String username, final String status, final String message, public static TimerTask recordLogininfor(final String username, final String status, final String message,
final Object... args) final Object... args)
{ {
final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); final String userAgent = ServletUtils.getRequest().getHeader("User-Agent");
final String ip = IpUtils.getIpAddr(); final String ip = IpUtils.getIpAddr();
return new TimerTask() return new TimerTask()
{ {
@ -54,9 +54,9 @@ public class AsyncFactory
// 打印信息到日志 // 打印信息到日志
sys_user_logger.info(s.toString(), args); sys_user_logger.info(s.toString(), args);
// 获取客户端操作系统 // 获取客户端操作系统
String os = userAgent.getOperatingSystem().getName(); String os = UserAgentUtils.getOperatingSystem(userAgent);
// 获取客户端浏览器 // 获取客户端浏览器
String browser = userAgent.getBrowser().getName(); String browser = UserAgentUtils.getBrowser(userAgent);
// 封装对象 // 封装对象
SysLogininfor logininfor = new SysLogininfor(); SysLogininfor logininfor = new SysLogininfor();
logininfor.setUserName(username); logininfor.setUserName(username);

View File

@ -1,10 +1,10 @@
package com.ruoyi.framework.security.filter; package com.ruoyi.framework.security.filter;
import java.io.IOException; import java.io.IOException;
import javax.servlet.FilterChain; import jakarta.servlet.FilterChain;
import javax.servlet.ServletException; import jakarta.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;

View File

@ -2,8 +2,8 @@ package com.ruoyi.framework.security.handle;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import javax.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;

View File

@ -1,9 +1,9 @@
package com.ruoyi.framework.security.handle; package com.ruoyi.framework.security.handle;
import java.io.IOException; import java.io.IOException;
import javax.servlet.ServletException; import jakarta.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;

View File

@ -1,6 +1,6 @@
package com.ruoyi.framework.web.exception; package com.ruoyi.framework.web.exception;
import javax.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.AccessDeniedException;

View File

@ -1,6 +1,6 @@
package com.ruoyi.framework.web.service; package com.ruoyi.framework.web.service;
import javax.annotation.Resource; import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.BadCredentialsException;

View File

@ -3,7 +3,7 @@ package com.ruoyi.framework.web.service;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -15,10 +15,10 @@ import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.redis.RedisCache; import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.ServletUtils; import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.http.UserAgentUtils;
import com.ruoyi.common.utils.ip.AddressUtils; import com.ruoyi.common.utils.ip.AddressUtils;
import com.ruoyi.common.utils.ip.IpUtils; import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.common.utils.uuid.IdUtils; import com.ruoyi.common.utils.uuid.IdUtils;
import eu.bitwalker.useragentutils.UserAgent;
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts; import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.SignatureAlgorithm;
@ -161,12 +161,12 @@ public class TokenService
*/ */
public void setUserAgent(LoginUser loginUser) public void setUserAgent(LoginUser loginUser)
{ {
UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); String userAgent = ServletUtils.getRequest().getHeader("User-Agent");
String ip = IpUtils.getIpAddr(); String ip = IpUtils.getIpAddr();
loginUser.setIpaddr(ip); loginUser.setIpaddr(ip);
loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
loginUser.setBrowser(userAgent.getBrowser().getName()); loginUser.setBrowser(UserAgentUtils.getBrowser(userAgent));
loginUser.setOs(userAgent.getOperatingSystem().getName()); loginUser.setOs(UserAgentUtils.getOperatingSystem(userAgent));
} }
/** /**

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>ruoyi</artifactId> <artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<version>3.9.0</version> <version>3.9.1</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@ -32,7 +32,7 @@
<!-- 阿里数据库连接池 --> <!-- 阿里数据库连接池 -->
<dependency> <dependency>
<groupId>com.alibaba</groupId> <groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId> <artifactId>druid-spring-boot-3-starter</artifactId>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -5,7 +5,7 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;

View File

@ -1,8 +1,8 @@
package com.ruoyi.generator.domain; package com.ruoyi.generator.domain;
import java.util.List; import java.util.List;
import javax.validation.Valid; import jakarta.validation.Valid;
import javax.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import com.ruoyi.common.constant.GenConstants; import com.ruoyi.common.constant.GenConstants;
import com.ruoyi.common.core.domain.BaseEntity; import com.ruoyi.common.core.domain.BaseEntity;

View File

@ -1,6 +1,6 @@
package com.ruoyi.generator.domain; package com.ruoyi.generator.domain;
import javax.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import com.ruoyi.common.core.domain.BaseEntity; import com.ruoyi.common.core.domain.BaseEntity;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;

View File

@ -1,7 +1,7 @@
package ${packageName}.controller; package ${packageName}.controller;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>ruoyi</artifactId> <artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<version>3.9.0</version> <version>3.9.1</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@ -21,12 +21,6 @@
<dependency> <dependency>
<groupId>org.quartz-scheduler</groupId> <groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId> <artifactId>quartz</artifactId>
<exclusions>
<exclusion>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<!-- 通用工具--> <!-- 通用工具-->

View File

@ -1,7 +1,7 @@
package com.ruoyi.quartz.controller; package com.ruoyi.quartz.controller;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.quartz.SchedulerException; import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;

View File

@ -1,7 +1,7 @@
package com.ruoyi.quartz.controller; package com.ruoyi.quartz.controller;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.DeleteMapping;

View File

@ -1,8 +1,8 @@
package com.ruoyi.quartz.domain; package com.ruoyi.quartz.domain;
import java.util.Date; import java.util.Date;
import javax.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import javax.validation.constraints.Size; import jakarta.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;

View File

@ -1,7 +1,7 @@
package com.ruoyi.quartz.service.impl; package com.ruoyi.quartz.service.impl;
import java.util.List; import java.util.List;
import javax.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import org.quartz.JobDataMap; import org.quartz.JobDataMap;
import org.quartz.JobKey; import org.quartz.JobKey;
import org.quartz.Scheduler; import org.quartz.Scheduler;

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>ruoyi</artifactId> <artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<version>3.9.0</version> <version>3.9.1</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -1,7 +1,7 @@
package com.ruoyi.system.domain; package com.ruoyi.system.domain;
import javax.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import javax.validation.constraints.Size; import jakarta.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel; import com.ruoyi.common.annotation.Excel;

View File

@ -1,7 +1,7 @@
package com.ruoyi.system.domain; package com.ruoyi.system.domain;
import javax.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import javax.validation.constraints.Size; import jakarta.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.core.domain.BaseEntity; import com.ruoyi.common.core.domain.BaseEntity;

View File

@ -1,8 +1,8 @@
package com.ruoyi.system.domain; package com.ruoyi.system.domain;
import javax.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import javax.validation.constraints.Size; import jakarta.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel; import com.ruoyi.common.annotation.Excel;

View File

@ -2,7 +2,7 @@ package com.ruoyi.system.service.impl;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import javax.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.ruoyi.common.annotation.DataSource; import com.ruoyi.common.annotation.DataSource;

View File

@ -4,7 +4,7 @@ import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;

View File

@ -4,7 +4,7 @@ import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.validation.Validator; import jakarta.validation.Validator;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;

View File

@ -1,6 +1,6 @@
{ {
"name": "ruoyi", "name": "ruoyi",
"version": "3.9.0", "version": "3.9.1",
"description": "若依管理系统", "description": "若依管理系统",
"author": "若依", "author": "若依",
"license": "MIT", "license": "MIT",

View File

@ -130,6 +130,16 @@
border-radius: 4px; border-radius: 4px;
} }
/* horizontal el menu */
.el-menu--horizontal .el-menu-item .svg-icon + span,
.el-menu--horizontal .el-submenu__title .svg-icon + span {
margin-left: 3px;
}
.el-menu--horizontal .el-menu--popup {
min-width: 120px !important;
}
@media (max-width: 768px) { @media (max-width: 768px) {
.pagination-container .el-pagination > .el-pagination__jump { .pagination-container .el-pagination > .el-pagination__jump {
display: none !important; display: none !important;

View File

@ -61,7 +61,7 @@
} }
.svg-icon { .svg-icon {
margin-right: 16px; margin-right: 10px !important;
} }
.el-menu { .el-menu {
@ -116,15 +116,17 @@
margin-left: 54px; margin-left: 54px;
} }
.submenu-title-noDropdown { .el-menu:not(.el-menu--horizontal) {
padding: 0 !important; .submenu-title-noDropdown {
position: relative;
.el-tooltip {
padding: 0 !important; padding: 0 !important;
position: relative;
.svg-icon { .el-tooltip {
margin-left: 20px; padding: 0 !important;
.svg-icon {
margin-left: 20px;
}
} }
} }
} }

View File

@ -94,7 +94,6 @@ export default {
display: inline-block; display: inline-block;
font-size: 14px; font-size: 14px;
line-height: 50px; line-height: 50px;
margin-left: 8px;
.no-redirect { .no-redirect {
color: #97a8be; color: #97a8be;
cursor: text; cursor: text;

View File

@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<template v-for="(item, index) in options"> <template v-for="(item, index) in options">
<template v-if="values.includes(item.value)"> <template v-if="isValueMatch(item.value)">
<span <span
v-if="(item.raw.listClass == 'default' || item.raw.listClass == '') && (item.raw.cssClass == '' || item.raw.cssClass == null)" v-if="(item.raw.listClass == 'default' || item.raw.listClass == '') && (item.raw.cssClass == '' || item.raw.cssClass == null)"
:key="item.value" :key="item.value"
@ -36,7 +36,6 @@ export default {
default: null, default: null,
}, },
value: [Number, String, Array], value: [Number, String, Array],
// 当未找到匹配的数据时显示value
showValue: { showValue: {
type: Boolean, type: Boolean,
default: true, default: true,
@ -54,6 +53,7 @@ export default {
computed: { computed: {
values() { values() {
if (this.value === null || typeof this.value === 'undefined' || this.value === '') return [] if (this.value === null || typeof this.value === 'undefined' || this.value === '') return []
if (typeof this.value === 'number' || typeof this.value === 'boolean') return [this.value]
return Array.isArray(this.value) ? this.value.map(item => '' + item) : String(this.value).split(this.separator) return Array.isArray(this.value) ? this.value.map(item => '' + item) : String(this.value).split(this.separator)
}, },
unmatch() { unmatch() {
@ -63,14 +63,18 @@ export default {
// 传入值为数组 // 传入值为数组
let unmatch = false // 添加一个标志来判断是否有未匹配项 let unmatch = false // 添加一个标志来判断是否有未匹配项
this.values.forEach(item => { this.values.forEach(item => {
if (!this.options.some(v => v.value === item)) { if (!this.options.some(v => v.value == item)) {
this.unmatchArray.push(item) this.unmatchArray.push(item)
unmatch = true // 如果有未匹配项将标志设置为true unmatch = true // 如果有未匹配项将标志设置为true
} }
}) })
return unmatch // 返回标志的值 return unmatch // 返回标志的值
}, },
},
methods: {
isValueMatch(itemValue) {
return this.values.some(val => val == itemValue)
}
}, },
filters: { filters: {
handleArray(array) { handleArray(array) {

View File

@ -1,170 +1,170 @@
<template> <template>
<el-color-picker <el-color-picker
v-model="theme" v-model="theme"
:predefine="['#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d', ]" :predefine="['#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d', ]"
class="theme-picker" class="theme-picker"
popper-class="theme-picker-dropdown" popper-class="theme-picker-dropdown"
/> />
</template> </template>
<script> <script>
const ORIGINAL_THEME = '#409EFF' // default color const ORIGINAL_THEME = '#409EFF' // default color
export default { export default {
data() { data() {
return { return {
chalk: '', // content of theme-chalk css chalk: '', // content of theme-chalk css
theme: '' theme: ''
} }
}, },
computed: { computed: {
defaultTheme() { defaultTheme() {
return this.$store.state.settings.theme return this.$store.state.settings.theme
} }
}, },
watch: { watch: {
defaultTheme: { defaultTheme: {
handler: function(val, oldVal) { handler: function(val, oldVal) {
this.theme = val this.theme = val
}, },
immediate: true immediate: true
}, },
async theme(val) { async theme(val) {
await this.setTheme(val) await this.setTheme(val)
} }
}, },
created() { created() {
if(this.defaultTheme !== ORIGINAL_THEME) { if(this.defaultTheme !== ORIGINAL_THEME) {
this.setTheme(this.defaultTheme) this.setTheme(this.defaultTheme)
} }
}, },
methods: { methods: {
async setTheme(val) { async setTheme(val) {
const oldVal = this.chalk ? this.theme : ORIGINAL_THEME const oldVal = this.chalk ? this.theme : ORIGINAL_THEME
if (typeof val !== 'string') return if (typeof val !== 'string') return
const themeCluster = this.getThemeCluster(val.replace('#', '')) const themeCluster = this.getThemeCluster(val.replace('#', ''))
const originalCluster = this.getThemeCluster(oldVal.replace('#', '')) const originalCluster = this.getThemeCluster(oldVal.replace('#', ''))
const getHandler = (variable, id) => { const getHandler = (variable, id) => {
return () => { return () => {
const originalCluster = this.getThemeCluster(ORIGINAL_THEME.replace('#', '')) const originalCluster = this.getThemeCluster(ORIGINAL_THEME.replace('#', ''))
const newStyle = this.updateStyle(this[variable], originalCluster, themeCluster) const newStyle = this.updateStyle(this[variable], originalCluster, themeCluster)
let styleTag = document.getElementById(id) let styleTag = document.getElementById(id)
if (!styleTag) { if (!styleTag) {
styleTag = document.createElement('style') styleTag = document.createElement('style')
styleTag.setAttribute('id', id) styleTag.setAttribute('id', id)
document.head.appendChild(styleTag) document.head.appendChild(styleTag)
} }
styleTag.innerText = newStyle styleTag.innerText = newStyle
} }
} }
if (!this.chalk) { if (!this.chalk) {
const url = `/styles/theme-chalk/index.css` const url = `/styles/theme-chalk/index.css`
await this.getCSSString(url, 'chalk') await this.getCSSString(url, 'chalk')
} }
const chalkHandler = getHandler('chalk', 'chalk-style') const chalkHandler = getHandler('chalk', 'chalk-style')
chalkHandler() chalkHandler()
const styles = [].slice.call(document.querySelectorAll('style')) const styles = [].slice.call(document.querySelectorAll('style'))
.filter(style => { .filter(style => {
const text = style.innerText const text = style.innerText
return new RegExp(oldVal, 'i').test(text) && !/Chalk Variables/.test(text) return new RegExp(oldVal, 'i').test(text) && !/Chalk Variables/.test(text)
}) })
styles.forEach(style => { styles.forEach(style => {
const { innerText } = style const { innerText } = style
if (typeof innerText !== 'string') return if (typeof innerText !== 'string') return
style.innerText = this.updateStyle(innerText, originalCluster, themeCluster) style.innerText = this.updateStyle(innerText, originalCluster, themeCluster)
}) })
this.$emit('change', val) this.$emit('change', val)
}, },
updateStyle(style, oldCluster, newCluster) { updateStyle(style, oldCluster, newCluster) {
let newStyle = style let newStyle = style
oldCluster.forEach((color, index) => { oldCluster.forEach((color, index) => {
newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index]) newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index])
}) })
return newStyle return newStyle
}, },
getCSSString(url, variable) { getCSSString(url, variable) {
return new Promise(resolve => { return new Promise(resolve => {
const xhr = new XMLHttpRequest() const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => { xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status === 200) { if (xhr.readyState === 4 && xhr.status === 200) {
this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '') this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '')
resolve() resolve()
} }
} }
xhr.open('GET', url) xhr.open('GET', url)
xhr.send() xhr.send()
}) })
}, },
getThemeCluster(theme) { getThemeCluster(theme) {
const tintColor = (color, tint) => { const tintColor = (color, tint) => {
let red = parseInt(color.slice(0, 2), 16) let red = parseInt(color.slice(0, 2), 16)
let green = parseInt(color.slice(2, 4), 16) let green = parseInt(color.slice(2, 4), 16)
let blue = parseInt(color.slice(4, 6), 16) let blue = parseInt(color.slice(4, 6), 16)
if (tint === 0) { // when primary color is in its rgb space if (tint === 0) { // when primary color is in its rgb space
return [red, green, blue].join(',') return [red, green, blue].join(',')
} else { } else {
red += Math.round(tint * (255 - red)) red += Math.round(tint * (255 - red))
green += Math.round(tint * (255 - green)) green += Math.round(tint * (255 - green))
blue += Math.round(tint * (255 - blue)) blue += Math.round(tint * (255 - blue))
red = red.toString(16) red = red.toString(16)
green = green.toString(16) green = green.toString(16)
blue = blue.toString(16) blue = blue.toString(16)
return `#${red}${green}${blue}` return `#${red}${green}${blue}`
} }
} }
const shadeColor = (color, shade) => { const shadeColor = (color, shade) => {
let red = parseInt(color.slice(0, 2), 16) let red = parseInt(color.slice(0, 2), 16)
let green = parseInt(color.slice(2, 4), 16) let green = parseInt(color.slice(2, 4), 16)
let blue = parseInt(color.slice(4, 6), 16) let blue = parseInt(color.slice(4, 6), 16)
red = Math.round((1 - shade) * red) red = Math.round((1 - shade) * red)
green = Math.round((1 - shade) * green) green = Math.round((1 - shade) * green)
blue = Math.round((1 - shade) * blue) blue = Math.round((1 - shade) * blue)
red = red.toString(16) red = red.toString(16)
green = green.toString(16) green = green.toString(16)
blue = blue.toString(16) blue = blue.toString(16)
return `#${red}${green}${blue}` return `#${red}${green}${blue}`
} }
const clusters = [theme] const clusters = [theme]
for (let i = 0; i <= 9; i++) { for (let i = 0; i <= 9; i++) {
clusters.push(tintColor(theme, Number((i / 10).toFixed(2)))) clusters.push(tintColor(theme, Number((i / 10).toFixed(2))))
} }
clusters.push(shadeColor(theme, 0.1)) clusters.push(shadeColor(theme, 0.1))
return clusters return clusters
} }
} }
} }
</script> </script>
<style> <style>
.theme-message, .theme-message,
.theme-picker-dropdown { .theme-picker-dropdown {
z-index: 99999 !important; z-index: 99999 !important;
} }
.theme-picker .el-color-picker__trigger { .theme-picker .el-color-picker__trigger {
height: 26px !important; height: 26px !important;
width: 26px !important; width: 26px !important;
padding: 2px; padding: 2px;
} }
.theme-picker-dropdown .el-color-dropdown__link-btn { .theme-picker-dropdown .el-color-dropdown__link-btn {
display: none; display: none;
} }
</style> </style>

View File

@ -162,7 +162,7 @@ export default {
this.$store.dispatch('app/toggleSideBarHide', true) this.$store.dispatch('app/toggleSideBarHide', true)
} }
} }
}, }
} }
</script> </script>
@ -171,7 +171,7 @@ export default {
float: left; float: left;
height: 50px !important; height: 50px !important;
line-height: 50px !important; line-height: 50px !important;
color: #999093 !important; color: #303133 !important;
padding: 0 5px !important; padding: 0 5px !important;
margin: 0 10px !important; margin: 0 10px !important;
} }
@ -186,7 +186,7 @@ export default {
float: left; float: left;
height: 50px !important; height: 50px !important;
line-height: 50px !important; line-height: 50px !important;
color: #999093 !important; color: #303133 !important;
padding: 0 5px !important; padding: 0 5px !important;
margin: 0 10px !important; margin: 0 10px !important;
} }

View File

@ -80,6 +80,39 @@ export default {
min-height: 0px; min-height: 0px;
} }
} }
/* 移动端fixed-header优化 */
@media screen and (max-width: 991px) {
.fixed-header + .app-main {
padding-bottom: max(60px, calc(constant(safe-area-inset-bottom) + 40px));
padding-bottom: max(60px, calc(env(safe-area-inset-bottom) + 40px));
overscroll-behavior-y: none;
}
.hasTagsView .fixed-header + .app-main {
padding-bottom: max(60px, calc(constant(safe-area-inset-bottom) + 40px));
padding-bottom: max(60px, calc(env(safe-area-inset-bottom) + 40px));
overscroll-behavior-y: none;
}
}
@supports (-webkit-touch-callout: none) {
@media screen and (max-width: 991px) {
.fixed-header + .app-main {
padding-bottom: max(17px, calc(constant(safe-area-inset-bottom) + 10px));
padding-bottom: max(17px, calc(env(safe-area-inset-bottom) + 10px));
height: calc(100svh - 50px);
height: calc(100dvh - 50px);
}
.hasTagsView .fixed-header + .app-main {
padding-bottom: max(17px, calc(constant(safe-area-inset-bottom) + 10px));
padding-bottom: max(17px, calc(env(safe-area-inset-bottom) + 10px));
height: calc(100svh - 84px);
height: calc(100dvh - 84px);
}
}
}
</style> </style>
<style lang="scss"> <style lang="scss">

View File

@ -1,10 +1,13 @@
<template> <template>
<div class="navbar"> <div class="navbar" :class="'nav' + navType">
<hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" /> <hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
<breadcrumb v-if="!topNav" id="breadcrumb-container" class="breadcrumb-container" /> <breadcrumb v-if="navType == 1" id="breadcrumb-container" class="breadcrumb-container" />
<top-nav v-if="topNav" id="topmenu-container" class="topmenu-container" /> <top-nav v-if="navType == 2" id="topmenu-container" class="topmenu-container" />
<template v-if="navType == 3">
<logo v-show="showLogo" :collapse="false"></logo>
<top-bar id="topbar-container" class="topbar-container" />
</template>
<div class="right-menu"> <div class="right-menu">
<template v-if="device!=='mobile'"> <template v-if="device!=='mobile'">
<search id="header-search" class="right-menu-item" /> <search id="header-search" class="right-menu-item" />
@ -50,6 +53,8 @@
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
import Breadcrumb from '@/components/Breadcrumb' import Breadcrumb from '@/components/Breadcrumb'
import TopNav from '@/components/TopNav' import TopNav from '@/components/TopNav'
import TopBar from './TopBar'
import Logo from './Sidebar/Logo'
import Hamburger from '@/components/Hamburger' import Hamburger from '@/components/Hamburger'
import Screenfull from '@/components/Screenfull' import Screenfull from '@/components/Screenfull'
import SizeSelect from '@/components/SizeSelect' import SizeSelect from '@/components/SizeSelect'
@ -61,7 +66,9 @@ export default {
emits: ['setLayout'], emits: ['setLayout'],
components: { components: {
Breadcrumb, Breadcrumb,
Logo,
TopNav, TopNav,
TopBar,
Hamburger, Hamburger,
Screenfull, Screenfull,
SizeSelect, SizeSelect,
@ -81,9 +88,14 @@ export default {
return this.$store.state.settings.showSettings return this.$store.state.settings.showSettings
} }
}, },
topNav: { navType: {
get() { get() {
return this.$store.state.settings.topNav return this.$store.state.settings.navType
}
},
showLogo: {
get() {
return this.$store.state.settings.sidebarLogo
} }
} }
}, },
@ -110,20 +122,33 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.navbar.nav3 {
.hamburger-container {
display: none !important;
}
}
.navbar { .navbar {
height: 50px; height: 50px;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
background: #fff; background: #fff;
box-shadow: 0 1px 4px rgba(0,21,41,.08); box-shadow: 0 1px 4px rgba(0,21,41,.08);
display: flex;
align-items: center;
// padding: 0 8px;
box-sizing: border-box;
.hamburger-container { .hamburger-container {
line-height: 46px; line-height: 46px;
height: 100%; height: 100%;
float: left;
cursor: pointer; cursor: pointer;
transition: background .3s; transition: background .3s;
-webkit-tap-highlight-color:transparent; -webkit-tap-highlight-color:transparent;
display: flex;
align-items: center;
flex-shrink: 0;
margin-right: 8px;
&:hover { &:hover {
background: rgba(0, 0, 0, .025) background: rgba(0, 0, 0, .025)
@ -131,7 +156,7 @@ export default {
} }
.breadcrumb-container { .breadcrumb-container {
float: left; flex-shrink: 0;
} }
.topmenu-container { .topmenu-container {
@ -139,15 +164,26 @@ export default {
left: 50px; left: 50px;
} }
.topbar-container {
flex: 1;
min-width: 0;
display: flex;
align-items: center;
overflow: hidden;
margin-left: 8px;
}
.errLog-container { .errLog-container {
display: inline-block; display: inline-block;
vertical-align: top; vertical-align: top;
} }
.right-menu { .right-menu {
float: right;
height: 100%; height: 100%;
line-height: 50px; line-height: 50px;
display: flex;
align-items: center;
margin-left: auto;
&:focus { &:focus {
outline: none; outline: none;

View File

@ -3,6 +3,27 @@
<div class="drawer-container"> <div class="drawer-container">
<div> <div>
<div class="setting-drawer-content"> <div class="setting-drawer-content">
<div class="setting-drawer-title">
<h3 class="drawer-title">菜单导航设置</h3>
</div>
<div class="nav-wrap">
<el-tooltip content="左侧菜单" placement="bottom">
<div class="item left" @click="handleNavType(1)" :style="{'--theme': theme}" :class="{ activeItem: navType == 1 }">
<b></b><b></b>
</div>
</el-tooltip>
<el-tooltip content="混合菜单" placement="bottom">
<div class="item mix" @click="handleNavType(2)" :style="{'--theme': theme}" :class="{ activeItem: navType == 2 }">
<b></b><b></b>
</div>
</el-tooltip>
<el-tooltip content="顶部菜单" placement="bottom">
<div class="item top" @click="handleNavType(3)" :style="{'--theme': theme}" :class="{ activeItem: navType == 3 }">
<b></b><b></b>
</div>
</el-tooltip>
</div>
<div class="setting-drawer-title"> <div class="setting-drawer-title">
<h3 class="drawer-title">主题风格设置</h3> <h3 class="drawer-title">主题风格设置</h3>
</div> </div>
@ -39,11 +60,6 @@
<h3 class="drawer-title">系统布局配置</h3> <h3 class="drawer-title">系统布局配置</h3>
<div class="drawer-item">
<span>开启 TopNav</span>
<el-switch v-model="topNav" class="drawer-switch" />
</div>
<div class="drawer-item"> <div class="drawer-item">
<span>开启 Tags-Views</span> <span>开启 Tags-Views</span>
<el-switch v-model="tagsView" class="drawer-switch" /> <el-switch v-model="tagsView" class="drawer-switch" />
@ -93,6 +109,7 @@ export default {
return { return {
theme: this.$store.state.settings.theme, theme: this.$store.state.settings.theme,
sideTheme: this.$store.state.settings.sideTheme, sideTheme: this.$store.state.settings.sideTheme,
navType: this.$store.state.settings.navType,
showSettings: false showSettings: false
} }
}, },
@ -108,21 +125,6 @@ export default {
}) })
} }
}, },
topNav: {
get() {
return this.$store.state.settings.topNav
},
set(val) {
this.$store.dispatch('settings/changeSetting', {
key: 'topNav',
value: val
})
if (!val) {
this.$store.dispatch('app/toggleSideBarHide', false)
this.$store.commit("SET_SIDEBAR_ROUTERS", this.$store.state.permission.defaultRoutes)
}
}
},
tagsView: { tagsView: {
get() { get() {
return this.$store.state.settings.tagsView return this.$store.state.settings.tagsView
@ -180,6 +182,25 @@ export default {
} }
} }
}, },
watch: {
navType: {
handler(val) {
if (val == 1) {
this.$store.dispatch("app/toggleSideBarHide", false)
}
if (val == 2) {
}
if (val == 3) {
this.$store.dispatch("app/toggleSideBarHide", true)
}
if ([1, 3].includes(val)) {
this.$store.commit("SET_SIDEBAR_ROUTERS",this.$store.state.permission.defaultRoutes)
}
},
immediate: true,
deep: true
}
},
methods: { methods: {
themeChange(val) { themeChange(val) {
this.$store.dispatch('settings/changeSetting', { this.$store.dispatch('settings/changeSetting', {
@ -195,6 +216,13 @@ export default {
}) })
this.sideTheme = val this.sideTheme = val
}, },
handleNavType(val) {
this.$store.dispatch('settings/changeSetting', {
key: 'navType',
value: val
})
this.navType = val
},
openSetting() { openSetting() {
this.showSettings = true this.showSettings = true
}, },
@ -206,7 +234,7 @@ export default {
this.$cache.local.set( this.$cache.local.set(
"layout-setting", "layout-setting",
`{ `{
"topNav":${this.topNav}, "navType":${this.navType},
"tagsView":${this.tagsView}, "tagsView":${this.tagsView},
"tagsIcon":${this.tagsIcon}, "tagsIcon":${this.tagsIcon},
"fixedHeader":${this.fixedHeader}, "fixedHeader":${this.fixedHeader},
@ -229,70 +257,133 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.setting-drawer-content { .setting-drawer-content {
.setting-drawer-title { .setting-drawer-title {
margin-bottom: 12px; margin-bottom: 12px;
color: rgba(0, 0, 0, .85); color: rgba(0, 0, 0, .85);
font-size: 14px; font-size: 14px;
line-height: 22px; line-height: 22px;
font-weight: bold; font-weight: bold;
} }
.setting-drawer-block-checbox { .setting-drawer-block-checbox {
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
align-items: center; align-items: center;
margin-top: 10px; margin-top: 10px;
margin-bottom: 20px; margin-bottom: 20px;
.setting-drawer-block-checbox-item { .setting-drawer-block-checbox-item {
position: relative; position: relative;
margin-right: 16px; margin-right: 16px;
border-radius: 2px; border-radius: 2px;
cursor: pointer; cursor: pointer;
img { img {
width: 48px; width: 48px;
height: 48px; height: 48px;
} }
.setting-drawer-block-checbox-selectIcon { .setting-drawer-block-checbox-selectIcon {
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
padding-top: 15px; padding-top: 15px;
padding-left: 24px; padding-left: 24px;
color: #1890ff; color: #1890ff;
font-weight: 700; font-weight: 700;
font-size: 14px; font-size: 14px;
}
} }
} }
} }
}
.drawer-container { .drawer-container {
padding: 20px; padding: 20px;
font-size: 14px;
line-height: 1.5;
word-wrap: break-word;
.drawer-title {
margin-bottom: 12px;
color: rgba(0, 0, 0, .85);
font-size: 14px; font-size: 14px;
line-height: 1.5; line-height: 22px;
word-wrap: break-word; }
.drawer-title { .drawer-item {
margin-bottom: 12px; color: rgba(0, 0, 0, .65);
color: rgba(0, 0, 0, .85); font-size: 14px;
font-size: 14px; padding: 12px 0;
line-height: 22px; }
.drawer-switch {
float: right
}
}
// 导航模式
.nav-wrap {
display: flex;
justify-content: flex-start;
align-items: center;
margin-top: 10px;
margin-bottom: 20px;
.activeItem {
border: 2px solid #{'var(--theme)'} !important;
}
.item {
position: relative;
margin-right: 16px;
cursor: pointer;
width: 56px;
height: 48px;
border-radius: 4px;
background: #f0f2f5;
border: 2px solid transparent;
}
.left {
b:first-child {
display: block;
height: 30%;
background: #fff;
} }
b:last-child {
.drawer-item { width: 30%;
color: rgba(0, 0, 0, .65); background: #1b2a47;
font-size: 14px; position: absolute;
padding: 12px 0; height: 100%;
} top: 0;
border-radius: 4px 0 0 4px;
.drawer-switch {
float: right
} }
} }
.mix {
b:first-child {
border-radius: 4px 4px 0 0;
display: block;
height: 30%;
background: #1b2a47;
}
b:last-child {
width: 30%;
background: #1b2a47;
position: absolute;
height: 70%;
border-radius: 0 0 0 4px;
}
}
.top {
b:first-child {
display: block;
height: 30%;
background: #1b2a47;
border-radius: 4px 4px 0 0;
}
}
}
</style> </style>

View File

@ -1,13 +1,13 @@
<template> <template>
<div class="sidebar-logo-container" :class="{'collapse':collapse}" :style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }"> <div class="sidebar-logo-container" :class="{'collapse':collapse}" :style="{ backgroundColor: sideTheme === 'theme-dark' && navType !== 3 ? variables.menuBackground : variables.menuLightBackground }">
<transition name="sidebarLogoFade"> <transition name="sidebarLogoFade">
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/"> <router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
<img v-if="logo" :src="logo" class="sidebar-logo" /> <img v-if="logo" :src="logo" class="sidebar-logo" />
<h1 v-else class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }} </h1> <h1 v-else class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' && navType !== 3 ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }} </h1>
</router-link> </router-link>
<router-link v-else key="expand" class="sidebar-logo-link" to="/"> <router-link v-else key="expand" class="sidebar-logo-link" to="/">
<img v-if="logo" :src="logo" class="sidebar-logo" /> <img v-if="logo" :src="logo" class="sidebar-logo" />
<h1 class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }} </h1> <h1 class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' && navType !== 3 ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }} </h1>
</router-link> </router-link>
</transition> </transition>
</div> </div>
@ -31,6 +31,9 @@ export default {
}, },
sideTheme() { sideTheme() {
return this.$store.state.settings.sideTheme return this.$store.state.settings.sideTheme
},
navType() {
return this.$store.state.settings.navType
} }
}, },
data() { data() {
@ -54,7 +57,6 @@ export default {
.sidebar-logo-container { .sidebar-logo-container {
position: relative; position: relative;
width: 100%;
height: 50px; height: 50px;
line-height: 50px; line-height: 50px;
background: #2b2f3a; background: #2b2f3a;

View File

@ -0,0 +1,98 @@
<template>
<el-menu class="topbar-menu" :default-active="activeMenu" :active-text-color="theme" mode="horizontal">
<sidebar-item :key="route.path + index" v-for="(route, index) in topMenus" :item="route" :base-path="route.path" />
<el-submenu index="more" class="el-submenu__hide-arrow" v-if="moreRoutes.length > 0">
<template slot="title">更多菜单</template>
<sidebar-item :key="route.path + index" v-for="(route, index) in moreRoutes" :item="route" :base-path="route.path" />
</el-submenu>
</el-menu>
</template>
<script>
import SidebarItem from '../Sidebar/SidebarItem'
export default {
components: { SidebarItem },
data() {
return {
// 顶部栏初始数
visibleNumber: 5
}
},
computed: {
theme() {
return this.$store.state.settings.theme
},
topMenus() {
return this.$store.state.permission.sidebarRouters.filter((f) => !f.hidden).slice(0, this.visibleNumber)
},
moreRoutes() {
const sidebarRouters = this.$store.state.permission.sidebarRouters;
return sidebarRouters.filter((f) => !f.hidden).slice(this.visibleNumber, sidebarRouters.length - this.visibleNumber)
},
// 默认激活的菜单
activeMenu() {
const { meta, path } = this.$route
if (meta.activeMenu) {
return meta.activeMenu
}
return path
},
},
beforeMount() {
window.addEventListener('resize', this.setVisibleNumber)
},
beforeDestroy() {
window.removeEventListener('resize', this.setVisibleNumber)
},
mounted() {
this.setVisibleNumber()
},
methods: {
// 根据宽度计算设置显示栏数
setVisibleNumber() {
const width = document.body.getBoundingClientRect().width / 3
this.visibleNumber = parseInt(width / 85)
}
}
}
</script>
<style lang="scss">
/* menu item */
.topbar-menu.el-menu--horizontal .el-submenu__title, .topbar-menu.el-menu--horizontal .el-menu-item {
padding: 0 10px !important;
}
.el-menu--horizontal .el-menu--popup .el-menu-item:hover {
background-color: #f5f7fa !important;
}
/* submenu item */
.topbar-menu.el-menu--horizontal > .el-submenu .el-submenu__title {
float: left;
height: 47px !important;
line-height: 50px !important;
color: #303133;
margin: 0 15px !important;
}
/* topbar more arrow */
.topbar-menu .el-submenu .el-submenu__icon-arrow {
position: static;
vertical-align: middle;
margin-left: 8px;
margin-top: 0px;
}
/* menu__title el-menu-item */
.topbar-menu.el-menu--horizontal .el-submenu__title, .topbar-menu.el-menu--horizontal .el-menu-item {
height: 55px;
}
.el-menu--horizontal .el-menu .el-menu-item, .el-menu--horizontal .el-menu .el-submenu__title{
color: #303133;
}
</style>

View File

@ -15,9 +15,9 @@ module.exports = {
showSettings: true, showSettings: true,
/** /**
* 是否显示顶部导航 * 菜单导航模式 1、纯左侧 2、混合左侧+顶部) 3、纯顶部
*/ */
topNav: false, navType: 1,
/** /**
* 是否显示 tagsView * 是否显示 tagsView
@ -32,7 +32,7 @@ module.exports = {
/** /**
* 是否固定头部 * 是否固定头部
*/ */
fixedHeader: false, fixedHeader: true,
/** /**
* 是否显示logo * 是否显示logo

View File

@ -1,7 +1,7 @@
import defaultSettings from '@/settings' import defaultSettings from '@/settings'
import { useDynamicTitle } from '@/utils/dynamicTitle' import { useDynamicTitle } from '@/utils/dynamicTitle'
const { sideTheme, showSettings, topNav, tagsView, tagsIcon, fixedHeader, sidebarLogo, dynamicTitle, footerVisible, footerContent } = defaultSettings const { sideTheme, showSettings, navType, tagsView, tagsIcon, fixedHeader, sidebarLogo, dynamicTitle, footerVisible, footerContent } = defaultSettings
const storageSetting = JSON.parse(localStorage.getItem('layout-setting')) || '' const storageSetting = JSON.parse(localStorage.getItem('layout-setting')) || ''
const state = { const state = {
@ -9,7 +9,7 @@ const state = {
theme: storageSetting.theme || '#409EFF', theme: storageSetting.theme || '#409EFF',
sideTheme: storageSetting.sideTheme || sideTheme, sideTheme: storageSetting.sideTheme || sideTheme,
showSettings: showSettings, showSettings: showSettings,
topNav: storageSetting.topNav === undefined ? topNav : storageSetting.topNav, navType: storageSetting.navType === undefined ? navType : storageSetting.navType,
tagsView: storageSetting.tagsView === undefined ? tagsView : storageSetting.tagsView, tagsView: storageSetting.tagsView === undefined ? tagsView : storageSetting.tagsView,
tagsIcon: storageSetting.tagsIcon === undefined ? tagsIcon : storageSetting.tagsIcon, tagsIcon: storageSetting.tagsIcon === undefined ? tagsIcon : storageSetting.tagsIcon,
fixedHeader: storageSetting.fixedHeader === undefined ? fixedHeader : storageSetting.fixedHeader, fixedHeader: storageSetting.fixedHeader === undefined ? fixedHeader : storageSetting.fixedHeader,

View File

@ -88,7 +88,7 @@
<s> 满101046199 </s> <s> 满136919097 </s> <s> 满143961921 </s> <s> 满174951577 </s> <s> 满101046199 </s> <s> 满136919097 </s> <s> 满143961921 </s> <s> 满174951577 </s>
<s> 满161281055 </s> <s> 满138988063 </s> <s> 满151450850 </s> <s> 满224622315 </s> <s> 满161281055 </s> <s> 满138988063 </s> <s> 满151450850 </s> <s> 满224622315 </s>
<s> 满287842588 </s> <s> 满187944233 </s> <s> 满228578329 </s> <s> 满191164766 </s> <s> 满287842588 </s> <s> 满187944233 </s> <s> 满228578329 </s> <s> 满191164766 </s>
<a href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=EeCBXu51I1zPWRia2uskpjDRx6VrbnFN&authKey=Xm8yDxk0%2FyYGI11oxhXaQnTn4K7UwCk7Kn2MZTh3P1JxLctollAkyeySjnaILDkb&noverify=0&group_code=174569686" target="_blank">174569686</a> <a href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=EeCBXu51I1zPWRia2uskpjDRx6VrbnFN&authKey=Xm8yDxk0%2FyYGI11oxhXaQnTn4K7UwCk7Kn2MZTh3P1JxLctollAkyeySjnaILDkb&noverify=0&group_code=174569686" target="_blank">174569686</a>
</p> </p>
<p> <p>
<i class="el-icon-chat-dot-round"></i> 微信<a <i class="el-icon-chat-dot-round"></i> 微信<a
@ -112,6 +112,42 @@
<span>更新日志</span> <span>更新日志</span>
</div> </div>
<el-collapse accordion> <el-collapse accordion>
<el-collapse-item title="v3.9.1 - 2025-12-18">
<ol>
<li>支持防盗链功能</li>
<li>菜单导航设置支持纯顶部</li>
<li>使用yauaa代替bitwalker</li>
<li>用户头像更换后移除旧头像文件</li>
<li>支持Excel导出对象的多个子列表</li>
<li>升级oshi到最新版本6.9.1</li>
<li>升级druid到最新版本1.2.27</li>
<li>升级fastjson到最新版2.0.60</li>
<li>升级spring-security到5.7.14</li>
<li>升级tomcat到最新版本9.0.112</li>
<li>升级commons.io到最新版本2.21.0</li>
<li>用户导入添加验证提示</li>
<li>显示列信息支持对象格式</li>
<li>忽略用户密码字段的JSON序列化</li>
<li>网页标题设置新增SET_TITLE方法</li>
<li>自动识别json对象白名单配置范围缩小</li>
<li>登录/注册页面底部版权信息修改为读取配置</li>
<li>修复用户归属部门无法修改为空问题</li>
<li>修复固定头部时出现的导航栏偏移问题</li>
<li>修复v3时间控件between选择后清空报错问题</li>
<li>修复comboReadDict属性下多个sheet出现的报错</li>
<li>修复表单构建移除所有控件后切换路由回来空白问题</li>
<li>优化布局设置显示</li>
<li>优化字典组件值宽松匹配</li>
<li>优化获取字典类型值的方法</li>
<li>优化生成代码下载的zip文件名</li>
<li>优化日志记录参数拼装提升效率</li>
<li>优化导入文件检查标题行不能为空</li>
<li>优化表单构建关闭页签销毁复制插件</li>
<li>优化Excel统计行数值的单元格样式显示</li>
<li>优化数据权限控制逻辑放开permission限制</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v3.9.0 - 2025-05-28"> <el-collapse-item title="v3.9.0 - 2025-05-28">
<ol> <ol>
<li>优化菜单搜索查询页</li> <li>优化菜单搜索查询页</li>
@ -1060,7 +1096,7 @@ export default {
data() { data() {
return { return {
// 版本号 // 版本号
version: "3.9.0" version: "3.9.1"
} }
}, },
methods: { methods: {

View File

@ -1,148 +1,148 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<el-row :gutter="10"> <el-row :gutter="10">
<el-col :span="24" class="card-box"> <el-col :span="24" class="card-box">
<el-card> <el-card>
<div slot="header"><span><i class="el-icon-monitor"></i> 基本信息</span></div> <div slot="header"><span><i class="el-icon-monitor"></i> 基本信息</span></div>
<div class="el-table el-table--enable-row-hover el-table--medium"> <div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%"> <table cellspacing="0" style="width: 100%">
<tbody> <tbody>
<tr> <tr>
<td class="el-table__cell is-leaf"><div class="cell">Redis版本</div></td> <td class="el-table__cell is-leaf"><div class="cell">Redis版本</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.redis_version }}</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.redis_version }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">运行模式</div></td> <td class="el-table__cell is-leaf"><div class="cell">运行模式</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.redis_mode == "standalone" ? "单机" : "集群" }}</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.redis_mode == "standalone" ? "单机" : "集群" }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">端口</div></td> <td class="el-table__cell is-leaf"><div class="cell">端口</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.tcp_port }}</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.tcp_port }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">客户端数</div></td> <td class="el-table__cell is-leaf"><div class="cell">客户端数</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.connected_clients }}</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.connected_clients }}</div></td>
</tr> </tr>
<tr> <tr>
<td class="el-table__cell is-leaf"><div class="cell">运行时间()</div></td> <td class="el-table__cell is-leaf"><div class="cell">运行时间()</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.uptime_in_days }}</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.uptime_in_days }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">使用内存</div></td> <td class="el-table__cell is-leaf"><div class="cell">使用内存</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.used_memory_human }}</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.used_memory_human }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">使用CPU</div></td> <td class="el-table__cell is-leaf"><div class="cell">使用CPU</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ parseFloat(cache.info.used_cpu_user_children).toFixed(2) }}</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ parseFloat(cache.info.used_cpu_user_children).toFixed(2) }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">内存配置</div></td> <td class="el-table__cell is-leaf"><div class="cell">内存配置</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.maxmemory_human }}</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.maxmemory_human }}</div></td>
</tr> </tr>
<tr> <tr>
<td class="el-table__cell is-leaf"><div class="cell">AOF是否开启</div></td> <td class="el-table__cell is-leaf"><div class="cell">AOF是否开启</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.aof_enabled == "0" ? "否" : "是" }}</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.aof_enabled == "0" ? "否" : "是" }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">RDB是否成功</div></td> <td class="el-table__cell is-leaf"><div class="cell">RDB是否成功</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.rdb_last_bgsave_status }}</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.rdb_last_bgsave_status }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">Key数量</div></td> <td class="el-table__cell is-leaf"><div class="cell">Key数量</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="cache.dbSize">{{ cache.dbSize }} </div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.dbSize">{{ cache.dbSize }} </div></td>
<td class="el-table__cell is-leaf"><div class="cell">网络入口/出口</div></td> <td class="el-table__cell is-leaf"><div class="cell">网络入口/出口</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.instantaneous_input_kbps }}kps/{{cache.info.instantaneous_output_kbps}}kps</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.instantaneous_input_kbps }}kps/{{cache.info.instantaneous_output_kbps}}kps</div></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</el-card> </el-card>
</el-col> </el-col>
<el-col :span="12" class="card-box"> <el-col :span="12" class="card-box">
<el-card> <el-card>
<div slot="header"><span><i class="el-icon-pie-chart"></i> 命令统计</span></div> <div slot="header"><span><i class="el-icon-pie-chart"></i> 命令统计</span></div>
<div class="el-table el-table--enable-row-hover el-table--medium"> <div class="el-table el-table--enable-row-hover el-table--medium">
<div ref="commandstats" style="height: 420px" /> <div ref="commandstats" style="height: 420px" />
</div> </div>
</el-card> </el-card>
</el-col> </el-col>
<el-col :span="12" class="card-box"> <el-col :span="12" class="card-box">
<el-card> <el-card>
<div slot="header"><span><i class="el-icon-odometer"></i> 内存信息</span></div> <div slot="header"><span><i class="el-icon-odometer"></i> 内存信息</span></div>
<div class="el-table el-table--enable-row-hover el-table--medium"> <div class="el-table el-table--enable-row-hover el-table--medium">
<div ref="usedmemory" style="height: 420px" /> <div ref="usedmemory" style="height: 420px" />
</div> </div>
</el-card> </el-card>
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
</template> </template>
<script> <script>
import { getCache } from "@/api/monitor/cache" import { getCache } from "@/api/monitor/cache"
import * as echarts from "echarts" import * as echarts from "echarts"
export default { export default {
name: "Cache", name: "Cache",
data() { data() {
return { return {
// 统计命令信息 // 统计命令信息
commandstats: null, commandstats: null,
// 使用内存 // 使用内存
usedmemory: null, usedmemory: null,
// cache信息 // cache信息
cache: [] cache: []
} }
}, },
created() { created() {
this.getList() this.getList()
this.openLoading() this.openLoading()
}, },
methods: { methods: {
/** 查缓存询信息 */ /** 查缓存询信息 */
getList() { getList() {
getCache().then((response) => { getCache().then((response) => {
this.cache = response.data this.cache = response.data
this.$modal.closeLoading() this.$modal.closeLoading()
this.commandstats = echarts.init(this.$refs.commandstats, "macarons") this.commandstats = echarts.init(this.$refs.commandstats, "macarons")
this.commandstats.setOption({ this.commandstats.setOption({
tooltip: { tooltip: {
trigger: "item", trigger: "item",
formatter: "{a} <br/>{b} : {c} ({d}%)", formatter: "{a} <br/>{b} : {c} ({d}%)",
}, },
series: [ series: [
{ {
name: "命令", name: "命令",
type: "pie", type: "pie",
roseType: "radius", roseType: "radius",
radius: [15, 95], radius: [15, 95],
center: ["50%", "38%"], center: ["50%", "38%"],
data: response.data.commandStats, data: response.data.commandStats,
animationEasing: "cubicInOut", animationEasing: "cubicInOut",
animationDuration: 1000, animationDuration: 1000,
} }
] ]
}) })
this.usedmemory = echarts.init(this.$refs.usedmemory, "macarons") this.usedmemory = echarts.init(this.$refs.usedmemory, "macarons")
this.usedmemory.setOption({ this.usedmemory.setOption({
tooltip: { tooltip: {
formatter: "{b} <br/>{a} : " + this.cache.info.used_memory_human, formatter: "{b} <br/>{a} : " + this.cache.info.used_memory_human,
}, },
series: [ series: [
{ {
name: "峰值", name: "峰值",
type: "gauge", type: "gauge",
min: 0, min: 0,
max: 1000, max: 1000,
detail: { detail: {
formatter: this.cache.info.used_memory_human, formatter: this.cache.info.used_memory_human,
}, },
data: [ data: [
{ {
value: parseFloat(this.cache.info.used_memory_human), value: parseFloat(this.cache.info.used_memory_human),
name: "内存消耗", name: "内存消耗",
} }
] ]
} }
] ]
}) })
window.addEventListener("resize", () => { window.addEventListener("resize", () => {
this.commandstats.resize() this.commandstats.resize()
this.usedmemory.resize() this.usedmemory.resize()
}) })
}) })
}, },
// 打开加载层 // 打开加载层
openLoading() { openLoading() {
this.$modal.loading("正在加载缓存监控数据,请稍候!") this.$modal.loading("正在加载缓存监控数据,请稍候!")
} }
} }
} }
</script> </script>

View File

@ -1,207 +1,207 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<el-row :gutter="10"> <el-row :gutter="10">
<el-col :span="12" class="card-box"> <el-col :span="12" class="card-box">
<el-card> <el-card>
<div slot="header"><span><i class="el-icon-cpu"></i> CPU</span></div> <div slot="header"><span><i class="el-icon-cpu"></i> CPU</span></div>
<div class="el-table el-table--enable-row-hover el-table--medium"> <div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%;"> <table cellspacing="0" style="width: 100%;">
<thead> <thead>
<tr> <tr>
<th class="el-table__cell is-leaf"><div class="cell">属性</div></th> <th class="el-table__cell is-leaf"><div class="cell">属性</div></th>
<th class="el-table__cell is-leaf"><div class="cell"></div></th> <th class="el-table__cell is-leaf"><div class="cell"></div></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td class="el-table__cell is-leaf"><div class="cell">核心数</div></td> <td class="el-table__cell is-leaf"><div class="cell">核心数</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.cpu">{{ server.cpu.cpuNum }}</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="server.cpu">{{ server.cpu.cpuNum }}</div></td>
</tr> </tr>
<tr> <tr>
<td class="el-table__cell is-leaf"><div class="cell">用户使用率</div></td> <td class="el-table__cell is-leaf"><div class="cell">用户使用率</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.cpu">{{ server.cpu.used }}%</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="server.cpu">{{ server.cpu.used }}%</div></td>
</tr> </tr>
<tr> <tr>
<td class="el-table__cell is-leaf"><div class="cell">系统使用率</div></td> <td class="el-table__cell is-leaf"><div class="cell">系统使用率</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.cpu">{{ server.cpu.sys }}%</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="server.cpu">{{ server.cpu.sys }}%</div></td>
</tr> </tr>
<tr> <tr>
<td class="el-table__cell is-leaf"><div class="cell">当前空闲率</div></td> <td class="el-table__cell is-leaf"><div class="cell">当前空闲率</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.cpu">{{ server.cpu.free }}%</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="server.cpu">{{ server.cpu.free }}%</div></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</el-card> </el-card>
</el-col> </el-col>
<el-col :span="12" class="card-box"> <el-col :span="12" class="card-box">
<el-card> <el-card>
<div slot="header"><span><i class="el-icon-tickets"></i> 内存</span></div> <div slot="header"><span><i class="el-icon-tickets"></i> 内存</span></div>
<div class="el-table el-table--enable-row-hover el-table--medium"> <div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%;"> <table cellspacing="0" style="width: 100%;">
<thead> <thead>
<tr> <tr>
<th class="el-table__cell is-leaf"><div class="cell">属性</div></th> <th class="el-table__cell is-leaf"><div class="cell">属性</div></th>
<th class="el-table__cell is-leaf"><div class="cell">内存</div></th> <th class="el-table__cell is-leaf"><div class="cell">内存</div></th>
<th class="el-table__cell is-leaf"><div class="cell">JVM</div></th> <th class="el-table__cell is-leaf"><div class="cell">JVM</div></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td class="el-table__cell is-leaf"><div class="cell">总内存</div></td> <td class="el-table__cell is-leaf"><div class="cell">总内存</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.mem">{{ server.mem.total }}G</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="server.mem">{{ server.mem.total }}G</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.total }}M</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.total }}M</div></td>
</tr> </tr>
<tr> <tr>
<td class="el-table__cell is-leaf"><div class="cell">已用内存</div></td> <td class="el-table__cell is-leaf"><div class="cell">已用内存</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.mem">{{ server.mem.used}}G</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="server.mem">{{ server.mem.used}}G</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.used}}M</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.used}}M</div></td>
</tr> </tr>
<tr> <tr>
<td class="el-table__cell is-leaf"><div class="cell">剩余内存</div></td> <td class="el-table__cell is-leaf"><div class="cell">剩余内存</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.mem">{{ server.mem.free }}G</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="server.mem">{{ server.mem.free }}G</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.free }}M</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.free }}M</div></td>
</tr> </tr>
<tr> <tr>
<td class="el-table__cell is-leaf"><div class="cell">使用率</div></td> <td class="el-table__cell is-leaf"><div class="cell">使用率</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.mem" :class="{'text-danger': server.mem.usage > 80}">{{ server.mem.usage }}%</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="server.mem" :class="{'text-danger': server.mem.usage > 80}">{{ server.mem.usage }}%</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm" :class="{'text-danger': server.jvm.usage > 80}">{{ server.jvm.usage }}%</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm" :class="{'text-danger': server.jvm.usage > 80}">{{ server.jvm.usage }}%</div></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</el-card> </el-card>
</el-col> </el-col>
<el-col :span="24" class="card-box"> <el-col :span="24" class="card-box">
<el-card> <el-card>
<div slot="header"> <div slot="header">
<span><i class="el-icon-monitor"></i> 服务器信息</span> <span><i class="el-icon-monitor"></i> 服务器信息</span>
</div> </div>
<div class="el-table el-table--enable-row-hover el-table--medium"> <div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%;"> <table cellspacing="0" style="width: 100%;">
<tbody> <tbody>
<tr> <tr>
<td class="el-table__cell is-leaf"><div class="cell">服务器名称</div></td> <td class="el-table__cell is-leaf"><div class="cell">服务器名称</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.sys">{{ server.sys.computerName }}</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="server.sys">{{ server.sys.computerName }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">操作系统</div></td> <td class="el-table__cell is-leaf"><div class="cell">操作系统</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.sys">{{ server.sys.osName }}</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="server.sys">{{ server.sys.osName }}</div></td>
</tr> </tr>
<tr> <tr>
<td class="el-table__cell is-leaf"><div class="cell">服务器IP</div></td> <td class="el-table__cell is-leaf"><div class="cell">服务器IP</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.sys">{{ server.sys.computerIp }}</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="server.sys">{{ server.sys.computerIp }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">系统架构</div></td> <td class="el-table__cell is-leaf"><div class="cell">系统架构</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.sys">{{ server.sys.osArch }}</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="server.sys">{{ server.sys.osArch }}</div></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</el-card> </el-card>
</el-col> </el-col>
<el-col :span="24" class="card-box"> <el-col :span="24" class="card-box">
<el-card> <el-card>
<div slot="header"> <div slot="header">
<span><i class="el-icon-coffee-cup"></i> Java虚拟机信息</span> <span><i class="el-icon-coffee-cup"></i> Java虚拟机信息</span>
</div> </div>
<div class="el-table el-table--enable-row-hover el-table--medium"> <div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%;table-layout:fixed;"> <table cellspacing="0" style="width: 100%;table-layout:fixed;">
<tbody> <tbody>
<tr> <tr>
<td class="el-table__cell is-leaf"><div class="cell">Java名称</div></td> <td class="el-table__cell is-leaf"><div class="cell">Java名称</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.name }}</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.name }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">Java版本</div></td> <td class="el-table__cell is-leaf"><div class="cell">Java版本</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.version }}</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.version }}</div></td>
</tr> </tr>
<tr> <tr>
<td class="el-table__cell is-leaf"><div class="cell">启动时间</div></td> <td class="el-table__cell is-leaf"><div class="cell">启动时间</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.startTime }}</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.startTime }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">运行时长</div></td> <td class="el-table__cell is-leaf"><div class="cell">运行时长</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.runTime }}</div></td> <td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.runTime }}</div></td>
</tr> </tr>
<tr> <tr>
<td colspan="1" class="el-table__cell is-leaf"><div class="cell">安装路径</div></td> <td colspan="1" class="el-table__cell is-leaf"><div class="cell">安装路径</div></td>
<td colspan="3" class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.home }}</div></td> <td colspan="3" class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.home }}</div></td>
</tr> </tr>
<tr> <tr>
<td colspan="1" class="el-table__cell is-leaf"><div class="cell">项目路径</div></td> <td colspan="1" class="el-table__cell is-leaf"><div class="cell">项目路径</div></td>
<td colspan="3" class="el-table__cell is-leaf"><div class="cell" v-if="server.sys">{{ server.sys.userDir }}</div></td> <td colspan="3" class="el-table__cell is-leaf"><div class="cell" v-if="server.sys">{{ server.sys.userDir }}</div></td>
</tr> </tr>
<tr> <tr>
<td colspan="1" class="el-table__cell is-leaf"><div class="cell">运行参数</div></td> <td colspan="1" class="el-table__cell is-leaf"><div class="cell">运行参数</div></td>
<td colspan="3" class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.inputArgs }}</div></td> <td colspan="3" class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.inputArgs }}</div></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</el-card> </el-card>
</el-col> </el-col>
<el-col :span="24" class="card-box"> <el-col :span="24" class="card-box">
<el-card> <el-card>
<div slot="header"> <div slot="header">
<span><i class="el-icon-receiving"></i> 磁盘状态</span> <span><i class="el-icon-receiving"></i> 磁盘状态</span>
</div> </div>
<div class="el-table el-table--enable-row-hover el-table--medium"> <div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%;"> <table cellspacing="0" style="width: 100%;">
<thead> <thead>
<tr> <tr>
<th class="el-table__cell el-table__cell is-leaf"><div class="cell">盘符路径</div></th> <th class="el-table__cell el-table__cell is-leaf"><div class="cell">盘符路径</div></th>
<th class="el-table__cell is-leaf"><div class="cell">文件系统</div></th> <th class="el-table__cell is-leaf"><div class="cell">文件系统</div></th>
<th class="el-table__cell is-leaf"><div class="cell">盘符类型</div></th> <th class="el-table__cell is-leaf"><div class="cell">盘符类型</div></th>
<th class="el-table__cell is-leaf"><div class="cell">总大小</div></th> <th class="el-table__cell is-leaf"><div class="cell">总大小</div></th>
<th class="el-table__cell is-leaf"><div class="cell">可用大小</div></th> <th class="el-table__cell is-leaf"><div class="cell">可用大小</div></th>
<th class="el-table__cell is-leaf"><div class="cell">已用大小</div></th> <th class="el-table__cell is-leaf"><div class="cell">已用大小</div></th>
<th class="el-table__cell is-leaf"><div class="cell">已用百分比</div></th> <th class="el-table__cell is-leaf"><div class="cell">已用百分比</div></th>
</tr> </tr>
</thead> </thead>
<tbody v-if="server.sysFiles"> <tbody v-if="server.sysFiles">
<tr v-for="(sysFile, index) in server.sysFiles" :key="index"> <tr v-for="(sysFile, index) in server.sysFiles" :key="index">
<td class="el-table__cell is-leaf"><div class="cell">{{ sysFile.dirName }}</div></td> <td class="el-table__cell is-leaf"><div class="cell">{{ sysFile.dirName }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">{{ sysFile.sysTypeName }}</div></td> <td class="el-table__cell is-leaf"><div class="cell">{{ sysFile.sysTypeName }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">{{ sysFile.typeName }}</div></td> <td class="el-table__cell is-leaf"><div class="cell">{{ sysFile.typeName }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">{{ sysFile.total }}</div></td> <td class="el-table__cell is-leaf"><div class="cell">{{ sysFile.total }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">{{ sysFile.free }}</div></td> <td class="el-table__cell is-leaf"><div class="cell">{{ sysFile.free }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">{{ sysFile.used }}</div></td> <td class="el-table__cell is-leaf"><div class="cell">{{ sysFile.used }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell" :class="{'text-danger': sysFile.usage > 80}">{{ sysFile.usage }}%</div></td> <td class="el-table__cell is-leaf"><div class="cell" :class="{'text-danger': sysFile.usage > 80}">{{ sysFile.usage }}%</div></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</el-card> </el-card>
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
</template> </template>
<script> <script>
import { getServer } from "@/api/monitor/server" import { getServer } from "@/api/monitor/server"
export default { export default {
name: "Server", name: "Server",
data() { data() {
return { return {
// 服务器信息 // 服务器信息
server: [] server: []
} }
}, },
created() { created() {
this.getList() this.getList()
this.openLoading() this.openLoading()
}, },
methods: { methods: {
/** 查询服务器信息 */ /** 查询服务器信息 */
getList() { getList() {
getServer().then(response => { getServer().then(response => {
this.server = response.data this.server = response.data
this.$modal.closeLoading() this.$modal.closeLoading()
}) })
}, },
// 打开加载层 // 打开加载层
openLoading() { openLoading() {
this.$modal.loading("正在加载服务监控数据,请稍候!") this.$modal.loading("正在加载服务监控数据,请稍候!")
} }
} }
} }
</script> </script>

View File

@ -153,6 +153,7 @@ import DraggableItem from './DraggableItem'
let oldActiveId let oldActiveId
let tempActiveData let tempActiveData
let clipboard = null
export default { export default {
components: { components: {
@ -211,7 +212,7 @@ export default {
} }
}, },
mounted() { mounted() {
const clipboard = new ClipboardJS('#copyNode', { clipboard = new ClipboardJS('#copyNode', {
text: trigger => { text: trigger => {
const codeStr = this.generateCode() const codeStr = this.generateCode()
this.$notify({ this.$notify({
@ -226,6 +227,9 @@ export default {
this.$message.error('代码复制失败') this.$message.error('代码复制失败')
}) })
}, },
beforeDestroy() {
clipboard.destroy()
},
methods: { methods: {
activeFormItem(element) { activeFormItem(element) {
this.activeData = element this.activeData = element