135 Commits

Author SHA1 Message Date
5e64a93d11 若依 3.5.0 2021-05-25 09:37:55 +08:00
4aabf5d8be Redis设置HashKey序列化 2021-05-24 15:10:55 +08:00
cf6254a8d5 用户登录后记录最后登录IP&时间 2021-05-24 14:21:29 +08:00
883cff5de9 新增IE浏览器版本过低提示页面 2021-05-24 14:15:41 +08:00
7e79c4f249 生成vue模板导出按钮点击后添加遮罩 2021-05-24 11:34:03 +08:00
504638eb41 !234 【轻量级 PR】:删去两处冗余代码
Merge pull request !234 from xivLi/master
2021-05-24 11:30:21 +08:00
2adac4a899 升级fastjson到最新版1.2.76 2021-05-24 11:25:17 +08:00
56a943bf48 升级druid到最新版本v1.2.6 2021-05-24 11:24:54 +08:00
77ebca264a 修复请求形参未传值记录日志异常问题 2021-05-23 19:22:30 +08:00
7b94ae3a9a xss校验json条件优化 2021-05-23 19:20:36 +08:00
bd1edc6dcc !233 修正方法名单词拼写错误
Merge pull request !233 from lyqwer/N/A
2021-05-23 17:15:18 +08:00
09d166e97d !231 修正注释
Merge pull request !231 from 刘立伟/N/A
2021-05-23 17:14:52 +08:00
f3e5d908d6 quartz模块下 domain包中的 SysJob类不需要实现Serializable接口 2021-05-22 16:05:12 +08:00
9651a7d7fc 删去utils.uuid下 IdUtils类中 多余的import(同包下的UUID类) 2021-05-22 15:52:19 +08:00
b2914cbcb1 修正方法名单词拼写错误 2021-05-21 17:24:48 +08:00
d0a4d6b111 修正注释 2021-05-18 10:21:48 +08:00
b431703262 导出按钮点击之后添加遮罩 2021-05-17 15:37:58 +08:00
99726be9ac 修正导入表权限标识 2021-05-11 14:26:20 +08:00
32f333a00e 删除操作日志记录日志 2021-05-11 14:25:39 +08:00
be5c19b764 树级结构更新子节点使用replaceFirst 2021-05-11 14:25:25 +08:00
dd384e4a31 上传媒体类型添加视频格式 2021-05-11 14:25:08 +08:00
bdde195e2b !225 【bug修复】文件上传时出现java.nio.file.FileAlreadyExistsException
Merge pull request !225 from CANYON/master
2021-05-11 14:19:53 +08:00
14ea071306 修复文件上传时java.nio.file.FileAlreadyExistsException 2021-05-08 15:31:16 +08:00
50034301ac 添加新群号:201396349 2021-05-06 20:40:25 +08:00
85ed712c50 !222 update ruoyi-ui/src/assets/styles/element-ui.scss.
Merge pull request !222 from leizhuogogo/N/A
2021-05-06 20:39:36 +08:00
f67d682345 !221 update ruoyi-common/src/main/java/com/ruoyi/common/utils/Arith.java.
Merge pull request !221 from phper08/N/A
2021-05-06 20:39:26 +08:00
3f1427eef9 update ruoyi-ui/src/assets/styles/element-ui.scss. 2021-05-06 17:50:28 +08:00
7d0f5e94ef update ruoyi-common/src/main/java/com/ruoyi/common/utils/Arith.java. 2021-05-03 13:18:33 +08:00
62081aebb9 修复开启TopNav后,左侧打开外链问题 2021-04-21 16:34:44 +08:00
3f07632cd4 修复一级菜单包屑显示重复问题 2021-04-21 15:14:36 +08:00
2c94587263 优化ExcelUtil空值处理 2021-04-21 09:53:14 +08:00
2575c17f47 主题颜色保存配置 2021-04-19 16:59:56 +08:00
b6f13c546b 过滤BindingResult对象,防止异常 2021-04-18 21:54:53 +08:00
e60a69b670 兼容顶部栏一级菜单内部跳转 2021-04-18 18:33:13 +08:00
cb18eec802 修正模板字符编码 2021-04-18 15:45:49 +08:00
bef080c60a 升级mybatis到最新版3.5.6 阻止远程代码执行漏洞 2021-04-18 15:45:37 +08:00
de73cf300b 优化树表代码生成模板 2021-04-15 10:39:09 +08:00
cbcee86d2c 优化树表代码生成模板 2021-04-15 10:37:22 +08:00
cc50224d90 固定顶部导航栏&窗口大小改变实时更新栏数 2021-04-14 11:01:16 +08:00
ff09e1cf55 数据监控默认地址修改 2021-04-13 18:15:11 +08:00
fe2ccbdc1b 优化代码生成导出模板名称 2021-04-13 14:49:42 +08:00
c8df1f5e1f 布局设置支持保存&重置配置 2021-04-13 09:47:28 +08:00
e71c00e6fa 富文本编辑器支持自定义上传地址 2021-04-13 09:38:32 +08:00
dcb9839596 !209 导出 Excel 工作表的名称 由 ${businessName} 更改为 ${functionName}
Merge pull request !209 from lihy2021/N/A
2021-04-13 09:36:49 +08:00
f13b6d92d2 !205 富文本编辑器自定义上传地址
Merge pull request !205 from hechieh/N/A
2021-04-13 09:36:42 +08:00
6595c68df1 导出 Excel 工作表的名称 由 ${businessName} 更改为 ${functionName} 2021-04-12 14:52:14 +08:00
5a2a0c09b6 优化主子表代码生成 2021-04-12 13:26:54 +08:00
9aac65ff32 新增菜单导航显示风格TopNav(false为左侧导航菜单,true为顶部导航菜单) 2021-04-12 09:54:08 +08:00
5f2350569a 页签新增关闭右侧 2021-04-10 21:15:45 +08:00
ccf05b697d 富文本编辑器自定义上传地址 2021-04-10 17:08:49 +08:00
6810243ab7 修复树表数据显示不全&加载慢问题 2021-04-09 08:31:13 +08:00
b56b8846d9 修改主题后mini类型按钮无效问题 2021-04-08 13:54:48 +08:00
8e1e4cd8fe 通用下载完成后删除节点 2021-04-08 13:46:22 +08:00
9fa5c79713 !200 update ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java.
Merge pull request !200 from yvan7123/N/A
2021-04-08 13:42:30 +08:00
40a72c2b7e !199 规范命名、用户角色单个逻辑删除、去除多余代码
Merge pull request !199 from fuzui/some_problems
2021-04-08 13:40:28 +08:00
6fe9a358b7 update ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java. 2021-04-06 11:47:27 +08:00
9f6bc13e64 🔥 去除多余代码 2021-04-02 23:06:48 +08:00
ba5ab4b091 用户、角色单条删除时,使其逻辑删除 2021-04-02 22:49:19 +08:00
17407d0127 调整cache_repeat_key为驼峰格式 2021-04-02 22:40:15 +08:00
30f330f4a0 修复firefox下表单构建拖拽会新打卡一个选项卡 2021-03-30 17:45:35 +08:00
6ca7870393 修正注释 2021-03-30 15:30:18 +08:00
2a7342ec43 !195 读取excel增加非空判断
Merge pull request !195 from 叫我宏锅锅/yuhong
2021-03-30 15:23:04 +08:00
fc57e91b80 !196 RepeatedlyRequestWrapper.ServletInputStream 实现available方法
Merge pull request !196 from wangyang/master
2021-03-30 15:17:34 +08:00
347f36ceff fix:RepeatedlyRequestWrapper.ServletInputStream 实现available方法 2021-03-29 10:17:31 +08:00
9b9c1b999f 读取excel增加
if(row == null)
{
    continue;
}
判断。防止有时候Excel表中有空数据行,导致读取时出现空指针异常。
2021-03-29 09:00:44 +08:00
ec627704b2 取消 2021-03-29 08:58:46 +08:00
9e6b2a1265 读取excel增加
if(row == null)
{
    continue;
}
判断。防止有时候Excel表中有空数据行,导致读取时出现空指针异常。
2021-03-29 08:56:51 +08:00
266a5e844f !193 修复request.getInputStream() 重复读取错误问题
Merge pull request !193 from MccRay/master
2021-03-27 13:22:01 +08:00
0822680158 update ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java.
修复 getInputStream() has already been called for this request异常问题
2021-03-25 17:39:51 +08:00
090e258bf9 显隐列初始默认隐藏列 2021-03-24 15:46:37 +08:00
505fa06dc3 !192 update ruoyi-ui/src/views/system/user/profile/userAvatar.vue.
Merge pull request !192 from 谢凯/N/A
2021-03-24 15:32:24 +08:00
6cf53bfacb !191 update ruoyi-ui/src/views/system/user/profile/userAvatar.vue.
Merge pull request !191 from allworldg/N/A
2021-03-24 15:31:51 +08:00
881dc3fcf0 update ruoyi-ui/src/views/system/user/profile/userAvatar.vue. 2021-03-22 17:52:34 +08:00
b65c8d4512 update ruoyi-ui/src/views/system/user/profile/userAvatar.vue. 2021-03-21 21:20:23 +08:00
67371a9028 个人信息添加手机&邮箱重复验证 2021-03-21 08:53:11 +08:00
01b545e4ab 通用Controller添加响应返回消息 2021-03-16 14:43:55 +08:00
c2e9200626 数据监控默认账户密码防止越权访问 2021-03-14 16:37:34 +08:00
9502203bbe !187 【漏洞修复】升级commons-collections版本,解决3.2.1版本的反序列化漏洞问题
Merge pull request !187 from Delusive/master
2021-03-14 16:32:07 +08:00
c0f1569ad9 修复commons-collections引起的反序列化漏洞 2021-03-12 16:59:06 +08:00
61432480c8 删除多余的代码 2021-03-07 11:06:47 +08:00
78deee063d !180 富文本编辑组件支持只读
Merge pull request !180 from JOSWAY/master
2021-03-07 10:44:04 +08:00
4ad003649e !182 解决 Byte[] 类型 to string 死循环的问题
Merge pull request !182 from sproutcat/master
2021-03-07 10:39:41 +08:00
616f5a6aca !185 update ruoyi-ui/src/views/monitor/logininfor/index.vue.
Merge pull request !185 from asher/N/A
2021-03-07 10:39:30 +08:00
fea3ff7b2a !184 update ruoyi-ui/src/views/monitor/operlog/index.vue.
Merge pull request !184 from asher/N/A
2021-03-07 10:39:22 +08:00
f938a24df0 update ruoyi-ui/src/views/monitor/logininfor/index.vue. 2021-03-04 17:00:07 +08:00
45fa686a64 update ruoyi-ui/src/views/monitor/operlog/index.vue. 2021-03-04 16:58:18 +08:00
0d8c3b4940 升级oshi到最新版本v5.6.0 2021-03-03 13:53:03 +08:00
64e807f27b 添加新群号:186866453 2021-02-24 13:45:24 +08:00
tzg
9a384a2fc2 解决 Byte[] 类型 to string 死循环的问题 2021-02-23 16:30:14 +08:00
339b932a3d 富文本编辑组件支持只读 2021-02-22 17:49:17 +08:00
0b5c7f4c96 若依 3.4.0 2021-02-22 09:40:28 +08:00
265e3010aa 升级SpringBoot到最新版本2.2.13 2021-02-21 11:01:56 +08:00
b7a36bfe8a 修改ip字段长度防止ipv6地址长度不够 2021-02-10 12:48:52 +08:00
3a51c53823 角色非自定义权限范围清空选择值 2021-02-06 10:34:12 +08:00
df504c5c14 修复四级菜单无法显示问题 2021-02-06 09:29:50 +08:00
baeed2e8d3 修复角色管理-编辑角色-功能权限显示异常 2021-02-02 16:25:37 +08:00
2903de1561 升级element-ui到最新版本2.15.0 2021-01-24 12:06:19 +08:00
cca2f5c62d update README.md 2021-01-14 13:20:08 +08:00
ce54416e4f 升级fastjson到最新版1.2.75 2021-01-13 16:20:39 +08:00
bd19892aff 修复生成树表代码异常 2021-01-13 16:19:23 +08:00
14a859899c 代码生成模板支持主子表 2021-01-08 10:47:36 +08:00
f3cb18c836 !161 修复导入数据为负浮点数时,导入结果会丢失精度问题
Merge pull request !161 from 嘿白熊/master
2021-01-08 10:46:36 +08:00
7d0eb3b8c7 用户显隐列添加不同key防止被复用 2021-01-07 13:28:16 +08:00
654aa6b30c 表格右侧工具栏组件支持显隐列 2021-01-06 17:30:39 +08:00
b7cdd10f9f 修复导入数据为负浮点数时丢失精度问题 2021-01-06 11:11:11 +08:00
9bd35cb7be 升级druid到最新版本v1.2.4 2021-01-06 11:00:20 +08:00
7a86c714fc 代码生成支持文件上传组件 2021-01-06 11:00:09 +08:00
edd090a098 代码生成支持文件上传组件 2021-01-05 20:59:14 +08:00
3cd886785b 图片组件添加预览功能 2021-01-05 20:33:41 +08:00
ae5a0f9774 新增文件上传组件 2021-01-05 20:11:09 +08:00
074b3c735d 设置单图上传控件 2021-01-05 16:17:41 +08:00
a118738d0f 单图上传组件添加移除 2021-01-05 16:13:22 +08:00
56fa3912ae 修复IE11浏览器报错问题 2021-01-05 09:44:08 +08:00
2116ee1822 Update copyright 2021-01-04 17:49:52 +08:00
57a2eb4217 操作按钮组调整为朴素按钮样式 2021-01-04 17:48:46 +08:00
647af5b485 菜单组件修改允许空字符串 2020-12-29 17:27:48 +08:00
3f33219134 !156 修复 用户管理 -> 修改用户 手机号码 和 邮箱 不能清空
Merge pull request !156 from li_oxen/N/A
2020-12-29 17:14:31 +08:00
a04bb71777 修复 用户管理 -> 修改用户 手机号码 和 邮箱 不能清空 2020-12-29 13:51:15 +08:00
63992ac906 代码生成数据库文本类型生成表单文本域 2020-12-29 11:14:48 +08:00
0187344798 Excel注解支持Image图片导出 2020-12-27 10:04:45 +08:00
a9c6ba12e6 代码生成日期控件区分范围 2020-12-25 09:35:20 +08:00
ba068eae65 修正侧边栏静态路由丢失问题 2020-12-24 19:32:18 +08:00
1a87ee7c19 防止get请求参数值为false或0等特殊值会导致无法正确的传参
Merge pull request !146 from wugh/hotfix/gitee-issue_I2A5FU
2020-12-22 16:19:51 +08:00
3c4b01ab83 权限工具类增加admin判断 2020-12-21 19:43:57 +08:00
886ce995af [bug修复] 解决get请求,如果参数值为false或0等特殊值会导致无法正确传参 2020-12-21 11:58:15 +08:00
52264b6e2a 优化多级菜单之间切换无法缓存的问题 2020-12-21 10:50:48 +08:00
fd7e88a518 配置文件新增redis数据库索引属性 2020-12-21 10:26:54 +08:00
854a6f805f !145 redis 增加 database配置项,设置不同的库
Merge pull request !145 from li_oxen/N/A
2020-12-21 10:13:47 +08:00
1caec85f46 redis 增加 database配置项,设置不同的库 2020-12-19 10:08:09 +08:00
c86dc20743 移除path-to-regexp正则匹配插件 2020-12-17 12:05:29 +08:00
ecc7a8be46 优化多级菜单之间切换无法缓存的问题 2020-12-16 20:57:48 +08:00
e23ad20186 README 2020-12-16 10:20:02 +08:00
6cf2a8edeb 升级SpringBoot到最新版本2.2.12 提升启动速度 2020-12-16 10:05:53 +08:00
67198aede9 README 2020-12-15 10:06:12 +08:00
4f33da2a3f 登录后push添加catch防止出现检查错误 2020-12-15 10:04:09 +08:00
115 changed files with 3532 additions and 1653 deletions

View File

@ -1,13 +1,15 @@
## 平台简介
若依是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。
* 前端采用Vue、Element UI。
* 后端采用Spring Boot、Spring Security、Redis & Jwt。
* 权限认证使用Jwt支持多终端认证系统。
* 支持加载动态权限菜单,多方式轻松权限控制。
* 高效率开发,使用代码生成器可以一键生成前后端代码。
* 提供了一个Oracle版本[RuoYi-Vue-Oracle](https://github.com/yangzongzhuan/RuoYi-Vue-Oracle),保持同步更新。
* 提供了单应用版本[RuoYi-Vue-fast](https://github.com/yangzongzhuan/RuoYi-Vue-fast)Oracle版本[RuoYi-Vue-Oracle](https://github.com/yangzongzhuan/RuoYi-Vue-Oracle),保持同步更新。
* 不分离版本,请移步[RuoYi](https://gitee.com/y_project/RuoYi),微服务版本,请移步[RuoYi-Cloud](https://gitee.com/y_project/RuoYi-Cloud)
* 感谢[Vue-Element-Admin](https://github.com/PanJiaChen/vue-element-admin)[eladmin-web](https://gitee.com/elunez/eladmin-web?_from=gitee_search)。
* 特别鸣谢:[element](https://github.com/ElemeFE/element)[vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)[eladmin-web](https://github.com/elunez/eladmin-web)。
* 阿里云折扣场:[点我进入](http://aly.ruoyi.vip),腾讯云秒杀场:[点我进入](http://txy.ruoyi.vip)  
* 阿里云优惠券:[点我领取](https://www.aliyun.com/minisite/goods?userCode=brki8iof&share_source=copy_link),腾讯云优惠券:[点我领取](https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=198c8df2ed259157187173bc7f4f32fd&from=console)  
@ -28,8 +30,9 @@
13. 代码生成前后端代码的生成java、html、xml、sql支持CRUD下载 。
14. 系统接口根据业务代码自动生成相关的api接口文档。
15. 服务监控监视当前系统CPU、内存、磁盘、堆栈等相关信息。
16. 在线构建器拖动表单元素生成相应的HTML代码
17. 连接池监视监视当前系统数据库连接池状态可进行分析SQL找出系统性能瓶颈
16. 缓存监控:对系统的缓存信息查询,命令统计等
17. 在线构建器拖动表单元素生成相应的HTML代码
18. 连接池监视监视当前系统数据库连接池状态可进行分析SQL找出系统性能瓶颈。
## 在线体验
@ -72,11 +75,11 @@
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/b6115bc8c31de52951982e509930b20684a.jpg"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-6d73c2140ce694e3de4c05035fdc1868d4c.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-5e4daac0bb59612c5038448acbcef235e3a.png"/></td>
</tr>
</table>
## 若依前后端分离交流群
QQ群 [![加入QQ群](https://img.shields.io/badge/已满-937441-blue.svg)](https://jq.qq.com/?_wv=1027&k=5bVB1og) [![加入QQ群](https://img.shields.io/badge/已满-887144332-blue.svg)](https://jq.qq.com/?_wv=1027&k=5eiA4DH) [![加入QQ群](https://img.shields.io/badge/已满-180251782-blue.svg)](https://jq.qq.com/?_wv=1027&k=5AxMKlC) [![加入QQ群](https://img.shields.io/badge/104180207-blue.svg)](https://jq.qq.com/?_wv=1027&k=51G72yr) 点击按钮入群。
QQ群 [![加入QQ群](https://img.shields.io/badge/已满-937441-blue.svg)](https://jq.qq.com/?_wv=1027&k=5bVB1og) [![加入QQ群](https://img.shields.io/badge/已满-887144332-blue.svg)](https://jq.qq.com/?_wv=1027&k=5eiA4DH) [![加入QQ群](https://img.shields.io/badge/已满-180251782-blue.svg)](https://jq.qq.com/?_wv=1027&k=5AxMKlC) [![加入QQ群](https://img.shields.io/badge/已满-104180207-blue.svg)](https://jq.qq.com/?_wv=1027&k=51G72yr) [![加入QQ群](https://img.shields.io/badge/已满-186866453-blue.svg)](https://jq.qq.com/?_wv=1027&k=VvjN2nvu) [![加入QQ群](https://img.shields.io/badge/201396349-blue.svg)](https://jq.qq.com/?_wv=1027&k=5vYAqA05) 点击按钮入群。

74
pom.xml
View File

@ -6,28 +6,30 @@
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId>
<version>3.3.0</version>
<version>3.5.0</version>
<name>ruoyi</name>
<url>http://www.ruoyi.vip</url>
<description>若依管理系统</description>
<properties>
<ruoyi.version>3.3.0</ruoyi.version>
<ruoyi.version>3.5.0</ruoyi.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<druid.version>1.2.2</druid.version>
<druid.version>1.2.6</druid.version>
<bitwalker.version>1.21</bitwalker.version>
<swagger.version>2.9.2</swagger.version>
<kaptcha.version>2.3.2</kaptcha.version>
<mybatis-spring-boot.version>2.1.4</mybatis-spring-boot.version>
<pagehelper.boot.version>1.3.0</pagehelper.boot.version>
<fastjson.version>1.2.74</fastjson.version>
<oshi.version>5.3.6</oshi.version>
<jna.version>5.6.0</jna.version>
<fastjson.version>1.2.76</fastjson.version>
<oshi.version>5.6.0</oshi.version>
<jna.version>5.7.0</jna.version>
<commons.io.version>2.5</commons.io.version>
<commons.fileupload.version>1.3.3</commons.fileupload.version>
<commons.collections.version>3.2.2</commons.collections.version>
<poi.version>4.1.2</poi.version>
<velocity.version>1.7</velocity.version>
<jwt.version>0.9.1</jwt.version>
@ -36,37 +38,44 @@
<!-- 依赖声明 -->
<dependencyManagement>
<dependencies>
<!-- SpringBoot的依赖配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.18.RELEASE</version>
<version>2.2.13.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- 解析客户端操作系统、浏览器等 -->
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
<version>${bitwalker.version}</version>
</dependency>
<!-- SpringBoot集成mybatis框架 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-spring-boot.version}</version>
</dependency>
<!-- pagehelper 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${pagehelper.boot.version}</version>
</dependency>
<!-- 获取系统信息 -->
<dependency>
<groupId>com.github.oshi</groupId>
@ -85,7 +94,7 @@
<artifactId>jna-platform</artifactId>
<version>${jna.version}</version>
</dependency>
<!-- swagger2-->
<dependency>
<groupId>io.springfox</groupId>
@ -102,91 +111,104 @@
</exclusion>
</exclusions>
</dependency>
<!-- swagger2-UI-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<!--io常用工具类 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons.io.version}</version>
</dependency>
<!--文件上传工具类 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${commons.fileupload.version}</version>
</dependency>
<!-- excel工具 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>
<!--velocity代码生成使用模板 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>${velocity.version}</version>
<exclusions>
<exclusion>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- collections工具类 -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>${commons.collections.version}</version>
</dependency>
<!-- 阿里JSON解析器 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!--Token生成与解析-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jwt.version}</version>
</dependency>
<!--验证码 -->
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>${kaptcha.version}</version>
</dependency>
<!-- 定时任务-->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-quartz</artifactId>
<version>${ruoyi.version}</version>
</dependency>
<!-- 代码生成-->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-generator</artifactId>
<version>${ruoyi.version}</version>
</dependency>
<!-- 核心模块-->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-framework</artifactId>
<version>${ruoyi.version}</version>
</dependency>
<!-- 系统模块-->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-system</artifactId>
<version>${ruoyi.version}</version>
</dependency>
<!-- 通用工具-->
<dependency>
<groupId>com.ruoyi</groupId>

View File

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

View File

@ -48,6 +48,7 @@ public class SysOperlogController extends BaseController
return util.exportExcel(list, "操作日志");
}
@Log(title = "操作日志", businessType = BusinessType.DELETE)
@PreAuthorize("@ss.hasPermi('monitor:operlog:remove')")
@DeleteMapping("/{operIds}")
public AjaxResult remove(@PathVariable Long[] operIds)

View File

@ -12,6 +12,7 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysUser;
@ -19,6 +20,7 @@ import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.service.ISysUserService;
@ -59,6 +61,16 @@ public class SysProfileController extends BaseController
@PutMapping
public AjaxResult updateProfile(@RequestBody SysUser user)
{
if (StringUtils.isNotEmpty(user.getPhonenumber())
&& UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user)))
{
return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
}
if (StringUtils.isNotEmpty(user.getEmail())
&& UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user)))
{
return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
}
if (userService.updateUserProfile(user) > 0)
{
LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());

View File

@ -43,8 +43,8 @@ spring:
allow:
url-pattern: /druid/*
# 控制台管理用户名和密码
login-username:
login-password:
login-username: ruoyi
login-password: 123456
filter:
stat:
enabled: true

View File

@ -3,9 +3,9 @@ ruoyi:
# 名称
name: RuoYi
# 版本
version: 3.3.0
version: 3.5.0
# 版权年份
copyrightYear: 2020
copyrightYear: 2021
# 实例演示开关
demoEnabled: true
# 文件路径 示例( Windows配置D:/ruoyi/uploadPathLinux配置 /home/ruoyi/uploadPath
@ -62,6 +62,8 @@ spring:
host: localhost
# 端口默认为6379
port: 6379
# 数据库索引
database: 0
# 密码
password:
# 连接超时时间

View File

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

View File

@ -149,7 +149,7 @@ public @interface Excel
public enum ColumnType
{
NUMERIC(0), STRING(1);
NUMERIC(0), STRING(1), IMAGE(2);
private final int value;
ColumnType(int value)

View File

@ -13,6 +13,9 @@ public class GenConstants
/** 树表(增删改查) */
public static final String TPL_TREE = "tree";
/** 主子表(增删改查) */
public static final String TPL_SUB = "sub";
/** 树编码字段 */
public static final String TREE_CODE = "treeCode";
@ -29,8 +32,10 @@ public class GenConstants
public static final String PARENT_MENU_NAME = "parentMenuName";
/** 数据库字符串类型 */
public static final String[] COLUMNTYPE_STR = { "char", "varchar", "nvarchar", "varchar2", "tinytext", "text",
"mediumtext", "longtext" };
public static final String[] COLUMNTYPE_STR = { "char", "varchar", "nvarchar", "varchar2" };
/** 数据库文本类型 */
public static final String[] COLUMNTYPE_TEXT = { "tinytext", "text", "mediumtext", "longtext" };
/** 数据库时间类型 */
public static final String[] COLUMNTYPE_TIME = { "datetime", "time", "date", "timestamp" };
@ -74,8 +79,11 @@ public class GenConstants
/** 日期控件 */
public static final String HTML_DATETIME = "datetime";
/** 上传控件 */
public static final String HTML_UPLOAD_IMAGE = "uploadImage";
/** 图片上传控件 */
public static final String HTML_IMAGE_UPLOAD = "imageUpload";
/** 文件上传控件 */
public static final String HTML_FILE_UPLOAD = "fileUpload";
/** 富文本控件 */
public static final String HTML_EDITOR = "editor";

View File

@ -25,7 +25,7 @@ import com.ruoyi.common.utils.sql.SqlUtil;
*/
public class BaseController
{
protected final Logger logger = LoggerFactory.getLogger(BaseController.class);
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 将前台传递过来的日期格式的字符串自动转化为Date类型
@ -84,6 +84,49 @@ public class BaseController
return rows > 0 ? AjaxResult.success() : AjaxResult.error();
}
/**
* 响应返回结果
*
* @param result 结果
* @return 操作结果
*/
protected AjaxResult toAjax(boolean result)
{
return result ? success() : error();
}
/**
* 返回成功
*/
public AjaxResult success()
{
return AjaxResult.success();
}
/**
* 返回失败消息
*/
public AjaxResult error()
{
return AjaxResult.error();
}
/**
* 返回成功消息
*/
public AjaxResult success(String message)
{
return AjaxResult.success(message);
}
/**
* 返回失败消息
*/
public AjaxResult error(String message)
{
return AjaxResult.error(message);
}
/**
* 页面跳转
*/

View File

@ -7,10 +7,11 @@ import java.nio.charset.Charset;
import java.text.NumberFormat;
import java.util.Set;
import com.ruoyi.common.utils.StringUtils;
import org.apache.commons.lang3.ArrayUtils;
/**
* 类型转换器
*
*
* @author ruoyi
*/
public class Convert
@ -19,7 +20,7 @@ public class Convert
* 转换为字符串<br>
* 如果给定的值为null或者转换失败返回默认值<br>
* 转换失败不会报错
*
*
* @param value 被转换的值
* @param defaultValue 转换错误时的默认值
* @return 结果
@ -41,7 +42,7 @@ public class Convert
* 转换为字符串<br>
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
* 转换失败不会报错
*
*
* @param value 被转换的值
* @return 结果
*/
@ -54,7 +55,7 @@ public class Convert
* 转换为字符<br>
* 如果给定的值为null或者转换失败返回默认值<br>
* 转换失败不会报错
*
*
* @param value 被转换的值
* @param defaultValue 转换错误时的默认值
* @return 结果
@ -78,7 +79,7 @@ public class Convert
* 转换为字符<br>
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
* 转换失败不会报错
*
*
* @param value 被转换的值
* @return 结果
*/
@ -91,7 +92,7 @@ public class Convert
* 转换为byte<br>
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>
* 转换失败不会报错
*
*
* @param value 被转换的值
* @param defaultValue 转换错误时的默认值
* @return 结果
@ -129,7 +130,7 @@ public class Convert
* 转换为byte<br>
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
* 转换失败不会报错
*
*
* @param value 被转换的值
* @return 结果
*/
@ -142,7 +143,7 @@ public class Convert
* 转换为Short<br>
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>
* 转换失败不会报错
*
*
* @param value 被转换的值
* @param defaultValue 转换错误时的默认值
* @return 结果
@ -180,7 +181,7 @@ public class Convert
* 转换为Short<br>
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
* 转换失败不会报错
*
*
* @param value 被转换的值
* @return 结果
*/
@ -193,7 +194,7 @@ public class Convert
* 转换为Number<br>
* 如果给定的值为空,或者转换失败,返回默认值<br>
* 转换失败不会报错
*
*
* @param value 被转换的值
* @param defaultValue 转换错误时的默认值
* @return 结果
@ -227,7 +228,7 @@ public class Convert
* 转换为Number<br>
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
* 转换失败不会报错
*
*
* @param value 被转换的值
* @return 结果
*/
@ -240,7 +241,7 @@ public class Convert
* 转换为int<br>
* 如果给定的值为空,或者转换失败,返回默认值<br>
* 转换失败不会报错
*
*
* @param value 被转换的值
* @param defaultValue 转换错误时的默认值
* @return 结果
@ -278,7 +279,7 @@ public class Convert
* 转换为int<br>
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
* 转换失败不会报错
*
*
* @param value 被转换的值
* @return 结果
*/
@ -289,7 +290,7 @@ public class Convert
/**
* 转换为Integer数组<br>
*
*
* @param str 被转换的值
* @return 结果
*/
@ -300,7 +301,7 @@ public class Convert
/**
* 转换为Long数组<br>
*
*
* @param str 被转换的值
* @return 结果
*/
@ -311,7 +312,7 @@ public class Convert
/**
* 转换为Integer数组<br>
*
*
* @param split 分隔符
* @param split 被转换的值
* @return 结果
@ -334,7 +335,7 @@ public class Convert
/**
* 转换为Long数组<br>
*
*
* @param split 分隔符
* @param str 被转换的值
* @return 结果
@ -357,7 +358,7 @@ public class Convert
/**
* 转换为String数组<br>
*
*
* @param str 被转换的值
* @return 结果
*/
@ -368,7 +369,7 @@ public class Convert
/**
* 转换为String数组<br>
*
*
* @param split 分隔符
* @param split 被转换的值
* @return 结果
@ -382,7 +383,7 @@ public class Convert
* 转换为long<br>
* 如果给定的值为空,或者转换失败,返回默认值<br>
* 转换失败不会报错
*
*
* @param value 被转换的值
* @param defaultValue 转换错误时的默认值
* @return 结果
@ -421,7 +422,7 @@ public class Convert
* 转换为long<br>
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
* 转换失败不会报错
*
*
* @param value 被转换的值
* @return 结果
*/
@ -434,7 +435,7 @@ public class Convert
* 转换为double<br>
* 如果给定的值为空,或者转换失败,返回默认值<br>
* 转换失败不会报错
*
*
* @param value 被转换的值
* @param defaultValue 转换错误时的默认值
* @return 结果
@ -473,7 +474,7 @@ public class Convert
* 转换为double<br>
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
* 转换失败不会报错
*
*
* @param value 被转换的值
* @return 结果
*/
@ -486,7 +487,7 @@ public class Convert
* 转换为Float<br>
* 如果给定的值为空,或者转换失败,返回默认值<br>
* 转换失败不会报错
*
*
* @param value 被转换的值
* @param defaultValue 转换错误时的默认值
* @return 结果
@ -524,7 +525,7 @@ public class Convert
* 转换为Float<br>
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
* 转换失败不会报错
*
*
* @param value 被转换的值
* @return 结果
*/
@ -537,7 +538,7 @@ public class Convert
* 转换为boolean<br>
* String支持的值为true、false、yes、ok、no1,0 如果给定的值为空,或者转换失败,返回默认值<br>
* 转换失败不会报错
*
*
* @param value 被转换的值
* @param defaultValue 转换错误时的默认值
* @return 结果
@ -583,7 +584,7 @@ public class Convert
* 转换为boolean<br>
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
* 转换失败不会报错
*
*
* @param value 被转换的值
* @return 结果
*/
@ -595,7 +596,7 @@ public class Convert
/**
* 转换为Enum对象<br>
* 如果给定的值为空,或者转换失败,返回默认值<br>
*
*
* @param clazz Enum的Class
* @param value 值
* @param defaultValue 默认值
@ -631,7 +632,7 @@ public class Convert
/**
* 转换为Enum对象<br>
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
*
*
* @param clazz Enum的Class
* @param value 值
* @return Enum
@ -645,7 +646,7 @@ public class Convert
* 转换为BigInteger<br>
* 如果给定的值为空,或者转换失败,返回默认值<br>
* 转换失败不会报错
*
*
* @param value 被转换的值
* @param defaultValue 转换错误时的默认值
* @return 结果
@ -683,7 +684,7 @@ public class Convert
* 转换为BigInteger<br>
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
* 转换失败不会报错
*
*
* @param value 被转换的值
* @return 结果
*/
@ -696,7 +697,7 @@ public class Convert
* 转换为BigDecimal<br>
* 如果给定的值为空,或者转换失败,返回默认值<br>
* 转换失败不会报错
*
*
* @param value 被转换的值
* @param defaultValue 转换错误时的默认值
* @return 结果
@ -742,7 +743,7 @@ public class Convert
* 转换为BigDecimal<br>
* 如果给定的值为空,或者转换失败,返回默认值<br>
* 转换失败不会报错
*
*
* @param value 被转换的值
* @return 结果
*/
@ -754,7 +755,7 @@ public class Convert
/**
* 将对象转为字符串<br>
* 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
*
*
* @param obj 对象
* @return 字符串
*/
@ -766,7 +767,7 @@ public class Convert
/**
* 将对象转为字符串<br>
* 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
*
*
* @param obj 对象
* @param charsetName 字符集
* @return 字符串
@ -779,7 +780,7 @@ public class Convert
/**
* 将对象转为字符串<br>
* 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
*
*
* @param obj 对象
* @param charset 字符集
* @return 字符串
@ -795,9 +796,14 @@ public class Convert
{
return (String) obj;
}
else if (obj instanceof byte[] || obj instanceof Byte[])
else if (obj instanceof byte[])
{
return str((Byte[]) obj, charset);
return str((byte[]) obj, charset);
}
else if (obj instanceof Byte[])
{
byte[] bytes = ArrayUtils.toPrimitive((Byte[]) obj);
return str(bytes, charset);
}
else if (obj instanceof ByteBuffer)
{
@ -808,7 +814,7 @@ public class Convert
/**
* 将byte数组转为字符串
*
*
* @param bytes byte数组
* @param charset 字符集
* @return 字符串
@ -820,7 +826,7 @@ public class Convert
/**
* 解码字节码
*
*
* @param data 字符串
* @param charset 字符集,如果此字段为空,则解码的结果取决于平台
* @return 解码后的字符串
@ -841,7 +847,7 @@ public class Convert
/**
* 将编码的byteBuffer数据转换为字符串
*
*
* @param data 数据
* @param charset 字符集,如果为空使用当前系统字符集
* @return 字符串
@ -858,7 +864,7 @@ public class Convert
/**
* 将编码的byteBuffer数据转换为字符串
*
*
* @param data 数据
* @param charset 字符集,如果为空使用当前系统字符集
* @return 字符串
@ -875,7 +881,7 @@ public class Convert
// ----------------------------------------------------------------------- 全角半角转换
/**
* 半角转全角
*
*
* @param input String.
* @return 全角字符串.
*/
@ -886,7 +892,7 @@ public class Convert
/**
* 半角转全角
*
*
* @param input String
* @param notConvertSet 不替换的字符集合
* @return 全角字符串.
@ -917,7 +923,7 @@ public class Convert
/**
* 全角转半角
*
*
* @param input String.
* @return 半角字符串
*/
@ -928,7 +934,7 @@ public class Convert
/**
* 替换全角为半角
*
*
* @param text 文本
* @param notConvertSet 不替换的字符集合
* @return 替换后的字符
@ -960,7 +966,7 @@ public class Convert
/**
* 数字金额大写转换 先写个完整的然后将如零拾替换成零
*
*
* @param n 数字
* @return 中文大写数字
*/

View File

@ -68,4 +68,14 @@ public class InvalidExtensionException extends FileUploadException
super(allowedExtension, extension, filename);
}
}
public static class InvalidVideoExtensionException extends InvalidExtensionException
{
private static final long serialVersionUID = 1L;
public InvalidVideoExtensionException(String[] allowedExtension, String extension, String filename)
{
super(allowedExtension, extension, filename);
}
}
}

View File

@ -29,8 +29,8 @@ public class RepeatableFilter implements Filter
throws IOException, ServletException
{
ServletRequest requestWrapper = null;
if (request instanceof HttpServletRequest && StringUtils.equalsAnyIgnoreCase(request.getContentType(),
MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_JSON_UTF8_VALUE))
if (request instanceof HttpServletRequest
&& StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE))
{
requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response);
}

View File

@ -38,18 +38,21 @@ public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper
@Override
public ServletInputStream getInputStream() throws IOException
{
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
return new ServletInputStream()
{
@Override
public int read() throws IOException
{
return bais.read();
}
@Override
public int available() throws IOException
{
return body.length;
}
@Override
public boolean isFinished()
{

View File

@ -99,7 +99,6 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper
public boolean isJsonRequest()
{
String header = super.getHeader(HttpHeaders.CONTENT_TYPE);
return MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(header)
|| MediaType.APPLICATION_JSON_UTF8_VALUE.equalsIgnoreCase(header);
return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE);
}
}

View File

@ -1,114 +1,114 @@
package com.ruoyi.common.utils;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* 精确的浮点数运算
*
* @author ruoyi
*/
public class Arith
{
/** 默认除法运算精度 */
private static final int DEF_DIV_SCALE = 10;
/** 这个类不能实例化 */
private Arith()
{
}
/**
* 提供精确的加法运算。
* @param v1 被加数
* @param v2 加数
* @return 两个参数的和
*/
public static double add(double v1, double v2)
{
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.add(b2).doubleValue();
}
/**
* 提供精确的减法运算。
* @param v1 被减数
* @param v2 减数
* @return 两个参数的差
*/
public static double sub(double v1, double v2)
{
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.subtract(b2).doubleValue();
}
/**
* 提供精确的乘法运算。
* @param v1 被乘数
* @param v2 乘数
* @return 两个参数的积
*/
public static double mul(double v1, double v2)
{
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.multiply(b2).doubleValue();
}
/**
* 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到
* 小数点以后10位以后的数字四舍五入。
* @param v1 被除数
* @param v2 除数
* @return 两个参数的商
*/
public static double div(double v1, double v2)
{
return div(v1, v2, DEF_DIV_SCALE);
}
/**
* 提供相对精确的除法运算。当发生除不尽的情况时由scale参数指
* 定精度,以后的数字四舍五入。
* @param v1 被除数
* @param v2 除数
* @param scale 表示表示需要精确到小数点以后几位。
* @return 两个参数的商
*/
public static double div(double v1, double v2, int scale)
{
if (scale < 0)
{
throw new IllegalArgumentException(
"The scale must be a positive integer or zero");
}
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
if (b1.compareTo(BigDecimal.ZERO) == 0)
{
return BigDecimal.ZERO.doubleValue();
}
return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue();
}
/**
* 提供精确的小数位四舍五入处理。
* @param v 需要四舍五入的数字
* @param scale 小数点后保留几位
* @return 四舍五入后的结果
*/
public static double round(double v, int scale)
{
if (scale < 0)
{
throw new IllegalArgumentException(
"The scale must be a positive integer or zero");
}
BigDecimal b = new BigDecimal(Double.toString(v));
BigDecimal one = new BigDecimal("1");
return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue();
}
}
package com.ruoyi.common.utils;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* 精确的浮点数运算
*
* @author ruoyi
*/
public class Arith
{
/** 默认除法运算精度 */
private static final int DEF_DIV_SCALE = 10;
/** 这个类不能实例化 */
private Arith()
{
}
/**
* 提供精确的加法运算。
* @param v1 被加数
* @param v2 加数
* @return 两个参数的和
*/
public static double add(double v1, double v2)
{
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.add(b2).doubleValue();
}
/**
* 提供精确的减法运算。
* @param v1 被减数
* @param v2 减数
* @return 两个参数的差
*/
public static double sub(double v1, double v2)
{
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.subtract(b2).doubleValue();
}
/**
* 提供精确的乘法运算。
* @param v1 被乘数
* @param v2 乘数
* @return 两个参数的积
*/
public static double mul(double v1, double v2)
{
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.multiply(b2).doubleValue();
}
/**
* 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到
* 小数点以后10位以后的数字四舍五入。
* @param v1 被除数
* @param v2 除数
* @return 两个参数的商
*/
public static double div(double v1, double v2)
{
return div(v1, v2, DEF_DIV_SCALE);
}
/**
* 提供相对精确的除法运算。当发生除不尽的情况时由scale参数指
* 定精度,以后的数字四舍五入。
* @param v1 被除数
* @param v2 除数
* @param scale 表示表示需要精确到小数点以后几位。
* @return 两个参数的商
*/
public static double div(double v1, double v2, int scale)
{
if (scale < 0)
{
throw new IllegalArgumentException(
"The scale must be a positive integer or zero");
}
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
if (b1.compareTo(BigDecimal.ZERO) == 0)
{
return BigDecimal.ZERO.doubleValue();
}
return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue();
}
/**
* 提供精确的小数位四舍五入处理。
* @param v 需要四舍五入的数字
* @param scale 小数点后保留几位
* @return 四舍五入后的结果
*/
public static double round(double v, int scale)
{
if (scale < 0)
{
throw new IllegalArgumentException(
"The scale must be a positive integer or zero");
}
BigDecimal b = new BigDecimal(Double.toString(v));
BigDecimal one = BigDecimal.ONE;
return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue();
}
}

View File

@ -22,7 +22,7 @@ public class ExceptionUtil
return str;
}
public static String getRootErrorMseeage(Exception e)
public static String getRootErrorMessage(Exception e)
{
Throwable root = ExceptionUtils.getRootCause(e);
root = (root == null ? e : root);

View File

@ -312,7 +312,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
}
/**
* 下划线转驼峰命名
* 驼峰转下划线命名
*/
public static String toUnderScoreCase(String str)
{

View File

@ -44,4 +44,33 @@ public class FileTypeUtils
}
return fileName.substring(separatorIndex + 1).toLowerCase();
}
/**
* 获取文件类型
*
* @param photoByte 文件字节码
* @return 后缀(不含".")
*/
public static String getFileExtendName(byte[] photoByte)
{
String strFileExtendName = "JPG";
if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
&& ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97))
{
strFileExtendName = "GIF";
}
else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70))
{
strFileExtendName = "JPG";
}
else if ((photoByte[0] == 66) && (photoByte[1] == 77))
{
strFileExtendName = "BMP";
}
else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71))
{
strFileExtendName = "PNG";
}
return strFileExtendName;
}
}

View File

@ -131,13 +131,12 @@ public class FileUploadUtils
{
File desc = new File(uploadDir + File.separator + fileName);
if (!desc.getParentFile().exists())
{
desc.getParentFile().mkdirs();
}
if (!desc.exists())
{
desc.createNewFile();
if (!desc.getParentFile().exists())
{
desc.getParentFile().mkdirs();
}
}
return desc;
}
@ -186,6 +185,11 @@ public class FileUploadUtils
throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,
fileName);
}
else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION)
{
throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension,
fileName);
}
else
{
throw new InvalidExtensionException(allowedExtension, extension, fileName);

View File

@ -0,0 +1,100 @@
package com.ruoyi.common.utils.file;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Arrays;
import org.apache.poi.util.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.StringUtils;
/**
* 图片处理工具类
*
* @author ruoyi
*/
public class ImageUtils
{
private static final Logger log = LoggerFactory.getLogger(ImageUtils.class);
public static byte[] getImage(String imagePath)
{
InputStream is = getFile(imagePath);
try
{
return IOUtils.toByteArray(is);
}
catch (Exception e)
{
log.error("图片加载异常 {}", e);
return null;
}
finally
{
IOUtils.closeQuietly(is);
}
}
public static InputStream getFile(String imagePath)
{
try
{
byte[] result = readFile(imagePath);
result = Arrays.copyOf(result, result.length);
return new ByteArrayInputStream(result);
}
catch (Exception e)
{
log.error("获取图片异常 {}", e);
}
return null;
}
/**
* 读取文件为字节数据
*
* @param key 地址
* @return 字节数据
*/
public static byte[] readFile(String url)
{
InputStream in = null;
ByteArrayOutputStream baos = null;
try
{
if (url.startsWith("http"))
{
// 网络地址
URL urlObj = new URL(url);
URLConnection urlConnection = urlObj.openConnection();
urlConnection.setConnectTimeout(30 * 1000);
urlConnection.setReadTimeout(60 * 1000);
urlConnection.setDoInput(true);
in = urlConnection.getInputStream();
}
else
{
// 本机地址
String localPath = RuoYiConfig.getProfile();
String downloadPath = localPath + StringUtils.substringAfter(url, Constants.RESOURCE_PREFIX);
in = new FileInputStream(downloadPath);
}
return IOUtils.toByteArray(in);
}
catch (Exception e)
{
log.error("获取文件路径异常 {}", e);
return null;
}
finally
{
IOUtils.closeQuietly(baos);
}
}
}

View File

@ -24,6 +24,8 @@ public class MimeTypeUtils
public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg",
"asf", "rm", "rmvb" };
public static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb" };
public static final String[] DEFAULT_ALLOWED_EXTENSION = {
// 图片
"bmp", "gif", "jpg", "jpeg", "png",
@ -31,6 +33,8 @@ public class MimeTypeUtils
"doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
// 压缩文件
"rar", "zip", "gz", "bz2",
// 视频格式
"mp4", "avi", "rmvb",
// pdf
"pdf" };

View File

@ -22,7 +22,7 @@ public class EscapeUtil
// special HTML characters
TEXT['\''] = "&#039;".toCharArray(); // 单引号
TEXT['"'] = "&#34;".toCharArray(); // 引号
TEXT['"'] = "&#34;".toCharArray(); // 引号
TEXT['&'] = "&#38;".toCharArray(); // &符
TEXT['<'] = "&#60;".toCharArray(); // 小于号
TEXT['>'] = "&#62;".toCharArray(); // 大于号

View File

@ -22,10 +22,12 @@ import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.ss.usermodel.DataValidation;
import org.apache.poi.ss.usermodel.DataValidationConstraint;
import org.apache.poi.ss.usermodel.DataValidationHelper;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
@ -37,6 +39,7 @@ import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFDataValidation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -51,6 +54,8 @@ import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileTypeUtils;
import com.ruoyi.common.utils.file.ImageUtils;
import com.ruoyi.common.utils.reflect.ReflectUtils;
/**
@ -102,16 +107,21 @@ public class ExcelUtil<T>
*/
private List<Object[]> fields;
/**
* 最大高度
*/
private short maxHeight;
/**
* 统计列表
*/
private Map<Integer, Double> statistics = new HashMap<Integer, Double>();
/**
* 数字格式
*/
private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00");
/**
* 实体对象
*/
@ -219,6 +229,10 @@ public class ExcelUtil<T>
{
// 从第2行开始取数据,默认第一行是表头.
Row row = sheet.getRow(i);
if(row == null)
{
continue;
}
T entity = null;
for (Map.Entry<Integer, Field> entry : fieldsMap.entrySet())
{
@ -239,7 +253,15 @@ public class ExcelUtil<T>
}
else
{
val = Convert.toStr(val);
String dateFormat = field.getAnnotation(Excel.class).dateFormat();
if (StringUtils.isNotEmpty(dateFormat))
{
val = DateUtils.parseDateToStr(dateFormat, (Date) val);
}
else
{
val = Convert.toStr(val);
}
}
}
else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val)))
@ -519,8 +541,52 @@ public class ExcelUtil<T>
}
else if (ColumnType.NUMERIC == attr.cellType())
{
cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value));
if (StringUtils.isNotNull(value))
{
cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value));
}
}
else if (ColumnType.IMAGE == attr.cellType())
{
ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1),
cell.getRow().getRowNum() + 1);
String imagePath = Convert.toStr(value);
if (StringUtils.isNotEmpty(imagePath))
{
byte[] data = ImageUtils.getImage(imagePath);
getDrawingPatriarch(cell.getSheet()).createPicture(anchor,
cell.getSheet().getWorkbook().addPicture(data, getImageType(data)));
}
}
}
/**
* 获取画布
*/
public static Drawing<?> getDrawingPatriarch(Sheet sheet)
{
if (sheet.getDrawingPatriarch() == null)
{
sheet.createDrawingPatriarch();
}
return sheet.getDrawingPatriarch();
}
/**
* 获取图片类型,设置图片插入类型
*/
public int getImageType(byte[] value)
{
String type = FileTypeUtils.getFileExtendName(value);
if ("JPG".equalsIgnoreCase(type))
{
return Workbook.PICTURE_TYPE_JPEG;
}
else if ("PNG".equalsIgnoreCase(type))
{
return Workbook.PICTURE_TYPE_PNG;
}
return Workbook.PICTURE_TYPE_JPEG;
}
/**
@ -536,7 +602,6 @@ public class ExcelUtil<T>
{
// 设置列宽
sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256));
row.setHeight((short) (attr.height() * 20));
}
// 如果设置了提示信息则鼠标放上去提示.
if (StringUtils.isNotEmpty(attr.prompt()))
@ -561,7 +626,7 @@ public class ExcelUtil<T>
try
{
// 设置行高
row.setHeight((short) (attr.height() * 20));
row.setHeight(maxHeight);
// 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.
if (attr.isExport())
{
@ -737,7 +802,7 @@ public class ExcelUtil<T>
}
return StringUtils.stripEnd(propertyString.toString(), separator);
}
/**
* 解析字典值
*
@ -763,7 +828,7 @@ public class ExcelUtil<T>
{
return DictUtils.getDictValue(dictType, dictLabel, separator);
}
/**
* 合计统计信息
*/
@ -800,7 +865,7 @@ public class ExcelUtil<T>
cell = row.createCell(0);
cell.setCellStyle(styles.get("total"));
cell.setCellValue("合计");
for (Integer key : keys)
{
cell = row.createCell(key);
@ -916,6 +981,21 @@ public class ExcelUtil<T>
}
}
this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList());
this.maxHeight = getRowHeight();
}
/**
* 根据注解获取最大行高
*/
public short getRowHeight()
{
double maxHeight = 0;
for (Object[] os : this.fields)
{
Excel excel = (Excel) os[1];
maxHeight = maxHeight > excel.height() ? maxHeight : excel.height();
}
return (short) (maxHeight * 20);
}
/**
@ -986,7 +1066,7 @@ public class ExcelUtil<T>
}
else
{
if ((Double) val % 1 > 0)
if ((Double) val % 1 != 0)
{
val = new BigDecimal(val.toString());
}

View File

@ -1,7 +1,5 @@
package com.ruoyi.common.utils.uuid;
import com.ruoyi.common.utils.uuid.UUID;
/**
* ID生成器工具类
*

View File

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

View File

@ -16,6 +16,7 @@ import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.HandlerMapping;
import com.alibaba.fastjson.JSON;
@ -196,7 +197,7 @@ public class LogAspect
{
for (int i = 0; i < paramsArray.length; i++)
{
if (!isFilterObject(paramsArray[i]))
if (StringUtils.isNotNull(paramsArray[i]) && !isFilterObject(paramsArray[i]))
{
Object jsonObj = JSON.toJSON(paramsArray[i]);
params += jsonObj.toString() + " ";
@ -237,6 +238,7 @@ public class LogAspect
return entry.getValue() instanceof MultipartFile;
}
}
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse;
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
|| o instanceof BindingResult;
}
}

View File

@ -8,8 +8,10 @@ import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
/**
* redis配置
@ -31,12 +33,17 @@ public class RedisConfig extends CachingConfigurerSupport
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
serializer.setObjectMapper(mapper);
template.setValueSerializer(serializer);
// 使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
// Hash的key也采用StringRedisSerializer的序列化方式
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}

View File

@ -78,9 +78,9 @@ public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
}
// 唯一标识指定key + 消息头)
String cache_repeat_key = Constants.REPEAT_SUBMIT_KEY + submitKey;
String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + submitKey;
Object sessionObj = redisCache.getCacheObject(cache_repeat_key);
Object sessionObj = redisCache.getCacheObject(cacheRepeatKey);
if (sessionObj != null)
{
Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;
@ -95,7 +95,7 @@ public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
}
Map<String, Object> cacheMap = new HashMap<String, Object>();
cacheMap.put(url, nowDataMap);
redisCache.setCacheObject(cache_repeat_key, cacheMap, intervalTime, TimeUnit.SECONDS);
redisCache.setCacheObject(cacheRepeatKey, cacheMap, intervalTime, TimeUnit.SECONDS);
return false;
}

View File

@ -8,15 +8,20 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.exception.user.CaptchaException;
import com.ruoyi.common.exception.user.CaptchaExpireException;
import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.system.service.ISysUserService;
/**
* 登录校验方法
@ -34,6 +39,9 @@ public class SysLoginService
@Autowired
private RedisCache redisCache;
@Autowired
private ISysUserService userService;
/**
* 登录验证
@ -82,7 +90,18 @@ public class SysLoginService
}
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
recordLoginInfo(loginUser.getUser());
// 生成token
return tokenService.createToken(loginUser);
}
/**
* 记录登录信息
*/
public void recordLoginInfo(SysUser user)
{
user.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
user.setLoginDate(DateUtils.getNowDate());
userService.updateUserProfile(user);
}
}

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId>
<version>3.3.0</version>
<version>3.5.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -23,6 +23,12 @@
<artifactId>velocity</artifactId>
</dependency>
<!-- collections工具类 -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
</dependency>
<!-- 通用工具-->
<dependency>
<groupId>com.ruoyi</groupId>

View File

@ -63,10 +63,12 @@ public class GenController extends BaseController
public AjaxResult getInfo(@PathVariable Long talbleId)
{
GenTable table = genTableService.selectGenTableById(talbleId);
List<GenTable> tables = genTableService.selectGenTableAll();
List<GenTableColumn> list = genTableColumnService.selectGenTableColumnListByTableId(talbleId);
Map<String, Object> map = new HashMap<String, Object>();
map.put("info", table);
map.put("rows", list);
map.put("tables", tables);
return AjaxResult.success(map);
}
@ -99,7 +101,7 @@ public class GenController extends BaseController
/**
* 导入表结构(保存)
*/
@PreAuthorize("@ss.hasPermi('tool:gen:list')")
@PreAuthorize("@ss.hasPermi('tool:gen:import')")
@Log(title = "代码生成", businessType = BusinessType.IMPORT)
@PostMapping("/importTable")
public AjaxResult importTableSave(String tables)

View File

@ -28,11 +28,17 @@ public class GenTable extends BaseEntity
@NotBlank(message = "表描述不能为空")
private String tableComment;
/** 关联父表的表名 */
private String subTableName;
/** 本表关联父表的外键名 */
private String subTableFkName;
/** 实体类名称(首字母大写) */
@NotBlank(message = "实体类名称不能为空")
private String className;
/** 使用的模板crud单表操作 tree树表操作 */
/** 使用的模板crud单表操作 tree树表操作 sub主子表操作 */
private String tplCategory;
/** 生成包路径 */
@ -64,6 +70,9 @@ public class GenTable extends BaseEntity
/** 主键信息 */
private GenTableColumn pkColumn;
/** 子表信息 */
private GenTable subTable;
/** 表列信息 */
@Valid
private List<GenTableColumn> columns;
@ -116,6 +125,26 @@ public class GenTable extends BaseEntity
this.tableComment = tableComment;
}
public String getSubTableName()
{
return subTableName;
}
public void setSubTableName(String subTableName)
{
this.subTableName = subTableName;
}
public String getSubTableFkName()
{
return subTableFkName;
}
public void setSubTableFkName(String subTableFkName)
{
this.subTableFkName = subTableFkName;
}
public String getClassName()
{
return className;
@ -216,6 +245,16 @@ public class GenTable extends BaseEntity
this.pkColumn = pkColumn;
}
public GenTable getSubTable()
{
return subTable;
}
public void setSubTable(GenTable subTable)
{
this.subTable = subTable;
}
public List<GenTableColumn> getColumns()
{
return columns;
@ -286,6 +325,16 @@ public class GenTable extends BaseEntity
this.parentMenuName = parentMenuName;
}
public boolean isSub()
{
return isSub(this.tplCategory);
}
public static boolean isSub(String tplCategory)
{
return tplCategory != null && StringUtils.equals(GenConstants.TPL_SUB, tplCategory);
}
public boolean isTree()
{
return isTree(this.tplCategory);

View File

@ -59,7 +59,7 @@ public class GenTableColumn extends BaseEntity
/** 查询方式EQ等于、NE不等于、GT大于、LT小于、LIKE模糊、BETWEEN范围 */
private String queryType;
/** 显示类型input文本框、textarea文本域、select下拉框、checkbox复选框、radio单选框、datetime日期控件、upload上传控件、editor富文本控件 */
/** 显示类型input文本框、textarea文本域、select下拉框、checkbox复选框、radio单选框、datetime日期控件、image图片上传控件、upload文件上传控件、editor富文本控件 */
private String htmlType;
/** 字典类型 */
@ -138,6 +138,11 @@ public class GenTableColumn extends BaseEntity
return javaField;
}
public String getCapJavaField()
{
return StringUtils.capitalize(javaField);
}
public void setIsPk(String isPk)
{
this.isPk = isPk;

View File

@ -34,6 +34,13 @@ public interface GenTableMapper
*/
public List<GenTable> selectDbTableListByNames(String[] tableNames);
/**
* 查询所有表信息
*
* @return 表信息集合
*/
public List<GenTable> selectGenTableAll();
/**
* 查询表ID业务信息
*

View File

@ -102,6 +102,17 @@ public class GenTableServiceImpl implements IGenTableService
return genTableMapper.selectDbTableListByNames(tableNames);
}
/**
* 查询所有表信息
*
* @return 表信息集合
*/
@Override
public List<GenTable> selectGenTableAll()
{
return genTableMapper.selectGenTableAll();
}
/**
* 修改业务
*
@ -185,9 +196,10 @@ public class GenTableServiceImpl implements IGenTableService
Map<String, String> dataMap = new LinkedHashMap<>();
// 查询表信息
GenTable table = genTableMapper.selectGenTableById(tableId);
// 查询列信息
List<GenTableColumn> columns = table.getColumns();
setPkColumn(table, columns);
// 设置主子表信息
setSubTable(table);
// 设置主键列信息
setPkColumn(table);
VelocityInitializer.initVelocity();
VelocityContext context = VelocityUtils.prepareContext(table);
@ -231,9 +243,10 @@ public class GenTableServiceImpl implements IGenTableService
{
// 查询表信息
GenTable table = genTableMapper.selectGenTableByName(tableName);
// 查询列信息
List<GenTableColumn> columns = table.getColumns();
setPkColumn(table, columns);
// 设置主子表信息
setSubTable(table);
// 设置主键列信息
setPkColumn(table);
VelocityInitializer.initVelocity();
@ -276,6 +289,10 @@ public class GenTableServiceImpl implements IGenTableService
List<String> tableColumnNames = tableColumns.stream().map(GenTableColumn::getColumnName).collect(Collectors.toList());
List<GenTableColumn> dbTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName);
if (StringUtils.isEmpty(dbTableColumns))
{
throw new CustomException("同步数据失败,原表结构不存在");
}
List<String> dbTableColumnNames = dbTableColumns.stream().map(GenTableColumn::getColumnName).collect(Collectors.toList());
dbTableColumns.forEach(column -> {
@ -319,9 +336,10 @@ public class GenTableServiceImpl implements IGenTableService
{
// 查询表信息
GenTable table = genTableMapper.selectGenTableByName(tableName);
// 查询列信息
List<GenTableColumn> columns = table.getColumns();
setPkColumn(table, columns);
// 设置主子表信息
setSubTable(table);
// 设置主键列信息
setPkColumn(table);
VelocityInitializer.initVelocity();
@ -375,6 +393,17 @@ public class GenTableServiceImpl implements IGenTableService
{
throw new CustomException("树名称字段不能为空");
}
else if (GenConstants.TPL_SUB.equals(genTable.getTplCategory()))
{
if (StringUtils.isEmpty(genTable.getSubTableName()))
{
throw new CustomException("关联子表的表名不能为空");
}
else if (StringUtils.isEmpty(genTable.getSubTableFkName()))
{
throw new CustomException("子表关联的外键名不能为空");
}
}
}
}
@ -382,11 +411,10 @@ public class GenTableServiceImpl implements IGenTableService
* 设置主键列信息
*
* @param table 业务表信息
* @param columns 业务字段列表
*/
public void setPkColumn(GenTable table, List<GenTableColumn> columns)
public void setPkColumn(GenTable table)
{
for (GenTableColumn column : columns)
for (GenTableColumn column : table.getColumns())
{
if (column.isPk())
{
@ -396,7 +424,36 @@ public class GenTableServiceImpl implements IGenTableService
}
if (StringUtils.isNull(table.getPkColumn()))
{
table.setPkColumn(columns.get(0));
table.setPkColumn(table.getColumns().get(0));
}
if (GenConstants.TPL_SUB.equals(table.getTplCategory()))
{
for (GenTableColumn column : table.getSubTable().getColumns())
{
if (column.isPk())
{
table.getSubTable().setPkColumn(column);
break;
}
}
if (StringUtils.isNull(table.getSubTable().getPkColumn()))
{
table.getSubTable().setPkColumn(table.getSubTable().getColumns().get(0));
}
}
}
/**
* 设置主子表信息
*
* @param table 业务表信息
*/
public void setSubTable(GenTable table)
{
String subTableName = table.getSubTableName();
if (StringUtils.isNotEmpty(subTableName))
{
table.setSubTable(genTableMapper.selectGenTableByName(subTableName));
}
}

View File

@ -35,6 +35,13 @@ public interface IGenTableService
*/
public List<GenTable> selectDbTableListByNames(String[] tableNames);
/**
* 查询所有表信息
*
* @return 表信息集合
*/
public List<GenTable> selectGenTableAll();
/**
* 查询业务信息
*

View File

@ -40,13 +40,14 @@ public class GenUtils
column.setCreateBy(table.getCreateBy());
// 设置java字段名
column.setJavaField(StringUtils.toCamelCase(columnName));
// 设置默认类型
column.setJavaType(GenConstants.TYPE_STRING);
if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType))
if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType) || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType))
{
column.setJavaType(GenConstants.TYPE_STRING);
// 字符串长度超过500设置为文本域
Integer columnLength = getColumnLength(column.getColumnType());
String htmlType = columnLength >= 500 ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT;
String htmlType = columnLength >= 500 || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType) ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT;
column.setHtmlType(htmlType);
}
else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType))
@ -111,10 +112,15 @@ public class GenUtils
{
column.setHtmlType(GenConstants.HTML_SELECT);
}
// 文件字段设置上传控件
// 图片字段设置图片上传控件
else if (StringUtils.endsWithIgnoreCase(columnName, "image"))
{
column.setHtmlType(GenConstants.HTML_UPLOAD_IMAGE);
column.setHtmlType(GenConstants.HTML_IMAGE_UPLOAD);
}
// 文件字段设置文件上传控件
else if (StringUtils.endsWithIgnoreCase(columnName, "file"))
{
column.setHtmlType(GenConstants.HTML_FILE_UPLOAD);
}
// 内容字段设置富文本控件
else if (StringUtils.endsWithIgnoreCase(columnName, "content"))

View File

@ -7,7 +7,7 @@ import com.ruoyi.common.constant.Constants;
/**
* VelocityEngine工厂
*
* @author RuoYi
* @author ruoyi
*/
public class VelocityInitializer
{
@ -22,7 +22,7 @@ public class VelocityInitializer
// 加载classpath目录下的vm文件
p.setProperty("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
// 定义字符集
p.setProperty(Velocity.ENCODING_DEFAULT, Constants.UTF8);
p.setProperty(Velocity.INPUT_ENCODING, Constants.UTF8);
p.setProperty(Velocity.OUTPUT_ENCODING, Constants.UTF8);
// 初始化Velocity引擎指定配置Properties
Velocity.init(p);

View File

@ -54,7 +54,7 @@ public class VelocityUtils
velocityContext.put("author", genTable.getFunctionAuthor());
velocityContext.put("datetime", DateUtils.getDate());
velocityContext.put("pkColumn", genTable.getPkColumn());
velocityContext.put("importList", getImportList(genTable.getColumns()));
velocityContext.put("importList", getImportList(genTable));
velocityContext.put("permissionPrefix", getPermissionPrefix(moduleName, businessName));
velocityContext.put("columns", genTable.getColumns());
velocityContext.put("table", genTable);
@ -63,6 +63,10 @@ public class VelocityUtils
{
setTreeVelocityContext(velocityContext, genTable);
}
if (GenConstants.TPL_SUB.equals(tplCategory))
{
setSubVelocityContext(velocityContext, genTable);
}
return velocityContext;
}
@ -96,6 +100,24 @@ public class VelocityUtils
}
}
public static void setSubVelocityContext(VelocityContext context, GenTable genTable)
{
GenTable subTable = genTable.getSubTable();
String subTableName = genTable.getSubTableName();
String subTableFkName = genTable.getSubTableFkName();
String subClassName = genTable.getSubTable().getClassName();
String subTableFkClassName = StringUtils.convertToCamelCase(subTableFkName);
context.put("subTable", subTable);
context.put("subTableName", subTableName);
context.put("subTableFkName", subTableFkName);
context.put("subTableFkClassName", subTableFkClassName);
context.put("subTableFkclassName", StringUtils.uncapitalize(subTableFkClassName));
context.put("subClassName", subClassName);
context.put("subclassName", StringUtils.uncapitalize(subClassName));
context.put("subImportList", getImportList(genTable.getSubTable()));
}
/**
* 获取模板信息
*
@ -120,6 +142,11 @@ public class VelocityUtils
{
templates.add("vm/vue/index-tree.vue.vm");
}
else if (GenConstants.TPL_SUB.equals(tplCategory))
{
templates.add("vm/vue/index.vue.vm");
templates.add("vm/java/sub-domain.java.vm");
}
return templates;
}
@ -147,6 +174,10 @@ public class VelocityUtils
{
fileName = StringUtils.format("{}/domain/{}.java", javaPath, className);
}
if (template.contains("sub-domain.java.vm") && StringUtils.equals(GenConstants.TPL_SUB, genTable.getTplCategory()))
{
fileName = StringUtils.format("{}/domain/{}.java", javaPath, genTable.getSubTable().getClassName());
}
else if (template.contains("mapper.java.vm"))
{
fileName = StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className);
@ -201,13 +232,19 @@ public class VelocityUtils
/**
* 根据列类型获取导入包
*
* @param columns 列集合
*
* @param genTable 业务表对象
* @return 返回需要导入的包列表
*/
public static HashSet<String> getImportList(List<GenTableColumn> columns)
public static HashSet<String> getImportList(GenTable genTable)
{
List<GenTableColumn> columns = genTable.getColumns();
GenTable subGenTable = genTable.getSubTable();
HashSet<String> importList = new HashSet<String>();
if (StringUtils.isNotNull(subGenTable))
{
importList.add("java.util.List");
}
for (GenTableColumn column : columns)
{
if (!column.isSuperColumn() && GenConstants.TYPE_DATE.equals(column.getJavaType()))

View File

@ -5,24 +5,26 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<mapper namespace="com.ruoyi.generator.mapper.GenTableMapper">
<resultMap type="GenTable" id="GenTableResult">
<id property="tableId" column="table_id" />
<result property="tableName" column="table_name" />
<result property="tableComment" column="table_comment" />
<result property="className" column="class_name" />
<result property="tplCategory" column="tpl_category" />
<result property="packageName" column="package_name" />
<result property="moduleName" column="module_name" />
<result property="businessName" column="business_name" />
<result property="functionName" column="function_name" />
<result property="functionAuthor" column="function_author" />
<result property="genType" column="gen_type" />
<result property="genPath" column="gen_path" />
<result property="options" column="options" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="remark" column="remark" />
<id property="tableId" column="table_id" />
<result property="tableName" column="table_name" />
<result property="tableComment" column="table_comment" />
<result property="subTableName" column="sub_table_name" />
<result property="subTableFkName" column="sub_table_fk_name" />
<result property="className" column="class_name" />
<result property="tplCategory" column="tpl_category" />
<result property="packageName" column="package_name" />
<result property="moduleName" column="module_name" />
<result property="businessName" column="business_name" />
<result property="functionName" column="function_name" />
<result property="functionAuthor" column="function_author" />
<result property="genType" column="gen_type" />
<result property="genPath" column="gen_path" />
<result property="options" column="options" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="remark" column="remark" />
<collection property="columns" javaType="java.util.List" resultMap="GenTableColumnResult" />
</resultMap>
@ -52,7 +54,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap>
<sql id="selectGenTableVo">
select table_id, table_name, table_comment, class_name, tpl_category, package_name, module_name, business_name, function_name, function_author, gen_type, gen_path, options, create_by, create_time, update_by, update_time, remark from gen_table
select table_id, table_name, table_comment, sub_table_name, sub_table_fk_name, class_name, tpl_category, package_name, module_name, business_name, function_name, function_author, gen_type, gen_path, options, create_by, create_time, update_by, update_time, remark from gen_table
</sql>
<select id="selectGenTableList" parameterType="GenTable" resultMap="GenTableResult">
@ -108,7 +110,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</select>
<select id="selectGenTableById" parameterType="Long" resultMap="GenTableResult">
SELECT t.table_id, t.table_name, t.table_comment, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.gen_type, t.gen_path, t.options, t.remark,
SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.gen_type, t.gen_path, t.options, t.remark,
c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort
FROM gen_table t
LEFT JOIN gen_table_column c ON t.table_id = c.table_id
@ -116,13 +118,21 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</select>
<select id="selectGenTableByName" parameterType="String" resultMap="GenTableResult">
SELECT t.table_id, t.table_name, t.table_comment, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.gen_type, t.gen_path, t.options, t.remark,
SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.gen_type, t.gen_path, t.options, t.remark,
c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort
FROM gen_table t
LEFT JOIN gen_table_column c ON t.table_id = c.table_id
where t.table_name = #{tableName} order by c.sort
</select>
<select id="selectGenTableAll" parameterType="String" resultMap="GenTableResult">
SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.options, t.remark,
c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort
FROM gen_table t
LEFT JOIN gen_table_column c ON t.table_id = c.table_id
order by c.sort
</select>
<insert id="insertGenTable" parameterType="GenTable" useGeneratedKeys="true" keyProperty="tableId">
insert into gen_table (
<if test="tableName != null">table_name,</if>
@ -162,6 +172,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<set>
<if test="tableName != null">table_name = #{tableName},</if>
<if test="tableComment != null and tableComment != ''">table_comment = #{tableComment},</if>
<if test="subTableName != null">sub_table_name = #{subTableName},</if>
<if test="subTableFkName != null">sub_table_fk_name = #{subTableFkName},</if>
<if test="className != null and className != ''">class_name = #{className},</if>
<if test="functionAuthor != null and functionAuthor != ''">function_author = #{functionAuthor},</if>
<if test="genType != null and genType != ''">gen_type = #{genType},</if>

View File

@ -1,114 +1,114 @@
package ${packageName}.controller;
import java.util.List;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import ${packageName}.domain.${ClassName};
import ${packageName}.service.I${ClassName}Service;
import com.ruoyi.common.utils.poi.ExcelUtil;
#if($table.crud)
import com.ruoyi.common.core.page.TableDataInfo;
#elseif($table.tree)
#end
/**
* ${functionName}Controller
*
* @author ${author}
* @date ${datetime}
*/
@RestController
@RequestMapping("/${moduleName}/${businessName}")
public class ${ClassName}Controller extends BaseController
{
@Autowired
private I${ClassName}Service ${className}Service;
/**
* 查询${functionName}列表
*/
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')")
@GetMapping("/list")
#if($table.crud)
public TableDataInfo list(${ClassName} ${className})
{
startPage();
List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});
return getDataTable(list);
}
#elseif($table.tree)
public AjaxResult list(${ClassName} ${className})
{
List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});
return AjaxResult.success(list);
}
#end
/**
* 导出${functionName}列表
*/
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:export')")
@Log(title = "${functionName}", businessType = BusinessType.EXPORT)
@GetMapping("/export")
public AjaxResult export(${ClassName} ${className})
{
List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});
ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}.class);
return util.exportExcel(list, "${businessName}");
}
/**
* 获取${functionName}详细信息
*/
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:query')")
@GetMapping(value = "/{${pkColumn.javaField}}")
public AjaxResult getInfo(@PathVariable("${pkColumn.javaField}") ${pkColumn.javaType} ${pkColumn.javaField})
{
return AjaxResult.success(${className}Service.select${ClassName}ById(${pkColumn.javaField}));
}
/**
* 新增${functionName}
*/
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:add')")
@Log(title = "${functionName}", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody ${ClassName} ${className})
{
return toAjax(${className}Service.insert${ClassName}(${className}));
}
/**
* 修改${functionName}
*/
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:edit')")
@Log(title = "${functionName}", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody ${ClassName} ${className})
{
return toAjax(${className}Service.update${ClassName}(${className}));
}
/**
* 删除${functionName}
*/
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:remove')")
@Log(title = "${functionName}", businessType = BusinessType.DELETE)
@DeleteMapping("/{${pkColumn.javaField}s}")
public AjaxResult remove(@PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s)
{
return toAjax(${className}Service.delete${ClassName}ByIds(${pkColumn.javaField}s));
}
}
package ${packageName}.controller;
import java.util.List;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import ${packageName}.domain.${ClassName};
import ${packageName}.service.I${ClassName}Service;
import com.ruoyi.common.utils.poi.ExcelUtil;
#if($table.crud || $table.sub)
import com.ruoyi.common.core.page.TableDataInfo;
#elseif($table.tree)
#end
/**
* ${functionName}Controller
*
* @author ${author}
* @date ${datetime}
*/
@RestController
@RequestMapping("/${moduleName}/${businessName}")
public class ${ClassName}Controller extends BaseController
{
@Autowired
private I${ClassName}Service ${className}Service;
/**
* 查询${functionName}列表
*/
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')")
@GetMapping("/list")
#if($table.crud || $table.sub)
public TableDataInfo list(${ClassName} ${className})
{
startPage();
List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});
return getDataTable(list);
}
#elseif($table.tree)
public AjaxResult list(${ClassName} ${className})
{
List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});
return AjaxResult.success(list);
}
#end
/**
* 导出${functionName}列表
*/
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:export')")
@Log(title = "${functionName}", businessType = BusinessType.EXPORT)
@GetMapping("/export")
public AjaxResult export(${ClassName} ${className})
{
List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});
ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}.class);
return util.exportExcel(list, "${functionName}数据");
}
/**
* 获取${functionName}详细信息
*/
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:query')")
@GetMapping(value = "/{${pkColumn.javaField}}")
public AjaxResult getInfo(@PathVariable("${pkColumn.javaField}") ${pkColumn.javaType} ${pkColumn.javaField})
{
return AjaxResult.success(${className}Service.select${ClassName}ById(${pkColumn.javaField}));
}
/**
* 新增${functionName}
*/
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:add')")
@Log(title = "${functionName}", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody ${ClassName} ${className})
{
return toAjax(${className}Service.insert${ClassName}(${className}));
}
/**
* 修改${functionName}
*/
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:edit')")
@Log(title = "${functionName}", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody ${ClassName} ${className})
{
return toAjax(${className}Service.update${ClassName}(${className}));
}
/**
* 删除${functionName}
*/
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:remove')")
@Log(title = "${functionName}", businessType = BusinessType.DELETE)
@DeleteMapping("/{${pkColumn.javaField}s}")
public AjaxResult remove(@PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s)
{
return toAjax(${className}Service.delete${ClassName}ByIds(${pkColumn.javaField}s));
}
}

View File

@ -6,7 +6,7 @@ import ${import};
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
#if($table.crud)
#if($table.crud || $table.sub)
import com.ruoyi.common.core.domain.BaseEntity;
#elseif($table.tree)
import com.ruoyi.common.core.domain.TreeEntity;
@ -18,7 +18,7 @@ import com.ruoyi.common.core.domain.TreeEntity;
* @author ${author}
* @date ${datetime}
*/
#if($table.crud)
#if($table.crud || $table.sub)
#set($Entity="BaseEntity")
#elseif($table.tree)
#set($Entity="TreeEntity")
@ -49,6 +49,11 @@ public class ${ClassName} extends ${Entity}
private $column.javaType $column.javaField;
#end
#end
#if($table.sub)
/** $table.subTable.functionName信息 */
private List<${subClassName}> ${subclassName}List;
#end
#foreach ($column in $columns)
#if(!$table.isSuperColumn($column.javaField))
@ -69,6 +74,18 @@ public class ${ClassName} extends ${Entity}
#end
#end
#if($table.sub)
public List<${subClassName}> get${subClassName}List()
{
return ${subclassName}List;
}
public void set${subClassName}List(List<${subClassName}> ${subclassName}List)
{
this.${subclassName}List = ${subclassName}List;
}
#end
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
@ -79,6 +96,9 @@ public class ${ClassName} extends ${Entity}
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#end
.append("${column.javaField}", get${AttrName}())
#end
#if($table.sub)
.append("${subclassName}List", get${subClassName}List())
#end
.toString();
}

View File

@ -1,61 +1,91 @@
package ${packageName}.mapper;
import java.util.List;
import ${packageName}.domain.${ClassName};
/**
* ${functionName}Mapper接口
*
* @author ${author}
* @date ${datetime}
*/
public interface ${ClassName}Mapper
{
/**
* 查询${functionName}
*
* @param ${pkColumn.javaField} ${functionName}ID
* @return ${functionName}
*/
public ${ClassName} select${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField});
/**
* 查询${functionName}列表
*
* @param ${className} ${functionName}
* @return ${functionName}集合
*/
public List<${ClassName}> select${ClassName}List(${ClassName} ${className});
/**
* 新增${functionName}
*
* @param ${className} ${functionName}
* @return 结果
*/
public int insert${ClassName}(${ClassName} ${className});
/**
* 修改${functionName}
*
* @param ${className} ${functionName}
* @return 结果
*/
public int update${ClassName}(${ClassName} ${className});
/**
* 删除${functionName}
*
* @param ${pkColumn.javaField} ${functionName}ID
* @return 结果
*/
public int delete${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField});
/**
* 批量删除${functionName}
*
* @param ${pkColumn.javaField}s 需要删除的数据ID
* @return 结果
*/
public int delete${ClassName}ByIds(${pkColumn.javaType}[] ${pkColumn.javaField}s);
}
package ${packageName}.mapper;
import java.util.List;
import ${packageName}.domain.${ClassName};
#if($table.sub)
import ${packageName}.domain.${subClassName};
#end
/**
* ${functionName}Mapper接口
*
* @author ${author}
* @date ${datetime}
*/
public interface ${ClassName}Mapper
{
/**
* 查询${functionName}
*
* @param ${pkColumn.javaField} ${functionName}ID
* @return ${functionName}
*/
public ${ClassName} select${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField});
/**
* 查询${functionName}列表
*
* @param ${className} ${functionName}
* @return ${functionName}集合
*/
public List<${ClassName}> select${ClassName}List(${ClassName} ${className});
/**
* 新增${functionName}
*
* @param ${className} ${functionName}
* @return 结果
*/
public int insert${ClassName}(${ClassName} ${className});
/**
* 修改${functionName}
*
* @param ${className} ${functionName}
* @return 结果
*/
public int update${ClassName}(${ClassName} ${className});
/**
* 删除${functionName}
*
* @param ${pkColumn.javaField} ${functionName}ID
* @return 结果
*/
public int delete${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField});
/**
* 批量删除${functionName}
*
* @param ${pkColumn.javaField}s 需要删除的数据ID
* @return 结果
*/
public int delete${ClassName}ByIds(${pkColumn.javaType}[] ${pkColumn.javaField}s);
#if($table.sub)
/**
* 批量删除${subTable.functionName}
*
* @param customerIds 需要删除的数据ID
* @return 结果
*/
public int delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaType}[] ${pkColumn.javaField}s);
/**
* 批量新增${subTable.functionName}
*
* @param ${subclassName}List ${subTable.functionName}列表
* @return 结果
*/
public int batch${subClassName}(List<${subClassName}> ${subclassName}List);
/**
* 通过${functionName}ID删除${subTable.functionName}信息
*
* @param ${pkColumn.javaField} ${functionName}ID
* @return 结果
*/
public int delete${subClassName}By${subTableFkClassName}(${pkColumn.javaType} ${pkColumn.javaField});
#end
}

View File

@ -9,6 +9,12 @@ import com.ruoyi.common.utils.DateUtils;
#end
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#if($table.sub)
import java.util.ArrayList;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.transaction.annotation.Transactional;
import ${packageName}.domain.${subClassName};
#end
import ${packageName}.mapper.${ClassName}Mapper;
import ${packageName}.domain.${ClassName};
import ${packageName}.service.I${ClassName}Service;
@ -55,6 +61,9 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service
* @param ${className} ${functionName}
* @return 结果
*/
#if($table.sub)
@Transactional
#end
@Override
public int insert${ClassName}(${ClassName} ${className})
{
@ -63,7 +72,13 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service
${className}.setCreateTime(DateUtils.getNowDate());
#end
#end
#if($table.sub)
int rows = ${className}Mapper.insert${ClassName}(${className});
insert${subClassName}(${className});
return rows;
#else
return ${className}Mapper.insert${ClassName}(${className});
#end
}
/**
@ -72,6 +87,9 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service
* @param ${className} ${functionName}
* @return 结果
*/
#if($table.sub)
@Transactional
#end
@Override
public int update${ClassName}(${ClassName} ${className})
{
@ -79,6 +97,10 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service
#if($column.javaField == 'updateTime')
${className}.setUpdateTime(DateUtils.getNowDate());
#end
#end
#if($table.sub)
${className}Mapper.delete${subClassName}By${subTableFkClassName}(${className}.get${pkColumn.capJavaField}());
insert${subClassName}(${className});
#end
return ${className}Mapper.update${ClassName}(${className});
}
@ -89,9 +111,15 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service
* @param ${pkColumn.javaField}s 需要删除的${functionName}ID
* @return 结果
*/
#if($table.sub)
@Transactional
#end
@Override
public int delete${ClassName}ByIds(${pkColumn.javaType}[] ${pkColumn.javaField}s)
{
#if($table.sub)
${className}Mapper.delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaField}s);
#end
return ${className}Mapper.delete${ClassName}ByIds(${pkColumn.javaField}s);
}
@ -104,6 +132,35 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service
@Override
public int delete${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField})
{
#if($table.sub)
${className}Mapper.delete${subClassName}By${subTableFkClassName}(${pkColumn.javaField});
#end
return ${className}Mapper.delete${ClassName}ById(${pkColumn.javaField});
}
#if($table.sub)
/**
* 新增${subTable.functionName}信息
*
* @param ${className} ${functionName}对象
*/
public void insert${subClassName}(${ClassName} ${className})
{
List<${subClassName}> ${subclassName}List = ${className}.get${subClassName}List();
Long ${pkColumn.javaField} = ${className}.get${pkColumn.capJavaField}();
if (StringUtils.isNotNull(${subclassName}List))
{
List<${subClassName}> list = new ArrayList<${subClassName}>();
for (${subClassName} ${subclassName} : ${subclassName}List)
{
${subclassName}.set${subTableFkClassName}(${pkColumn.javaField});
list.add(${subclassName});
}
if (list.size() > 0)
{
${className}Mapper.batch${subClassName}(list);
}
}
}
#end
}

View File

@ -0,0 +1,76 @@
package ${packageName}.domain;
#foreach ($import in $subImportList)
import ${import};
#end
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
/**
* ${subTable.functionName}对象 ${subTableName}
*
* @author ${author}
* @date ${datetime}
*/
public class ${subClassName} extends BaseEntity
{
private static final long serialVersionUID = 1L;
#foreach ($column in $subTable.columns)
#if(!$table.isSuperColumn($column.javaField))
/** $column.columnComment */
#if($column.list)
#set($parentheseIndex=$column.columnComment.indexOf(""))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if($parentheseIndex != -1)
@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
#elseif($column.javaType == 'Date')
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd")
#else
@Excel(name = "${comment}")
#end
#end
private $column.javaType $column.javaField;
#end
#end
#foreach ($column in $subTable.columns)
#if(!$table.isSuperColumn($column.javaField))
#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]"))
#set($AttrName=$column.javaField)
#else
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#end
public void set${AttrName}($column.javaType $column.javaField)
{
this.$column.javaField = $column.javaField;
}
public $column.javaType get${AttrName}()
{
return $column.javaField;
}
#end
#end
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
#foreach ($column in $subTable.columns)
#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]"))
#set($AttrName=$column.javaField)
#else
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#end
.append("${column.javaField}", get${AttrName}())
#end
.toString();
}
}

View File

@ -38,20 +38,33 @@
<el-option label="请选择字典生成" value="" />
</el-select>
</el-form-item>
#elseif($column.htmlType == "datetime")
#elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
<el-form-item label="${comment}" prop="${column.javaField}">
<el-date-picker clearable size="small" style="width: 200px"
<el-date-picker clearable size="small"
v-model="queryParams.${column.javaField}"
type="date"
value-format="yyyy-MM-dd"
placeholder="选择${comment}">
</el-date-picker>
</el-form-item>
#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
<el-form-item label="${comment}">
<el-date-picker
v-model="daterange${AttrName}"
size="small"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
#end
#end
#end
<el-form-item>
<el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
@ -60,6 +73,7 @@
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
@ -94,8 +108,12 @@
#elseif($column.list && "" != $column.dictType)
<el-table-column label="${comment}" align="center" prop="${javaField}" :formatter="${javaField}Format" />
#elseif($column.list && "" != $javaField)
#if(${foreach.index} == 1)
<el-table-column label="${comment}" prop="${javaField}" />
#else
<el-table-column label="${comment}" align="center" prop="${javaField}" />
#end
#end
#end
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
@ -106,6 +124,13 @@
@click="handleUpdate(scope.row)"
v-hasPermi="['${moduleName}:${businessName}:edit']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-plus"
@click="handleAdd(scope.row)"
v-hasPermi="['${moduleName}:${businessName}:add']"
>新增</el-button>
<el-button
size="mini"
type="text"
@ -139,9 +164,13 @@
<el-form-item label="${comment}" prop="${field}">
<el-input v-model="form.${field}" placeholder="请输入${comment}" />
</el-form-item>
#elseif($column.htmlType == "uploadImage")
#elseif($column.htmlType == "imageUpload")
<el-form-item label="${comment}">
<uploadImage v-model="form.${field}"/>
<imageUpload v-model="form.${field}"/>
</el-form-item>
#elseif($column.htmlType == "fileUpload")
<el-form-item label="${comment}">
<fileUpload v-model="form.${field}"/>
</el-form-item>
#elseif($column.htmlType == "editor")
<el-form-item label="${comment}">
@ -201,7 +230,7 @@
</el-form-item>
#elseif($column.htmlType == "datetime")
<el-form-item label="${comment}" prop="${field}">
<el-date-picker clearable size="small" style="width: 200px"
<el-date-picker clearable size="small"
v-model="form.${field}"
type="date"
value-format="yyyy-MM-dd"
@ -230,8 +259,14 @@ import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${Busin
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "uploadImage")
import UploadImage from '@/components/UploadImage';
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "imageUpload")
import ImageUpload from '@/components/ImageUpload';
#break
#end
#end
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "fileUpload")
import FileUpload from '@/components/FileUpload';
#break
#end
#end
@ -246,8 +281,14 @@ export default {
name: "${BusinessName}",
components: {
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "uploadImage")
UploadImage,
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "imageUpload")
ImageUpload,
#break
#end
#end
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "fileUpload")
FileUpload,
#break
#end
#end
@ -283,6 +324,10 @@ export default {
#if(${column.dictType} != '')
// $comment字典
${column.javaField}Options: [],
#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
// $comment时间范围
daterange${AttrName}: [],
#end
#end
// 查询参数
@ -329,6 +374,21 @@ export default {
/** 查询${functionName}列表 */
getList() {
this.loading = true;
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
this.queryParams.params = {};
#break
#end
#end
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
if (null != this.daterange${AttrName} && '' != this.daterange${AttrName}) {
this.queryParams.params["begin${AttrName}"] = this.daterange${AttrName}[0];
this.queryParams.params["end${AttrName}"] = this.daterange${AttrName}[1];
}
#end
#end
list${BusinessName}(this.queryParams).then(response => {
this.${businessName}List = this.handleTree(response.data, "${treeCode}", "${treeParentCode}");
this.loading = false;
@ -345,7 +405,7 @@ export default {
children: node.children
};
},
/** 查询部门下拉树结构 */
/** 查询${functionName}下拉树结构 */
getTreeselect() {
list${BusinessName}().then(response => {
this.${businessName}Options = [];
@ -397,20 +457,31 @@ export default {
},
/** 重置按钮操作 */
resetQuery() {
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
this.daterange${AttrName} = [];
#end
#end
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
handleAdd(row) {
this.reset();
this.getTreeselect();
this.getTreeselect();
if (row != null && row.${treeCode}) {
this.form.${treeParentCode} = row.${treeCode};
} else {
this.form.${treeParentCode} = 0;
}
this.open = true;
this.title = "添加${functionName}";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
this.getTreeselect();
this.getTreeselect();
if (row != null) {
this.form.${treeParentCode} = row.${treeCode};
}

View File

@ -38,20 +38,33 @@
<el-option label="请选择字典生成" value="" />
</el-select>
</el-form-item>
#elseif($column.htmlType == "datetime")
#elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
<el-form-item label="${comment}" prop="${column.javaField}">
<el-date-picker clearable size="small" style="width: 200px"
<el-date-picker clearable size="small"
v-model="queryParams.${column.javaField}"
type="date"
value-format="yyyy-MM-dd"
placeholder="选择${comment}">
</el-date-picker>
</el-form-item>
#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
<el-form-item label="${comment}">
<el-date-picker
v-model="daterange${AttrName}"
size="small"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
#end
#end
#end
<el-form-item>
<el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
@ -60,6 +73,7 @@
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
@ -69,6 +83,7 @@
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single"
@ -79,6 +94,7 @@
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@ -89,13 +105,15 @@
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
:loading="exportLoading"
@click="handleExport"
v-hasPermi="['${moduleName}:${businessName}:export']"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="${businessName}List" @selection-change="handleSelectionChange">
@ -168,9 +186,13 @@
<el-form-item label="${comment}" prop="${field}">
<el-input v-model="form.${field}" placeholder="请输入${comment}" />
</el-form-item>
#elseif($column.htmlType == "uploadImage")
#elseif($column.htmlType == "imageUpload")
<el-form-item label="${comment}">
<uploadImage v-model="form.${field}"/>
<imageUpload v-model="form.${field}"/>
</el-form-item>
#elseif($column.htmlType == "fileUpload")
<el-form-item label="${comment}">
<fileUpload v-model="form.${field}"/>
</el-form-item>
#elseif($column.htmlType == "editor")
<el-form-item label="${comment}">
@ -230,7 +252,7 @@
</el-form-item>
#elseif($column.htmlType == "datetime")
<el-form-item label="${comment}" prop="${field}">
<el-date-picker clearable size="small" style="width: 200px"
<el-date-picker clearable size="small"
v-model="form.${field}"
type="date"
value-format="yyyy-MM-dd"
@ -244,6 +266,38 @@
#end
#end
#end
#end
#if($table.sub)
<el-divider content-position="center">${subTable.functionName}信息</el-divider>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd${subClassName}">添加</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" icon="el-icon-delete" size="mini" @click="handleDelete${subClassName}">删除</el-button>
</el-col>
</el-row>
<el-table :data="${subclassName}List" :row-class-name="row${subClassName}Index" @selection-change="handle${subClassName}SelectionChange" ref="${subclassName}">
<el-table-column type="selection" width="50" align="center" />
<el-table-column label="序号" align="center" prop="index" width="50"/>
#foreach($column in $subTable.columns)
#set($javaField=$column.javaField)
#set($parentheseIndex=$column.columnComment.indexOf(""))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if($column.pk || $javaField == ${subTableFkclassName})
#elseif($column.list && "" != $javaField)
<el-table-column label="$comment" prop="${javaField}">
<template slot-scope="scope">
<el-input v-model="scope.row.$javaField" placeholder="请输入$comment" />
</template>
</el-table-column>
#end
#end
</el-table>
#end
</el-form>
<div slot="footer" class="dialog-footer">
@ -257,8 +311,14 @@
<script>
import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName}, export${BusinessName} } from "@/api/${moduleName}/${businessName}";
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "uploadImage")
import UploadImage from '@/components/UploadImage';
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "imageUpload")
import ImageUpload from '@/components/ImageUpload';
#break
#end
#end
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "fileUpload")
import FileUpload from '@/components/FileUpload';
#break
#end
#end
@ -273,8 +333,14 @@ export default {
name: "${BusinessName}",
components: {
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "uploadImage")
UploadImage,
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "imageUpload")
ImageUpload,
#break
#end
#end
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "fileUpload")
FileUpload,
#break
#end
#end
@ -289,8 +355,14 @@ export default {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 选中数组
ids: [],
#if($table.sub)
// 子表选中数据
checked${subClassName}: [],
#end
// 非单个禁用
single: true,
// 非多个禁用
@ -301,6 +373,10 @@ export default {
total: 0,
// ${functionName}表格数据
${businessName}List: [],
#if($table.sub)
// ${subTable.functionName}表格数据
${subclassName}List: [],
#end
// 弹出层标题
title: "",
// 是否显示弹出层
@ -315,6 +391,10 @@ export default {
#if(${column.dictType} != '')
// $comment字典
${column.javaField}Options: [],
#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
// $comment时间范围
daterange${AttrName}: [],
#end
#end
// 查询参数
@ -363,6 +443,21 @@ export default {
/** 查询${functionName}列表 */
getList() {
this.loading = true;
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
this.queryParams.params = {};
#break
#end
#end
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
if (null != this.daterange${AttrName} && '' != this.daterange${AttrName}) {
this.queryParams.params["begin${AttrName}"] = this.daterange${AttrName}[0];
this.queryParams.params["end${AttrName}"] = this.daterange${AttrName}[1];
}
#end
#end
list${BusinessName}(this.queryParams).then(response => {
this.${businessName}List = response.rows;
this.total = response.total;
@ -404,6 +499,9 @@ export default {
#end
#end
};
#if($table.sub)
this.${subclassName}List = [];
#end
this.resetForm("form");
},
/** 搜索按钮操作 */
@ -413,6 +511,12 @@ export default {
},
/** 重置按钮操作 */
resetQuery() {
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
this.daterange${AttrName} = [];
#end
#end
this.resetForm("queryForm");
this.handleQuery();
},
@ -438,6 +542,9 @@ export default {
#if($column.htmlType == "checkbox")
this.form.$column.javaField = this.form.${column.javaField}.split(",");
#end
#end
#if($table.sub)
this.${subclassName}List = response.data.${subclassName}List;
#end
this.open = true;
this.title = "修改${functionName}";
@ -451,6 +558,9 @@ export default {
#if($column.htmlType == "checkbox")
this.form.$column.javaField = this.form.${column.javaField}.join(",");
#end
#end
#if($table.sub)
this.form.${subclassName}List = this.${subclassName}List;
#end
if (this.form.${pkColumn.javaField} != null) {
update${BusinessName}(this.form).then(response => {
@ -482,6 +592,40 @@ export default {
this.msgSuccess("删除成功");
})
},
#if($table.sub)
/** ${subTable.functionName}序号 */
row${subClassName}Index({ row, rowIndex }) {
row.index = rowIndex + 1;
},
/** ${subTable.functionName}添加按钮操作 */
handleAdd${subClassName}() {
let obj = {};
#foreach($column in $subTable.columns)
#if($column.pk || $column.javaField == ${subTableFkclassName})
#elseif($column.list && "" != $javaField)
obj.$column.javaField = "";
#end
#end
this.${subclassName}List.push(obj);
},
/** ${subTable.functionName}删除按钮操作 */
handleDelete${subClassName}() {
if (this.checked${subClassName}.length == 0) {
this.$alert("请先选择要删除的${subTable.functionName}数据", "提示", { confirmButtonText: "确定", });
} else {
this.${subclassName}List.splice(this.checked${subClassName}[0].index - 1, 1);
}
},
/** 单选框选中数据 */
handle${subClassName}SelectionChange(selection) {
if (selection.length > 1) {
this.$refs.${subclassName}.clearSelection();
this.$refs.${subclassName}.toggleRowSelection(selection.pop());
} else {
this.checked${subClassName} = selection;
}
},
#end
/** 导出按钮操作 */
handleExport() {
const queryParams = this.queryParams;
@ -489,10 +633,12 @@ export default {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
}).then(() => {
this.exportLoading = true;
return export${BusinessName}(queryParams);
}).then(response => {
this.download(response.msg);
this.exportLoading = false;
})
}
}

View File

@ -9,6 +9,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="${column.javaField}" column="${column.columnName}" />
#end
</resultMap>
#if($table.sub)
<resultMap id="${ClassName}${subClassName}Result" type="${ClassName}" extends="${ClassName}Result">
<collection property="${subclassName}List" notNullColumn="sub_${subTable.pkColumn.columnName}" javaType="java.util.List" resultMap="${subClassName}Result" />
</resultMap>
<resultMap type="${subClassName}" id="${subClassName}Result">
#foreach ($column in $subTable.columns)
<result property="${column.javaField}" column="sub_${column.columnName}" />
#end
</resultMap>
#end
<sql id="select${ClassName}Vo">
select#foreach($column in $columns) $column.columnName#if($velocityCount != $columns.size()),#end#end from ${tableName}
@ -46,9 +58,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</where>
</select>
<select id="select${ClassName}ById" parameterType="${pkColumn.javaType}" resultMap="${ClassName}Result">
<select id="select${ClassName}ById" parameterType="${pkColumn.javaType}" resultMap="#if($table.sub)${ClassName}${subClassName}Result#else${ClassName}Result#end">
#if($table.crud || $table.tree)
<include refid="select${ClassName}Vo"/>
where ${pkColumn.columnName} = #{${pkColumn.javaField}}
#elseif($table.sub)
select#foreach($column in $columns) a.$column.columnName#if($velocityCount != $columns.size()),#end#end,
#foreach($column in $subTable.columns) b.$column.columnName as sub_$column.columnName#if($velocityCount != $subTable.columns.size()),#end#end
from ${tableName} a
left join ${subTableName} b on b.${subTableFkName} = a.${pkColumn.columnName}
where a.${pkColumn.columnName} = #{${pkColumn.javaField}}
#end
</select>
<insert id="insert${ClassName}" parameterType="${ClassName}"#if($pkColumn.increment) useGeneratedKeys="true" keyProperty="$pkColumn.javaField"#end>
@ -91,5 +112,24 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
#{${pkColumn.javaField}}
</foreach>
</delete>
#if($table.sub)
<delete id="delete${subClassName}By${subTableFkClassName}s" parameterType="String">
delete from ${subTableName} where ${subTableFkName} in
<foreach item="${subTableFkclassName}" collection="array" open="(" separator="," close=")">
#{${subTableFkclassName}}
</foreach>
</delete>
<delete id="delete${subClassName}By${subTableFkClassName}" parameterType="Long">
delete from ${subTableName} where ${subTableFkName} = #{${subTableFkclassName}}
</delete>
<insert id="batch${subClassName}">
insert into ${subTableName}(#foreach($column in $subTable.columns) $column.columnName#if($velocityCount != $subTable.columns.size()),#end#end) values
<foreach item="item" index="index" collection="list" separator=",">
(#foreach($column in $subTable.columns) #{item.$column.javaField}#if($velocityCount != $subTable.columns.size()),#end#end)
</foreach>
</insert>
#end
</mapper>

View File

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

View File

@ -1,6 +1,5 @@
package com.ruoyi.quartz.domain;
import java.io.Serializable;
import java.util.Date;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
@ -19,7 +18,7 @@ import com.ruoyi.quartz.util.CronUtils;
*
* @author ruoyi
*/
public class SysJob extends BaseEntity implements Serializable
public class SysJob extends BaseEntity
{
private static final long serialVersionUID = 1L;

View File

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

View File

@ -241,7 +241,7 @@ public class SysDeptServiceImpl implements ISysDeptService
List<SysDept> children = deptMapper.selectChildrenDeptById(deptId);
for (SysDept child : children)
{
child.setAncestors(child.getAncestors().replace(oldAncestors, newAncestors));
child.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors));
}
if (children.size() > 0)
{

View File

@ -1,465 +1,466 @@
package com.ruoyi.system.service.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.TreeSelect;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.vo.MetaVo;
import com.ruoyi.system.domain.vo.RouterVo;
import com.ruoyi.system.mapper.SysMenuMapper;
import com.ruoyi.system.mapper.SysRoleMapper;
import com.ruoyi.system.mapper.SysRoleMenuMapper;
import com.ruoyi.system.service.ISysMenuService;
/**
* 菜单 业务层处理
*
* @author ruoyi
*/
@Service
public class SysMenuServiceImpl implements ISysMenuService
{
public static final String PREMISSION_STRING = "perms[\"{0}\"]";
@Autowired
private SysMenuMapper menuMapper;
@Autowired
private SysRoleMapper roleMapper;
@Autowired
private SysRoleMenuMapper roleMenuMapper;
/**
* 根据用户查询系统菜单列表
*
* @param userId 用户ID
* @return 菜单列表
*/
@Override
public List<SysMenu> selectMenuList(Long userId)
{
return selectMenuList(new SysMenu(), userId);
}
/**
* 查询系统菜单列表
*
* @param menu 菜单信息
* @return 菜单列表
*/
@Override
public List<SysMenu> selectMenuList(SysMenu menu, Long userId)
{
List<SysMenu> menuList = null;
// 管理员显示所有菜单信息
if (SysUser.isAdmin(userId))
{
menuList = menuMapper.selectMenuList(menu);
}
else
{
menu.getParams().put("userId", userId);
menuList = menuMapper.selectMenuListByUserId(menu);
}
return menuList;
}
/**
* 根据用户ID查询权限
*
* @param userId 用户ID
* @return 权限列表
*/
@Override
public Set<String> selectMenuPermsByUserId(Long userId)
{
List<String> perms = menuMapper.selectMenuPermsByUserId(userId);
Set<String> permsSet = new HashSet<>();
for (String perm : perms)
{
if (StringUtils.isNotEmpty(perm))
{
permsSet.addAll(Arrays.asList(perm.trim().split(",")));
}
}
return permsSet;
}
/**
* 根据用户ID查询菜单
*
* @param userId 用户名称
* @return 菜单列表
*/
@Override
public List<SysMenu> selectMenuTreeByUserId(Long userId)
{
List<SysMenu> menus = null;
if (SecurityUtils.isAdmin(userId))
{
menus = menuMapper.selectMenuTreeAll();
}
else
{
menus = menuMapper.selectMenuTreeByUserId(userId);
}
return getChildPerms(menus, 0);
}
/**
* 根据角色ID查询菜单树信息
*
* @param roleId 角色ID
* @return 选中菜单列表
*/
@Override
public List<Integer> selectMenuListByRoleId(Long roleId)
{
SysRole role = roleMapper.selectRoleById(roleId);
return menuMapper.selectMenuListByRoleId(roleId, role.isMenuCheckStrictly());
}
/**
* 构建前端路由所需要的菜单
*
* @param menus 菜单列表
* @return 路由列表
*/
@Override
public List<RouterVo> buildMenus(List<SysMenu> menus)
{
List<RouterVo> routers = new LinkedList<RouterVo>();
for (SysMenu menu : menus)
{
RouterVo router = new RouterVo();
router.setHidden("1".equals(menu.getVisible()));
router.setName(getRouteName(menu));
router.setPath(getRouterPath(menu));
router.setComponent(getComponent(menu));
router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache())));
List<SysMenu> cMenus = menu.getChildren();
if (!cMenus.isEmpty() && cMenus.size() > 0 && UserConstants.TYPE_DIR.equals(menu.getMenuType()))
{
router.setAlwaysShow(true);
router.setRedirect("noRedirect");
router.setChildren(buildMenus(cMenus));
}
else if (isMeunFrame(menu))
{
List<RouterVo> childrenList = new ArrayList<RouterVo>();
RouterVo children = new RouterVo();
children.setPath(menu.getPath());
children.setComponent(menu.getComponent());
children.setName(StringUtils.capitalize(menu.getPath()));
children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache())));
childrenList.add(children);
router.setChildren(childrenList);
}
routers.add(router);
}
return routers;
}
/**
* 构建前端所需要树结构
*
* @param menus 菜单列表
* @return 树结构列表
*/
@Override
public List<SysMenu> buildMenuTree(List<SysMenu> menus)
{
List<SysMenu> returnList = new ArrayList<SysMenu>();
List<Long> tempList = new ArrayList<Long>();
for (SysMenu dept : menus)
{
tempList.add(dept.getMenuId());
}
for (Iterator<SysMenu> iterator = menus.iterator(); iterator.hasNext();)
{
SysMenu menu = (SysMenu) iterator.next();
// 如果是顶级节点, 遍历该父节点的所有子节点
if (!tempList.contains(menu.getParentId()))
{
recursionFn(menus, menu);
returnList.add(menu);
}
}
if (returnList.isEmpty())
{
returnList = menus;
}
return returnList;
}
/**
* 构建前端所需要下拉树结构
*
* @param menus 菜单列表
* @return 下拉树结构列表
*/
@Override
public List<TreeSelect> buildMenuTreeSelect(List<SysMenu> menus)
{
List<SysMenu> menuTrees = buildMenuTree(menus);
return menuTrees.stream().map(TreeSelect::new).collect(Collectors.toList());
}
/**
* 根据菜单ID查询信息
*
* @param menuId 菜单ID
* @return 菜单信息
*/
@Override
public SysMenu selectMenuById(Long menuId)
{
return menuMapper.selectMenuById(menuId);
}
/**
* 是否存在菜单子节点
*
* @param menuId 菜单ID
* @return 结果
*/
@Override
public boolean hasChildByMenuId(Long menuId)
{
int result = menuMapper.hasChildByMenuId(menuId);
return result > 0 ? true : false;
}
/**
* 查询菜单使用数量
*
* @param menuId 菜单ID
* @return 结果
*/
@Override
public boolean checkMenuExistRole(Long menuId)
{
int result = roleMenuMapper.checkMenuExistRole(menuId);
return result > 0 ? true : false;
}
/**
* 新增保存菜单信息
*
* @param menu 菜单信息
* @return 结果
*/
@Override
public int insertMenu(SysMenu menu)
{
return menuMapper.insertMenu(menu);
}
/**
* 修改保存菜单信息
*
* @param menu 菜单信息
* @return 结果
*/
@Override
public int updateMenu(SysMenu menu)
{
return menuMapper.updateMenu(menu);
}
/**
* 删除菜单管理信息
*
* @param menuId 菜单ID
* @return 结果
*/
@Override
public int deleteMenuById(Long menuId)
{
return menuMapper.deleteMenuById(menuId);
}
/**
* 校验菜单名称是否唯一
*
* @param menu 菜单信息
* @return 结果
*/
@Override
public String checkMenuNameUnique(SysMenu menu)
{
Long menuId = StringUtils.isNull(menu.getMenuId()) ? -1L : menu.getMenuId();
SysMenu info = menuMapper.checkMenuNameUnique(menu.getMenuName(), menu.getParentId());
if (StringUtils.isNotNull(info) && info.getMenuId().longValue() != menuId.longValue())
{
return UserConstants.NOT_UNIQUE;
}
return UserConstants.UNIQUE;
}
/**
* 获取路由名称
*
* @param menu 菜单信息
* @return 路由名称
*/
public String getRouteName(SysMenu menu)
{
String routerName = StringUtils.capitalize(menu.getPath());
// 非外链并且是一级目录(类型为目录)
if (isMeunFrame(menu))
{
routerName = StringUtils.EMPTY;
}
return routerName;
}
/**
* 获取路由地址
*
* @param menu 菜单信息
* @return 路由地址
*/
public String getRouterPath(SysMenu menu)
{
String routerPath = menu.getPath();
// 非外链并且是一级目录(类型为目录)
if (0 == menu.getParentId().intValue() && UserConstants.TYPE_DIR.equals(menu.getMenuType())
&& UserConstants.NO_FRAME.equals(menu.getIsFrame()))
{
routerPath = "/" + menu.getPath();
}
// 非外链并且是一级目录(类型为菜单)
else if (isMeunFrame(menu))
{
routerPath = "/";
}
return routerPath;
}
/**
* 获取组件信息
*
* @param menu 菜单信息
* @return 组件信息
*/
public String getComponent(SysMenu menu)
{
String component = UserConstants.LAYOUT;
if (StringUtils.isNotEmpty(menu.getComponent()) && !isMeunFrame(menu))
{
component = menu.getComponent();
}
else if (StringUtils.isEmpty(menu.getComponent()) && isParentView(menu))
{
component = UserConstants.PARENT_VIEW;
}
return component;
}
/**
* 是否为菜单内部跳转
*
* @param menu 菜单信息
* @return 结果
*/
public boolean isMeunFrame(SysMenu menu)
{
return menu.getParentId().intValue() == 0 && UserConstants.TYPE_MENU.equals(menu.getMenuType())
&& menu.getIsFrame().equals(UserConstants.NO_FRAME);
}
/**
* 是否为parent_view组件
*
* @param menu 菜单信息
* @return 结果
*/
public boolean isParentView(SysMenu menu)
{
return menu.getParentId().intValue() != 0 && UserConstants.TYPE_DIR.equals(menu.getMenuType());
}
/**
* 根据父节点的ID获取所有子节点
*
* @param list 分类表
* @param parentId 传入的父节点ID
* @return String
*/
public List<SysMenu> getChildPerms(List<SysMenu> list, int parentId)
{
List<SysMenu> returnList = new ArrayList<SysMenu>();
for (Iterator<SysMenu> iterator = list.iterator(); iterator.hasNext();)
{
SysMenu t = (SysMenu) iterator.next();
// 一、根据传入的某个父节点ID,遍历该父节点的所有子节点
if (t.getParentId() == parentId)
{
recursionFn(list, t);
returnList.add(t);
}
}
return returnList;
}
/**
* 递归列表
*
* @param list
* @param t
*/
private void recursionFn(List<SysMenu> list, SysMenu t)
{
// 得到子节点列表
List<SysMenu> childList = getChildList(list, t);
t.setChildren(childList);
for (SysMenu tChild : childList)
{
if (hasChild(list, tChild))
{
recursionFn(list, tChild);
}
}
}
/**
* 得到子节点列表
*/
private List<SysMenu> getChildList(List<SysMenu> list, SysMenu t)
{
List<SysMenu> tlist = new ArrayList<SysMenu>();
Iterator<SysMenu> it = list.iterator();
while (it.hasNext())
{
SysMenu n = (SysMenu) it.next();
if (n.getParentId().longValue() == t.getMenuId().longValue())
{
tlist.add(n);
}
}
return tlist;
}
/**
* 判断是否有子节点
*/
private boolean hasChild(List<SysMenu> list, SysMenu t)
{
return getChildList(list, t).size() > 0 ? true : false;
}
}
package com.ruoyi.system.service.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.TreeSelect;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.vo.MetaVo;
import com.ruoyi.system.domain.vo.RouterVo;
import com.ruoyi.system.mapper.SysMenuMapper;
import com.ruoyi.system.mapper.SysRoleMapper;
import com.ruoyi.system.mapper.SysRoleMenuMapper;
import com.ruoyi.system.service.ISysMenuService;
/**
* 菜单 业务层处理
*
* @author ruoyi
*/
@Service
public class SysMenuServiceImpl implements ISysMenuService
{
public static final String PREMISSION_STRING = "perms[\"{0}\"]";
@Autowired
private SysMenuMapper menuMapper;
@Autowired
private SysRoleMapper roleMapper;
@Autowired
private SysRoleMenuMapper roleMenuMapper;
/**
* 根据用户查询系统菜单列表
*
* @param userId 用户ID
* @return 菜单列表
*/
@Override
public List<SysMenu> selectMenuList(Long userId)
{
return selectMenuList(new SysMenu(), userId);
}
/**
* 查询系统菜单列表
*
* @param menu 菜单信息
* @return 菜单列表
*/
@Override
public List<SysMenu> selectMenuList(SysMenu menu, Long userId)
{
List<SysMenu> menuList = null;
// 管理员显示所有菜单信息
if (SysUser.isAdmin(userId))
{
menuList = menuMapper.selectMenuList(menu);
}
else
{
menu.getParams().put("userId", userId);
menuList = menuMapper.selectMenuListByUserId(menu);
}
return menuList;
}
/**
* 根据用户ID查询权限
*
* @param userId 用户ID
* @return 权限列表
*/
@Override
public Set<String> selectMenuPermsByUserId(Long userId)
{
List<String> perms = menuMapper.selectMenuPermsByUserId(userId);
Set<String> permsSet = new HashSet<>();
for (String perm : perms)
{
if (StringUtils.isNotEmpty(perm))
{
permsSet.addAll(Arrays.asList(perm.trim().split(",")));
}
}
return permsSet;
}
/**
* 根据用户ID查询菜单
*
* @param userId 用户名称
* @return 菜单列表
*/
@Override
public List<SysMenu> selectMenuTreeByUserId(Long userId)
{
List<SysMenu> menus = null;
if (SecurityUtils.isAdmin(userId))
{
menus = menuMapper.selectMenuTreeAll();
}
else
{
menus = menuMapper.selectMenuTreeByUserId(userId);
}
return getChildPerms(menus, 0);
}
/**
* 根据角色ID查询菜单树信息
*
* @param roleId 角色ID
* @return 选中菜单列表
*/
@Override
public List<Integer> selectMenuListByRoleId(Long roleId)
{
SysRole role = roleMapper.selectRoleById(roleId);
return menuMapper.selectMenuListByRoleId(roleId, role.isMenuCheckStrictly());
}
/**
* 构建前端路由所需要的菜单
*
* @param menus 菜单列表
* @return 路由列表
*/
@Override
public List<RouterVo> buildMenus(List<SysMenu> menus)
{
List<RouterVo> routers = new LinkedList<RouterVo>();
for (SysMenu menu : menus)
{
RouterVo router = new RouterVo();
router.setHidden("1".equals(menu.getVisible()));
router.setName(getRouteName(menu));
router.setPath(getRouterPath(menu));
router.setComponent(getComponent(menu));
router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache())));
List<SysMenu> cMenus = menu.getChildren();
if (!cMenus.isEmpty() && cMenus.size() > 0 && UserConstants.TYPE_DIR.equals(menu.getMenuType()))
{
router.setAlwaysShow(true);
router.setRedirect("noRedirect");
router.setChildren(buildMenus(cMenus));
}
else if (isMenuFrame(menu))
{
router.setMeta(null);
List<RouterVo> childrenList = new ArrayList<RouterVo>();
RouterVo children = new RouterVo();
children.setPath(menu.getPath());
children.setComponent(menu.getComponent());
children.setName(StringUtils.capitalize(menu.getPath()));
children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache())));
childrenList.add(children);
router.setChildren(childrenList);
}
routers.add(router);
}
return routers;
}
/**
* 构建前端所需要树结构
*
* @param menus 菜单列表
* @return 树结构列表
*/
@Override
public List<SysMenu> buildMenuTree(List<SysMenu> menus)
{
List<SysMenu> returnList = new ArrayList<SysMenu>();
List<Long> tempList = new ArrayList<Long>();
for (SysMenu dept : menus)
{
tempList.add(dept.getMenuId());
}
for (Iterator<SysMenu> iterator = menus.iterator(); iterator.hasNext();)
{
SysMenu menu = (SysMenu) iterator.next();
// 如果是顶级节点, 遍历该父节点的所有子节点
if (!tempList.contains(menu.getParentId()))
{
recursionFn(menus, menu);
returnList.add(menu);
}
}
if (returnList.isEmpty())
{
returnList = menus;
}
return returnList;
}
/**
* 构建前端所需要下拉树结构
*
* @param menus 菜单列表
* @return 下拉树结构列表
*/
@Override
public List<TreeSelect> buildMenuTreeSelect(List<SysMenu> menus)
{
List<SysMenu> menuTrees = buildMenuTree(menus);
return menuTrees.stream().map(TreeSelect::new).collect(Collectors.toList());
}
/**
* 根据菜单ID查询信息
*
* @param menuId 菜单ID
* @return 菜单信息
*/
@Override
public SysMenu selectMenuById(Long menuId)
{
return menuMapper.selectMenuById(menuId);
}
/**
* 是否存在菜单子节点
*
* @param menuId 菜单ID
* @return 结果
*/
@Override
public boolean hasChildByMenuId(Long menuId)
{
int result = menuMapper.hasChildByMenuId(menuId);
return result > 0 ? true : false;
}
/**
* 查询菜单使用数量
*
* @param menuId 菜单ID
* @return 结果
*/
@Override
public boolean checkMenuExistRole(Long menuId)
{
int result = roleMenuMapper.checkMenuExistRole(menuId);
return result > 0 ? true : false;
}
/**
* 新增保存菜单信息
*
* @param menu 菜单信息
* @return 结果
*/
@Override
public int insertMenu(SysMenu menu)
{
return menuMapper.insertMenu(menu);
}
/**
* 修改保存菜单信息
*
* @param menu 菜单信息
* @return 结果
*/
@Override
public int updateMenu(SysMenu menu)
{
return menuMapper.updateMenu(menu);
}
/**
* 删除菜单管理信息
*
* @param menuId 菜单ID
* @return 结果
*/
@Override
public int deleteMenuById(Long menuId)
{
return menuMapper.deleteMenuById(menuId);
}
/**
* 校验菜单名称是否唯一
*
* @param menu 菜单信息
* @return 结果
*/
@Override
public String checkMenuNameUnique(SysMenu menu)
{
Long menuId = StringUtils.isNull(menu.getMenuId()) ? -1L : menu.getMenuId();
SysMenu info = menuMapper.checkMenuNameUnique(menu.getMenuName(), menu.getParentId());
if (StringUtils.isNotNull(info) && info.getMenuId().longValue() != menuId.longValue())
{
return UserConstants.NOT_UNIQUE;
}
return UserConstants.UNIQUE;
}
/**
* 获取路由名称
*
* @param menu 菜单信息
* @return 路由名称
*/
public String getRouteName(SysMenu menu)
{
String routerName = StringUtils.capitalize(menu.getPath());
// 非外链并且是一级目录(类型为目录)
if (isMenuFrame(menu))
{
routerName = StringUtils.EMPTY;
}
return routerName;
}
/**
* 获取路由地址
*
* @param menu 菜单信息
* @return 路由地址
*/
public String getRouterPath(SysMenu menu)
{
String routerPath = menu.getPath();
// 非外链并且是一级目录(类型为目录)
if (0 == menu.getParentId().intValue() && UserConstants.TYPE_DIR.equals(menu.getMenuType())
&& UserConstants.NO_FRAME.equals(menu.getIsFrame()))
{
routerPath = "/" + menu.getPath();
}
// 非外链并且是一级目录(类型为菜单)
else if (isMenuFrame(menu))
{
routerPath = "/";
}
return routerPath;
}
/**
* 获取组件信息
*
* @param menu 菜单信息
* @return 组件信息
*/
public String getComponent(SysMenu menu)
{
String component = UserConstants.LAYOUT;
if (StringUtils.isNotEmpty(menu.getComponent()) && !isMenuFrame(menu))
{
component = menu.getComponent();
}
else if (StringUtils.isEmpty(menu.getComponent()) && isParentView(menu))
{
component = UserConstants.PARENT_VIEW;
}
return component;
}
/**
* 是否为菜单内部跳转
*
* @param menu 菜单信息
* @return 结果
*/
public boolean isMenuFrame(SysMenu menu)
{
return menu.getParentId().intValue() == 0 && UserConstants.TYPE_MENU.equals(menu.getMenuType())
&& menu.getIsFrame().equals(UserConstants.NO_FRAME);
}
/**
* 是否为parent_view组件
*
* @param menu 菜单信息
* @return 结果
*/
public boolean isParentView(SysMenu menu)
{
return menu.getParentId().intValue() != 0 && UserConstants.TYPE_DIR.equals(menu.getMenuType());
}
/**
* 根据父节点的ID获取所有子节点
*
* @param list 分类表
* @param parentId 传入的父节点ID
* @return String
*/
public List<SysMenu> getChildPerms(List<SysMenu> list, int parentId)
{
List<SysMenu> returnList = new ArrayList<SysMenu>();
for (Iterator<SysMenu> iterator = list.iterator(); iterator.hasNext();)
{
SysMenu t = (SysMenu) iterator.next();
// 一、根据传入的某个父节点ID,遍历该父节点的所有子节点
if (t.getParentId() == parentId)
{
recursionFn(list, t);
returnList.add(t);
}
}
return returnList;
}
/**
* 递归列表
*
* @param list
* @param t
*/
private void recursionFn(List<SysMenu> list, SysMenu t)
{
// 得到子节点列表
List<SysMenu> childList = getChildList(list, t);
t.setChildren(childList);
for (SysMenu tChild : childList)
{
if (hasChild(list, tChild))
{
recursionFn(list, tChild);
}
}
}
/**
* 得到子节点列表
*/
private List<SysMenu> getChildList(List<SysMenu> list, SysMenu t)
{
List<SysMenu> tlist = new ArrayList<SysMenu>();
Iterator<SysMenu> it = list.iterator();
while (it.hasNext())
{
SysMenu n = (SysMenu) it.next();
if (n.getParentId().longValue() == t.getMenuId().longValue())
{
tlist.add(n);
}
}
return tlist;
}
/**
* 判断是否有子节点
*/
private boolean hasChild(List<SysMenu> list, SysMenu t)
{
return getChildList(list, t).size() > 0 ? true : false;
}
}

View File

@ -131,7 +131,7 @@
<if test="parentId != null">parent_id = #{parentId},</if>
<if test="orderNum != null and orderNum != ''">order_num = #{orderNum},</if>
<if test="path != null and path != ''">path = #{path},</if>
<if test="component != null and component != ''">component = #{component},</if>
<if test="component != null">component = #{component},</if>
<if test="isFrame != null and isFrame != ''">is_frame = #{isFrame},</if>
<if test="isCache != null and isCache != ''">is_cache = #{isCache},</if>
<if test="menuType != null and menuType != ''">menu_type = #{menuType},</if>

View File

@ -135,12 +135,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
where role_id = #{roleId}
</update>
<update id="updateRoleStatus" parameterType="SysRole">
update sys_user set status = #{status} where user_id = #{userId}
</update>
<delete id="deleteRoleById" parameterType="Long">
delete from sys_role where role_id = #{roleId}
update sys_role set del_flag = '2' where role_id = #{roleId}
</delete>
<delete id="deleteRoleByIds" parameterType="Long">

View File

@ -141,8 +141,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="deptId != null and deptId != 0">dept_id = #{deptId},</if>
<if test="userName != null and userName != ''">user_name = #{userName},</if>
<if test="nickName != null and nickName != ''">nick_name = #{nickName},</if>
<if test="email != null and email != ''">email = #{email},</if>
<if test="phonenumber != null and phonenumber != ''">phonenumber = #{phonenumber},</if>
<if test="email != null ">email = #{email},</if>
<if test="phonenumber != null ">phonenumber = #{phonenumber},</if>
<if test="sex != null and sex != ''">sex = #{sex},</if>
<if test="avatar != null and avatar != ''">avatar = #{avatar},</if>
<if test="password != null and password != ''">password = #{password},</if>
@ -169,7 +169,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</update>
<delete id="deleteUserById" parameterType="Long">
delete from sys_user where user_id = #{userId}
update sys_user set del_flag = '2' where user_id = #{userId}
</delete>
<delete id="deleteUserByIds" parameterType="Long">

View File

@ -1,6 +1,6 @@
{
"name": "ruoyi",
"version": "3.3.0",
"version": "3.5.0",
"description": "若依管理系统",
"author": "若依",
"license": "MIT",
@ -41,15 +41,14 @@
"clipboard": "2.0.6",
"core-js": "3.8.1",
"echarts": "4.9.0",
"element-ui": "2.14.1",
"element-ui": "2.15.0",
"file-saver": "2.0.4",
"fuse.js": "6.4.3",
"highlight.js": "10.4.1",
"highlight.js": "9.18.5",
"js-beautify": "1.13.0",
"js-cookie": "2.2.1",
"jsencrypt": "3.0.0-rc.1",
"nprogress": "0.2.0",
"path-to-regexp": "6.2.0",
"quill": "1.3.7",
"screenfull": "5.0.2",
"sortablejs": "1.10.2",
@ -70,8 +69,8 @@
"eslint": "7.15.0",
"eslint-plugin-vue": "7.2.0",
"lint-staged": "10.5.3",
"sass": "1.30.0",
"runjs": "4.4.2",
"sass": "1.32.0",
"sass-loader": "10.1.0",
"script-ext-html-webpack-plugin": "2.1.5",
"svg-sprite-loader": "5.1.1",

File diff suppressed because one or more lines are too long

View File

@ -7,6 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= webpackConfig.name %></title>
<!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
<style>
html,
body,

View File

@ -1,84 +1,92 @@
// cover some element-ui styles
.el-breadcrumb__inner,
.el-breadcrumb__inner a {
font-weight: 400 !important;
}
.el-upload {
input[type="file"] {
display: none !important;
}
}
.el-upload__input {
display: none;
}
.cell {
.el-tag {
margin-right: 0px;
}
}
.small-padding {
.cell {
padding-left: 5px;
padding-right: 5px;
}
}
.fixed-width {
.el-button--mini {
padding: 7px 10px;
width: 60px;
}
}
.status-col {
.cell {
padding: 0 10px;
text-align: center;
.el-tag {
margin-right: 0px;
}
}
}
// to fixed https://github.com/ElemeFE/element/issues/2461
.el-dialog {
transform: none;
left: 0;
position: relative;
margin: 0 auto;
}
// refine element ui upload
.upload-container {
.el-upload {
width: 100%;
.el-upload-dragger {
width: 100%;
height: 200px;
}
}
}
// dropdown
.el-dropdown-menu {
a {
display: block
}
}
// fix date-picker ui bug in filter-item
.el-range-editor.el-input__inner {
display: inline-flex !important;
}
// to fix el-date-picker css style
.el-range-separator {
box-sizing: content-box;
}
// cover some element-ui styles
.el-breadcrumb__inner,
.el-breadcrumb__inner a {
font-weight: 400 !important;
}
.el-upload {
input[type="file"] {
display: none !important;
}
}
.el-upload__input {
display: none;
}
.cell {
.el-tag {
margin-right: 0px;
}
}
.small-padding {
.cell {
padding-left: 5px;
padding-right: 5px;
}
}
.fixed-width {
.el-button--mini {
padding: 7px 10px;
width: 60px;
}
}
.status-col {
.cell {
padding: 0 10px;
text-align: center;
.el-tag {
margin-right: 0px;
}
}
}
// to fixed https://github.com/ElemeFE/element/issues/2461
.el-dialog {
transform: none;
left: 0;
position: relative;
margin: 0 auto;
}
// refine element ui upload
.upload-container {
.el-upload {
width: 100%;
.el-upload-dragger {
width: 100%;
height: 200px;
}
}
}
// dropdown
.el-dropdown-menu {
a {
display: block
}
}
// fix date-picker ui bug in filter-item
.el-range-editor.el-input__inner {
display: inline-flex !important;
}
// to fix el-date-picker css style
.el-range-separator {
box-sizing: content-box;
}
.el-menu--collapse
> div
> .el-submenu
> .el-submenu__title
.el-submenu__icon-arrow {
display: none;
}

View File

@ -106,7 +106,6 @@
}
.el-table .fixed-width .el-button--mini {
color: #409EFF;
padding-left: 0;
padding-right: 0;
width: inherit;

View File

@ -135,9 +135,6 @@
margin-left: 20px;
}
.el-submenu__icon-arrow {
display: none;
}
}
}

View File

@ -10,8 +10,6 @@
</template>
<script>
import pathToRegexp from 'path-to-regexp'
export default {
data() {
return {
@ -49,18 +47,13 @@ export default {
}
return name.trim() === '首页'
},
pathCompile(path) {
const { params } = this.$route
var toPath = pathToRegexp.compile(path)
return toPath(params)
},
handleLink(item) {
const { redirect, path } = item
if (redirect) {
this.$router.push(redirect)
return
}
this.$router.push(this.pathCompile(path))
this.$router.push(path)
}
}
}

View File

@ -1,195 +1,261 @@
<template>
<div class="editor" ref="editor" :style="styles"></div>
</template>
<script>
import Quill from "quill";
import "quill/dist/quill.core.css";
import "quill/dist/quill.snow.css";
import "quill/dist/quill.bubble.css";
export default {
name: "Editor",
props: {
/* 编辑器的内容 */
value: {
type: String,
default: "",
},
/* 高度 */
height: {
type: Number,
default: null,
},
/* 最小高度 */
minHeight: {
type: Number,
default: null,
},
},
data() {
return {
Quill: null,
currentValue: "",
options: {
theme: "snow",
bounds: document.body,
debug: "warn",
modules: {
// 工具栏配置
toolbar: [
["bold", "italic", "underline", "strike"], // 加粗 斜体 下划线 删除线
["blockquote", "code-block"], // 引用 代码块
[{ list: "ordered" }, { list: "bullet" }], // 有序、无序列表
[{ indent: "-1" }, { indent: "+1" }], // 缩进
[{ size: ["small", false, "large", "huge"] }], // 字体大小
[{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题
[{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色
[{ align: [] }], // 对齐方式
["clean"], // 清除文本格式
["link", "image", "video"] // 链接、图片、视频
],
},
placeholder: "请输入内容",
readOnly: false,
},
};
},
computed: {
styles() {
let style = {};
if (this.minHeight) {
style.minHeight = `${this.minHeight}px`;
}
if (this.height) {
style.height = `${this.height}px`;
}
return style;
},
},
watch: {
value: {
handler(val) {
if (val !== this.currentValue) {
this.currentValue = val === null ? "" : val;
if (this.Quill) {
this.Quill.pasteHTML(this.currentValue);
}
}
},
immediate: true,
},
},
mounted() {
this.init();
},
beforeDestroy() {
this.Quill = null;
},
methods: {
init() {
const editor = this.$refs.editor;
this.Quill = new Quill(editor, this.options);
this.Quill.pasteHTML(this.currentValue);
this.Quill.on("text-change", (delta, oldDelta, source) => {
const html = this.$refs.editor.children[0].innerHTML;
const text = this.Quill.getText();
const quill = this.Quill;
this.currentValue = html;
this.$emit("input", html);
this.$emit("on-change", { html, text, quill });
});
this.Quill.on("text-change", (delta, oldDelta, source) => {
this.$emit("on-text-change", delta, oldDelta, source);
});
this.Quill.on("selection-change", (range, oldRange, source) => {
this.$emit("on-selection-change", range, oldRange, source);
});
this.Quill.on("editor-change", (eventName, ...args) => {
this.$emit("on-editor-change", eventName, ...args);
});
},
},
};
</script>
<style>
.editor, .ql-toolbar {
white-space: pre-wrap!important;
line-height: normal !important;
}
.quill-img {
display: none;
}
.ql-snow .ql-tooltip[data-mode="link"]::before {
content: "请输入链接地址:";
}
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
border-right: 0px;
content: "保存";
padding-right: 0px;
}
.ql-snow .ql-tooltip[data-mode="video"]::before {
content: "请输入视频地址:";
}
.ql-snow .ql-picker.ql-size .ql-picker-label::before,
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
content: "14px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before {
content: "10px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before {
content: "18px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before {
content: "32px";
}
.ql-snow .ql-picker.ql-header .ql-picker-label::before,
.ql-snow .ql-picker.ql-header .ql-picker-item::before {
content: "文本";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
content: "标题1";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
content: "标题2";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
content: "标题3";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
content: "标题4";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
content: "标题5";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
content: "标题6";
}
.ql-snow .ql-picker.ql-font .ql-picker-label::before,
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
content: "标准字体";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before {
content: "衬线字体";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before {
content: "等宽字体";
}
</style>
<template>
<div>
<el-upload
:action="uploadUrl"
:on-success="handleUploadSuccess"
:on-error="handleUploadError"
name="file"
:show-file-list="false"
:headers="headers"
style="display: none"
ref="upload"
v-if="this.uploadUrl"
>
</el-upload>
<div class="editor" ref="editor" :style="styles"></div>
</div>
</template>
<script>
import Quill from "quill";
import "quill/dist/quill.core.css";
import "quill/dist/quill.snow.css";
import "quill/dist/quill.bubble.css";
import { getToken } from "@/utils/auth";
export default {
name: "Editor",
props: {
/* 编辑器的内容 */
value: {
type: String,
default: "",
},
/* 高度 */
height: {
type: Number,
default: null,
},
/* 最小高度 */
minHeight: {
type: Number,
default: null,
},
/* 只读 */
readOnly: {
type: Boolean,
default: false,
},
/* 上传地址 */
uploadUrl: {
type: String,
default: "",
}
},
data() {
return {
headers: {
Authorization: "Bearer " + getToken()
},
Quill: null,
currentValue: "",
options: {
theme: "snow",
bounds: document.body,
debug: "warn",
modules: {
// 工具栏配置
toolbar: [
["bold", "italic", "underline", "strike"], // 加粗 斜体 下划线 删除线
["blockquote", "code-block"], // 引用 代码块
[{ list: "ordered" }, { list: "bullet" }], // 有序、无序列表
[{ indent: "-1" }, { indent: "+1" }], // 缩进
[{ size: ["small", false, "large", "huge"] }], // 字体大小
[{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题
[{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色
[{ align: [] }], // 对齐方式
["clean"], // 清除文本格式
["link", "image"] // 链接、图片
],
},
placeholder: "请输入内容",
readOnly: this.readOnly,
},
};
},
computed: {
styles() {
let style = {};
if (this.minHeight) {
style.minHeight = `${this.minHeight}px`;
}
if (this.height) {
style.height = `${this.height}px`;
}
return style;
},
},
watch: {
value: {
handler(val) {
if (val !== this.currentValue) {
this.currentValue = val === null ? "" : val;
if (this.Quill) {
this.Quill.pasteHTML(this.currentValue);
}
}
},
immediate: true,
},
},
mounted() {
this.init();
},
beforeDestroy() {
this.Quill = null;
},
methods: {
init() {
const editor = this.$refs.editor;
this.Quill = new Quill(editor, this.options);
// 如果设置了上传地址则自定义图片上传事件
if (this.uploadUrl) {
let toolbar = this.Quill.getModule("toolbar");
toolbar.addHandler("image", (value) => {
this.uploadType = "image";
if (value) {
this.$refs.upload.$children[0].$refs.input.click();
} else {
this.quill.format("image", false);
}
});
toolbar.addHandler("video", (value) => {
this.uploadType = "video";
if (value) {
this.$refs.upload.$children[0].$refs.input.click();
} else {
this.quill.format("video", false);
}
});
}
this.Quill.pasteHTML(this.currentValue);
this.Quill.on("text-change", (delta, oldDelta, source) => {
const html = this.$refs.editor.children[0].innerHTML;
const text = this.Quill.getText();
const quill = this.Quill;
this.currentValue = html;
this.$emit("input", html);
this.$emit("on-change", { html, text, quill });
});
this.Quill.on("text-change", (delta, oldDelta, source) => {
this.$emit("on-text-change", delta, oldDelta, source);
});
this.Quill.on("selection-change", (range, oldRange, source) => {
this.$emit("on-selection-change", range, oldRange, source);
});
this.Quill.on("editor-change", (eventName, ...args) => {
this.$emit("on-editor-change", eventName, ...args);
});
},
handleUploadSuccess(res, file) {
// 获取富文本组件实例
let quill = this.Quill;
// 如果上传成功
if (res.code == 200) {
// 获取光标所在位置
let length = quill.getSelection().index;
// 插入图片 res.url为服务器返回的图片地址
quill.insertEmbed(length, "image", res.url);
// 调整光标到最后
quill.setSelection(length + 1);
} else {
this.$message.error("图片插入失败");
}
},
handleUploadError() {
this.$message.error("图片插入失败");
},
},
};
</script>
<style>
.editor, .ql-toolbar {
white-space: pre-wrap !important;
line-height: normal !important;
}
.quill-img {
display: none;
}
.ql-snow .ql-tooltip[data-mode="link"]::before {
content: "请输入链接地址:";
}
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
border-right: 0px;
content: "保存";
padding-right: 0px;
}
.ql-snow .ql-tooltip[data-mode="video"]::before {
content: "请输入视频地址:";
}
.ql-snow .ql-picker.ql-size .ql-picker-label::before,
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
content: "14px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before {
content: "10px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before {
content: "18px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before {
content: "32px";
}
.ql-snow .ql-picker.ql-header .ql-picker-label::before,
.ql-snow .ql-picker.ql-header .ql-picker-item::before {
content: "文本";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
content: "标题1";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
content: "标题2";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
content: "标题3";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
content: "标题4";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
content: "标题5";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
content: "标题6";
}
.ql-snow .ql-picker.ql-font .ql-picker-label::before,
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
content: "标准字体";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before {
content: "衬线字体";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before {
content: "等宽字体";
}
</style>

View File

@ -0,0 +1,179 @@
<template>
<div class="upload-file">
<el-upload
:action="uploadFileUrl"
:before-upload="handleBeforeUpload"
:file-list="fileList"
:limit="1"
:on-error="handleUploadError"
:on-exceed="handleExceed"
:on-success="handleUploadSuccess"
:show-file-list="false"
:headers="headers"
class="upload-file-uploader"
ref="upload"
>
<!-- 上传按钮 -->
<el-button size="mini" type="primary">选取文件</el-button>
<!-- 上传提示 -->
<div class="el-upload__tip" slot="tip" v-if="showTip">
请上传
<template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> </template>
<template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>
的文件
</div>
</el-upload>
<!-- 文件列表 -->
<transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
<li :key="file.uid" class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in list">
<el-link :href="file.url" :underline="false" target="_blank">
<span class="el-icon-document"> {{ getFileName(file.name) }} </span>
</el-link>
<div class="ele-upload-list__item-content-action">
<el-link :underline="false" @click="handleDelete(index)" type="danger">删除</el-link>
</div>
</li>
</transition-group>
</div>
</template>
<script>
import { getToken } from "@/utils/auth";
export default {
props: {
// 值
value: [String, Object, Array],
// 大小限制(MB)
fileSize: {
type: Number,
default: 5,
},
// 文件类型, 例如['png', 'jpg', 'jpeg']
fileType: {
type: Array,
default: () => ["doc", "xls", "ppt", "txt", "pdf"],
},
// 是否显示提示
isShowTip: {
type: Boolean,
default: true
}
},
data() {
return {
uploadFileUrl: process.env.VUE_APP_BASE_API + "/common/upload", // 上传的图片服务器地址
headers: {
Authorization: "Bearer " + getToken(),
},
fileList: [],
};
},
computed: {
// 是否显示提示
showTip() {
return this.isShowTip && (this.fileType || this.fileSize);
},
// 列表
list() {
let temp = 1;
if (this.value) {
// 首先将值转为数组
const list = Array.isArray(this.value) ? this.value : [this.value];
// 然后将数组转为对象数组
return list.map((item) => {
if (typeof item === "string") {
item = { name: item, url: item };
}
item.uid = item.uid || new Date().getTime() + temp++;
return item;
});
} else {
this.fileList = [];
return [];
}
},
},
methods: {
// 上传前校检格式和大小
handleBeforeUpload(file) {
// 校检文件类型
if (this.fileType) {
let fileExtension = "";
if (file.name.lastIndexOf(".") > -1) {
fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
}
const isTypeOk = this.fileType.some((type) => {
if (file.type.indexOf(type) > -1) return true;
if (fileExtension && fileExtension.indexOf(type) > -1) return true;
return false;
});
if (!isTypeOk) {
this.$message.error(`文件格式不正确, 请上传${this.fileType.join("/")}格式文件!`);
return false;
}
}
// 校检文件大小
if (this.fileSize) {
const isLt = file.size / 1024 / 1024 < this.fileSize;
if (!isLt) {
this.$message.error(`上传文件大小不能超过 ${this.fileSize} MB!`);
return false;
}
}
return true;
},
// 文件个数超出
handleExceed() {
this.$message.error(`只允许上传单个文件`);
},
// 上传失败
handleUploadError(err) {
this.$message.error("上传失败, 请重试");
},
// 上传成功回调
handleUploadSuccess(res, file) {
this.$message.success("上传成功");
this.$emit("input", res.url);
},
// 删除文件
handleDelete(index) {
this.fileList.splice(index, 1);
this.$emit("input", '');
},
// 获取文件名称
getFileName(name) {
if (name.lastIndexOf("/") > -1) {
return name.slice(name.lastIndexOf("/") + 1).toLowerCase();
} else {
return "";
}
}
},
created() {
this.fileList = this.list;
},
};
</script>
<style scoped lang="scss">
.upload-file-uploader {
margin-bottom: 5px;
}
.upload-file-list .el-upload-list__item {
border: 1px solid #e4e7ed;
line-height: 2;
margin-bottom: 10px;
position: relative;
}
.upload-file-list .ele-upload-list__item-content {
display: flex;
justify-content: space-between;
align-items: center;
color: inherit;
}
.ele-upload-list__item-content-action .el-link {
margin-right: 10px;
}
</style>

View File

@ -20,7 +20,7 @@
<script>
// fuse is a lightweight fuzzy-search module
// make search results more in line with expectations
import Fuse from 'fuse.js'
import Fuse from 'fuse.js/dist/fuse.min.js'
import path from 'path'
export default {

View File

@ -11,9 +11,28 @@
:headers="headers"
style="display: inline-block; vertical-align: top"
>
<img v-if="value" :src="value" class="avatar" />
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
<el-image v-if="!value" :src="value">
<div slot="error" class="image-slot">
<i class="el-icon-plus" />
</div>
</el-image>
<div v-else class="image">
<el-image :src="value" :style="`width:150px;height:150px;`" fit="fill"/>
<div class="mask">
<div class="actions">
<span title="预览" @click.stop="dialogVisible = true">
<i class="el-icon-zoom-in" />
</span>
<span title="移除" @click.stop="removeImage">
<i class="el-icon-delete" />
</span>
</div>
</div>
</div>
</el-upload>
<el-dialog :visible.sync="dialogVisible" title="预览" width="800" append-to-body>
<img :src="value" style="display: block; max-width: 100%; margin: 0 auto;">
</el-dialog>
</div>
</template>
@ -21,9 +40,9 @@
import { getToken } from "@/utils/auth";
export default {
components: {},
data() {
return {
dialogVisible: false,
uploadImgUrl: process.env.VUE_APP_BASE_API + "/common/upload", //
headers: {
Authorization: "Bearer " + getToken(),
@ -37,6 +56,9 @@ export default {
},
},
methods: {
removeImage() {
this.$emit("input", "");
},
handleUploadSuccess(res) {
this.$emit("input", res.url);
this.loading.close();
@ -61,8 +83,18 @@ export default {
</script>
<style scoped lang="scss">
.avatar {
width: 100%;
height: 100%;
.image {
position: relative;
.mask {
opacity: 0;
position: absolute;
top: 0;
width: 100%;
background-color: rgba(0, 0, 0, 0.5);
transition: all 0.3s;
}
&:hover .mask {
opacity: 1;
}
}
</style>

View File

@ -1,4 +1,3 @@
<!-- @author Shiyn/ huangmx 20200807优化-->
<template>
<div class="top-right-btn">
<el-row>
@ -8,31 +7,81 @@
<el-tooltip class="item" effect="dark" content="刷新" placement="top">
<el-button size="mini" circle icon="el-icon-refresh" @click="refresh()" />
</el-tooltip>
<el-tooltip class="item" effect="dark" content="显隐列" placement="top" v-if="columns">
<el-button size="mini" circle icon="el-icon-menu" @click="showColumn()" />
</el-tooltip>
</el-row>
<el-dialog :title="title" :visible.sync="open" append-to-body>
<el-transfer
:titles="['显示', '隐藏']"
v-model="value"
:data="columns"
@change="dataChange"
></el-transfer>
</el-dialog>
</div>
</template>
<script>
export default {
name: "RightToolbar",
data() {
return {};
return {
// 显隐数据
value: [],
// 弹出层标题
title: "显示/隐藏",
// 是否显示弹出层
open: false,
};
},
props: {
showSearch: {
type: Boolean,
default: true,
},
columns: {
type: Array,
},
},
created() {
// 显隐列初始默认隐藏列
for (let item in this.columns) {
if (this.columns[item].visible === false) {
this.value.push(parseInt(item));
}
}
},
methods: {
//搜索
// 搜索
toggleSearch() {
this.$emit("update:showSearch", !this.showSearch);
},
//刷新
// 刷新
refresh() {
this.$emit("queryTable");
},
// 右侧列表元素变化
dataChange(data) {
for (var item in this.columns) {
const key = this.columns[item].key;
this.columns[item].visible = !data.includes(key);
}
},
// 打开显隐列dialog
showColumn() {
this.open = true;
},
},
};
</script>
<style lang="scss" scoped>
::v-deep .el-transfer__button {
border-radius: 50%;
padding: 12px;
display: block;
margin-left: 0px;
}
::v-deep .el-transfer__button:first-child {
margin-bottom: 10px;
}
</style>

View File

@ -35,7 +35,6 @@ export default {
if (typeof val !== 'string') return
const themeCluster = this.getThemeCluster(val.replace('#', ''))
const originalCluster = this.getThemeCluster(oldVal.replace('#', ''))
console.log(themeCluster, originalCluster)
const $message = this.$message({
message: ' Compiling the theme',

View File

@ -0,0 +1,182 @@
<template>
<el-menu
:default-active="activeMenu"
mode="horizontal"
@select="handleSelect"
>
<template v-for="(item, index) in topMenus">
<el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber"
><svg-icon :icon-class="item.meta.icon" />
{{ item.meta.title }}</el-menu-item
>
</template>
<!-- 顶部菜单超出数量折叠 -->
<el-submenu index="more" v-if="topMenus.length > visibleNumber">
<template slot="title">更多菜单</template>
<template v-for="(item, index) in topMenus">
<el-menu-item
:index="item.path"
:key="index"
v-if="index >= visibleNumber"
><svg-icon :icon-class="item.meta.icon" />
{{ item.meta.title }}</el-menu-item
>
</template>
</el-submenu>
</el-menu>
</template>
<script>
import { constantRoutes } from "@/router";
export default {
data() {
return {
// 顶部栏初始数
visibleNumber: 5,
// 是否为首次加载
isFrist: false,
// 当前激活菜单的 index
currentIndex: undefined
};
},
computed: {
theme() {
return this.$store.state.settings.theme;
},
// 顶部显示菜单
topMenus() {
let topMenus = [];
this.routers.map((menu) => {
if (menu.hidden !== true) {
// 兼容顶部栏一级菜单内部跳转
if (menu.path === "/") {
topMenus.push(menu.children[0]);
} else {
topMenus.push(menu);
}
}
});
return topMenus;
},
// 所有的路由信息
routers() {
return this.$store.state.permission.topbarRouters;
},
// 设置子路由
childrenMenus() {
var childrenMenus = [];
this.routers.map((router) => {
for (var item in router.children) {
if (router.children[item].parentPath === undefined) {
if(router.path === "/") {
router.children[item].path = "/redirect/" + router.children[item].path;
} else {
if(!this.ishttp(router.children[item].path)) {
router.children[item].path = router.path + "/" + router.children[item].path;
}
}
router.children[item].parentPath = router.path;
}
childrenMenus.push(router.children[item]);
}
});
return constantRoutes.concat(childrenMenus);
},
// 默认激活的菜单
activeMenu() {
const path = this.$route.path;
let activePath = this.routers[0].path;
if (path.lastIndexOf("/") > 0) {
const tmpPath = path.substring(1, path.length);
activePath = "/" + tmpPath.substring(0, tmpPath.indexOf("/"));
} else if ("/index" == path || "" == path) {
if (!this.isFrist) {
this.isFrist = true;
} else {
activePath = "index";
}
}
var routes = this.activeRoutes(activePath);
if (routes.length === 0) {
activePath = this.currentIndex || this.routers[0].path
this.activeRoutes(activePath);
}
return activePath;
},
},
beforeMount() {
window.addEventListener('resize', this.setVisibleNumber)
},
beforeDestroy() {
window.removeEventListener('resize', this.setVisibleNumber)
},
mounted() {
this.setVisibleNumber();
},
methods: {
// 根据宽度计算设置显示栏数
setVisibleNumber() {
const width = document.body.getBoundingClientRect().width / 3;
this.visibleNumber = parseInt(width / 85);
},
// 菜单选择事件
handleSelect(key, keyPath) {
this.currentIndex = key;
if (this.ishttp(key)) {
// http(s):// 路径新窗口打开
window.open(key, "_blank");
} else if (key.indexOf("/redirect") !== -1) {
// /redirect 路径内部打开
this.$router.push({ path: key.replace("/redirect", "") });
} else {
// 显示左侧联动菜单
this.activeRoutes(key);
}
},
// 当前激活的路由
activeRoutes(key) {
var routes = [];
if (this.childrenMenus && this.childrenMenus.length > 0) {
this.childrenMenus.map((item) => {
if (key == item.parentPath || (key == "index" && "" == item.path)) {
routes.push(item);
}
});
}
if(routes.length > 0) {
this.$store.commit("SET_SIDEBAR_ROUTERS", routes);
}
return routes;
},
ishttp(url) {
return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1
}
},
};
</script>
<style lang="scss">
.el-menu--horizontal > .el-menu-item {
float: left;
height: 50px;
line-height: 50px;
margin: 0;
border-bottom: 3px solid transparent;
color: #999093;
padding: 0 5px;
margin: 0 10px;
}
.el-menu--horizontal > .el-menu-item.is-active {
border-bottom: 3px solid #{'var(--theme)'};
color: #303133;
}
/* submenu item */
.el-menu--horizontal > .el-submenu .el-submenu__title {
height: 50px !important;
line-height: 50px !important;
}
</style>

View File

@ -1,8 +1,7 @@
<!-- @author ruoyi 20201128 支持三级以上菜单缓存 -->
<template>
<section class="app-main">
<transition name="fade-transform" mode="out-in">
<keep-alive :max="20" :exclude="notCacheName">
<keep-alive :include="cachedViews">
<router-view :key="key" />
</keep-alive>
</transition>
@ -10,119 +9,17 @@
</template>
<script>
import Global from "@/layout/components/global.js";
export default {
name: 'AppMain',
computed: {
notCacheName() {
var visitedViews = this.$store.state.tagsView.visitedViews;
var noCacheViews = [];
Object.keys(visitedViews).some((index) => {
if (visitedViews[index].meta.noCache) {
noCacheViews.push(visitedViews[index].name);
}
});
return noCacheViews;
cachedViews() {
return this.$store.state.tagsView.cachedViews
},
key() {
return this.$route.path;
},
},
mounted() {
// 关闭标签触发
Global.$on("removeCache", (name, view) => {
this.removeCache(name, view);
});
},
methods: {
// 获取有keep-alive子节点的Vnode
getVnode() {
// 判断子集非空
if (this.$children.length == 0) return false;
let vnode;
for (let item of this.$children) {
// 如果data中有key则代表找到了keep-alive下面的子集这个key就是router-view上的key
if (item.$vnode.data.key) {
vnode = item.$vnode;
break;
}
}
return vnode ? vnode : false;
},
// 移除keep-alive缓存
removeCache(name, view = {}) {
let vnode = this.getVnode();
if (!vnode) return false;
let componentInstance = vnode.parent.componentInstance;
// 这个key是用来获取前缀用来后面正则匹配用的
let keyStart = vnode.key.split("/")[0];
let thisKey = `${keyStart}${view.fullPath}`;
let regKey = `${keyStart}${view.path}`;
this[name]({ componentInstance, thisKey, regKey });
},
// 移除其他
closeOthersTags({ componentInstance, thisKey }) {
Object.keys(componentInstance.cache).forEach((key, index) => {
if (key != thisKey) {
// 销毁实例(这里存在多个key指向一个缓存的情况可能前面一个已经清除掉了所有要加判断)
if (componentInstance.cache[key]) {
componentInstance.cache[key].componentInstance.$destroy();
}
// 删除缓存
delete componentInstance.cache[key];
// 移除key中对应的key
componentInstance.keys.splice(index, 1);
}
});
},
// 移除所有缓存
closeAllTags({ componentInstance }) {
// 销毁实例
Object.keys(componentInstance.cache).forEach((key) => {
if (componentInstance.cache[key]) {
componentInstance.cache[key].componentInstance.$destroy();
}
});
// 删除缓存
componentInstance.cache = {};
// 移除key中对应的key
componentInstance.keys = [];
},
// 移除单个缓存
closeSelectedTag({ componentInstance, regKey }) {
let reg = new RegExp(`^${regKey}`);
Object.keys(componentInstance.cache).forEach((key, i) => {
if (reg.test(key)) {
// 销毁实例
if (componentInstance.cache[key]) {
componentInstance.cache[key].componentInstance.$destroy();
}
// 删除缓存
delete componentInstance.cache[key];
// 移除key中对应的key
componentInstance.keys.splice(i, 1);
}
});
},
// 刷新单个缓存
refreshSelectedTag({ componentInstance, thisKey }) {
Object.keys(componentInstance.cache).forEach((key, index) => {
if (null != thisKey && key.replace("/redirect", "") == thisKey) {
// 1 销毁实例(这里存在多个key指向一个缓存的情况可能前面一个已经清除掉了所有要加判断)
if (componentInstance.cache[key]) {
componentInstance.cache[key].componentInstance.$destroy();
}
// 2 删除缓存
delete componentInstance.cache[key];
// 3 移除key中对应的key
componentInstance.keys.splice(index, 1);
}
});
},
},
};
return this.$route.path
}
}
}
</script>
<style lang="scss" scoped>
@ -134,7 +31,7 @@ export default {
overflow: hidden;
}
.fixed-header + .app-main {
.fixed-header+.app-main {
padding-top: 50px;
}
@ -144,7 +41,7 @@ export default {
min-height: calc(100vh - 84px);
}
.fixed-header + .app-main {
.fixed-header+.app-main {
padding-top: 84px;
}
}

View File

@ -2,7 +2,8 @@
<div class="navbar">
<hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" />
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!topNav"/>
<top-nav id="topmenu-container" class="topmenu-container" v-if="topNav"/>
<div class="right-menu">
<template v-if="device!=='mobile'">
@ -48,6 +49,7 @@
<script>
import { mapGetters } from 'vuex'
import Breadcrumb from '@/components/Breadcrumb'
import TopNav from '@/components/TopNav'
import Hamburger from '@/components/Hamburger'
import Screenfull from '@/components/Screenfull'
import SizeSelect from '@/components/SizeSelect'
@ -58,6 +60,7 @@ import RuoYiDoc from '@/components/RuoYi/Doc'
export default {
components: {
Breadcrumb,
TopNav,
Hamburger,
Screenfull,
SizeSelect,
@ -81,6 +84,11 @@ export default {
value: val
})
}
},
topNav: {
get() {
return this.$store.state.settings.topNav
}
}
},
methods: {
@ -127,6 +135,11 @@ export default {
float: left;
}
.topmenu-container {
position: absolute;
left: 50px;
}
.errLog-container {
display: inline-block;
vertical-align: top;

View File

@ -41,6 +41,11 @@
<el-divider/>
<h3 class="drawer-title">系统布局配置</h3>
<div class="drawer-item">
<span>开启 TopNav</span>
<el-switch v-model="topNav" class="drawer-switch" />
</div>
<div class="drawer-item">
<span>开启 Tags-Views</span>
@ -57,6 +62,10 @@
<el-switch v-model="sidebarLogo" class="drawer-switch" />
</div>
<el-divider/>
<el-button size="small" type="primary" plain icon="el-icon-document-add" @click="saveSetting">保存配置</el-button>
<el-button size="small" plain icon="el-icon-refresh" @click="resetSetting">重置配置</el-button>
</div>
</div>
</template>
@ -67,15 +76,12 @@ import ThemePicker from '@/components/ThemePicker'
export default {
components: { ThemePicker },
data() {
return {}
return {
theme: this.$store.state.settings.theme,
sideTheme: this.$store.state.settings.sideTheme
};
},
computed: {
theme() {
return this.$store.state.settings.theme
},
sideTheme() {
return this.$store.state.settings.sideTheme
},
fixedHeader: {
get() {
return this.$store.state.settings.fixedHeader
@ -87,6 +93,20 @@ export default {
})
}
},
topNav: {
get() {
return this.$store.state.settings.topNav
},
set(val) {
this.$store.dispatch('settings/changeSetting', {
key: 'topNav',
value: val
})
if (!val) {
this.$store.commit("SET_SIDEBAR_ROUTERS", this.$store.state.permission.defaultRoutes);
}
}
},
tagsView: {
get() {
return this.$store.state.settings.tagsView
@ -116,12 +136,46 @@ export default {
key: 'theme',
value: val
})
this.theme = val;
},
handleTheme(val) {
this.$store.dispatch('settings/changeSetting', {
key: 'sideTheme',
value: val
})
this.sideTheme = val;
},
saveSetting() {
const loading = this.$loading({
lock: true,
fullscreen: false,
text: "正在保存到本地,请稍后...",
spinner: "el-icon-loading",
background: "rgba(0, 0, 0, 0.7)"
});
localStorage.setItem(
"layout-setting",
`{
"topNav":${this.topNav},
"tagsView":${this.tagsView},
"fixedHeader":${this.fixedHeader},
"sidebarLogo":${this.sidebarLogo},
"sideTheme":"${this.sideTheme}",
"theme":"${this.theme}"
}`
);
setTimeout(loading.close(), 1000)
},
resetSetting() {
this.$loading({
lock: true,
fullscreen: false,
text: "正在清除设置缓存并刷新,请稍后...",
spinner: "el-icon-loading",
background: "rgba(0, 0, 0, 0.7)"
});
localStorage.removeItem("layout-setting")
setTimeout("window.location.reload()", 1000)
}
}
}

View File

@ -13,7 +13,7 @@
mode="vertical"
>
<sidebar-item
v-for="(route, index) in permission_routes"
v-for="(route, index) in sidebarRouters"
:key="route.path + index"
:item="route"
:base-path="route.path"
@ -33,7 +33,7 @@ export default {
components: { SidebarItem, Logo },
computed: {
...mapState(["settings"]),
...mapGetters(["permission_routes", "sidebar"]),
...mapGetters(["sidebarRouters", "sidebar"]),
activeMenu() {
const route = this.$route;
const { meta, path } = route;

View File

@ -21,6 +21,7 @@
<li @click="refreshSelectedTag(selectedTag)">刷新页面</li>
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">关闭当前</li>
<li @click="closeOthersTags">关闭其他</li>
<li v-if="!isLastView()" @click="closeRightTags">关闭右侧</li>
<li @click="closeAllTags(selectedTag)">关闭所有</li>
</ul>
</div>
@ -29,7 +30,6 @@
<script>
import ScrollPane from './ScrollPane'
import path from 'path'
import Global from "@/layout/components/global.js";
export default {
components: { ScrollPane },
@ -84,6 +84,13 @@ export default {
isAffix(tag) {
return tag.meta && tag.meta.affix
},
isLastView() {
try {
return this.selectedTag.fullPath === this.visitedViews[this.visitedViews.length - 1].fullPath
} catch (err) {
return false
}
},
filterAffixTags(routes, basePath = '/') {
let tags = []
routes.forEach(route => {
@ -145,7 +152,6 @@ export default {
})
})
})
Global.$emit("removeCache", "refreshSelectedTag", this.selectedTag);
},
closeSelectedTag(view) {
this.$store.dispatch('tagsView/delView', view).then(({ visitedViews }) => {
@ -153,14 +159,19 @@ export default {
this.toLastView(visitedViews, view)
}
})
Global.$emit("removeCache", "closeSelectedTag", view);
},
closeRightTags() {
this.$store.dispatch('tagsView/delRightTags', this.selectedTag).then(visitedViews => {
if (!visitedViews.find(i => i.fullPath === this.$route.fullPath)) {
this.toLastView(visitedViews)
}
})
},
closeOthersTags() {
this.$router.push(this.selectedTag)
this.$router.push(this.selectedTag).catch(()=>{});
this.$store.dispatch('tagsView/delOthersViews', this.selectedTag).then(() => {
this.moveToCurrentTag()
})
Global.$emit("removeCache", "closeOthersTags", this.selectedTag);
},
closeAllTags(view) {
this.$store.dispatch('tagsView/delAllViews').then(({ visitedViews }) => {
@ -169,7 +180,6 @@ export default {
}
this.toLastView(visitedViews, view)
})
Global.$emit("removeCache", "closeAllTags");
},
toLastView(visitedViews, view) {
const latestView = visitedViews.slice(-1)[0]

View File

@ -1,3 +0,0 @@
import Vue from 'vue'
const global = new Vue()
export default global

View File

@ -20,9 +20,6 @@ import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels,
import Pagination from "@/components/Pagination";
// 自定义表格工具扩展
import RightToolbar from "@/components/RightToolbar"
// 代码高亮插件
import hljs from 'highlight.js'
import 'highlight.js/styles/github-gist.css'
// 全局方法挂载
Vue.prototype.getDicts = getDicts
@ -52,7 +49,6 @@ Vue.component('Pagination', Pagination)
Vue.component('RightToolbar', RightToolbar)
Vue.use(permission)
Vue.use(hljs.vuePlugin);
/**
* If you don't want to use mock-server

View File

@ -19,32 +19,20 @@ router.beforeEach((to, from, next) => {
} else {
if (store.getters.roles.length === 0) {
// 判断当前用户是否已拉取完user_info信息
store.dispatch('GetInfo').then(res => {
// 拉取user_info
const roles = res.roles
store.dispatch('GenerateRoutes', { roles }).then(accessRoutes => {
// 测试 默认静态页面
// store.dispatch('permission/generateRoutes', { roles }).then(accessRoutes => {
store.dispatch('GetInfo').then(() => {
store.dispatch('GenerateRoutes').then(accessRoutes => {
// 根据roles权限生成可访问的路由表
router.addRoutes(accessRoutes) // 动态添加可访问路由表
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
})
})
.catch(err => {
store.dispatch('FedLogOut').then(() => {
}).catch(err => {
store.dispatch('LogOut').then(() => {
Message.error(err)
next({ path: '/' })
})
})
} else {
next()
// 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓
// if (hasPermission(store.getters.roles, to.meta.roles)) {
// next()
// } else {
// next({ path: '/401', replace: true, query: { noGoBack: true }})
// }
// 可删 ↑
}
}
} else {

View File

@ -11,6 +11,11 @@ module.exports = {
*/
showSettings: false,
/**
* 是否显示顶部导航
*/
topNav: false,
/**
* 是否显示 tagsView
*/

View File

@ -10,6 +10,9 @@ const getters = {
introduction: state => state.user.introduction,
roles: state => state.user.roles,
permissions: state => state.user.permissions,
permission_routes: state => state.permission.routes
permission_routes: state => state.permission.routes,
topbarRouters:state => state.permission.topbarRouters,
defaultRoutes:state => state.permission.defaultRoutes,
sidebarRouters:state => state.permission.sidebarRouters,
}
export default getters

View File

@ -6,13 +6,30 @@ import ParentView from '@/components/ParentView';
const permission = {
state: {
routes: [],
addRoutes: []
addRoutes: [],
defaultRoutes: [],
topbarRouters: [],
sidebarRouters: []
},
mutations: {
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
}
},
SET_DEFAULT_ROUTES: (state, routes) => {
state.defaultRoutes = constantRoutes.concat(routes)
},
SET_TOPBAR_ROUTES: (state, routes) => {
// 顶部导航菜单默认添加统计报表栏指向首页
const index = [{
path: 'index',
meta: { title: '统计报表', icon: 'dashboard'}
}]
state.topbarRouters = routes.concat(index);
},
SET_SIDEBAR_ROUTERS: (state, routes) => {
state.sidebarRouters = routes
},
},
actions: {
// 生成路由
@ -20,10 +37,16 @@ const permission = {
return new Promise(resolve => {
// 向后端请求路由数据
getRouters().then(res => {
const accessedRoutes = filterAsyncRouter(res.data)
accessedRoutes.push({ path: '*', redirect: '/404', hidden: true })
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
const sdata = JSON.parse(JSON.stringify(res.data))
const rdata = JSON.parse(JSON.stringify(res.data))
const sidebarRoutes = filterAsyncRouter(sdata)
const rewriteRoutes = filterAsyncRouter(rdata, false, true)
rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true })
commit('SET_ROUTES', rewriteRoutes)
commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes))
commit('SET_DEFAULT_ROUTES', sidebarRoutes)
commit('SET_TOPBAR_ROUTES', sidebarRoutes)
resolve(rewriteRoutes)
})
})
}
@ -31,8 +54,11 @@ const permission = {
}
// 遍历后台传来的路由字符串,转换为组件对象
function filterAsyncRouter(asyncRouterMap) {
function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
return asyncRouterMap.filter(route => {
if (type && route.children) {
route.children = filterChildren(route.children)
}
if (route.component) {
// Layout ParentView 组件特殊处理
if (route.component === 'Layout') {
@ -44,14 +70,41 @@ function filterAsyncRouter(asyncRouterMap) {
}
}
if (route.children != null && route.children && route.children.length) {
route.children = filterAsyncRouter(route.children)
route.children = filterAsyncRouter(route.children, route, type)
} else {
delete route['children']
delete route['redirect']
}
return true
})
}
function filterChildren(childrenMap, lastRouter = false) {
var children = []
childrenMap.forEach((el, index) => {
if (el.children && el.children.length) {
if (el.component === 'ParentView') {
el.children.forEach(c => {
c.path = el.path + '/' + c.path
if (c.children && c.children.length) {
children = children.concat(filterChildren(c.children, c))
return
}
children.push(c)
})
return
}
}
if (lastRouter) {
el.path = lastRouter.path + '/' + el.path
}
children = children.concat(el)
})
return children
}
export const loadView = (view) => { // 路由懒加载
return (resolve) => require([`@/views/${view}`], resolve)
return (resolve) => require([`@/views/${view}`], resolve)
}
export default permission

View File

@ -1,17 +1,18 @@
import variables from '@/assets/styles/element-variables.scss'
import defaultSettings from '@/settings'
const { sideTheme, showSettings, tagsView, fixedHeader, sidebarLogo } = defaultSettings
const { sideTheme, showSettings, topNav, tagsView, fixedHeader, sidebarLogo } = defaultSettings
const storageSetting = JSON.parse(localStorage.getItem('layout-setting')) || ''
const state = {
theme: variables.theme,
sideTheme: sideTheme,
theme: storageSetting.theme || variables.theme,
sideTheme: storageSetting.sideTheme || sideTheme,
showSettings: showSettings,
tagsView: tagsView,
fixedHeader: fixedHeader,
sidebarLogo: sidebarLogo
topNav: storageSetting.topNav === undefined ? topNav : storageSetting.topNav,
tagsView: storageSetting.tagsView === undefined ? tagsView : storageSetting.tagsView,
fixedHeader: storageSetting.fixedHeader === undefined ? fixedHeader : storageSetting.fixedHeader,
sidebarLogo: storageSetting.sidebarLogo === undefined ? sidebarLogo : storageSetting.sidebarLogo
}
const mutations = {
CHANGE_SETTING: (state, { key, value }) => {
if (state.hasOwnProperty(key)) {

View File

@ -62,6 +62,23 @@ const mutations = {
break
}
}
},
DEL_RIGHT_VIEWS: (state, view) => {
const index = state.visitedViews.findIndex(v => v.path === view.path)
if (index === -1) {
return
}
state.visitedViews = state.visitedViews.filter((item, idx) => {
if (idx <= index || (item.meta && item.meta.affix)) {
return true
}
const i = state.cachedViews.indexOf(item.name)
if (i > -1) {
state.cachedViews.splice(i, 1)
}
return false
})
}
}
@ -148,6 +165,13 @@ const actions = {
updateVisitedView({ commit }, view) {
commit('UPDATE_VISITED_VIEW', view)
},
delRightTags({ commit }, view) {
return new Promise(resolve => {
commit('DEL_RIGHT_VIEWS', view)
resolve([...state.visitedViews])
})
}
}

View File

@ -49,7 +49,7 @@ const user = {
// 获取用户信息
GetInfo({ commit, state }) {
return new Promise((resolve, reject) => {
getInfo(state.token).then(res => {
getInfo().then(res => {
const user = res.user
const avatar = user.avatar == "" ? require("@/assets/images/profile.jpg") : process.env.VUE_APP_BASE_API + user.avatar;
if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组

View File

@ -9,9 +9,10 @@ export function checkPermi(value) {
if (value && value instanceof Array && value.length > 0) {
const permissions = store.getters && store.getters.permissions
const permissionDatas = value
const all_permission = "*:*:*";
const hasPermission = permissions.some(permission => {
return permissionDatas.includes(permission)
return all_permission === permission || permissionDatas.includes(permission)
})
if (!hasPermission) {
@ -33,9 +34,10 @@ export function checkRole(value) {
if (value && value instanceof Array && value.length > 0) {
const roles = store.getters && store.getters.roles
const permissionRoles = value
const super_admin = "admin";
const hasRole = roles.some(role => {
return permissionRoles.includes(role)
return super_admin === role || permissionRoles.includes(role)
})
if (!hasRole) {

View File

@ -1,103 +1,103 @@
import axios from 'axios'
import { Notification, MessageBox, Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
import errorCode from '@/utils/errorCode'
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 创建axios实例
const service = axios.create({
// axios中请求配置有baseURL选项表示请求URL公共部分
baseURL: process.env.VUE_APP_BASE_API,
// 超时
timeout: 10000
})
// request拦截器
service.interceptors.request.use(config => {
// 是否需要设置 token
const isToken = (config.headers || {}).isToken === false
if (getToken() && !isToken) {
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
}
// get请求映射params参数
if (config.method === 'get' && config.params) {
let url = config.url + '?';
for (const propName of Object.keys(config.params)) {
const value = config.params[propName];
var part = encodeURIComponent(propName) + "=";
if (value && typeof(value) !== "undefined") {
if (typeof value === 'object') {
for (const key of Object.keys(value)) {
let params = propName + '[' + key + ']';
var subPart = encodeURIComponent(params) + "=";
url += subPart + encodeURIComponent(value[key]) + "&";
}
} else {
url += part + encodeURIComponent(value) + "&";
}
}
}
url = url.slice(0, -1);
config.params = {};
config.url = url;
}
return config
}, 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 (code === 401) {
MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
store.dispatch('LogOut').then(() => {
location.href = '/index';
})
})
} else if (code === 500) {
Message({
message: msg,
type: 'error'
})
return Promise.reject(new Error(msg))
} 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.substr(message.length - 3) + "异常";
}
Message({
message: message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
export default service
import axios from 'axios'
import { Notification, MessageBox, Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
import errorCode from '@/utils/errorCode'
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 创建axios实例
const service = axios.create({
// axios中请求配置有baseURL选项表示请求URL公共部分
baseURL: process.env.VUE_APP_BASE_API,
// 超时
timeout: 10000
})
// request拦截器
service.interceptors.request.use(config => {
// 是否需要设置 token
const isToken = (config.headers || {}).isToken === false
if (getToken() && !isToken) {
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
}
// get请求映射params参数
if (config.method === 'get' && config.params) {
let url = config.url + '?';
for (const propName of Object.keys(config.params)) {
const value = config.params[propName];
var part = encodeURIComponent(propName) + "=";
if (value !== null && typeof(value) !== "undefined") {
if (typeof value === 'object') {
for (const key of Object.keys(value)) {
let params = propName + '[' + key + ']';
var subPart = encodeURIComponent(params) + "=";
url += subPart + encodeURIComponent(value[key]) + "&";
}
} else {
url += part + encodeURIComponent(value) + "&";
}
}
}
url = url.slice(0, -1);
config.params = {};
config.url = url;
}
return config
}, 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 (code === 401) {
MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
store.dispatch('LogOut').then(() => {
location.href = '/index';
})
})
} else if (code === 500) {
Message({
message: msg,
type: 'error'
})
return Promise.reject(new Error(msg))
} 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.substr(message.length - 3) + "异常";
}
Message({
message: message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
export default service

View File

@ -58,12 +58,12 @@ export function addDateRange(params, dateRange, propName) {
var search = params;
search.params = {};
if (null != dateRange && '' != dateRange) {
if (typeof(propName) === "undefined") {
if (typeof (propName) === "undefined") {
search.params["beginTime"] = dateRange[0];
search.params["endTime"] = dateRange[1];
} else {
search.params[propName + "BeginTime"] = dateRange[0];
search.params[propName + "EndTime"] = dateRange[1];
search.params["begin" + propName] = dateRange[0];
search.params["end" + propName] = dateRange[1];
}
}
return search;
@ -129,24 +129,47 @@ export function praseStrEmpty(str) {
* @param {*} id id字段 默认 'id'
* @param {*} parentId 父节点字段 默认 'parentId'
* @param {*} children 孩子节点字段 默认 'children'
* @param {*} rootId 根Id 默认 0
*/
export function handleTree(data, id, parentId, children, rootId) {
id = id || 'id'
parentId = parentId || 'parentId'
children = children || 'children'
rootId = rootId || Math.min.apply(Math, data.map(item => { return item[parentId] })) || 0
//对源数据深度克隆
const cloneData = JSON.parse(JSON.stringify(data))
//循环所有项
const treeData = cloneData.filter(father => {
let branchArr = cloneData.filter(child => {
//返回每一项的子级数组
return father[id] === child[parentId]
});
branchArr.length > 0 ? father.children = branchArr : '';
//返回第一层
return father[parentId] === rootId;
});
return treeData != '' ? treeData : data;
export function handleTree(data, id, parentId, children) {
let config = {
id: id || 'id',
parentId: parentId || 'parentId',
childrenList: children || 'children'
};
var childrenListMap = {};
var nodeIds = {};
var tree = [];
for (let d of data) {
let parentId = d[config.parentId];
if (childrenListMap[parentId] == null) {
childrenListMap[parentId] = [];
}
nodeIds[d[config.id]] = d;
childrenListMap[parentId].push(d);
}
for (let d of data) {
let parentId = d[config.parentId];
if (nodeIds[parentId] == null) {
tree.push(d);
}
}
for (let t of tree) {
adaptToChildrenList(t);
}
function adaptToChildrenList(o) {
if (childrenListMap[o[config.id]] !== null) {
o[config.childrenList] = childrenListMap[o[config.id]];
}
if (o[config.childrenList]) {
for (let c of o[config.childrenList]) {
adaptToChildrenList(c);
}
}
}
return tree;
}

View File

@ -36,5 +36,5 @@ export function resolveBlob(res, mimeType) {
aLink.setAttribute('download', fileName) // 设置下载文件名称
document.body.appendChild(aLink)
aLink.click()
document.body.appendChild(aLink)
document.body.removeChild(aLink);
}

View File

@ -119,9 +119,10 @@
</p>
<p>
<i class="el-icon-user-solid"></i> QQ群<s>满937441</s>
<s>满887144332</s> <s>满180251782</s>
<a href="https://jq.qq.com/?_wv=1027&k=4WWwqNxN" target="_blank"
> 104180207</a
<s>满887144332</s> <s>满180251782</s> <s>满104180207</s>
<s>满186866453</s>
<a href="https://jq.qq.com/?_wv=1027&k=5vYAqA05" target="_blank">
201396349</a
>
</p>
<p>
@ -146,6 +147,78 @@
<span>更新日志</span>
</div>
<el-collapse accordion>
<el-collapse-item title="v3.5.0 - 2021-05-25">
<ol>
<li>新增菜单导航显示风格TopNavfalse为左侧导航菜单true为顶部导航菜单</li>
<li>布局设置支持保存&重置配置</li>
<li>修复树表数据显示不全&加载慢问题</li>
<li>新增IE浏览器版本过低提示页面</li>
<li>用户登录后记录最后登录IP&时间</li>
<li>页面导出按钮点击之后添加遮罩</li>
<li>富文本编辑器支持自定义上传地址</li>
<li>富文本编辑组件新增readOnly属性</li>
<li>页签TagsView新增关闭右侧功能</li>
<li>显隐列组件加载初始默认隐藏列</li>
<li>关闭头像上传窗口还原默认图片</li>
<li>个人信息添加手机&邮箱重复验证</li>
<li>代码生成模板导出按钮点击后添加遮罩</li>
<li>代码生成模板树表操作列添加新增按钮</li>
<li>代码生成模板修复主子表字段重名问题</li>
<li>升级fastjson到最新版1.2.76</li>
<li>升级druid到最新版本v1.2.6</li>
<li>升级mybatis到最新版3.5.6 阻止远程代码执行漏洞</li>
<li>升级oshi到最新版本v5.6.0</li>
<li>velocity剔除commons-collections版本防止3.2.1版本的反序列化漏洞</li>
<li>数据监控页默认账户密码防止越权访问</li>
<li>修复firefox下表单构建拖拽会新打卡一个选项卡</li>
<li>修正后端导入表权限标识</li>
<li>修正前端操作日志&登录日志权限标识</li>
<li>设置Redis配置HashKey序列化</li>
<li>删除操作日志记录信息</li>
<li>上传媒体类型添加视频格式</li>
<li>修复请求形参未传值记录日志异常问题</li>
<li>优化xss校验json请求条件</li>
<li>树级结构更新子节点使用replaceFirst</li>
<li>优化ExcelUtil空值处理</li>
<li>日志记录过滤BindingResult对象防止异常</li>
<li>修改主题后mini类型按钮无效问题</li>
<li>优化通用下载完成后删除节点</li>
<li>通用Controller添加响应返回消息</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v3.4.0 - 2021-02-22">
<ol>
<li>代码生成模板支持主子表</li>
<li>表格右侧工具栏组件支持显隐列</li>
<li>图片组件添加预览&移除功能</li>
<li>Excel注解支持Image图片导出</li>
<li>操作按钮组调整为朴素按钮样式</li>
<li>代码生成支持文件上传组件</li>
<li>代码生成日期控件区分范围</li>
<li>代码生成数据库文本类型生成表单文本域</li>
<li>用户手机邮箱&菜单组件修改允许空字符串</li>
<li>升级SpringBoot到最新版本2.2.13 提升启动速度</li>
<li>升级druid到最新版本v1.2.4</li>
<li>升级fastjson到最新版1.2.75</li>
<li>升级element-ui到最新版本2.15.0</li>
<li>修复IE11浏览器报错问题</li>
<li>优化多级菜单之间切换无法缓存的问题</li>
<li>修复四级菜单无法显示问题</li>
<li>修正侧边栏静态路由丢失问题</li>
<li>修复角色管理-编辑角色-功能权限显示异常</li>
<li>配置文件新增redis数据库索引属性</li>
<li>权限工具类增加admin判断</li>
<li>角色非自定义权限范围清空选择值</li>
<li>修复导入数据为负浮点数时丢失精度问题</li>
<li>移除path-to-regexp正则匹配插件</li>
<li>修复生成树表代码异常</li>
<li>修改ip字段长度防止ipv6地址长度不够</li>
<li>防止get请求参数值为false或0等特殊值会导致无法正确的传参</li>
<li>登录后push添加catch防止出现检查错误</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v3.3.0 - 2020-12-14">
<ol>
<li>新增缓存监控功能</li>
@ -493,7 +566,7 @@ export default {
data() {
return {
// 版本号
version: "3.3.0",
version: "3.5.0",
};
},
methods: {

View File

@ -48,7 +48,7 @@
</el-form>
<!-- 底部 -->
<div class="el-login-footer">
<span>Copyright © 2018-2020 ruoyi.vip All Rights Reserved.</span>
<span>Copyright © 2018-2021 ruoyi.vip All Rights Reserved.</span>
</div>
</div>
</template>
@ -126,15 +126,12 @@ export default {
Cookies.remove("password");
Cookies.remove('rememberMe');
}
this.$store
.dispatch("Login", this.loginForm)
.then(() => {
this.$router.push({ path: this.redirect || "/" });
})
.catch(() => {
this.loading = false;
this.getCode();
});
this.$store.dispatch("Login", this.loginForm).then(() => {
this.$router.push({ path: this.redirect || "/" }).catch(()=>{});
}).catch(() => {
this.loading = false;
this.getCode();
});
}
});
}

View File

@ -8,7 +8,7 @@ export default {
name: "Druid",
data() {
return {
src: process.env.VUE_APP_BASE_API + "/druid/index.html",
src: process.env.VUE_APP_BASE_API + "/druid/login.html",
height: document.documentElement.clientHeight - 94.5 + "px;",
loading: true
};

View File

@ -31,7 +31,7 @@
</el-select>
</el-form-item>
<el-form-item>
<el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
@ -40,6 +40,7 @@
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
@ -49,6 +50,7 @@
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single"
@ -59,6 +61,7 @@
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@ -69,8 +72,10 @@
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
:loading="exportLoading"
@click="handleExport"
v-hasPermi="['monitor:job:export']"
>导出</el-button>
@ -78,6 +83,7 @@
<el-col :span="1.5">
<el-button
type="info"
plain
icon="el-icon-s-operation"
size="mini"
@click="handleJobLog"
@ -269,6 +275,8 @@ export default {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 选中数组
ids: [],
// 非单个禁用
@ -472,10 +480,12 @@ export default {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
}).then(() => {
this.exportLoading = true;
return exportJob(queryParams);
}).then(response => {
this.download(response.msg);
this.exportLoading = false;
})
}
}

View File

@ -56,7 +56,7 @@
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
@ -65,6 +65,7 @@
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@ -75,6 +76,7 @@
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
@click="handleClean"
@ -84,8 +86,10 @@
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
:loading="exportLoading"
@click="handleExport"
v-hasPermi="['monitor:job:export']"
>导出</el-button>
@ -172,6 +176,8 @@ export default {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 选中数组
ids: [],
// 非多个禁用
@ -285,10 +291,12 @@ export default {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
}).then(() => {
this.exportLoading = true;
return exportJobLog(queryParams);
}).then(response => {
this.download(response.msg);
this.exportLoading = false;
})
}
}

View File

@ -50,7 +50,7 @@
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
@ -59,6 +59,7 @@
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@ -69,6 +70,7 @@
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
@click="handleClean"
@ -78,10 +80,12 @@
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
:loading="exportLoading"
@click="handleExport"
v-hasPermi="['system:logininfor:export']"
v-hasPermi="['monitor:logininfor:export']"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
@ -123,6 +127,8 @@ export default {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 选中数组
ids: [],
// 非多个禁用
@ -218,10 +224,12 @@ export default {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
}).then(() => {
this.exportLoading = true;
return exportLogininfor(queryParams);
}).then(response => {
this.download(response.msg);
this.exportLoading = false;
})
}
}

View File

@ -20,7 +20,7 @@
/>
</el-form-item>
<el-form-item>
<el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>

View File

@ -66,7 +66,7 @@
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
@ -75,6 +75,7 @@
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@ -85,6 +86,7 @@
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
@click="handleClean"
@ -94,10 +96,12 @@
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
:loading="exportLoading"
@click="handleExport"
v-hasPermi="['system:config:export']"
v-hasPermi="['monitor:operlog:export']"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
@ -192,6 +196,8 @@ export default {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 选中数组
ids: [],
// 非多个禁用
@ -306,10 +312,12 @@ export default {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
}).then(() => {
this.exportLoading = true;
return exportOperlog(queryParams);
}).then(response => {
this.download(response.msg);
this.exportLoading = false;
})
}
}

View File

@ -44,7 +44,7 @@
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
@ -53,6 +53,7 @@
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
@ -62,6 +63,7 @@
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single"
@ -72,6 +74,7 @@
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@ -82,8 +85,10 @@
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
:loading="exportLoading"
@click="handleExport"
v-hasPermi="['system:config:export']"
>导出</el-button>
@ -91,6 +96,7 @@
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-refresh"
size="mini"
@click="handleClearCache"
@ -183,6 +189,8 @@ export default {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 选中数组
ids: [],
// 非单个禁用
@ -339,10 +347,12 @@ export default {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
}).then(() => {
this.exportLoading = true;
return exportConfig(queryParams);
}).then(response => {
this.download(response.msg);
this.exportLoading = false;
})
},
/** 清理缓存按钮操作 */

View File

@ -21,7 +21,7 @@
</el-select>
</el-form-item>
<el-form-item>
<el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
@ -30,6 +30,7 @@
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"

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