221 Commits

Author SHA1 Message Date
RuoYi
b99039b609 优化页签滚动条层级 2026-06-10 22:33:46 +08:00
RuoYi
f6763c1ab6 升级项目相关依赖到最新 2026-06-10 19:29:38 +08:00
RuoYi
9213f18332 优化代码 2026-06-10 19:29:28 +08:00
RuoYi
8f8b4cd50b 禁止html后缀文件上传 2026-06-10 19:29:10 +08:00
RuoYi
0aafd12699 用户密码支持自定义配置规则 2026-04-17 13:12:22 +08:00
RuoYi
7f901d79ec 角色权限变更后刷新所有持有该角色的在线用户权限 2026-04-16 16:34:27 +08:00
RuoYi
9509819297 优化代码生成同步操作column_type没更新问题 2026-04-16 14:24:26 +08:00
RuoYi
613d0bbc94 优化白名单支持对通配符路径匹配 2026-04-16 13:43:39 +08:00
RuoYi
b33d43840e 通知公告新增阅读用户列表 2026-04-14 16:12:36 +08:00
RuoYi
795f975aa4 通知公告新增阅读用户列表 2026-04-14 15:40:13 +08:00
RuoYi
0432efbcc6 通知公告新增详细显示 2026-04-14 14:37:21 +08:00
RuoYi
e3d5fb66eb 新增标签页样式chrome风格 2026-04-13 00:40:35 +08:00
RuoYi
9c63b051b8 新增代码生成详情页功能 2026-04-12 09:57:59 +08:00
RuoYi
e6354dae14 自动导入配置 2026-04-10 11:01:04 +08:00
RuoYi
4960e13e5b 代码生成修改拖拽时显示手指样式 2026-04-10 10:59:55 +08:00
RuoYi
60b6e2aee8 用户列表新增抽屉效果详细信息 2026-04-09 12:48:08 +08:00
RuoYi
b35c25db2c 优化样式 2026-04-03 22:32:51 +08:00
RuoYi
e8d5c496fa 数据权限注解支持自定义字段名 2026-04-03 15:39:00 +08:00
RuoYi
6b7b3ffc15 新增Excel导入组件ExcelImportDialog 2026-04-03 08:55:39 +08:00
RuoYi
50560a49a8 update tree-panel ref 2026-04-01 21:48:28 +08:00
RuoYi
7975bf5ee8 新增树分割组件TreePanel 2026-04-01 21:35:07 +08:00
RuoYi
83cfba6fda 支持表格列显隐状态记忆 2026-03-31 19:23:33 +08:00
RuoYi
d3e60cdc04 支持多sheet导出 2026-03-31 16:31:37 +08:00
RuoYi
af1ebb77a0 代码生成支持表单布局选项 2026-03-30 16:49:22 +08:00
RuoYi
6b1815fb7e update sqlkeyword 2026-03-30 16:48:29 +08:00
RuoYi
99345f5ed2 若依 3.9.2 2026-03-26 08:22:41 +08:00
RuoYi
d8b5dc3533 优化页签全屏显示 2026-03-26 00:53:01 +08:00
RuoYi
595ef23933 优化快速点击页签刷新出现404问题 2026-03-25 21:22:28 +08:00
RuoYi
c2cee3cf33 修复typescript版多表代码生成报错问题 2026-03-25 12:33:45 +08:00
RuoYi
75ff7774e2 菜单管理列表新增类型显示 2026-03-24 15:33:49 +08:00
RuoYi
9927538189 优化菜单主题风格 2026-03-24 10:31:21 +08:00
RuoYi
7ae9d76793 升级axios到最新版本0.30.3 2026-03-23 11:45:00 +08:00
RuoYi
687715caf1 优化页签显示 2026-03-23 11:43:29 +08:00
RuoYi
78cc7fcbb8 添加持久化标签页开关功能 2026-03-22 23:29:44 +08:00
RuoYi
5fea654e5f 添加持久化标签页开关功能 2026-03-22 21:04:46 +08:00
RuoYi
c36496cc67 菜单搜索支持文本高亮&数量提示 2026-03-22 16:54:50 +08:00
RuoYi
58328fc477 优化tag全屏为页签内区域 2026-03-22 15:02:17 +08:00
RuoYi
0e3269d14e 页签左右滚动效果兼容所有环境不依赖behavior 2026-03-22 14:45:20 +08:00
RuoYi
ac784e1eb8 优化页签功能&支持全屏按钮操作 2026-03-22 13:59:14 +08:00
RuoYi
49629a3157 保存排序添加编辑权限 2026-03-21 19:10:09 +08:00
RuoYi
198dec87b4 优化点击任务名称查看详细 2026-03-21 14:28:36 +08:00
RuoYi
dc036af586 字典类型列表新增抽屉效果详细信息 2026-03-21 13:40:19 +08:00
RuoYi
9b5a8281af 菜单管理支持批量保存排序 2026-03-21 13:40:04 +08:00
RuoYi
752647f4be 菜单管理支持批量保存排序 2026-03-21 12:42:16 +08:00
RuoYi
a25128fe03 部门管理支持批量保存排序 2026-03-21 11:41:55 +08:00
RuoYi
2a11781ef8 新增锁定屏幕功能 2026-03-20 21:52:09 +08:00
RuoYi
f1cf8e44c1 新增锁定屏幕功能 2026-03-20 20:37:07 +08:00
RuoYi
761af45ec6 优化定时任务详情页展示&补充执行时间字段 2026-03-20 16:38:20 +08:00
RuoYi
d554cbc7e9 操作日志详细页面优化 2026-03-20 13:05:58 +08:00
RuoYi
14c0506bc3 首页新增通知公告消息提醒 2026-03-20 10:37:14 +08:00
RuoYi
670dddf08a 优化RightToolbar搜索栏切换动画 2026-03-19 19:42:06 +08:00
RuoYi
4fd277c38f TypeScript前端代码生成模板同步到最新 2026-03-18 13:06:10 +08:00
RuoYi
4fd53ffef3 TypeScript前端代码生成模板同步到最新 2026-03-12 12:26:21 +08:00
RuoYi
929fe2b802 update README.md 2026-03-10 16:45:06 +08:00
RuoYi
b9052413f7 update pom.xml 2026-03-10 16:44:42 +08:00
RuoYi
0d6c2af081 update dependencies 2026-03-09 16:30:55 +08:00
RuoYi
85d8a2fa80 update deprecated 2026-03-09 16:30:20 +08:00
RuoYi
f82ea7942a 优化Excel自定义格式样式重复创建问题 2026-03-09 15:04:55 +08:00
RuoYi
d76f52b5ca 删除无用的注解 2026-03-01 11:23:45 +08:00
RuoYi
4c75cc4062 update address url 2026-02-26 10:14:07 +08:00
RuoYi
fa18b9cf8b 修复字典类型输入框提示信息 2026-02-11 15:35:32 +08:00
RuoYi
1f7b712dc5 优化字典类型属性提醒说明 2026-02-05 12:45:58 +08:00
RuoYi
c0e188e414 update README.md 2026-01-28 14:28:30 +08:00
RuoYi
e8945ad50e update README.md 2026-01-28 14:20:39 +08:00
RuoYi
b1bc7033c4 添加新群号:127358632 2026-01-28 14:19:10 +08:00
RuoYi
604b6877f0 优化代码 2026-01-28 14:18:51 +08:00
RuoYi
4a0faad4bc 添加菜单路由地址和名称的校验规则 2026-01-09 11:14:38 +08:00
RuoYi
eafe812ae7 优化防重提交间隔时间可自定义 2026-01-08 13:39:32 +08:00
RuoYi
70d125a6f4 修复Excel自定义格式样式污染问题 2026-01-06 13:53:23 +08:00
RuoYi
493dd513e5 copyright 2026 2026-01-04 16:08:32 +08:00
RuoYi
8df58e7512 将isAdmin方法统一到SecurityUtils 2026-01-04 16:07:06 +08:00
RuoYi
6a35245aaf 修正UserAgent解析逻辑,正确设置浏览器和操作系统字段 2025-12-31 09:29:10 +08:00
RuoYi
b2cd0b51c2 修正UserAgent解析逻辑,正确设置浏览器和操作系统字段 2025-12-21 10:51:25 +08:00
RuoYi
a0be25948d 优化topbar顶部菜单样式 2025-12-18 14:00:02 +08:00
RuoYi
ab1bbf21ed 若依 3.9.1 2025-12-18 09:06:24 +08:00
RuoYi
2c82a847eb 默认固定头部 2025-12-18 09:05:51 +08:00
RuoYi
2b3b29a4a3 优化字典组件值宽松匹配 2025-12-16 16:41:56 +08:00
RuoYi
f57dea5332 菜单导航设置支持纯顶部 2025-12-16 13:02:05 +08:00
RuoYi
fab921a1d2 升级quartz到最新版本2.5.2 2025-12-12 11:04:33 +08:00
RuoYi
336a47660f 升级spring-boot到最新版本3.5.8 2025-12-11 14:40:16 +08:00
RuoYi
575b1b727f 升级spring-boot-mybatis到最新版3.0.5 2025-12-11 14:37:58 +08:00
RuoYi
542979e703 升级springdoc到最新版本2.8.14 2025-12-11 14:37:19 +08:00
RuoYi
c2202fee27 升级druid到最新版本1.2.27 2025-12-11 14:34:31 +08:00
RuoYi
17a726942f 升级oshi到最新版本6.9.1 2025-12-11 14:34:14 +08:00
RuoYi
362db4a071 升级commons.io到最新版本2.21.0 2025-12-11 14:33:38 +08:00
RuoYi
771b9af7c0 升级fastjson到最新版2.0.60 2025-12-11 14:33:14 +08:00
RuoYi
7a787a919e update sqlkeyword 2025-12-11 14:33:05 +08:00
RuoYi
8fdbd05ce7 使用yauaa代替bitwalker 2025-12-09 14:29:23 +08:00
RuoYi
970275755d 优化用户密码字段序列化配置 2025-12-05 14:59:52 +08:00
RuoYi
516116f125 优化数据权限控制逻辑,放开permission限制 2025-12-04 17:33:15 +08:00
RuoYi
d3493e94d6 支持Excel导出对象的多个子列表 2025-12-04 16:32:48 +08:00
RuoYi
3beb3ae3f9 优化表单构建关闭页签销毁复制插件 2025-12-04 13:53:57 +08:00
RuoYi
f77c0ec97d 修复优雅停机出现的冲突问题 2025-12-04 13:51:36 +08:00
RuoYi
55854ec195 忽略用户密码字段的JSON序列化 2025-12-03 14:38:31 +08:00
RuoYi
088e9afbc9 优化代码 2025-12-03 11:48:40 +08:00
RuoYi
456e1534ee 优化生成代码下载的zip文件名 2025-12-03 10:27:04 +08:00
RuoYi
352ed6eee7 网页标题设置新增SET_TITLE方法 2025-12-02 19:30:01 +08:00
RuoYi
0cb12f5440 支持Excel导出对象的多个子列表 2025-12-02 19:13:23 +08:00
RuoYi
169ca65ed3 登录/注册页面底部版权信息修改为读取配置 2025-12-02 15:29:35 +08:00
RuoYi
cd6c2fc5f1 修复v3时间控件between选择后清空报错问题 2025-12-02 14:57:45 +08:00
RuoYi
e24918b054 修复表单构建移除所有控件后切换路由回来空白问题 2025-12-02 13:08:27 +08:00
RuoYi
2d36603c46 修复comboReadDict属性下多个sheet出现的报错(ICWQ8E) 2025-11-13 11:34:45 +08:00
RuoYi
964569f715 update ruoyi-admin pom.xml 2025-10-14 12:14:12 +08:00
RuoYi
e37b8ff9b8 添加新群号:174569686 2025-10-05 20:11:22 +08:00
RuoYi
5896895bab 升级fastjson到最新版2.0.58 2025-09-05 09:17:39 +08:00
RuoYi
0c8e9ad74e 修复固定头部时出现的导航栏偏移问题(ICV9OH) 2025-09-04 19:58:58 +08:00
RuoYi
f0346b3def 支持防盗链功能 2025-09-02 11:31:18 +08:00
RuoYi
d1cdbbd22c 优化代码 2025-09-02 08:53:31 +08:00
RuoYi
e2badb7c68 升级oshi到最新版本6.8.3 2025-08-28 13:38:19 +08:00
RuoYi
40f54f9700 优化代码 2025-08-28 13:37:57 +08:00
RuoYi
534bd7b8cd 优化代码 2025-08-27 15:34:47 +08:00
RuoYi
6c811c88c2 用户导入添加验证提示 2025-08-23 11:14:45 +08:00
RuoYi
eed3ae8f80 优化布局设置显示 2025-08-23 11:14:36 +08:00
RuoYi
5aa5f42042 修复用户归属部门无法修改为空问题 2025-08-21 14:48:00 +08:00
RuoYi
29e4eae4e2 columns default value 2025-08-09 16:12:51 +08:00
RuoYi
84e1f8a210 显示列信息支持对象格式 2025-08-09 13:23:50 +08:00
RuoYi
2eceac52cf 自动识别json对象白名单配置范围缩小 2025-08-09 10:57:40 +08:00
RuoYi
624c52b568 升级spring-boot到最新版本3.5.4 2025-08-07 15:12:17 +08:00
RuoYi
0e698bcd5b 升级pagehelper到最新版2.1.1 2025-08-07 15:11:35 +08:00
RuoYi
c044ec0bb8 升级oshi到最新版本6.8.2 2025-08-07 15:10:41 +08:00
RuoYi
c6d9a03f9f 添加新群号:191164766 2025-06-20 11:39:46 +08:00
RuoYi
3030c3fa11 优化定时任务包名白名单匹配方式 2025-06-20 11:34:35 +08:00
RuoYi
9dfea3c77f 优化Excel统计行数值的单元格样式显示 2025-06-19 14:48:04 +08:00
RuoYi
ada072c767 用户头像更换后移除旧头像文件 2025-06-06 14:58:29 +08:00
RuoYi
c8a47f8b39 若依 3.9.0 2025-05-28 09:04:44 +08:00
RuoYi
92f8a44f67 注册账号设置默认密码最后更新时间 2025-05-26 10:58:19 +08:00
RuoYi
e0bfca5bdb 升级fastjson到最新版2.0.57 2025-05-26 09:00:13 +08:00
RuoYi
3f753cd801 添加底部版权信息及开关 2025-05-24 14:24:46 +08:00
RuoYi
f3bdf8c7e8 添加页签图标显示开关功能 2025-05-23 14:57:21 +08:00
RuoYi
9a37444e68 update pwdUpdateDate 2025-05-23 10:45:05 +08:00
RuoYi
e43efd179c 账号密码支持自定义更新周期 2025-05-23 09:06:07 +08:00
RuoYi
99e9f21253 初始密码支持自定义修改策略 2025-05-22 23:07:18 +08:00
RuoYi
6ee698b1ab 升级oshi到最新版本6.8.1 2025-05-15 09:14:42 +08:00
RuoYi
992537f343 升级commons.io到最新版本2.19.0 2025-05-15 09:14:23 +08:00
RuoYi
fbd5052f99 delete eslint&vue-meta 2025-05-15 09:14:02 +08:00
RuoYi
f8a918c62c 优化导航栏显示昵称&设置 2025-05-09 13:53:05 +08:00
RuoYi
df0954d5df 菜单搜索支持键盘选择&悬浮主题背景 2025-05-07 13:23:35 +08:00
RuoYi
8af2aa929a 图片上传组件新增disabled属性 2025-05-06 19:13:58 +08:00
RuoYi
fb7743de1c add columnName Drag 2025-05-06 14:53:17 +08:00
RuoYi
eefe15e36b 修复上传组件被多次引用拖动仅对第一个有效的问题 2025-05-06 13:10:37 +08:00
RuoYi
b6874d4320 update icon 2025-05-06 11:08:15 +08:00
RuoYi
6d00fc8603 上传组件新增拖动排序属性 2025-04-30 10:28:30 +08:00
RuoYi
00b2eeb4c3 优化Excel匹配数值型.0结尾 2025-04-28 11:24:35 +08:00
RuoYi
838fc115ca update editor index 2025-04-27 14:03:01 +08:00
RuoYi
62630a5a95 remove all semicolons 2025-04-27 10:05:55 +08:00
RuoYi
f30e737be2 Excel导入导出支持多图片 2025-04-25 10:09:02 +08:00
RuoYi
5069654cb7 富文本复制粘贴图片上传至url 2025-04-24 14:24:20 +08:00
RuoYi
fc6b8eefae update package.json 2025-04-24 11:08:59 +08:00
RuoYi
d6c22b8a42 优化低版本node无法启动的问题 2025-04-22 12:06:28 +08:00
RuoYi
33303f13d9 优化代码 2025-04-22 12:06:12 +08:00
RuoYi
0154365cc0 显隐列组件支持全选/全不选 2025-04-21 15:22:19 +08:00
RuoYi
2cbae4126d 优化菜单搜索查询页 2025-04-21 13:22:49 +08:00
RuoYi
a8ba5b1b0e 支持文件&图片组件自定义地址&参数 2025-04-18 12:57:34 +08:00
RuoYi
cf59bb007d 添加新群号:287842588 2025-04-18 12:57:09 +08:00
RuoYi
fd2568b0e7 springdoc proxy 2025-04-17 15:26:23 +08:00
RuoYi
7ab047745f 优化角色禁用不允许分配 2025-04-17 15:24:35 +08:00
RuoYi
3d09ea31fc update status name 2025-04-17 15:24:08 +08:00
RuoYi
0188c81853 添加新群号:287842588 2025-04-01 19:14:31 +08:00
RuoYi
e39629472c remove dev runjs 2025-03-18 16:06:40 +08:00
RuoYi
df911695f0 登录页和注册页表头使用VUE_APP_TITLE配置值 2025-03-18 16:05:14 +08:00
RuoYi
13b2f5149b 优化代码 2025-03-14 16:11:22 +08:00
RuoYi
495e640888 菜单管理新增路由名称 2025-03-06 11:03:43 +08:00
RuoYi
3a224461d8 优化代码 2025-03-04 20:16:53 +08:00
RuoYi
5fdd49582b 优化顶部菜单搜索栏为多层级显示(IBESXH) 2025-03-03 12:08:36 +08:00
RuoYi
e763e67c61 优化导出Excel日期格式双击离开后与设定的格式不一致问题 2025-03-01 15:33:49 +08:00
RuoYi
9962534193 优化代码 2025-03-01 15:33:07 +08:00
RuoYi
dd1f6a7b9e pagination更换成flex布局 2025-03-01 15:29:58 +08:00
RuoYi
7eb86458cc 优化前端处理路由函数代码 2025-03-01 15:29:42 +08:00
RuoYi
d3de651360 优化前端树结构性能问题 2025-03-01 15:29:13 +08:00
RuoYi
637a09cfa9 修复代码生成主子表校验必填失效问题 2025-02-28 21:53:15 +08:00
RuoYi
499648c90e 代码生成列表支持按时间排序 2025-02-28 19:44:41 +08:00
RuoYi
896c0a23de 文件上传组件新增类型 2025-02-28 19:44:23 +08:00
RuoYi
50e1a727d4 优化空指针异常时无法获取错误信息问题 2025-02-28 19:44:04 +08:00
RuoYi
af80841751 文件上传组件新增disabled属性 2025-02-28 13:03:50 +08:00
RuoYi
4bbe3f3b56 优化代码 2025-02-28 13:03:33 +08:00
RuoYi
57293a2ecf copyright 2025 2025-01-07 10:53:33 +08:00
RuoYi
98cdcae01f 若依 3.8.9 2024-12-30 08:54:15 +08:00
RuoYi
927ad037a2 代码生成新增配置是否允许文件覆盖到本地 2024-12-25 16:00:25 +08:00
RuoYi
8a672152f8 优化导入带标题文件关闭清理 2024-12-25 16:00:06 +08:00
RuoYi
1883a40dcf update sqlkeyword 2024-12-25 00:07:36 +08:00
RuoYi
8b63f468b4 优化特殊字符密码修改失败问题 2024-12-17 14:35:30 +08:00
RuoYi
bfc6fe5ae1 优化TopNav内链菜单点击没有高亮(IB8WHJ) 2024-12-17 14:35:13 +08:00
RuoYi
45e8c51751 优化菜单管理切换Mini布局错乱问题 2024-12-17 14:35:02 +08:00
RuoYi
353476b88c update README 2024-12-17 14:34:48 +08:00
RuoYi
db2f438279 用户管理过滤掉已禁用部门(IB5H7F) 2024-12-11 11:48:44 +08:00
RuoYi
739a1c262d 修改主题样式本地读取 2024-12-10 16:41:20 +08:00
RuoYi
88f620c44d 白名单支持对通配符路径匹配 2024-12-07 14:41:58 +08:00
RuoYi
aec1a93e16 Excel注解支持wrapText是否允许内容换行 2024-12-07 14:41:35 +08:00
RuoYi
ed96bf80e7 修复导出子列表对象只能在最后的问题 2024-12-07 14:41:17 +08:00
RuoYi
4a9011a727 修复默认关闭Tags-Views时,内链页面打不开 2024-11-27 19:59:11 +08:00
RuoYi
8e99b826fa 修复TopNav无法正确获取active的问题 2024-11-27 19:58:58 +08:00
RuoYi
c5615001c8 菜单面包屑导航支持多层级显示 2024-11-25 22:35:42 +08:00
RuoYi
b376ab792a 优化代码 2024-11-25 22:35:19 +08:00
RuoYi
0ca6619a80 分栏参数微调 2024-11-22 14:51:08 +08:00
RuoYi
a691f28f30 用户管理支持分栏拖动 2024-11-22 14:10:36 +08:00
RuoYi
32f0636d46 用户头像http(s)链接支持 2024-11-22 14:10:15 +08:00
RuoYi
4c013a4f73 update .env.staging 2024-11-22 14:09:34 +08:00
RuoYi
a69a436159 支持自定义显示Excel属性列 2024-11-07 22:48:40 +08:00
RuoYi
8ba8524250 升级spring-boot到最新版本3.3.5 2024-11-06 21:40:02 +08:00
RuoYi
bb74a620ba 升级springdoc到最新版本2.6.0 2024-11-06 21:39:47 +08:00
RuoYi
ff8ded2704 升级oshi到最新版本6.6.5 2024-11-05 16:40:36 +08:00
RuoYi
fa8b909408 优化无用户编号不校验数据权限 2024-11-05 16:39:55 +08:00
RuoYi
433f588747 校检文件名是否包含特殊字符 2024-11-05 16:39:14 +08:00
RuoYi
35c1f02392 优化身份证脱敏正则 2024-10-21 16:47:53 +08:00
RuoYi
1a7bcc10bd 优化权限更新后同步缓存 2024-10-21 16:47:44 +08:00
RuoYi
4008d1f30d 优化上传图片带域名不增加前缀 2024-10-21 16:44:57 +08:00
RuoYi
27e07c6a80 升级quill到最新版本2.0.2 2024-10-21 16:44:20 +08:00
RuoYi
587858abe3 升级axios到最新版本0.28.1 2024-10-21 16:43:52 +08:00
RuoYi
0adc70d510 操作日志记录DELETE请求参数(IAMV6F) 2024-10-17 13:09:23 +08:00
RuoYi
4e1ec5e98e 升级fastjson到最新版2.0.53 2024-10-17 13:09:09 +08:00
RuoYi
7d0724b9bf 修复码生成上级菜单显示问题(I9CTIJ) 2024-10-17 13:08:04 +08:00
RuoYi
0bbb8b7c06 修复角色禁用权限不失效问题(IAA8ZX) 2024-09-21 11:30:57 +08:00
RuoYi
19d7e347e4 优化代码 2024-09-08 10:50:55 +08:00
RuoYi
9369590f57 升级oshi到最新版本6.6.3 2024-08-30 21:49:32 +08:00
RuoYi
16af81344c update sqlkeyword 2024-08-30 21:49:23 +08:00
RuoYi
6fc05bc616 修改时间范围日期格式 2024-07-08 16:55:23 +08:00
RuoYi
b84d5554c2 remove sub resultType 2024-07-08 16:55:06 +08:00
RuoYi
583f057de6 若依 3.8.8 2024-06-30 08:11:13 +08:00
RuoYi
232d228e52 菜单管理新增路由名称 2024-06-29 19:01:48 +08:00
RuoYi
92ea278242 删除多余的依赖 2024-06-27 17:34:19 +08:00
RuoYi
534f7d13d3 update springboot3 2024-06-27 16:03:30 +08:00
21 changed files with 582 additions and 800 deletions

View File

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

10
pom.xml
View File

@@ -17,12 +17,12 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version>
<spring-boot.version>4.0.6</spring-boot.version>
<mybatis-spring-boot.version>4.0.1</mybatis-spring-boot.version>
<spring-boot.version>3.5.14</spring-boot.version>
<mybatis-spring-boot.version>3.0.5</mybatis-spring-boot.version>
<druid.version>1.2.28</druid.version>
<yauaa.version>8.1.1</yauaa.version>
<kaptcha.version>2.3.3</kaptcha.version>
<pagehelper.boot.version>4.1.0</pagehelper.boot.version>
<pagehelper.boot.version>2.1.1</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>
@@ -30,7 +30,7 @@
<velocity.version>2.3</velocity.version>
<jwt.version>0.9.1</jwt.version>
<jaxb-api.version>2.3.1</jaxb-api.version>
<springdoc.version>3.0.3</springdoc.version>
<springdoc.version>2.8.17</springdoc.version>
</properties>
<!-- 依赖声明 -->
@@ -49,7 +49,7 @@
<!-- 阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-4-starter</artifactId>
<artifactId>druid-spring-boot-3-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.jdbc.autoconfigure.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
/**
* 启动程序

View File

@@ -60,9 +60,6 @@ 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>tools.jackson.core</groupId>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
@@ -95,11 +95,6 @@
<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 tools.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.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, ElementType.METHOD })
@Target(ElementType.FIELD)
@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 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.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 com.ruoyi.common.annotation.Sensitive;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.DesensitizedType;
@@ -18,26 +18,14 @@ import com.ruoyi.common.utils.SecurityUtils;
*
* @author ruoyi
*/
public class SensitiveJsonSerializer extends StdSerializer<String>
public class SensitiveJsonSerializer extends JsonSerializer<String> implements ContextualSerializer
{
private final DesensitizedType desensitizedType;
public SensitiveJsonSerializer()
{
super(String.class);
this.desensitizedType = null;
}
public SensitiveJsonSerializer(DesensitizedType desensitizedType)
{
super(String.class);
this.desensitizedType = desensitizedType;
}
private DesensitizedType desensitizedType;
@Override
public void serialize(String value, JsonGenerator gen, SerializationContext ctxt) throws JacksonException
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException
{
if (desensitizedType != null && desensitization())
if (desensitization())
{
gen.writeString(desensitizedType.desensitizer().apply(value));
}
@@ -48,14 +36,16 @@ public class SensitiveJsonSerializer extends StdSerializer<String>
}
@Override
public ValueSerializer<?> createContextual(SerializationContext ctxt, BeanProperty property) throws DatabindException
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
throws JsonMappingException
{
Sensitive annotation = property.getAnnotation(Sensitive.class);
if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass()))
{
return new SensitiveJsonSerializer(annotation.desensitizedType());
this.desensitizedType = annotation.desensitizedType();
return this;
}
return ctxt.findValueSerializer(property.getType());
return prov.findValueSerializer(property.getType(), property);
}
/**

View File

@@ -1,15 +1,11 @@
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;
/**
* 登录用户身份权限
@@ -263,16 +259,8 @@ public class LoginUser implements UserDetails
}
@Override
@JSONField(serialize = false)
public Collection<? extends GrantedAuthority> getAuthorities()
{
if (permissions == null || permissions.isEmpty())
{
return Collections.emptyList();
}
return permissions.stream()
.filter(Objects::nonNull)
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
return null;
}
}

View File

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

View File

@@ -6,7 +6,6 @@ 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;
@@ -434,24 +433,12 @@ 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 结果
* @return boolean 结果
*/
public static boolean containsAny(Collection<String> collection, String... array)
{
@@ -472,18 +459,6 @@ 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);
}
/**
* 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写
*
@@ -507,163 +482,6 @@ 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);
}
/**
* 驼峰转下划线命名
*/
@@ -735,22 +553,6 @@ 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

@@ -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-webmvc</artifactId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- SpringBoot 拦截器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aspectj</artifactId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- 阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-4-starter</artifactId>
<artifactId>druid-spring-boot-3-starter</artifactId>
</dependency>
<!-- 验证码 -->

View File

@@ -1,6 +1,9 @@
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;
@@ -16,4 +19,12 @@ 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.boot4.autoconfigure.DruidDataSourceBuilder;
import com.alibaba.druid.spring.boot4.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceBuilder;
import com.alibaba.druid.spring.boot3.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,
BasicThreadFactory.builder().namingPattern("schedule-pool-%d").daemon(true).build(),
new ThreadPoolExecutor.CallerRunsPolicy())
new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(),
new ThreadPoolExecutor.CallerRunsPolicy())
{
@Override
protected void afterExecute(Runnable r, Throwable t)

View File

@@ -34,7 +34,6 @@ public class PermitAllUrlProperties implements InitializingBean, ApplicationCont
public String ASTERISK = "*";
@SuppressWarnings("deprecation")
@Override
public void afterPropertiesSet()
{
@@ -46,7 +45,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-4-starter</artifactId>
<artifactId>druid-spring-boot-3-starter</artifactId>
</dependency>
</dependencies>

View File

@@ -19,8 +19,8 @@
<!-- 定时任务 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
<groupId>org.quartz-scheduler</groupId>
<artifactId>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,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>