240 Commits

Author SHA1 Message Date
RuoYi
41720e624c 优化页签滚动条层级 2026-06-10 22:33:18 +08:00
RuoYi
09f1b3495f 升级项目相关依赖到最新 2026-06-10 19:16:03 +08:00
RuoYi
1a8d111459 优化代码 2026-06-10 19:09:08 +08:00
RuoYi
87d02b0e41 禁止html后缀文件上传 2026-06-10 19:08:18 +08:00
RuoYi
7da12b0c07 用户密码支持自定义配置规则 2026-04-17 13:10:02 +08:00
RuoYi
dbe9834e4c 角色权限变更后刷新所有持有该角色的在线用户权限 2026-04-16 16:33:50 +08:00
RuoYi
69aaabd09d 优化代码生成同步操作column_type没更新问题 2026-04-16 14:24:07 +08:00
RuoYi
557efc4d44 优化白名单支持对通配符路径匹配 2026-04-16 13:42:57 +08:00
RuoYi
58f3c43c50 修复脱敏不生效问题(IIPBZR) 2026-04-15 13:03:51 +08:00
RuoYi
d454d9729e 通知公告新增阅读用户列表 2026-04-14 16:11:36 +08:00
RuoYi
0728a04b1b 通知公告新增阅读用户列表 2026-04-14 15:39:49 +08:00
RuoYi
ba33fe9e03 通知公告新增详细显示 2026-04-14 14:35:23 +08:00
RuoYi
60e542d809 新增标签页样式chrome风格 2026-04-13 00:41:03 +08:00
RuoYi
290f7b4cd9 新增代码生成详情页功能 2026-04-12 09:57:04 +08:00
RuoYi
4b9800eb54 自动导入配置 2026-04-10 10:59:14 +08:00
RuoYi
02907f967e 代码生成修改拖拽时显示手指样式 2026-04-10 10:58:52 +08:00
RuoYi
157228e1d0 用户列表新增抽屉效果详细信息 2026-04-09 12:47:23 +08:00
RuoYi
df4261f4d3 优化样式 2026-04-03 22:33:20 +08:00
RuoYi
abffc253c0 数据权限注解支持自定义字段名 2026-04-03 15:38:16 +08:00
RuoYi
cf25c229a9 新增Excel导入组件ExcelImportDialog 2026-04-03 08:53:41 +08:00
RuoYi
ea3410e170 update tree-panel ref 2026-04-01 21:48:51 +08:00
RuoYi
7979124798 新增树分割组件TreePanel 2026-04-01 21:34:10 +08:00
RuoYi
3b42c7c633 支持表格列显隐状态记忆 2026-03-31 19:22:43 +08:00
RuoYi
26d7bb25f0 支持多sheet导出 2026-03-31 16:31:18 +08:00
RuoYi
6775ed78d1 代码生成支持表单布局选项 2026-03-30 16:44:47 +08:00
RuoYi
b3a2572410 update sqlkeyword 2026-03-30 16:35:58 +08:00
RuoYi
0e2d75c23c 若依 3.9.2 2026-03-26 08:23:30 +08:00
RuoYi
2a26aa1356 优化页签全屏显示 2026-03-26 00:52:04 +08:00
RuoYi
3fa6a4b5df 优化快速点击页签刷新出现404问题 2026-03-25 21:21:58 +08:00
RuoYi
3c2186cf73 修复typescript版多表代码生成报错问题 2026-03-25 12:32:24 +08:00
RuoYi
7282667d41 菜单管理列表新增类型显示 2026-03-24 15:33:27 +08:00
RuoYi
4b1fad5f18 优化菜单主题风格 2026-03-24 10:30:32 +08:00
RuoYi
58bc7043a0 升级axios到最新版本0.30.3 2026-03-23 11:42:53 +08:00
RuoYi
677400b933 优化页签显示 2026-03-23 11:42:30 +08:00
RuoYi
959a2ffd67 添加持久化标签页开关功能 2026-03-22 23:29:23 +08:00
RuoYi
ec0ca11cc6 添加持久化标签页开关功能 2026-03-22 21:03:58 +08:00
RuoYi
0f734eda88 菜单搜索支持文本高亮&数量提示 2026-03-22 16:54:12 +08:00
RuoYi
d3831bf455 优化tag全屏为页签内区域 2026-03-22 15:01:19 +08:00
RuoYi
9c6ae1dd60 页签左右滚动效果兼容所有环境不依赖behavior 2026-03-22 14:44:55 +08:00
RuoYi
20dc6b3925 优化页签功能&支持全屏按钮操作 2026-03-22 13:58:14 +08:00
RuoYi
fa224503e4 保存排序添加编辑权限 2026-03-21 19:08:52 +08:00
RuoYi
b869c9622a 优化点击任务名称查看详细 2026-03-21 14:28:19 +08:00
RuoYi
f7665b9ec5 字典类型列表新增抽屉效果详细信息 2026-03-21 13:37:46 +08:00
RuoYi
b508e05b0c 菜单管理支持批量保存排序 2026-03-21 12:41:51 +08:00
RuoYi
01fd7be61a 部门管理支持批量保存排序 2026-03-21 11:39:53 +08:00
RuoYi
16a78c8113 新增锁定屏幕功能 2026-03-20 21:51:17 +08:00
RuoYi
01780ea59c 新增锁定屏幕功能 2026-03-20 20:36:07 +08:00
RuoYi
9f0b31ebbd 优化定时任务详情页展示&补充执行时间字段 2026-03-20 16:37:08 +08:00
RuoYi
fde9db31e4 操作日志详细页面优化 2026-03-20 13:04:05 +08:00
RuoYi
5b52281fe1 首页新增通知公告消息提醒 2026-03-20 10:36:10 +08:00
RuoYi
f80143eccb 优化RightToolbar搜索栏切换动画 2026-03-19 19:41:08 +08:00
RuoYi
7816597b86 TypeScript前端代码生成模板同步到最新 2026-03-12 12:24:35 +08:00
RuoYi
a6f142db96 remove test code 2026-03-10 22:01:28 +08:00
RuoYi
51d11c39c8 update README.md 2026-03-10 16:30:44 +08:00
RuoYi
c5677434cf 项目升级到 Spring Boot 4 2026-03-10 16:20:30 +08:00
RuoYi
1d294464a7 升级fastjson到最新版2.0.61 2026-03-09 16:43:20 +08:00
RuoYi
140a7fb66f 升级oshi到最新版本6.10.0 2026-03-09 16:42:47 +08:00
RuoYi
0d85aa50a7 优化Excel自定义格式样式重复创建问题 2026-03-09 15:01:42 +08:00
RuoYi
ed916656dd 删除无用的注解 2026-03-01 11:20:16 +08:00
若依
85cb89f1b6 !1148 修复IP查询失败的问题
update address url
2026-02-26 02:03:11 +00:00
chniccs
8aef19ba49 update ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java.
修复此Issuse https://gitee.com/y_project/RuoYi-Vue/issues/IDR6LP  经验证发现使用http协议时会出现IP地址查询失败的情况,替换为https后正常


Signed-off-by: chniccs <chniccs@163.com>
2026-02-25 01:37:13 +00:00
若依
83bc1a331a !1146 update ruoyi-ui/src/views/system/dict/index.vue.
Merge pull request !1146 from NewYoung208/master
2026-02-11 07:33:44 +00:00
NewYoung208
b9c7731cdb update ruoyi-ui/src/views/system/dict/index.vue.
优化代码

Signed-off-by: NewYoung208 <niuyang208@163.com>
2026-02-11 07:07:57 +00:00
RuoYi
850b86e0de 优化字典类型属性提醒说明 2026-02-05 12:45:43 +08:00
RuoYi
8a1cf9ed55 update README.md 2026-01-28 14:27:46 +08:00
RuoYi
600e06904b update README.md 2026-01-28 13:59:34 +08:00
RuoYi
6281609d9e 添加新群号:127358632 2026-01-28 13:49:51 +08:00
RuoYi
2cf825dd3d 优化代码 2026-01-28 13:42:42 +08:00
RuoYi
245baa705b 添加菜单路由地址和名称的校验规则 2026-01-09 11:13:58 +08:00
RuoYi
cfe076ebd0 优化防重提交间隔时间可自定义 2026-01-08 13:39:16 +08:00
RuoYi
1f2f11f80f 修复Excel自定义格式样式污染问题 2026-01-06 13:53:11 +08:00
RuoYi
12fb035b2e copyright 2026 2026-01-04 15:27:17 +08:00
RuoYi
98a8545ca0 将isAdmin方法统一到SecurityUtils 2026-01-04 15:26:52 +08:00
若依
fdb1853a34 !1129 将isAdmin方法统一到SecurityUtils
Merge pull request !1129 from MicyToy/enhance/unified_is_admin
2026-01-04 07:09:51 +00:00
Tiany
8c6b4a96b7 将isAdmin方法统一到SecurityUtils 2025-12-22 16:13:10 +08:00
若依
5e83011d56 !1127 fix: 修正 UserAgent 解析逻辑,正确设置浏览器和操作系统字段
Merge pull request !1127 from yeleiyun/fix-useragent-bug
2025-12-21 02:45:34 +00:00
yeleiyun
b12dab2d2e fix: 修正 UserAgent 解析逻辑,正确设置浏览器和操作系统字段 2025-12-20 22:26:20 +08:00
RuoYi
7b75f9ac0b 优化topbar顶部菜单样式 2025-12-18 13:59:15 +08:00
RuoYi
4615293be9 若依 3.9.1 2025-12-18 09:04:16 +08:00
RuoYi
ba5cf9de6e 默认固定头部 2025-12-18 09:03:05 +08:00
RuoYi
6de392bac2 优化字典组件值宽松匹配 2025-12-16 16:40:55 +08:00
RuoYi
49f62e565a 菜单导航设置支持纯顶部 2025-12-16 11:40:06 +08:00
RuoYi
5579b57bef 升级druid到最新版本1.2.27 2025-12-11 14:30:27 +08:00
RuoYi
88609b3b24 升级oshi到最新版本6.9.1 2025-12-11 14:30:06 +08:00
RuoYi
03f3f47397 升级commons.io到最新版本2.21.0 2025-12-11 14:29:45 +08:00
RuoYi
b5400d962b 升级fastjson到最新版2.0.60 2025-12-11 14:29:20 +08:00
RuoYi
e7ef3241c5 update sqlkeyword 2025-12-11 14:29:03 +08:00
RuoYi
eb6878e18f 使用yauaa代替bitwalker 2025-12-09 14:30:03 +08:00
RuoYi
8499225192 优化用户密码字段序列化配置 2025-12-05 14:59:14 +08:00
RuoYi
4a5e45d160 优化数据权限控制逻辑,放开permission限制 2025-12-04 17:31:57 +08:00
RuoYi
188e50ff1c 支持Excel导出对象的多个子列表 2025-12-04 16:32:30 +08:00
RuoYi
bd66cc7260 优化表单构建关闭页签销毁复制插件 2025-12-04 13:15:20 +08:00
RuoYi
866b47000c 忽略用户密码字段的JSON序列化 2025-12-03 14:38:14 +08:00
RuoYi
f38f8b2c3e 升级tomcat到最新版本9.0.112 2025-12-03 11:39:56 +08:00
RuoYi
faa86ac946 优化代码 2025-12-03 11:39:27 +08:00
RuoYi
ad280e824c 优化生成代码下载的zip文件名 2025-12-03 10:26:27 +08:00
RuoYi
6e1aa42ebe 网页标题设置新增SET_TITLE方法 2025-12-02 19:30:16 +08:00
RuoYi
315901041f 支持Excel导出对象的多个子列表 2025-12-02 19:13:04 +08:00
RuoYi
91263711d4 登录/注册页面底部版权信息修改为读取配置 2025-12-02 15:28:44 +08:00
RuoYi
9372d3401f 修复v3时间控件between选择后清空报错问题 2025-12-02 14:56:34 +08:00
RuoYi
0eaa090f4b 修复表单构建移除所有控件后切换路由回来空白问题 2025-12-02 13:07:37 +08:00
RuoYi
a5adee3c5f 修复comboReadDict属性下多个sheet出现的报错(ICWQ8E) 2025-11-13 11:35:04 +08:00
RuoYi
075e96466f 添加新群号:174569686 2025-10-05 20:10:10 +08:00
RuoYi
41496b6d8a 升级spring-security到安全版本 2025-09-05 09:18:13 +08:00
RuoYi
4a401984c1 升级fastjson到最新版2.0.58 2025-09-05 09:16:51 +08:00
RuoYi
e5faee66c8 修复固定头部时出现的导航栏偏移问题(ICV9OH) 2025-09-04 19:58:16 +08:00
RuoYi
7558c176eb 支持防盗链功能 2025-09-02 11:30:54 +08:00
RuoYi
4a5b0e6079 升级oshi到最新版本6.8.3 2025-08-28 13:33:23 +08:00
RuoYi
08637e31e5 优化代码 2025-08-28 13:32:57 +08:00
RuoYi
512b157801 优化代码 2025-08-27 15:34:24 +08:00
若依
5e8efaa94a !1082 修复每次登录把部门id更新为null
Merge pull request !1082 from afterglow/master
2025-08-27 06:48:16 +00:00
RuoYi
5f11fed41b 用户导入添加验证提示 2025-08-23 11:13:51 +08:00
RuoYi
b60b5de750 优化布局设置显示 2025-08-23 11:12:42 +08:00
lcs
41ff3843e6 修复每次登录把部门id更新为null 2025-08-22 16:01:01 +08:00
RuoYi
6a2e8a35e9 修复用户归属部门无法修改为空问题 2025-08-21 14:47:48 +08:00
RuoYi
769165575f columns default value 2025-08-09 16:11:36 +08:00
RuoYi
18c8d4ec9c 显示列信息支持对象格式 2025-08-09 13:21:54 +08:00
RuoYi
191fd29301 自动识别json对象白名单配置范围缩小 2025-08-09 10:57:26 +08:00
RuoYi
47510fe2de 升级tomcat到最新版本9.0.108 2025-08-07 15:24:27 +08:00
RuoYi
725c7dcea2 添加新群号:191164766 2025-06-20 11:39:20 +08:00
RuoYi
158ccaebe0 优化定时任务包名白名单匹配方式 2025-06-20 11:33:25 +08:00
RuoYi
7b9060af26 优化Excel统计行数值的单元格样式显示 2025-06-19 14:47:49 +08:00
RuoYi
1a2f20e859 升级oshi到最新版本6.8.2 2025-06-18 13:51:41 +08:00
RuoYi
09faecb5d3 升级tomcat到最新版本9.0.106 2025-06-18 13:40:59 +08:00
RuoYi
d46e62a21a 用户头像更换后移除旧头像文件 2025-06-06 14:58:01 +08:00
RuoYi
fa88922637 若依 3.9.0 2025-05-28 09:04:45 +08:00
RuoYi
65159934ab 注册账号设置默认密码最后更新时间 2025-05-26 10:57:49 +08:00
RuoYi
1642bba612 升级fastjson到最新版2.0.57 2025-05-26 08:59:40 +08:00
RuoYi
a7a61fee8d update vue.config.js 2025-05-24 14:31:02 +08:00
RuoYi
db6d5d34e6 添加底部版权信息及开关 2025-05-24 14:24:23 +08:00
RuoYi
9ceca3a68e 添加页签图标显示开关功能 2025-05-23 14:56:38 +08:00
RuoYi
cf2579612c update pwdUpdateDate 2025-05-23 10:44:51 +08:00
RuoYi
c0355a0f5a 账号密码支持自定义更新周期 2025-05-23 09:04:50 +08:00
RuoYi
8ff013552a 初始密码支持自定义修改策略 2025-05-22 23:03:30 +08:00
RuoYi
673249d373 升级tomcat到最新版本9.0.105 2025-05-15 10:54:38 +08:00
RuoYi
fe3a92a812 升级oshi到最新版本6.8.1 2025-05-15 09:05:21 +08:00
RuoYi
67b6a0e11b 升级commons.io到最新版本2.19.0 2025-05-15 09:04:36 +08:00
RuoYi
bc70351e34 delete vue-meta 2025-05-15 08:52:28 +08:00
RuoYi
fe0c1fcb5b delete eslint 2025-05-15 08:13:34 +08:00
RuoYi
9f39dfd0c1 优化导航栏显示昵称&设置 2025-05-09 13:45:39 +08:00
RuoYi
131abe876d 菜单搜索支持键盘选择&悬浮主题背景 2025-05-07 13:22:43 +08:00
RuoYi
46708ceee4 图片上传组件新增disabled属性 2025-05-06 19:13:32 +08:00
RuoYi
ecd201550f add columnName Drag 2025-05-06 14:52:36 +08:00
若依
ff3f3f2631 !1013 修复图片上传组件在同一页面中被多次引用时,仅有第一个组件拖拽功能生效的问题
Merge pull request !1013 from 稚屿/N/A
2025-05-06 05:03:41 +00:00
若依
d3cc8f0fb7 !1012 修复文件上传组件在同一页面中被多次引用时,仅有第一个组件拖拽功能生效的问题
Merge pull request !1012 from 稚屿/N/A
2025-05-06 05:03:36 +00:00
稚屿
6cafa3373e 修复图片上传组件在同一页面中被多次引用时,仅有第一个组件拖拽功能生效的问题
Signed-off-by: 稚屿 <1491182878@qq.com>
2025-05-06 04:53:32 +00:00
稚屿
42fbf09dde 修复图片上传组件在同一页面中被多次引用时,仅有第一个组件拖拽功能生效的问题
Signed-off-by: 稚屿 <1491182878@qq.com>
2025-05-06 04:48:46 +00:00
RuoYi
88b0f5bcb2 update icon 2025-05-05 11:20:27 +08:00
RuoYi
e852fdb687 上传组件新增拖动排序属性 2025-04-30 10:28:59 +08:00
RuoYi
baf2f6f46b 优化Excel匹配数值型.0结尾 2025-04-28 11:24:24 +08:00
RuoYi
e19f1abfeb update editor index 2025-04-27 14:02:36 +08:00
RuoYi
38ed092de7 remove all semicolons 2025-04-27 10:05:51 +08:00
RuoYi
27a037ed3d Excel导入导出支持多图片 2025-04-25 10:09:21 +08:00
RuoYi
87173cbe75 富文本复制粘贴图片上传至url 2025-04-24 14:23:29 +08:00
RuoYi
29a5b6da53 update vue.config.js 2025-04-24 11:08:10 +08:00
RuoYi
b1d2139559 update package.json 2025-04-24 11:07:54 +08:00
RuoYi
43d78c2cf5 优化低版本node无法启动的问题 2025-04-22 12:05:43 +08:00
RuoYi
8f4eb24bf2 优化代码 2025-04-22 12:03:31 +08:00
RuoYi
a9f9133e31 显隐列组件支持全选/全不选 2025-04-21 15:21:51 +08:00
RuoYi
09810ccf1d 优化菜单搜索查询页 2025-04-21 13:22:00 +08:00
RuoYi
0d9fb8b5c0 支持文件&图片组件自定义地址&参数 2025-04-18 12:55:58 +08:00
RuoYi
c6b0efcdc2 优化角色禁用不允许分配 2025-04-17 15:08:10 +08:00
RuoYi
84fef1f675 update status name 2025-04-17 15:07:38 +08:00
RuoYi
11fed08b56 添加新群号:287842588 2025-04-01 19:15:21 +08:00
RuoYi
f83b6fbfa2 remove dev runjs 2025-03-18 15:49:01 +08:00
若依
eef81e6ca9 !997 登录页和注册页表头使用VUE_APP_TITLE配置值
Merge pull request !997 from myifengs/master
2025-03-18 07:48:23 +00:00
Chingfeng Li
5a03a754e8 登录页肯注册页表头使用VUE_APP_TITLE配置值 2025-03-18 14:53:46 +08:00
RuoYi
245dea7215 升级tomcat到最新版本9.0.102 2025-03-14 16:09:22 +08:00
RuoYi
51632f8e60 优化代码 2025-03-14 16:09:01 +08:00
RuoYi
525ebf92d2 菜单管理新增路由名称 2025-03-06 11:02:21 +08:00
RuoYi
d3b23a831e 优化代码 2025-03-04 20:03:11 +08:00
若依
89ab3bd058 !990 优化服务监控和缓存监控页面,页边距保持一致
Merge pull request !990 from NewYoung208/master
2025-03-04 11:18:04 +00:00
若依
9e16beb48f !989 update ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java.
Merge pull request !989 from 程子/N/A
2025-03-04 11:16:45 +00:00
NewYoung208
8d5ecc7ff4 优化服务监控和缓存监控页面,页边距保持一致 2025-03-04 16:58:16 +08:00
程子
6e314dd3e8 update ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java.
添加String转换Boolean值时对(是、否)的支持

Signed-off-by: 程子 <395030787@qq.com>
2025-03-03 08:04:15 +00:00
RuoYi
193c256e71 优化顶部菜单搜索栏为多层级显示(IBESXH) 2025-03-03 12:07:38 +08:00
RuoYi
4df52a6b40 优化导出Excel日期格式双击离开后与设定的格式不一致问题 2025-03-01 15:21:22 +08:00
RuoYi
079b7eeecf 优化代码 2025-03-01 15:17:01 +08:00
RuoYi
ba24010709 pagination更换成flex布局 2025-03-01 15:07:43 +08:00
RuoYi
bd257f85e6 优化前端处理路由函数代码 2025-03-01 15:07:21 +08:00
RuoYi
40c7ca34a8 优化前端树结构性能问题 2025-03-01 14:53:39 +08:00
RuoYi
1ef73d7360 修复代码生成主子表校验必填失效问题 2025-02-28 21:52:56 +08:00
RuoYi
bd233fd62f 代码生成列表支持按时间排序 2025-02-28 19:38:34 +08:00
RuoYi
fabddc518a 文件上传组件新增类型 2025-02-28 19:36:25 +08:00
RuoYi
ca61b6c68d 优化空指针异常时无法获取错误信息问题 2025-02-28 19:35:13 +08:00
RuoYi
51e5cf2a09 升级tomcat到最新版本9.0.100 2025-02-28 13:00:01 +08:00
RuoYi
00acc37916 文件上传组件新增disabled属性 2025-02-28 12:59:41 +08:00
RuoYi
511ff0f125 优化代码 2025-02-28 12:58:03 +08:00
RuoYi
bf46e38c29 添加新群号 2025-01-15 15:07:57 +08:00
RuoYi
698a5198d9 copyright 2025 2025-01-07 10:43:54 +08:00
RuoYi
5e6c917ab0 若依 3.8.9 2024-12-30 08:49:55 +08:00
RuoYi
9a51563144 代码生成新增配置是否允许文件覆盖到本地 2024-12-25 15:48:16 +08:00
RuoYi
3b2704c181 优化导入带标题文件关闭清理 2024-12-25 15:47:32 +08:00
RuoYi
7232217061 update sqlkeyword 2024-12-25 00:05:16 +08:00
RuoYi
25fd29c5ea 优化特殊字符密码修改失败问题 2024-12-17 14:27:18 +08:00
RuoYi
2d6a6a162f 优化TopNav内链菜单点击没有高亮(IB8WHJ) 2024-12-17 11:56:51 +08:00
RuoYi
164c62743f 优化菜单管理切换Mini布局错乱问题 2024-12-17 11:24:10 +08:00
RuoYi
4ee169b0c8 update README 2024-12-13 20:07:48 +08:00
RuoYi
d487ffc92f 用户管理过滤掉已禁用部门(IB5H7F) 2024-12-11 11:20:09 +08:00
RuoYi
5a1e7bae2c 修改主题样式本地读取 2024-12-07 17:06:50 +08:00
RuoYi
1810f30491 白名单支持对通配符路径匹配 2024-12-04 08:51:17 +08:00
RuoYi
6efceac460 Excel注解支持wrapText是否允许内容换行 2024-12-03 08:58:44 +08:00
RuoYi
77a6350460 修复导出子列表对象只能在最后的问题 2024-12-02 20:36:53 +08:00
若依
58a21ff9d7 !961 修复默认关闭Tags-Views时,内链页面打不开
Merge pull request !961 from Lyb刘同学/master
2024-11-27 09:28:06 +00:00
liuyuanbo
7f507f5dfa 修复默认关闭Tags-Views时,内链页面打不开 2024-11-27 17:24:53 +08:00
若依
a1a45ef7ac !958 修复TopNav无法正确获取active的问题
Merge pull request !958 from Lyb刘同学/N/A
2024-11-27 00:55:53 +00:00
Lyb刘同学
b343308a97 修复TopNav无法正确获取active的问题
Signed-off-by: Lyb刘同学 <1553592282@qq.com>
2024-11-26 09:24:21 +00:00
RuoYi
0bf7457eb7 优化代码 2024-11-25 22:27:10 +08:00
RuoYi
0f77f524d0 面板兼容移动端显示 2024-11-25 15:39:49 +08:00
RuoYi
747d816be2 参数键值更换为多行文本 2024-11-25 12:15:21 +08:00
RuoYi
262d9e1ff0 菜单面包屑导航支持多层级显示 2024-11-22 20:44:39 +08:00
RuoYi
ab37956874 分栏参数微调 2024-11-22 14:45:58 +08:00
RuoYi
86ab3bf600 用户管理支持分栏拖动 2024-11-22 12:19:56 +08:00
RuoYi
f76908912e 用户头像http(s)链接支持 2024-11-20 10:42:41 +08:00
RuoYi
8df4c72ad1 update .env.staging 2024-11-20 10:41:20 +08:00
RuoYi
6bdcbabc09 update pom.xml 2024-11-08 16:31:54 +08:00
RuoYi
58fca720a9 升级pom依赖到安全版本 2024-11-08 16:24:14 +08:00
RuoYi
e4ccbc6601 支持自定义显示Excel属性列 2024-11-07 22:15:27 +08:00
RuoYi
430e6d4dea 升级spring-framework到安全版本 2024-11-07 22:14:51 +08:00
RuoYi
a0e6295693 升级oshi到最新版本6.6.5 2024-11-05 16:23:52 +08:00
RuoYi
52ba823328 优化无用户编号不校验数据权限 2024-11-05 16:23:42 +08:00
RuoYi
91ae9a164c 校检文件名是否包含特殊字符 2024-11-05 12:49:40 +08:00
RuoYi
d3326987a4 优化身份证脱敏正则 2024-10-21 16:19:17 +08:00
若依
4de087b1ad !937 update ruoyi-ui/src/components/ImageUpload/index.vue.
Merge pull request !937 from AZP/N/A
2024-10-21 08:05:26 +00:00
AZP
5b959b32d7 update ruoyi-ui/src/components/ImageUpload/index.vue.
【fix】修复后台前端上传图片如果图片路径已经携带域名就无需增加前缀域名

Signed-off-by: AZP <2198774759@qq.com>
2024-10-21 03:39:18 +00:00
RuoYi
4358621473 优化权限更新后同步缓存 2024-10-21 10:24:45 +08:00
RuoYi
adb8d51932 操作日志记录DELETE请求参数(IAMV6F) 2024-10-17 12:42:40 +08:00
RuoYi
08a5deb285 升级fastjson到最新版2.0.53 2024-10-17 12:42:24 +08:00
RuoYi
dc9f3ee722 升级quill到最新版本2.0.2 2024-10-15 16:18:02 +08:00
RuoYi
78bb30bb5f 修复码生成上级菜单显示问题(I9CTIJ) 2024-09-27 16:15:17 +08:00
RuoYi
5fad997d38 修复角色禁用权限不失效问题(IAA8ZX) 2024-09-21 11:28:52 +08:00
RuoYi
22a795d041 优化代码 2024-09-08 10:29:41 +08:00
RuoYi
8a0a3a03fe 升级oshi到最新版本6.6.3 2024-08-30 21:46:03 +08:00
RuoYi
ad86486285 update sqlkeyword 2024-08-30 21:45:16 +08:00
RuoYi
3ef6000794 修改时间范围日期格式 2024-07-08 16:45:36 +08:00
RuoYi
f812e99a0d remove sub resultType 2024-07-08 16:38:34 +08:00
RuoYi
2feae7619f avatar add headers 2024-07-02 16:08:30 +08:00
RuoYi
212e3b4977 升级axios到最新版本0.28.1 2024-07-02 12:58:28 +08:00
RuoYi
99e66bf11c 若依 3.8.8 2024-06-30 08:02:22 +08:00
RuoYi
a96d4bf2ed 菜单管理新增路由名称 2024-06-29 19:08:09 +08:00
36 changed files with 897 additions and 655 deletions

View File

@@ -13,8 +13,8 @@
若依是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。
* 本仓库为RuoYi-Vue的Spring Boot 3 的版本,保持同步更新
* 后端采用Spring Boot3、Spring Security、Redis & Jwt。
* 前端采用Vue、Element UI
* 后端采用Spring Boot、Spring Security、Redis & Jwt。
* 权限认证使用Jwt支持多终端认证系统。
* 支持加载动态权限菜单,多方式轻松权限控制。
* 高效率开发,使用代码生成器可以一键生成前后端代码。

20
pom.xml
View File

@@ -17,27 +17,27 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version>
<spring-boot.version>3.5.11</spring-boot.version>
<mybatis-spring-boot.version>3.0.5</mybatis-spring-boot.version>
<spring-boot.version>4.0.6</spring-boot.version>
<mybatis-spring-boot.version>4.0.1</mybatis-spring-boot.version>
<druid.version>1.2.28</druid.version>
<yauaa.version>8.1.0</yauaa.version>
<yauaa.version>8.1.1</yauaa.version>
<kaptcha.version>2.3.3</kaptcha.version>
<pagehelper.boot.version>2.1.1</pagehelper.boot.version>
<fastjson.version>2.0.61</fastjson.version>
<oshi.version>6.10.0</oshi.version>
<commons.io.version>2.21.0</commons.io.version>
<pagehelper.boot.version>4.1.0</pagehelper.boot.version>
<fastjson.version>2.0.62</fastjson.version>
<oshi.version>7.3.0</oshi.version>
<commons.io.version>2.22.0</commons.io.version>
<poi.version>4.1.2</poi.version>
<velocity.version>2.3</velocity.version>
<jwt.version>0.9.1</jwt.version>
<jaxb-api.version>2.3.1</jaxb-api.version>
<springdoc.version>2.8.16</springdoc.version>
<springdoc.version>3.0.3</springdoc.version>
</properties>
<!-- 依赖声明 -->
<dependencyManagement>
<dependencies>
<!-- SpringBoot的依赖配置-->
<!-- SpringBoot的依赖配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
@@ -49,7 +49,7 @@
<!-- 阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-3-starter</artifactId>
<artifactId>druid-spring-boot-4-starter</artifactId>
<version>${druid.version}</version>
</dependency>

View File

@@ -2,7 +2,7 @@ package com.ruoyi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
/**
* 启动程序

View File

@@ -60,6 +60,9 @@ spring:
max-file-size: 10MB
# 设置总上传的文件大小
max-request-size: 20MB
jackson:
time-zone: GMT+8
date-format: yyyy-MM-dd HH:mm:ss
# 服务模块
devtools:
restart:

View File

@@ -55,7 +55,7 @@
<!-- JSON工具类 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<groupId>tools.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
@@ -95,6 +95,11 @@
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- pool 对象池 -->
<dependency>
<groupId>org.apache.commons</groupId>

View File

@@ -5,7 +5,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import tools.jackson.databind.annotation.JsonSerialize;
import com.ruoyi.common.config.serializer.SensitiveJsonSerializer;
import com.ruoyi.common.enums.DesensitizedType;
@@ -15,7 +15,7 @@ import com.ruoyi.common.enums.DesensitizedType;
* @author ruoyi
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Target({ ElementType.FIELD, ElementType.METHOD })
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveJsonSerializer.class)
public @interface Sensitive

View File

@@ -1,13 +1,13 @@
package com.ruoyi.common.config.serializer;
import java.io.IOException;
import java.util.Objects;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import tools.jackson.core.JacksonException;
import tools.jackson.core.JsonGenerator;
import tools.jackson.databind.BeanProperty;
import tools.jackson.databind.DatabindException;
import tools.jackson.databind.SerializationContext;
import tools.jackson.databind.ValueSerializer;
import tools.jackson.databind.ser.std.StdSerializer;
import com.ruoyi.common.annotation.Sensitive;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.DesensitizedType;
@@ -18,14 +18,26 @@ import com.ruoyi.common.utils.SecurityUtils;
*
* @author ruoyi
*/
public class SensitiveJsonSerializer extends JsonSerializer<String> implements ContextualSerializer
public class SensitiveJsonSerializer extends StdSerializer<String>
{
private DesensitizedType desensitizedType;
private final DesensitizedType desensitizedType;
public SensitiveJsonSerializer()
{
super(String.class);
this.desensitizedType = null;
}
public SensitiveJsonSerializer(DesensitizedType desensitizedType)
{
super(String.class);
this.desensitizedType = desensitizedType;
}
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException
public void serialize(String value, JsonGenerator gen, SerializationContext ctxt) throws JacksonException
{
if (desensitization())
if (desensitizedType != null && desensitization())
{
gen.writeString(desensitizedType.desensitizer().apply(value));
}
@@ -36,16 +48,14 @@ public class SensitiveJsonSerializer extends JsonSerializer<String> implements C
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
throws JsonMappingException
public ValueSerializer<?> createContextual(SerializationContext ctxt, BeanProperty property) throws DatabindException
{
Sensitive annotation = property.getAnnotation(Sensitive.class);
if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass()))
{
this.desensitizedType = annotation.desensitizedType();
return this;
return new SensitiveJsonSerializer(annotation.desensitizedType());
}
return prov.findValueSerializer(property.getType(), property);
return ctxt.findValueSerializer(property.getType());
}
/**

View File

@@ -1,11 +1,15 @@
package com.ruoyi.common.core.domain.model;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import com.alibaba.fastjson2.annotation.JSONField;
import com.ruoyi.common.core.domain.entity.SysUser;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Set;
/**
* 登录用户身份权限
@@ -259,8 +263,16 @@ public class LoginUser implements UserDetails
}
@Override
@JSONField(serialize = false)
public Collection<? extends GrantedAuthority> getAuthorities()
{
return null;
if (permissions == null || permissions.isEmpty())
{
return Collections.emptyList();
}
return permissions.stream()
.filter(Objects::nonNull)
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
}
}

View File

@@ -2,7 +2,7 @@ package com.ruoyi.common.enums;
import java.util.HashMap;
import java.util.Map;
import org.springframework.lang.Nullable;
import org.jspecify.annotations.Nullable;
/**
* 请求方式

View File

@@ -6,6 +6,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.Strings;
import org.springframework.util.AntPathMatcher;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.text.StrFormatter;
@@ -433,12 +434,24 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
return list;
}
/**
* 检查子字符串是否存在
*
* @param seq 检查的字符串
* @param searchSeq 查找的字符串
* @return 结果
*/
public static boolean contains(final CharSequence seq, final CharSequence searchSeq)
{
return Strings.CS.contains(seq, searchSeq);
}
/**
* 判断给定的collection列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value
*
* @param collection 给定的集合
* @param array 给定的数组
* @return boolean 结果
* @return 结果
*/
public static boolean containsAny(Collection<String> collection, String... array)
{
@@ -459,6 +472,18 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
}
}
/**
* 判断是否包含给定数组中的任意一个。
*
* @param cs 要判断的字符串
* @param searchCharSequences 要判断的数组
* @return 结果
*/
public static boolean containsAny(final CharSequence cs, final CharSequence... searchCharSequences)
{
return Strings.CS.containsAny(cs, searchCharSequences);
}
/**
* 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写
*
@@ -482,6 +507,163 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
return false;
}
/**
* 检查是否包含要搜索的字符串,忽略大小写
*
* @param str 要检查的字符串
* @param searchStr 要查找的字符串
* @return 如果包含要搜索的字符串忽略大小写则返回true如果不包含或返回false
*/
public static boolean containsIgnoreCase(final CharSequence str, final CharSequence searchStr)
{
return Strings.CI.contains(str, searchStr);
}
/**
* 检查字符串是否以任意前缀开始
*
* @param sequence 要检查的字符串
* @param searchStrings 区分大小写的字符串前缀数组
* @return 结果
*/
public static boolean startsWithAny(final CharSequence sequence, final CharSequence... searchStrings)
{
return Strings.CS.startsWithAny(sequence, searchStrings);
}
/**
* 不区分大小写地检查一个字符串是否以指定前缀开头。
*
* @param str 待检查的字符串
* @param 要查找的前缀
* @return 结果
*/
public static boolean startsWithIgnoreCase(final CharSequence str, final CharSequence prefix)
{
return Strings.CI.startsWith(str, prefix);
}
/**
* 比较两个字符串是否相同
*
* @param cs1 第一个字符串
* @param cs2 第二个字符串
* @return 如果给定对象与字符串相等,则返回 true否则返回 false
*/
public static boolean equals(final CharSequence cs1, final CharSequence cs2)
{
return Strings.CS.equals(cs1, cs2);
}
/**
* 替换字符串中所有匹配的字符
*
* @param text 要搜索和替换的文本
* @param searchString 要搜索的字符串
* @param replacement 用于替换的字符串
* @return 处理完所有替换后的文本
*/
public static String replace(final String text, final String searchString, final String replacement)
{
return Strings.CS.replace(text, searchString, replacement);
}
/**
* 仅当子字符串位于源字符串末尾时才将其移除,否则返回源字符串。
* @param str 要搜索的源字符串
* @param remove 要搜索并移除的字符串
* @return 如果找到并移除了字符串,则返回移除后的子字符串
*/
public static String removeEnd(final String str, final String remove)
{
return Strings.CS.removeEnd(str, remove);
}
/**
* 查找字符串首次出现位置的索引
*
* @param seq 要检查的字符串
* @param searchSeq 要查找的字符串
* @return 返回指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1
*/
public static int indexOf(final CharSequence seq, final CharSequence searchSeq)
{
return Strings.CS.indexOf(seq, searchSeq);
}
/**
* 检查字符串是否以指定的后缀结尾
*
* @param str 要检查的字符
* @param suffix 要检查的后缀
* @return 若参数与该字符串末尾相符 true;否则 false
*/
public static boolean endsWith(final CharSequence str, final CharSequence suffix)
{
return Strings.CS.endsWith(str, suffix);
}
/**
* 将给定的字符串与数组进行比较
*
* @param string 要比较的字符串
* @param searchStrings 字符串数组
* @return 如果字符串等于(区分大小写){@code searchStrings}中的任意其他元素则返回true如果{@code searchStrings}为null或不包含匹配项则返回false
*/
public static boolean equalsAny(final CharSequence string, final CharSequence... searchStrings)
{
return Strings.CS.equalsAny(string, searchStrings);
}
/**
* 检查一个字符串是否以任意提供的区分大小写的后缀结尾。
*
* @param sequence 要检查的字符串
* @param searchStrings 要查找的区分大小写的字符串数组
* @return 如果输入参数{@code sequence}为null且未提供任何{@code searchStrings},或者输入{@code sequence}以任意提供的区分大小写的{@code searchStrings}结尾,则返回{@code true}。
*/
public static boolean endsWithAny(final CharSequence sequence, final CharSequence... searchStrings)
{
return Strings.CS.endsWithAny(sequence, searchStrings);
}
/**
* 不区分大小写地检查字符序列是否以指定的后缀结尾
*
* @param str 要检查的字符序列
* @param suffix 要查找的后缀
* @return 如果字符序列以该后缀结尾(不区分大小写),或两者均为{@code null},则返回{@code true}
*/
public static boolean endsWithIgnoreCase(final CharSequence str, final CharSequence suffix)
{
return Strings.CI.endsWith(str, suffix);
}
/**
* 指定范围内查找字符串,忽略大小写
*
* @param str 要检查的字符串
* @param searchStr 要查找的字符串
* @return 搜索字符串的第一个索引,如果未找到匹配项则返回 -1
*/
public static int indexOfIgnoreCase(final CharSequence str, final CharSequence searchStr)
{
return Strings.CI.indexOf(str, searchStr);
}
/**
* Compares given {@code string} to a CharSequences vararg of {@code searchStrings},
* returning {@code true} if the {@code string} is equal to any of the {@code searchStrings}, ignoring case.
*
* @param string to compare, may be {@code null}.
* @param searchStrings a vararg of strings, may be {@code null}.
* @return {@code true} if the string is equal (case-insensitive) to any other element of {@code searchStrings};
*/
public static boolean equalsAnyIgnoreCase(final CharSequence string, final CharSequence... searchStrings)
{
return Strings.CI.equalsAny(string, searchStrings);
}
/**
* 驼峰转下划线命名
*/
@@ -553,6 +735,22 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
return false;
}
/**
* 删除最后一个字符串
*
* @param str 输入字符串
* @param spit 以什么类型结尾的
* @return 截取后的字符串
*/
public static String lastStringDel(String str, String spit)
{
if (!StringUtils.isEmpty(str) && str.endsWith(spit))
{
return str.subSequence(0, str.length() - 1).toString();
}
return str;
}
/**
* 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如HELLO_WORLD->HelloWorld
*

View File

@@ -30,7 +30,7 @@ public class MimeTypeUtils
// 图片
"bmp", "gif", "jpg", "jpeg", "png",
// word excel powerpoint
"doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
"doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt",
// 压缩文件
"rar", "zip", "gz", "bz2",
// 视频格式

View File

@@ -7,13 +7,13 @@ import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Date;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.poi.ss.usermodel.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
/**
* 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.

View File

@@ -20,19 +20,19 @@
<!-- SpringBoot Web容器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>
<!-- SpringBoot 拦截器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<artifactId>spring-boot-starter-aspectj</artifactId>
</dependency>
<!-- 阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-3-starter</artifactId>
<artifactId>druid-spring-boot-4-starter</artifactId>
</dependency>
<!-- 验证码 -->

View File

@@ -1,9 +1,6 @@
package com.ruoyi.framework.config;
import java.util.TimeZone;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@@ -19,12 +16,4 @@ import org.springframework.context.annotation.EnableAspectJAutoProxy;
@MapperScan("com.ruoyi.**.mapper")
public class ApplicationConfig
{
/**
* 时区配置
*/
@Bean
public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization()
{
return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault());
}
}

View File

@@ -11,8 +11,8 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceBuilder;
import com.alibaba.druid.spring.boot3.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.spring.boot4.autoconfigure.DruidDataSourceBuilder;
import com.alibaba.druid.spring.boot4.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.util.Utils;
import com.ruoyi.common.enums.DataSourceType;
import com.ruoyi.common.utils.spring.SpringUtils;

View File

@@ -49,8 +49,8 @@ public class ThreadPoolConfig
protected ScheduledExecutorService scheduledExecutorService()
{
return new ScheduledThreadPoolExecutor(corePoolSize,
new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(),
new ThreadPoolExecutor.CallerRunsPolicy())
BasicThreadFactory.builder().namingPattern("schedule-pool-%d").daemon(true).build(),
new ThreadPoolExecutor.CallerRunsPolicy())
{
@Override
protected void afterExecute(Runnable r, Throwable t)

View File

@@ -34,6 +34,7 @@ public class PermitAllUrlProperties implements InitializingBean, ApplicationCont
public String ASTERISK = "*";
@SuppressWarnings("deprecation")
@Override
public void afterPropertiesSet()
{
@@ -45,7 +46,7 @@ public class PermitAllUrlProperties implements InitializingBean, ApplicationCont
// 获取方法上边的注解 替代path variable 为 *
Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class);
Optional.ofNullable(method).ifPresent(anonymous -> Objects.requireNonNull(info.getPathPatternsCondition().getPatternValues()) //
Optional.ofNullable(method).ifPresent(anonymous -> Objects.requireNonNull(info.getPathPatternsCondition().getPatternValues())
.forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK))));
// 获取类上边的注解, 替代path variable 为 *

View File

@@ -32,7 +32,7 @@
<!-- 阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-3-starter</artifactId>
<artifactId>druid-spring-boot-4-starter</artifactId>
</dependency>
</dependencies>

View File

@@ -315,7 +315,7 @@
<script setup lang="ts" name="${BusinessName}">
import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName} } from "@/api/${moduleName}/${businessName}"
#if($genView)
import ${BusinessName}ViewDrawer from "./view"
import ${BusinessName}ViewDrawer from "./view.vue"
#end
import type { ${ClassName}, ${BusinessName}QueryParams } from "@/types/api/${moduleName}/${businessName}"
import type { TreeSelect } from '@/types/api/common'

View File

@@ -394,7 +394,7 @@ import type { ${ClassName}, ${BusinessName}QueryParams } from "@/types/api/${mod
#end
import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName} } from "@/api/${moduleName}/${businessName}"
#if($genView)
import ${BusinessName}ViewDrawer from "./view"
import ${BusinessName}ViewDrawer from "./view.vue"
#end
const { proxy } = getCurrentInstance()

View File

@@ -19,8 +19,8 @@
<!-- 定时任务 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<!-- 通用工具-->

View File

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

View File

@@ -1,4 +1,4 @@
/**
/**
* v-hasPermi 操作权限处理
* Copyright (c) 2019 ruoyi
*/

View File

@@ -1,4 +1,4 @@
/**
/**
* v-hasRole 角色权限处理
* Copyright (c) 2019 ruoyi
*/

View File

@@ -109,7 +109,7 @@ export default {
return {
'--chrome-tab-active-bg': this.mixHexWithWhite(primary, 0.15),
'--chrome-tab-text-active': primary,
'--chrome-wing-r': '14px'
'--chrome-wing-r': '10px'
}
}
},
@@ -610,16 +610,19 @@ $tags-bar-height: 34px;
background: #f5f7fa !important;
border-radius: 6px 6px 0 0;
color: #303133 !important;
z-index: 2;
}
&.active {
height: 31px;
min-height: 31px;
height: 33px;
min-height: 33px;
padding: 0 14px;
z-index: 3;
color: var(--chrome-tab-text-active) !important;
font-weight: 500;
background: var(--chrome-tab-active-bg) !important;
border: none !important;
border-bottom: 2px solid var(--chrome-tab-active-bg) !important;
border-radius: var(--chrome-wing-r) var(--chrome-wing-r) 0 0;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
@@ -690,12 +693,33 @@ $tags-bar-height: 34px;
.el-scrollbar__bar {
opacity: 0;
transition: opacity 0.3s;
z-index: 10;
.tags-view-container:hover & {
opacity: 1;
}
}
.tags-view-container--chrome & {
.el-scrollbar {
position: relative;
}
.el-scrollbar__wrap {
&::-webkit-scrollbar {
width: 0 !important;
height: 0 !important;
}
scrollbar-width: none;
-ms-overflow-style: none;
}
.el-scrollbar__bar.is-horizontal {
z-index: 20;
height: 6px !important;
}
}
.tags-view-item {
.el-icon-close {
width: 16px;

View File

@@ -76,10 +76,10 @@ export default {
this.routers.map((router) => {
for (var item in router.children) {
if (router.children[item].parentPath === undefined) {
if(router.path === "/") {
if (router.path === "/") {
router.children[item].path = "/" + router.children[item].path
} else {
if(!isHttp(router.children[item].path)) {
if (!isHttp(router.children[item].path)) {
router.children[item].path = router.path + "/" + router.children[item].path
}
}
@@ -100,7 +100,7 @@ export default {
activePath = "/" + tmpPath.substring(0, tmpPath.indexOf("/"))
this.$store.dispatch('app/toggleSideBarHide', false)
}
} else if(!this.$route.children) {
} else if (!this.$route.children) {
activePath = path
this.$store.dispatch('app/toggleSideBarHide', true)
}
@@ -156,7 +156,7 @@ export default {
}
})
}
if(routes.length > 0) {
if (routes.length > 0) {
this.$store.commit("SET_SIDEBAR_ROUTERS", routes)
} else {
this.$store.dispatch('app/toggleSideBarHide', true)

View File

@@ -44,11 +44,11 @@ router.beforeEach((to, from, next) => {
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
})
}).catch(err => {
store.dispatch('LogOut').then(() => {
Message.error(err)
next({ path: '/' })
})
store.dispatch('LogOut').then(() => {
Message.error(err)
next({ path: '/' })
})
})
} else {
next()
}

View File

@@ -123,7 +123,7 @@ const mutations = {
if (i > -1) {
state.cachedViews.splice(i, 1)
}
if(item.meta.link) {
if (item.meta.link) {
const fi = state.iframeViews.findIndex(v => v.path === item.path)
state.iframeViews.splice(fi, 1)
}
@@ -144,7 +144,7 @@ const mutations = {
if (i > -1) {
state.cachedViews.splice(i, 1)
}
if(item.meta.link) {
if (item.meta.link) {
const fi = state.iframeViews.findIndex(v => v.path === item.path)
state.iframeViews.splice(fi, 1)
}

View File

@@ -1,7 +1,7 @@
import store from '@/store'
import router from '@/router'
import cache from '@/plugins/cache'
import { MessageBox, } from 'element-ui'
import { MessageBox } from 'element-ui'
import { login, logout, getInfo } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'
import { isHttp, isEmpty } from "@/utils/validate"

View File

@@ -68,59 +68,59 @@ service.interceptors.request.use(config => {
}
return config
}, error => {
console.log(error)
Promise.reject(error)
console.log(error)
Promise.reject(error)
})
// 响应拦截器
service.interceptors.response.use(res => {
// 未设置状态码则默认成功状态
const code = res.data.code || 200
// 获取错误信息
const msg = errorCode[code] || res.data.msg || errorCode['default']
// 二进制数据则直接返回
if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
return res.data
}
if (code === 401) {
if (!isRelogin.show) {
isRelogin.show = true
MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => {
isRelogin.show = false
store.dispatch('LogOut').then(() => {
location.href = '/index'
})
// 未设置状态码则默认成功状态
const code = res.data.code || 200
// 获取错误信息
const msg = errorCode[code] || res.data.msg || errorCode['default']
// 二进制数据则直接返回
if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
return res.data
}
if (code === 401) {
if (!isRelogin.show) {
isRelogin.show = true
MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => {
isRelogin.show = false
store.dispatch('LogOut').then(() => {
location.href = '/index'
})
}).catch(() => {
isRelogin.show = false
})
}
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
} else if (code === 500) {
Message({ message: msg, type: 'error' })
return Promise.reject(new Error(msg))
} else if (code === 601) {
Message({ message: msg, type: 'warning' })
return Promise.reject('error')
} else if (code !== 200) {
Notification.error({ title: msg })
return Promise.reject('error')
} else {
return res.data
}
},
error => {
console.log('err' + error)
let { message } = error
if (message == "Network Error") {
message = "后端接口连接异常"
} else if (message.includes("timeout")) {
message = "系统接口请求超时"
} else if (message.includes("Request failed with status code")) {
message = "系统接口" + message.slice(-3) + "异常"
}
Message({ message: message, type: 'error', duration: 5 * 1000 })
return Promise.reject(error)
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
} else if (code === 500) {
Message({ message: msg, type: 'error' })
return Promise.reject(new Error(msg))
} else if (code === 601) {
Message({ message: msg, type: 'warning' })
return Promise.reject('error')
} else if (code !== 200) {
Notification.error({ title: msg })
return Promise.reject('error')
} else {
return res.data
}
},
error => {
console.log('err' + error)
let { message } = error
if (message == "Network Error") {
message = "后端接口连接异常"
} else if (message.includes("timeout")) {
message = "系统接口请求超时"
} else if (message.includes("Request failed with status code")) {
message = "系统接口" + message.slice(-3) + "异常"
}
Message({ message: message, type: 'error', duration: 5 * 1000 })
return Promise.reject(error)
}
)
// 通用下载方法

View File

@@ -86,7 +86,7 @@ export function selectDictLabel(datas, value) {
// 回显数据字典(字符串、数组)
export function selectDictLabels(datas, value, separator) {
if (value === undefined || value.length ===0) {
if (value === undefined || value.length === 0) {
return ""
}
if (Array.isArray(value)) {

View File

@@ -1,148 +1,148 @@
<template>
<div class="app-container">
<el-row :gutter="10">
<el-col :span="24" class="card-box">
<el-card>
<div slot="header"><span><i class="el-icon-monitor"></i> 基本信息</span></div>
<div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%">
<tbody>
<tr>
<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">运行模式</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" 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" v-if="cache.info">{{ cache.info.connected_clients }}</div></td>
</tr>
<tr>
<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">使用内存</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" 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" v-if="cache.info">{{ cache.info.maxmemory_human }}</div></td>
</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" 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" 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" 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" v-if="cache.info">{{ cache.info.instantaneous_input_kbps }}kps/{{cache.info.instantaneous_output_kbps}}kps</div></td>
</tr>
</tbody>
</table>
</div>
</el-card>
</el-col>
<el-col :span="12" class="card-box">
<el-card>
<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 ref="commandstats" style="height: 420px" />
</div>
</el-card>
</el-col>
<el-col :span="12" class="card-box">
<el-card>
<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 ref="usedmemory" style="height: 420px" />
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
import { getCache } from "@/api/monitor/cache"
import * as echarts from "echarts"
export default {
name: "Cache",
data() {
return {
// 统计命令信息
commandstats: null,
// 使用内存
usedmemory: null,
// cache信息
cache: []
}
},
created() {
this.getList()
this.openLoading()
},
methods: {
/** 查缓存询信息 */
getList() {
getCache().then((response) => {
this.cache = response.data
this.$modal.closeLoading()
this.commandstats = echarts.init(this.$refs.commandstats, "macarons")
this.commandstats.setOption({
tooltip: {
trigger: "item",
formatter: "{a} <br/>{b} : {c} ({d}%)",
},
series: [
{
name: "命令",
type: "pie",
roseType: "radius",
radius: [15, 95],
center: ["50%", "38%"],
data: response.data.commandStats,
animationEasing: "cubicInOut",
animationDuration: 1000,
}
]
})
this.usedmemory = echarts.init(this.$refs.usedmemory, "macarons")
this.usedmemory.setOption({
tooltip: {
formatter: "{b} <br/>{a} : " + this.cache.info.used_memory_human,
},
series: [
{
name: "峰值",
type: "gauge",
min: 0,
max: 1000,
detail: {
formatter: this.cache.info.used_memory_human,
},
data: [
{
value: parseFloat(this.cache.info.used_memory_human),
name: "内存消耗",
}
]
}
]
})
window.addEventListener("resize", () => {
this.commandstats.resize()
this.usedmemory.resize()
})
})
},
// 打开加载层
openLoading() {
this.$modal.loading("正在加载缓存监控数据,请稍候!")
}
}
}
</script>
<template>
<div class="app-container">
<el-row :gutter="10">
<el-col :span="24" class="card-box">
<el-card>
<div slot="header"><span><i class="el-icon-monitor"></i> 基本信息</span></div>
<div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%">
<tbody>
<tr>
<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">运行模式</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" 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" v-if="cache.info">{{ cache.info.connected_clients }}</div></td>
</tr>
<tr>
<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">使用内存</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" 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" v-if="cache.info">{{ cache.info.maxmemory_human }}</div></td>
</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" 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" 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" 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" v-if="cache.info">{{ cache.info.instantaneous_input_kbps }}kps/{{cache.info.instantaneous_output_kbps}}kps</div></td>
</tr>
</tbody>
</table>
</div>
</el-card>
</el-col>
<el-col :span="12" class="card-box">
<el-card>
<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 ref="commandstats" style="height: 420px" />
</div>
</el-card>
</el-col>
<el-col :span="12" class="card-box">
<el-card>
<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 ref="usedmemory" style="height: 420px" />
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
import { getCache } from "@/api/monitor/cache"
import * as echarts from "echarts"
export default {
name: "Cache",
data() {
return {
// 统计命令信息
commandstats: null,
// 使用内存
usedmemory: null,
// cache信息
cache: []
}
},
created() {
this.getList()
this.openLoading()
},
methods: {
/** 查缓存询信息 */
getList() {
getCache().then((response) => {
this.cache = response.data
this.$modal.closeLoading()
this.commandstats = echarts.init(this.$refs.commandstats, "macarons")
this.commandstats.setOption({
tooltip: {
trigger: "item",
formatter: "{a} <br/>{b} : {c} ({d}%)",
},
series: [
{
name: "命令",
type: "pie",
roseType: "radius",
radius: [15, 95],
center: ["50%", "38%"],
data: response.data.commandStats,
animationEasing: "cubicInOut",
animationDuration: 1000,
}
]
})
this.usedmemory = echarts.init(this.$refs.usedmemory, "macarons")
this.usedmemory.setOption({
tooltip: {
formatter: "{b} <br/>{a} : " + this.cache.info.used_memory_human,
},
series: [
{
name: "峰值",
type: "gauge",
min: 0,
max: 1000,
detail: {
formatter: this.cache.info.used_memory_human,
},
data: [
{
value: parseFloat(this.cache.info.used_memory_human),
name: "内存消耗",
}
]
}
]
})
window.addEventListener("resize", () => {
this.commandstats.resize()
this.usedmemory.resize()
})
})
},
// 打开加载层
openLoading() {
this.$modal.loading("正在加载缓存监控数据,请稍候!")
}
}
}
</script>

View File

@@ -1,207 +1,207 @@
<template>
<div class="app-container">
<el-row :gutter="10">
<el-col :span="12" class="card-box">
<el-card>
<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">
<table cellspacing="0" style="width: 100%;">
<thead>
<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>
</tr>
</thead>
<tbody>
<tr>
<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>
</tr>
<tr>
<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>
</tr>
<tr>
<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>
</tr>
<tr>
<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>
</tr>
</tbody>
</table>
</div>
</el-card>
</el-col>
<el-col :span="12" class="card-box">
<el-card>
<div slot="header"><span><i class="el-icon-tickets"></i> 内存</span></div>
<div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%;">
<thead>
<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">JVM</div></th>
</tr>
</thead>
<tbody>
<tr>
<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.jvm">{{ server.jvm.total }}M</div></td>
</tr>
<tr>
<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.jvm">{{ server.jvm.used}}M</div></td>
</tr>
<tr>
<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.jvm">{{ server.jvm.free }}M</div></td>
</tr>
<tr>
<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.jvm" :class="{'text-danger': server.jvm.usage > 80}">{{ server.jvm.usage }}%</div></td>
</tr>
</tbody>
</table>
</div>
</el-card>
</el-col>
<el-col :span="24" class="card-box">
<el-card>
<div slot="header">
<span><i class="el-icon-monitor"></i> 服务器信息</span>
</div>
<div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%;">
<tbody>
<tr>
<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">操作系统</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.sys">{{ server.sys.osName }}</div></td>
</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" 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" v-if="server.sys">{{ server.sys.osArch }}</div></td>
</tr>
</tbody>
</table>
</div>
</el-card>
</el-col>
<el-col :span="24" class="card-box">
<el-card>
<div slot="header">
<span><i class="el-icon-coffee-cup"></i> Java虚拟机信息</span>
</div>
<div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%;table-layout:fixed;">
<tbody>
<tr>
<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">Java版本</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.version }}</div></td>
</tr>
<tr>
<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">运行时长</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.runTime }}</div></td>
</tr>
<tr>
<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>
</tr>
<tr>
<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>
</tr>
<tr>
<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>
</tr>
</tbody>
</table>
</div>
</el-card>
</el-col>
<el-col :span="24" class="card-box">
<el-card>
<div slot="header">
<span><i class="el-icon-receiving"></i> 磁盘状态</span>
</div>
<div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%;">
<thead>
<tr>
<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>
</tr>
</thead>
<tbody v-if="server.sysFiles">
<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.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.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.used }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell" :class="{'text-danger': sysFile.usage > 80}">{{ sysFile.usage }}%</div></td>
</tr>
</tbody>
</table>
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
import { getServer } from "@/api/monitor/server"
export default {
name: "Server",
data() {
return {
// 服务器信息
server: []
}
},
created() {
this.getList()
this.openLoading()
},
methods: {
/** 查询服务器信息 */
getList() {
getServer().then(response => {
this.server = response.data
this.$modal.closeLoading()
})
},
// 打开加载层
openLoading() {
this.$modal.loading("正在加载服务监控数据,请稍候!")
}
}
}
</script>
<template>
<div class="app-container">
<el-row :gutter="10">
<el-col :span="12" class="card-box">
<el-card>
<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">
<table cellspacing="0" style="width: 100%;">
<thead>
<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>
</tr>
</thead>
<tbody>
<tr>
<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>
</tr>
<tr>
<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>
</tr>
<tr>
<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>
</tr>
<tr>
<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>
</tr>
</tbody>
</table>
</div>
</el-card>
</el-col>
<el-col :span="12" class="card-box">
<el-card>
<div slot="header"><span><i class="el-icon-tickets"></i> 内存</span></div>
<div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%;">
<thead>
<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">JVM</div></th>
</tr>
</thead>
<tbody>
<tr>
<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.jvm">{{ server.jvm.total }}M</div></td>
</tr>
<tr>
<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.jvm">{{ server.jvm.used}}M</div></td>
</tr>
<tr>
<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.jvm">{{ server.jvm.free }}M</div></td>
</tr>
<tr>
<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.jvm" :class="{'text-danger': server.jvm.usage > 80}">{{ server.jvm.usage }}%</div></td>
</tr>
</tbody>
</table>
</div>
</el-card>
</el-col>
<el-col :span="24" class="card-box">
<el-card>
<div slot="header">
<span><i class="el-icon-monitor"></i> 服务器信息</span>
</div>
<div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%;">
<tbody>
<tr>
<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">操作系统</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.sys">{{ server.sys.osName }}</div></td>
</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" 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" v-if="server.sys">{{ server.sys.osArch }}</div></td>
</tr>
</tbody>
</table>
</div>
</el-card>
</el-col>
<el-col :span="24" class="card-box">
<el-card>
<div slot="header">
<span><i class="el-icon-coffee-cup"></i> Java虚拟机信息</span>
</div>
<div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%;table-layout:fixed;">
<tbody>
<tr>
<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">Java版本</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.version }}</div></td>
</tr>
<tr>
<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">运行时长</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.runTime }}</div></td>
</tr>
<tr>
<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>
</tr>
<tr>
<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>
</tr>
<tr>
<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>
</tr>
</tbody>
</table>
</div>
</el-card>
</el-col>
<el-col :span="24" class="card-box">
<el-card>
<div slot="header">
<span><i class="el-icon-receiving"></i> 磁盘状态</span>
</div>
<div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%;">
<thead>
<tr>
<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>
</tr>
</thead>
<tbody v-if="server.sysFiles">
<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.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.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.used }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell" :class="{'text-danger': sysFile.usage > 80}">{{ sysFile.usage }}%</div></td>
</tr>
</tbody>
</table>
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
import { getServer } from "@/api/monitor/server"
export default {
name: "Server",
data() {
return {
// 服务器信息
server: []
}
},
created() {
this.getList()
this.openLoading()
},
methods: {
/** 查询服务器信息 */
getList() {
getServer().then(response => {
this.server = response.data
this.$modal.closeLoading()
})
},
// 打开加载层
openLoading() {
this.$modal.loading("正在加载服务监控数据,请稍候!")
}
}
}
</script>

View File

@@ -527,7 +527,7 @@ export default {
},
/** 选择角色权限范围触发 */
dataScopeSelectChange(value) {
if(value !== '2') {
if (value !== '2') {
this.$refs.dept.setCheckedKeys([])
}
},

View File

@@ -316,7 +316,7 @@ export default {
},
/** 选择生成模板触发 */
tplSelectChange(value) {
if(value !== 'sub') {
if (value !== 'sub') {
this.info.subTableName = ''
this.info.subTableFkName = ''
}

View File

@@ -262,7 +262,7 @@ export default {
this.$modal.msgError("请选择要生成的数据")
return
}
if(row.genType === "1") {
if (row.genType === "1") {
genCode(row.tableName).then(() => {
this.$modal.msgSuccess("成功生成到自定义路径:" + row.genPath)
})