77 Commits

Author SHA1 Message Date
f92c573936 update ry-ui.js 2024-11-13 22:00:15 +08:00
edbc074351 add timezone 2024-11-12 13:39:21 +08:00
90dab826bf 支持自定义显示Excel属性列 2024-11-07 22:48:29 +08:00
69020cfc50 升级spring-boot到最新版本3.3.5 2024-11-06 21:22:44 +08:00
c122c21b95 升级springdoc到最新版本2.6.0 2024-11-06 21:22:00 +08:00
38583efa51 优化身份证脱敏正则 2024-10-21 17:28:09 +08:00
a7fa724fc3 升级oshi到最新版本6.6.5 2024-10-21 17:27:59 +08:00
428c9aa5eb 添加新群号:232896766 2024-10-21 17:27:48 +08:00
05e2c28343 修复角色禁用权限不失效问题 2024-09-21 11:03:13 +08:00
60a6765f09 用户导入响应消息对名称安全处理 2024-09-13 21:20:02 +08:00
76fb2cf1f8 修复记住我请求头过大的问题 2024-09-09 10:54:23 +08:00
96bf7d5c17 修复记住我失效的问题(IAPHA4) 2024-09-06 20:24:19 +08:00
1324818e46 update content 2024-09-04 19:44:36 +08:00
ba00eea7d4 优化异步树表格折叠同步子状态 2024-09-03 19:13:45 +08:00
75946b4ebc 升级oshi到最新版本6.6.3 2024-08-30 21:25:36 +08:00
e286718a1b update sqlkeyword 2024-08-30 21:25:26 +08:00
40b2bb4c46 修改时间范围日期格式 2024-07-08 16:32:16 +08:00
6096bb5e05 升级commons.io到最新版本2.16.1 2024-07-03 11:47:58 +08:00
00e2fab203 remove sub resultType 2024-07-03 11:47:47 +08:00
c65919c600 springdoc 2024-06-27 23:39:00 +08:00
0a6ff25875 优化代码 2024-06-27 16:49:30 +08:00
c22d0af018 修复主子表数据显示问题(IA61OI) 2024-06-24 17:23:44 +08:00
e33e13d3b4 update springboot3 2024-06-24 14:48:38 +08:00
32e448b6b4 添加新群号:287843737 2024-06-13 14:59:19 +08:00
116bed00a5 优化数据权限代码 2024-06-13 14:58:04 +08:00
4f5bf990bf 若依 v4.7.9 2024-06-06 09:08:49 +08:00
0144cb855f 优化代码 2024-06-05 11:53:53 +08:00
e8f9bfb5c6 新增表格示例(保存状态) 2024-06-03 13:31:33 +08:00
56b1354496 升级oshi到最新版本6.6.1 2024-06-03 13:31:11 +08:00
8e1a89bcbe 升级druid到最新版本1.2.23 2024-06-03 13:30:50 +08:00
4c9bfd8683 升级bootstrap-table到最新版本1.22.6 2024-06-03 13:29:36 +08:00
ec6d84aa88 Excel注解新增属性comboReadDict 2024-06-02 19:08:47 +08:00
05c29a2a6c update mapper 2024-06-01 17:55:50 +08:00
18ef1ea816 update sql css 2024-06-01 17:45:01 +08:00
3c574b45dc 代码生成支持表单布局选项 2024-06-01 11:54:05 +08:00
0e08cc7ca4 优化代码生成模板格式 2024-06-01 09:55:33 +08:00
b2d832edab 优化代码生成主子表关联查询方式 2024-06-01 09:31:17 +08:00
60889185d8 新增表格示例(全文检索) 2024-05-31 11:25:27 +08:00
b95280aba2 优化导入Excel时设置dictType属性重复查缓存问题 2024-05-30 13:31:34 +08:00
96b2c0d9b7 update sql 2024-05-29 12:20:37 +08:00
f0efa914fe 优化代码 2024-05-29 12:20:14 +08:00
edb1c614d0 限制用户操作数据权限范围 2024-05-29 12:19:57 +08:00
61c2e96aaa 新增表格示例(虚拟滚动) 2024-05-13 10:58:48 +08:00
f31e6bd1bb 优化树表格align属性在标题生效(I9FVBJ) 2024-04-18 09:48:01 +08:00
f5805fd79e 新增Anonymous匿名访问不鉴权注解 2024-04-15 13:49:50 +08:00
55913e8707 升级spring-framework到安全版本,防止漏洞风险 2024-04-11 16:31:12 +08:00
45f71a5125 新增数据脱敏过滤注解 2024-04-08 13:14:40 +08:00
4218cf7cfd Excel注解ColumnType类型新增文本 2024-03-22 16:20:02 +08:00
4963454f23 升级oshi到最新版本6.5.0 2024-03-15 09:20:47 +08:00
85cf318a94 update locale 2024-03-13 17:09:00 +08:00
82a0eaed4a 优化代码 2024-03-13 16:33:13 +08:00
f6ce55be47 定义统一Locale获取国际化(I8Z7DA) 2024-03-13 11:38:35 +08:00
fd37e8b4a7 定时任务白名单配置范围缩小 2024-03-11 11:15:11 +08:00
d43fea60e2 update copyright 2024 2024-03-11 09:40:08 +08:00
05075e21a3 joblog order by 2024-03-11 09:39:53 +08:00
f2f6b25ed2 用户密码新增非法字符验证 2024-03-01 21:50:45 +08:00
4a5ea50a14 update gen pom.xml 2024-03-01 21:50:34 +08:00
81c1ffb07e 升级oshi到最新版本6.4.13 2024-02-29 14:14:01 +08:00
6dd8bbfc38 定时任务屏蔽违规的字符 2024-02-29 14:13:51 +08:00
bcb6222c61 优化匹配方式 2024-02-29 14:13:38 +08:00
0ce40fc039 升级oshi到最新版本6.4.11 2024-01-19 11:54:25 +08:00
0cde5c2446 update http user-agent 2024-01-15 10:55:10 +08:00
69714747d7 !481 【轻量级 PR】:更新HttpUtils中的默认User-Agent
Merge pull request !481 from coderWu/master
2024-01-15 02:25:30 +00:00
83b90c3267 通知公告新增详细显示 2024-01-12 11:57:22 +08:00
5b19d33af9 优化登录注册页面 2024-01-09 19:49:18 +08:00
c8fd0b0470 优化登录注册页面 2024-01-09 19:46:56 +08:00
8c8d53a9cc 修复重置日期时出现的异常问题(I8PZFA) 2024-01-09 15:45:40 +08:00
f84b77993c 更新HttpUtils中的User-Agent 2024-01-08 22:24:56 +08:00
192c0c5241 修复tooltip单击复制文本不生效的问题 2024-01-08 15:18:03 +08:00
92d121175d 默认加载layer扩展皮肤 2024-01-08 15:15:04 +08:00
d6e599d6ac 未修改初始密码弹框提醒 2024-01-04 11:24:07 +08:00
a4c8026265 升级oshi到最新版本6.4.8 2023-12-05 11:36:30 +08:00
90022df5a6 升级commons.io到最新版本2.13.0 2023-12-05 11:36:12 +08:00
0699c2350a 添加新群号:174942938 2023-11-27 10:13:18 +08:00
fd4fe83e9f 修复页签关闭后存在的跳转问题(I8JDIS) 2023-11-25 15:43:51 +08:00
4933ea6d2f 操作日志列表重置回第一页 2023-11-25 15:43:28 +08:00
07ddbfeabf 修复高频率定时任务不执行问题(I8IQSX) 2023-11-25 15:43:15 +08:00
154 changed files with 2943 additions and 879 deletions

View File

@ -1,11 +1,11 @@
<p align="center">
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-dd77653d7c9f197dd9d93684f3c8dcfbab6.png">
</p>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v4.7.8</h1>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v4.7.9</h1>
<h4 align="center">基于SpringBoot开发的轻量级Java快速开发框架</h4>
<p align="center">
<a href="https://gitee.com/y_project/RuoYi/stargazers"><img src="https://gitee.com/y_project/RuoYi/badge/star.svg?theme=gvp"></a>
<a href="https://gitee.com/y_project/RuoYi"><img src="https://img.shields.io/badge/RuoYi-v4.7.8-brightgreen.svg"></a>
<a href="https://gitee.com/y_project/RuoYi"><img src="https://img.shields.io/badge/RuoYi-v4.7.9-brightgreen.svg"></a>
<a href="https://gitee.com/y_project/RuoYi/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
</p>
@ -99,4 +99,4 @@
## 若依交流群
QQ群 [![加入QQ群](https://img.shields.io/badge/已满-1389287-blue.svg)](https://jq.qq.com/?_wv=1027&k=5HBAaYN) [![加入QQ群](https://img.shields.io/badge/已满-1679294-blue.svg)](https://jq.qq.com/?_wv=1027&k=5cHeRVW) [![加入QQ群](https://img.shields.io/badge/已满-1529866-blue.svg)](https://jq.qq.com/?_wv=1027&k=53R0L5Z) [![加入QQ群](https://img.shields.io/badge/已满-1772718-blue.svg)](https://jq.qq.com/?_wv=1027&k=5g75dCU) [![加入QQ群](https://img.shields.io/badge/已满-1366522-blue.svg)](https://jq.qq.com/?_wv=1027&k=58cPoHA) [![加入QQ群](https://img.shields.io/badge/已满-1382251-blue.svg)](https://jq.qq.com/?_wv=1027&k=5Ofd4Pb) [![加入QQ群](https://img.shields.io/badge/已满-1145125-blue.svg)](https://jq.qq.com/?_wv=1027&k=5yugASz) [![加入QQ群](https://img.shields.io/badge/已满-86752435-blue.svg)](https://jq.qq.com/?_wv=1027&k=5Rf3d2P) [![加入QQ群](https://img.shields.io/badge/已满-134072510-blue.svg)](https://jq.qq.com/?_wv=1027&k=5ZIjaeP) [![加入QQ群](https://img.shields.io/badge/已满-210336300-blue.svg)](https://jq.qq.com/?_wv=1027&k=5CJw1jY) [![加入QQ群](https://img.shields.io/badge/已满-339522636-blue.svg)](https://jq.qq.com/?_wv=1027&k=5omzbKc) [![加入QQ群](https://img.shields.io/badge/已满-130035985-blue.svg)](https://jq.qq.com/?_wv=1027&k=qPIKBb7s) [![加入QQ群](https://img.shields.io/badge/已满-143151071-blue.svg)](https://jq.qq.com/?_wv=1027&k=4NsjKbtU) [![加入QQ群](https://img.shields.io/badge/已满-158781320-blue.svg)](https://jq.qq.com/?_wv=1027&k=VD2pkz2G) [![加入QQ群](https://img.shields.io/badge/已满-201531282-blue.svg)](https://jq.qq.com/?_wv=1027&k=HlshFwkJ) [![加入QQ群](https://img.shields.io/badge/已满-101526938-blue.svg)](https://jq.qq.com/?_wv=1027&k=0ARRrO9V) [![加入QQ群](https://img.shields.io/badge/已满-264355400-blue.svg)](https://jq.qq.com/?_wv=1027&k=up9k3ZXJ) [![加入QQ群](https://img.shields.io/badge/已满-298522656-blue.svg)](https://jq.qq.com/?_wv=1027&k=540WfdEr) [![加入QQ群](https://img.shields.io/badge/已满-139845794-blue.svg)](https://jq.qq.com/?_wv=1027&k=ss91fC4t) [![加入QQ群](https://img.shields.io/badge/已满-185760789-blue.svg)](https://jq.qq.com/?_wv=1027&k=Cqd66IKe) [![加入QQ群](https://img.shields.io/badge/175104288-blue.svg)](https://jq.qq.com/?_wv=1027&k=7FplYUnR)
QQ群 [![加入QQ群](https://img.shields.io/badge/已满-1389287-blue.svg)](https://jq.qq.com/?_wv=1027&k=5HBAaYN) [![加入QQ群](https://img.shields.io/badge/已满-1679294-blue.svg)](https://jq.qq.com/?_wv=1027&k=5cHeRVW) [![加入QQ群](https://img.shields.io/badge/已满-1529866-blue.svg)](https://jq.qq.com/?_wv=1027&k=53R0L5Z) [![加入QQ群](https://img.shields.io/badge/已满-1772718-blue.svg)](https://jq.qq.com/?_wv=1027&k=5g75dCU) [![加入QQ群](https://img.shields.io/badge/已满-1366522-blue.svg)](https://jq.qq.com/?_wv=1027&k=58cPoHA) [![加入QQ群](https://img.shields.io/badge/已满-1382251-blue.svg)](https://jq.qq.com/?_wv=1027&k=5Ofd4Pb) [![加入QQ群](https://img.shields.io/badge/已满-1145125-blue.svg)](https://jq.qq.com/?_wv=1027&k=5yugASz) [![加入QQ群](https://img.shields.io/badge/已满-86752435-blue.svg)](https://jq.qq.com/?_wv=1027&k=5Rf3d2P) [![加入QQ群](https://img.shields.io/badge/已满-134072510-blue.svg)](https://jq.qq.com/?_wv=1027&k=5ZIjaeP) [![加入QQ群](https://img.shields.io/badge/已满-210336300-blue.svg)](https://jq.qq.com/?_wv=1027&k=5CJw1jY) [![加入QQ群](https://img.shields.io/badge/已满-339522636-blue.svg)](https://jq.qq.com/?_wv=1027&k=5omzbKc) [![加入QQ群](https://img.shields.io/badge/已满-130035985-blue.svg)](https://jq.qq.com/?_wv=1027&k=qPIKBb7s) [![加入QQ群](https://img.shields.io/badge/已满-143151071-blue.svg)](https://jq.qq.com/?_wv=1027&k=4NsjKbtU) [![加入QQ群](https://img.shields.io/badge/已满-158781320-blue.svg)](https://jq.qq.com/?_wv=1027&k=VD2pkz2G) [![加入QQ群](https://img.shields.io/badge/已满-201531282-blue.svg)](https://jq.qq.com/?_wv=1027&k=HlshFwkJ) [![加入QQ群](https://img.shields.io/badge/已满-101526938-blue.svg)](https://jq.qq.com/?_wv=1027&k=0ARRrO9V) [![加入QQ群](https://img.shields.io/badge/已满-264355400-blue.svg)](https://jq.qq.com/?_wv=1027&k=up9k3ZXJ) [![加入QQ群](https://img.shields.io/badge/已满-298522656-blue.svg)](https://jq.qq.com/?_wv=1027&k=540WfdEr) [![加入QQ群](https://img.shields.io/badge/已满-139845794-blue.svg)](https://jq.qq.com/?_wv=1027&k=ss91fC4t) [![加入QQ群](https://img.shields.io/badge/已满-185760789-blue.svg)](https://jq.qq.com/?_wv=1027&k=Cqd66IKe) [![加入QQ群](https://img.shields.io/badge/已满-175104288-blue.svg)](https://jq.qq.com/?_wv=1027&k=7FplYUnR) [![加入QQ群](https://img.shields.io/badge/已满-174942938-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=lqMHu_5Fskm7H2S1vNAQTtzAUokVydwc&authKey=ptw0Fpch5pbNocML3CIJKKqZBaq2DI7cusKuzIgfMNiY3t9Pvd9hP%2BA8WYx3yaY1&noverify=0&group_code=174942938) [![加入QQ群](https://img.shields.io/badge/已满-287843737-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=blYlRDmwZXSXI5pVrPPU7ZJ1stFJ6Q2Q&authKey=ForGBWffHVlPt9NE3d7g4DoOIouBh%2BqvAj2lp1CLReHfZAUaK7SRrdwsChKpRJDJ&noverify=0&group_code=287843737) [![加入QQ群](https://img.shields.io/badge/232896766-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=KTVAIhggR3rR3uZWK9A8kR4yYNREQ4jo&authKey=An4DUV9e7uK8I8VgBbp949z0ypQoDrOoqvVg%2FWOr2vuNNDMZUAMPvqHor6TFMIgz&noverify=0&group_code=232896766)

80
pom.xml
View File

@ -5,30 +5,31 @@
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId>
<version>4.7.8</version>
<version>4.7.9</version>
<name>ruoyi</name>
<url>http://www.ruoyi.vip</url>
<description>若依管理系统</description>
<properties>
<ruoyi.version>4.7.8</ruoyi.version>
<ruoyi.version>4.7.9</ruoyi.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<java.version>17</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<shiro.version>1.13.0</shiro.version>
<shiro.version>2.0.1</shiro.version>
<mybatis-spring-boot.version>3.0.3</mybatis-spring-boot.version>
<thymeleaf.extras.shiro.version>2.1.0</thymeleaf.extras.shiro.version>
<druid.version>1.2.20</druid.version>
<druid.version>1.2.23</druid.version>
<bitwalker.version>1.21</bitwalker.version>
<kaptcha.version>2.3.3</kaptcha.version>
<swagger.version>3.0.0</swagger.version>
<pagehelper.boot.version>1.4.7</pagehelper.boot.version>
<pagehelper.boot.version>2.1.0</pagehelper.boot.version>
<fastjson.version>1.2.83</fastjson.version>
<oshi.version>6.4.7</oshi.version>
<commons.io.version>2.11.0</commons.io.version>
<oshi.version>6.6.5</oshi.version>
<commons.io.version>2.16.1</commons.io.version>
<poi.version>4.1.2</poi.version>
<velocity.version>2.3</velocity.version>
<springdoc.version>2.6.0</springdoc.version>
</properties>
<!-- 依赖声明 -->
@ -39,7 +40,7 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.5.15</version>
<version>3.3.5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@ -47,7 +48,7 @@
<!-- 阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<artifactId>druid-spring-boot-3-starter</artifactId>
<version>${druid.version}</version>
</dependency>
@ -62,6 +63,14 @@
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<classifier>jakarta</classifier>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<classifier>jakarta</classifier>
<version>${shiro.version}</version>
</dependency>
@ -69,7 +78,15 @@
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<classifier>jakarta</classifier>
<version>${shiro.version}</version>
<!-- 排除仍使用了javax.servlet的依赖 -->
<exclusions>
<exclusion>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Shiro使用EhCache缓存框架 -->
@ -86,6 +103,25 @@
<version>${thymeleaf.extras.shiro.version}</version>
</dependency>
<!-- mybatis依赖 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-spring-boot.version}</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.1.0</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
</dependency>
<!-- 解析客户端操作系统、浏览器等 -->
<dependency>
<groupId>eu.bitwalker</groupId>
@ -107,17 +143,11 @@
<version>${oshi.version}</version>
</dependency>
<!-- Swagger3依赖 -->
<!-- spring-doc -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>${swagger.version}</version>
<exclusions>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</exclusion>
</exclusions>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version>
</dependency>
<!-- io常用工具类 -->
@ -206,13 +236,19 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<version>3.13.0</version>
<configuration>
<parameters>true</parameters>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.3.0</version>
</plugin>
</plugins>
</build>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId>
<version>4.7.8</version>
<version>4.7.9</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
@ -30,23 +30,16 @@
<optional>true</optional> <!-- 表示依赖不会传递 -->
</dependency>
<!-- swagger3-->
<!-- spring-doc -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
</dependency>
<!-- 防止进入swagger页面报类型转换错误排除3.0.0中的引用手动增加1.6.2版本 -->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.6.2</version>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
</dependency>
<!-- Mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<!-- 核心模块-->
@ -89,7 +82,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.0.0</version>
<version>3.1.0</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
<warName>${project.artifactId}</warName>
@ -123,6 +116,7 @@
<exclude>**/*.min.js</exclude>
<exclude>**/*.min.css</exclude>
<exclude>**/fileinput.js</exclude>
<exclude>**/validate/**</exclude>
<exclude>**/bootstrap-table/**</exclude>
</excludes>
</configuration>

View File

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

View File

@ -16,6 +16,7 @@ import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.annotation.Excel.ColumnType;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.BaseEntity;
import com.ruoyi.common.core.page.PageDomain;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.page.TableSupport;
@ -126,6 +127,22 @@ public class DemoTableController extends BaseController
columns.add(new UserTableColumn("用户状态", "status"));
}
private final static List<DocumentModel> documents = new ArrayList<DocumentModel>();
{
documents.add(new DocumentModel(1, "247-XW·2024-D10-0001", "新闻热线[2024]000001", "索尼射击游戏《Concord》停止运营玩家将获全额退款", "索尼宣布多人射击游戏《Concord》将于9月6日停止运营玩家将获得全额退款。游戏总监Ryan Ellis在给玩家的信中表示这款游戏首次发布“并没有像我们预期的那样顺利”。《Concord》的开发历时8年投资超过1.5亿美元。游戏在Steam平台的售价为40美元采用买断制销售模式。据SteamDB统计游戏上市后的最高同时在线人数为697人。"));
documents.add(new DocumentModel(2, "247-XW·2024-D30-0002", "新闻热线[2024]000002", "网红账号被封,央媒:如此炫富毒瘤早就该拔了", "在社交平台上分享自己的生活日常,本来无可厚非。但无底线地展示物欲、宣扬拜金,取笑甚至嘲讽工薪者的烟火生活,就会遮蔽普通人的平凡质朴和坚韧奋斗,在无形中消解芸芸众生脚踏实地、自立自强的社会正气。对这种助长金钱至上、刺激公众焦虑,既污染网络生态,又撕裂社会和谐的炫富“毒瘤”,必须坚决拔除之。在国家有关部门的部署下,近日,多个网络平台开展“不良价值导向内容专项治理”行动,对“奢靡浪费”“炫富拜金”等问题从严打击,倡导理性、文明的消费观和价值观。"));
documents.add(new DocumentModel(3, "CT01-XW·2024-Y-0003", "新闻热线[2024]000003", "重庆一夫妻被骗至缅甸,家属:两人已被解救,预计很快能回国", "5月25日重庆一对夫妻在前往泰国后失联疑被诈骗集团骗至缅甸的消息引发广泛关注。警方已对此事立案调查而这对夫妻的亲属则每天生活在焦急和不安之中。亲属家都瘫痪了事情一经曝光迅速登上了热搜成为公众热议的话题。据了解这对夫妻原计划是去泰国谈生意但不幸的是他们的泰国之行变成了一场噩梦。亲属李先生透露4月14日他们夫妻二人抵达泰国不久后便疑似被人以10万元的价格卖到缅甸目前被困在缅甸妙瓦底的一个电信诈骗园区。"));
documents.add(new DocumentModel(4, "CT01-XW·2024-Y-0004", "新闻热线[2024]000004", "江滨社区联合派出所、金霞消防站开展电动自行车安全隐患夜查活动", "近日,长沙市开福区江滨社区联合派出所、金霞消防站深入居民小区、单位场所,以电动车自行车火灾防范为重点,开展消防安全夜查行动。此次夜查紧紧围绕老旧居民区、“三合一”场所、沿街门店、夜间经营使用场所等场所开展监督检查,重点检查电动自行车违规停放充电、堵塞疏散通道和安全出口,架空层违规作为电动自行车停放充电场所,电动自行车违规“进楼入户”“飞线充电”,电动自行车擅自改装等五大类问题。"));
documents.add(new DocumentModel(5, "CT01-XW·2024-Y-0005", "新闻热线[2024]000005", "《黑神话》让海外玩家迷上“悟空”", "备受全球玩家瞩目的首款国产3A游戏《黑神话悟空》日前正式发布。精美绝伦的东方美学世界、精彩纷呈的中国神话故事、酣畅淋漓的游戏体验这款游戏为全球玩家带来一场视觉与文化的双重盛宴。从“悟空”成功出海的背后海外人士看到了中国游戏产业的巨大进步感受到了中国文化的多元精彩并对下一个“悟空”的诞生及更多中国文化产品走向世界充满期待。"));
documents.add(new DocumentModel(6, "CT01-XW·2024-Y-0006", "新闻热线[2024]000006", "市场状况充满挑战极星宣布裁员全球约15%的员工", "据路透社报道极星周五以“充满挑战的市场状况”为由宣布计划在全球范围内裁减约450个职位。此次裁员之际许多人都对电动汽车需求降温表示担忧而且极星预计汽车业务最早将在2025年开始实现收支平衡。极星发言人表示“作为该商业计划的一部分我们需要调整我们的业务和运营规模。” “这涉及减少外部支出,遗憾的是,还包括我们的员工数量。”该公司还表示,希望减少对沃尔沃及其母公司吉利外部融资的依赖,最近还表示希望削减成本并提高电动汽车的利润率。"));
documents.add(new DocumentModel(7, "CT01-XW·2024-Y-0007", "新闻热线[2024]000007", "浙江隐秘富豪涉百亿非法集资案,部分资金流入新造车公司", "5月10日、11日上海北广投资管理有限公司下称“北广投”非法集资案在上海黄浦区人民法院一审公开开庭审理北广投实控人周敏、法定代表人朱江等30余名中高管被控非法吸收公众存款罪。根据财新报道这一案件中非法集资的资金有部分流入了两家新能源车企——爱驰汽车、万象汽车。同时有多位投资人引述与经侦部门沟通时的说法称该案事发时未兑付金额有130余亿元其中去往广微控股45亿元、万象汽车63亿元、爱驰汽车15亿元。"));
documents.add(new DocumentModel(8, "CT01-XW·2024-Y-0008", "新闻热线[2024]000008", "特斯拉宣布Model Y升级搭载HW4.0硬件售价仍25.89万元起", "2月1日特斯拉官方宣布ModelY升级。外观上新增ModelY专属色“快银车漆”并采用烈焰红代替中国红、星空灰代替冷光银性能上ModelY全系配备全新一代自动辅助驾驶硬件(HW4.0)通过搭载超远距离双目摄像头ModelY的最远探测距离达424米。由此特斯拉全系车型均配备了自动辅助驾驶硬件HW4.0。在售价方面特斯拉中国官网显示ModelY车型依然保持原价。ModelY后轮驱动版25.89万元起、ModelY长续航版29.99万元起、ModelY高性能版售价36.39万元起。"));
documents.add(new DocumentModel(9, "CT01-XW·2024-D10-0009", "新闻热线[2024]000009", "华为手机归来,谁最受伤?", "低迷周期下的智能手机市场在2023年下半年迎来了华为的回归这也给本就竞争激烈的市场环境带来了更大变数。1月29日有消息称华为已注册“星耀手机”品牌商标定位中端手机市场但上述消息并未获得华为方面确认。“目前星耀的相关信息我们看到了但是没有获得产品信息以及启动线下铺货的通知。对于和其他品牌的二选一问题听其他省份的经销商说过但目前华为渠道这边也没有更多动作。”一位广东区域的华为核心经销商对记者说。但华为手机的反扑已经开始。在多家调研机构公布的2023年四季度智能手机出货数据中华为手机的量正在明显上升当季增幅在35%到47%之间。不过,从全年数据来看,并未登上前五榜单。"));
documents.add(new DocumentModel(10, "CT01-XW·2024-D10-0010", "新闻热线[2024]000010", "疯狂裁员的硅谷大厂除了AI其它都是将就", "放眼望去近期科技企业财报形势一片大好裁员浪潮却仍在不断蔓延。国内职场动态看脉脉那硅谷裁员情况就得看layoff.fyi了。数据显示2024年103家科技企业进行了裁员28963位员工失去了饭碗。其中电子支付公司PayPal大笔一挥裁掉2500人微软则在开年就裁掉1900人。回望2023年谷歌、Meta、亚马逊、微软均为裁员重灾区裁员人数在一万左右。具体而言谷歌近日披露的财报指出2023年谷歌解雇了12000多名员工光是在遣散费和其他费用上就花费了21亿美元。而且裁员费用还在不断增加2024年刚过去一个月谷歌就已经花费了7亿美元用来裁员。"));
documents.add(new DocumentModel(11, "CT01-XW·2024-D30-0011", "新闻热线[2024]000011", "国产手机品牌重新崛起背后:市场正在逐步恢复活力,竞争也愈发激烈", "2024年伊始随着全球经济的逐渐复苏手机消费市场也展现出勃勃生机。中国信通院最新数据显示2023年中国市场手机出货量实现了6.5%的同比增长其中5G手机增长势头更为强劲占比高达82.8%。1月25日国际数据公司IDC发布了最新手机季度跟踪报告揭示了中国智能手机市场在2023年第四季度的出货量情况。报告显示该季度中国智能手机市场出货量达到了约7363万台同比增长1.2%。这是在连续十个季度同比下降后,中国智能手机市场首次实现反弹。这一积极信号表明,市场正在逐步恢复活力,各大品牌之间的竞争也愈发激烈。"));
documents.add(new DocumentModel(12, "CT01-XW·2024-D30-0012", "新闻热线[2024]000012", "SpaceX将于1月31日向国际空间站发射天鹅号货运飞船", "1月29日消息美国太空探索技术公司SpaceX计划于当地时间1月30日利用“猎鹰9号”火箭从佛罗里达州肯尼迪航天中心发射诺斯罗普·格鲁曼公司的“天鹅号”货运飞船至国际空间站。此次任务是执行NG-20商业补给将运送约8200多磅的物资、设备及科学实验器材。"));
}
/**
* 搜索相关
*/
@ -209,6 +226,15 @@ public class DemoTableController extends BaseController
return prefix + "/remember";
}
/**
* 表格保存状态
*/
@GetMapping("/cookie")
public String cookie()
{
return prefix + "/cookie";
}
/**
* 跳转至指定页
*/
@ -390,6 +416,15 @@ public class DemoTableController extends BaseController
return prefix + "/dynamicColumns";
}
/**
* 表格虚拟滚动
*/
@GetMapping("/virtualScroll")
public String virtualScroll()
{
return prefix + "/virtualScroll";
}
/**
* 自定义视图分页
*/
@ -399,6 +434,15 @@ public class DemoTableController extends BaseController
return prefix + "/customView";
}
/**
* 全文索引
*/
@GetMapping("/textSearch")
public String textSearch()
{
return prefix + "/textSearch";
}
/**
* 异步加载表格树
*/
@ -473,6 +517,57 @@ public class DemoTableController extends BaseController
return rspData;
}
/**
* 查询全文索引数据
*/
@PostMapping("/text/list")
@ResponseBody
public TableDataInfo textList(BaseEntity baseEntity)
{
TableDataInfo rspData = new TableDataInfo();
List<DocumentModel> documentList = new ArrayList<DocumentModel>(Arrays.asList(new DocumentModel[documents.size()]));
Collections.copy(documentList, documents);
// 查询条件过滤
if (StringUtils.isNotEmpty(baseEntity.getSearchValue()))
{
documentList.clear();
for (DocumentModel document : documents)
{
boolean indexFlag = false;
if (document.getTitle().contains(baseEntity.getSearchValue()))
{
indexFlag = true;
document.setTitle(document.getTitle().replace(baseEntity.getSearchValue(), "<font color=\"red\">" + baseEntity.getSearchValue() + "</font>"));
}
if (document.getContent().contains(baseEntity.getSearchValue()))
{
indexFlag = true;
document.setContent(document.getContent().replace(baseEntity.getSearchValue(), "<font color=\"red\">" + baseEntity.getSearchValue() + "</font>"));
}
if (indexFlag)
{
documentList.add(document);
}
}
}
PageDomain pageDomain = TableSupport.buildPageRequest();
if (null == pageDomain.getPageNum() || null == pageDomain.getPageSize())
{
rspData.setRows(documentList);
rspData.setTotal(documentList.size());
return rspData;
}
Integer pageNum = (pageDomain.getPageNum() - 1) * 10;
Integer pageSize = pageDomain.getPageNum() * 10;
if (pageSize > documentList.size())
{
pageSize = documentList.size();
}
rspData.setRows(documentList.subList(pageNum, pageSize));
rspData.setTotal(documentList.size());
return rspData;
}
/**
* 查询树表数据
*/
@ -844,3 +939,85 @@ class AreaModel
this.isTreeLeaf = isTreeLeaf;
}
}
class DocumentModel
{
/** 编号 */
private int tableId;
/** 档号 */
private String archiveNo;
/** 文件编号 */
private String docNo;
/** 标题 */
private String title;
/** 内容 */
private String content;
public DocumentModel()
{
}
public DocumentModel(int tableId, String archiveNo, String docNo, String title, String content)
{
this.tableId = tableId;
this.archiveNo = archiveNo;
this.docNo = docNo;
this.title = title;
this.content = content;
}
public int getTableId()
{
return tableId;
}
public String getArchiveNo()
{
return archiveNo;
}
public String getDocNo()
{
return docNo;
}
public String getTitle()
{
return title;
}
public String getContent()
{
return content;
}
public void setTableId(int tableId)
{
this.tableId = tableId;
}
public void setArchiveNo(String archiveNo)
{
this.archiveNo = archiveNo;
}
public void setDocNo(String docNo)
{
this.docNo = docNo;
}
public void setTitle(String title)
{
this.title = title;
}
public void setContent(String content)
{
this.content = content;
}
}

View File

@ -2,12 +2,12 @@ package com.ruoyi.web.controller.system;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.annotation.Resource;
import jakarta.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

View File

@ -2,8 +2,8 @@ package com.ruoyi.web.controller.system;
import java.util.Date;
import java.util.List;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;

View File

@ -1,7 +1,7 @@
package com.ruoyi.web.controller.system;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;

View File

@ -99,6 +99,17 @@ public class SysNoticeController extends BaseController
return toAjax(noticeService.updateNotice(notice));
}
/**
* 查询公告详细
*/
@RequiresPermissions("system:notice:list")
@GetMapping("/view/{noticeId}")
public String view(@PathVariable("noticeId") Long noticeId, ModelMap mmap)
{
mmap.put("notice", noticeService.selectNoticeById(noticeId));
return prefix + "/view";
}
/**
* 删除公告
*/

View File

@ -24,6 +24,7 @@ import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.ShiroUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
@ -129,6 +130,8 @@ public class SysUserController extends BaseController
@ResponseBody
public AjaxResult addSave(@Validated SysUser user)
{
deptService.checkDeptDataScope(user.getDeptId());
roleService.checkRoleDataScope(user.getRoleIds());
if (!userService.checkLoginNameUnique(user))
{
return error("新增用户'" + user.getLoginName() + "'失败,登录账号已存在");
@ -143,6 +146,7 @@ public class SysUserController extends BaseController
}
user.setSalt(ShiroUtils.randomSalt());
user.setPassword(passwordService.encryptPassword(user.getLoginName(), user.getPassword(), user.getSalt()));
user.setPwdUpdateDate(DateUtils.getNowDate());
user.setCreateBy(getLoginName());
return toAjax(userService.insertUser(user));
}
@ -187,6 +191,8 @@ public class SysUserController extends BaseController
{
userService.checkUserAllowed(user);
userService.checkUserDataScope(user.getUserId());
deptService.checkDeptDataScope(user.getDeptId());
roleService.checkRoleDataScope(user.getRoleIds());
if (!userService.checkLoginNameUnique(user))
{
return error("修改用户'" + user.getLoginName() + "'失败,登录账号已存在");
@ -257,6 +263,7 @@ public class SysUserController extends BaseController
public AjaxResult insertAuthRole(Long userId, Long[] roleIds)
{
userService.checkUserDataScope(userId);
roleService.checkRoleDataScope(roleIds);
userService.insertUserAuth(userId, roleIds);
AuthorizationUtils.clearAllCachedAuthorizationInfo();
return success();

View File

@ -15,19 +15,16 @@ import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.utils.StringUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
/**
* swagger 用户测试方法
*
* @author ruoyi
*/
@Api("用户信息管理")
@Tag(name = "用户信息管理")
@RestController
@RequestMapping("/test/user")
public class TestController extends BaseController
@ -38,7 +35,7 @@ public class TestController extends BaseController
users.put(2, new UserEntity(2, "ry", "admin123", "15666666666"));
}
@ApiOperation("获取用户列表")
@Operation(summary = "获取用户列表")
@GetMapping("/list")
public R<List<UserEntity>> userList()
{
@ -46,10 +43,10 @@ public class TestController extends BaseController
return R.ok(userList);
}
@ApiOperation("获取用户详细")
@ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class)
@Operation(summary = "获取用户详细")
@GetMapping("/{userId}")
public R<UserEntity> getUser(@PathVariable Integer userId)
public R<UserEntity> getUser(@PathVariable(name = "userId")
Integer userId)
{
if (!users.isEmpty() && users.containsKey(userId))
{
@ -61,13 +58,7 @@ public class TestController extends BaseController
}
}
@ApiOperation("新增用户")
@ApiImplicitParams({
@ApiImplicitParam(name = "userId", value = "用户id", dataType = "Integer", dataTypeClass = Integer.class),
@ApiImplicitParam(name = "username", value = "用户名称", dataType = "String", dataTypeClass = String.class),
@ApiImplicitParam(name = "password", value = "用户密码", dataType = "String", dataTypeClass = String.class),
@ApiImplicitParam(name = "mobile", value = "用户手机", dataType = "String", dataTypeClass = String.class)
})
@Operation(summary = "新增用户")
@PostMapping("/save")
public R<String> save(UserEntity user)
{
@ -79,9 +70,10 @@ public class TestController extends BaseController
return R.ok();
}
@ApiOperation("更新用户")
@Operation(summary = "更新用户")
@PutMapping("/update")
public R<String> update(@RequestBody UserEntity user)
public R<String> update(@RequestBody
UserEntity user)
{
if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId()))
{
@ -96,10 +88,10 @@ public class TestController extends BaseController
return R.ok();
}
@ApiOperation("删除用户信息")
@ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class)
@Operation(summary = "删除用户信息")
@DeleteMapping("/{userId}")
public R<String> delete(@PathVariable Integer userId)
public R<String> delete(@PathVariable(name = "userId")
Integer userId)
{
if (!users.isEmpty() && users.containsKey(userId))
{
@ -113,19 +105,19 @@ public class TestController extends BaseController
}
}
@ApiModel(value = "UserEntity", description = "用户实体")
@Schema(description = "用户实体")
class UserEntity
{
@ApiModelProperty("用户ID")
@Schema(title = "用户ID")
private Integer userId;
@ApiModelProperty("用户名称")
@Schema(title = "用户名称")
private String username;
@ApiModelProperty("用户密码")
@Schema(title = "用户密码")
private String password;
@ApiModelProperty("用户手机")
@Schema(title = "用户手机")
private String mobile;
public UserEntity()

View File

@ -1,17 +1,15 @@
package com.ruoyi.web.core.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.ruoyi.common.config.RuoYiConfig;
import io.swagger.annotations.ApiOperation;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
/**
* Swagger2的接口配置
@ -21,47 +19,47 @@ import springfox.documentation.spring.web.plugins.Docket;
@Configuration
public class SwaggerConfig
{
/** 是否开启swagger */
@Value("${swagger.enabled}")
private boolean enabled;
/** 系统基础配置 */
@Autowired
private RuoYiConfig ruoyiConfig;
/**
* 创建API
* 自定义的 OpenAPI 对象
*/
@Bean
public Docket createRestApi()
public OpenAPI customOpenApi()
{
return new Docket(DocumentationType.OAS_30)
// 是否启用Swagger
.enable(enabled)
// 用来创建该API的基本信息展示在文档的页面中自定义展示的信息
.apiInfo(apiInfo())
// 设置哪些接口暴露给Swagger展示
.select()
// 扫描所有有注解的api用这种方式更灵活
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
// 扫描指定包中的swagger注解
//.apis(RequestHandlerSelectors.basePackage("com.ruoyi.project.tool.swagger"))
// 扫描所有 .apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
return new OpenAPI().components(new Components()
// 设置认证的请求头
.addSecuritySchemes("apikey", securityScheme()))
.addSecurityItem(new SecurityRequirement().addList("apikey"))
.info(getApiInfo());
}
@Bean
public SecurityScheme securityScheme()
{
return new SecurityScheme()
.type(SecurityScheme.Type.APIKEY)
.name("Authorization")
.in(SecurityScheme.In.HEADER)
.scheme("Bearer");
}
/**
* 添加摘要信息
*/
private ApiInfo apiInfo()
@SuppressWarnings("static-access")
public Info getApiInfo()
{
// 用ApiInfoBuilder进行定制
return new ApiInfoBuilder()
// 设置标题
.title("标题若依管理系统_接口文档")
// 描述
.description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...")
// 作者信息
.contact(new Contact(RuoYiConfig.getName(), null, null))
// 版本
.version("版本号:" + RuoYiConfig.getVersion())
.build();
return new Info()
// 设置标题
.title("标题若依管理系统_接口文档")
// 描述
.description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...")
// 作者信息
.contact(new Contact().name(ruoyiConfig.getName()))
// 版本
.version("版本号:" + ruoyiConfig.getVersion());
}
}

View File

@ -3,9 +3,9 @@ ruoyi:
# 名称
name: RuoYi
# 版本
version: 4.7.8
version: 4.7.9
# 版权年份
copyrightYear: 2023
copyrightYear: 2024
# 实例演示开关
demoEnabled: true
# 文件路径 示例( Windows配置D:/ruoyi/uploadPathLinux配置 /home/ruoyi/uploadPath
@ -127,6 +127,20 @@ shiro:
# 是否开启记住我
enabled: true
# Springdoc配置
springdoc:
api-docs:
path: /v3/api-docs
swagger-ui:
enabled: true
path: /swagger-ui.html
tags-sorter: alpha
group-configs:
- group: 'default'
display-name: '测试模块'
paths-to-match: '/**'
packages-to-scan: com.ruoyi.web.controller.tool
# 防止XSS攻击
xss:
# 过滤开关
@ -135,8 +149,3 @@ xss:
excludes: /system/notice/*
# 匹配链接
urlPatterns: /system/*,/monitor/*,/tool/*
# Swagger配置
swagger:
# 是否开启swagger
enabled: true

File diff suppressed because one or more lines are too long

View File

@ -49,8 +49,8 @@ $.BootstrapTable = class extends $.BootstrapTable {
<button class="auto-refresh ${this.constants.buttonsClass}
${this.options.autoRefreshStatus ? ` ${this.constants.classes.buttonActive}` : ''}"
type="button" name="autoRefresh" title="${this.options.formatAutoRefresh()}">
${ this.options.showButtonIcons ? Utils.sprintf(this.constants.html.icon, this.options.iconsPrefix, this.options.icons.autoRefresh) : ''}
${ this.options.showButtonText ? this.options.formatAutoRefresh() : ''}
${this.options.showButtonIcons ? Utils.sprintf(this.constants.html.icon, this.options.iconsPrefix, this.options.icons.autoRefresh) : ''}
${this.options.showButtonText ? this.options.formatAutoRefresh() : ''}
</button>
`,
event: this.toggleAutoRefresh

View File

@ -0,0 +1,606 @@
/**
* @author: Dennis Hernández
* @update zhixin wen <wenzhixin2010@gmail.com>
*/
var Utils = $.fn.bootstrapTable.utils
var UtilsCookie = {
cookieIds: {
sortOrder: 'bs.table.sortOrder',
sortName: 'bs.table.sortName',
sortPriority: 'bs.table.sortPriority',
pageNumber: 'bs.table.pageNumber',
pageList: 'bs.table.pageList',
hiddenColumns: 'bs.table.hiddenColumns',
cardView: 'bs.table.cardView',
customView: 'bs.table.customView',
searchText: 'bs.table.searchText',
reorderColumns: 'bs.table.reorderColumns',
filterControl: 'bs.table.filterControl',
filterBy: 'bs.table.filterBy'
},
getCurrentHeader (that) {
return that.options.height ? that.$tableHeader : that.$header
},
getCurrentSearchControls (that) {
return that.options.height ? 'table select, table input' : 'select, input'
},
isCookieSupportedByBrowser () {
return navigator.cookieEnabled
},
isCookieEnabled (that, cookieName) {
return that.options.cookiesEnabled.includes(cookieName)
},
setCookie (that, cookieName, cookieValue) {
if (
!that.options.cookie ||
!UtilsCookie.isCookieEnabled(that, cookieName)
) {
return
}
return that._storage.setItem(`${that.options.cookieIdTable}.${cookieName}`, cookieValue)
},
getCookie (that, cookieName) {
if (
!cookieName ||
!UtilsCookie.isCookieEnabled(that, cookieName)
) {
return null
}
return that._storage.getItem(`${that.options.cookieIdTable}.${cookieName}`)
},
deleteCookie (that, cookieName) {
return that._storage.removeItem(`${that.options.cookieIdTable}.${cookieName}`)
},
calculateExpiration (cookieExpire) {
const time = cookieExpire.replace(/[0-9]*/, '') // s,mi,h,d,m,y
cookieExpire = cookieExpire.replace(/[A-Za-z]{1,2}/, '') // number
switch (time.toLowerCase()) {
case 's':
cookieExpire = +cookieExpire
break
case 'mi':
cookieExpire *= 60
break
case 'h':
cookieExpire = cookieExpire * 60 * 60
break
case 'd':
cookieExpire = cookieExpire * 24 * 60 * 60
break
case 'm':
cookieExpire = cookieExpire * 30 * 24 * 60 * 60
break
case 'y':
cookieExpire = cookieExpire * 365 * 24 * 60 * 60
break
default:
cookieExpire = undefined
break
}
if (!cookieExpire) {
return ''
}
const d = new Date()
d.setTime(d.getTime() + cookieExpire * 1000)
return d.toGMTString()
},
initCookieFilters (that) {
setTimeout(() => {
const parsedCookieFilters = JSON.parse(
UtilsCookie.getCookie(that, UtilsCookie.cookieIds.filterControl))
if (!that._filterControlValuesLoaded && parsedCookieFilters) {
const cachedFilters = {}
const header = UtilsCookie.getCurrentHeader(that)
const searchControls = UtilsCookie.getCurrentSearchControls(that)
const applyCookieFilters = (element, filteredCookies) => {
filteredCookies.forEach(cookie => {
const value = element.value.toString()
const text = cookie.text
if (
text === '' ||
element.type === 'radio' &&
value !== text
) {
return
}
if (
element.tagName === 'INPUT' &&
element.type === 'radio' &&
value === text
) {
element.checked = true
cachedFilters[cookie.field] = text
} else if (element.tagName === 'INPUT') {
element.value = text
cachedFilters[cookie.field] = text
} else if (
element.tagName === 'SELECT' &&
that.options.filterControlContainer
) {
element.value = text
cachedFilters[cookie.field] = text
} else if (text !== '' && element.tagName === 'SELECT') {
cachedFilters[cookie.field] = text
for (const currentElement of element) {
if (currentElement.value === text) {
currentElement.selected = true
return
}
}
const option = document.createElement('option')
option.value = text
option.text = text
element.add(option, element[1])
element.selectedIndex = 1
}
})
}
let filterContainer = header
if (that.options.filterControlContainer) {
filterContainer = $(`${that.options.filterControlContainer}`)
}
filterContainer.find(searchControls).each(function () {
const field = $(this).closest('[data-field]').data('field')
const filteredCookies = parsedCookieFilters.filter(cookie => cookie.field === field)
applyCookieFilters(this, filteredCookies)
})
that.initColumnSearch(cachedFilters)
that._filterControlValuesLoaded = true
that.initServer()
}
}, 250)
}
}
Object.assign($.fn.bootstrapTable.defaults, {
cookie: false,
cookieExpire: '2h',
cookiePath: null,
cookieDomain: null,
cookieSecure: null,
cookieSameSite: 'Lax',
cookieIdTable: '',
cookiesEnabled: [
'bs.table.sortOrder', 'bs.table.sortName', 'bs.table.sortPriority',
'bs.table.pageNumber', 'bs.table.pageList',
'bs.table.hiddenColumns', 'bs.table.searchText',
'bs.table.filterControl', 'bs.table.filterBy',
'bs.table.reorderColumns', 'bs.table.cardView', 'bs.table.customView'
],
cookieStorage: 'cookieStorage', // localStorage, sessionStorage, customStorage
cookieCustomStorageGet: null,
cookieCustomStorageSet: null,
cookieCustomStorageDelete: null,
// internal variable
_filterControls: [],
_filterControlValuesLoaded: false,
_storage: {
setItem: undefined,
getItem: undefined,
removeItem: undefined
}
})
$.fn.bootstrapTable.methods.push('getCookies')
$.fn.bootstrapTable.methods.push('deleteCookie')
Object.assign($.fn.bootstrapTable.utils, {
setCookie: UtilsCookie.setCookie,
getCookie: UtilsCookie.getCookie
})
$.BootstrapTable = class extends $.BootstrapTable {
init () {
if (this.options.cookie) {
if (
this.options.cookieStorage === 'cookieStorage' &&
!UtilsCookie.isCookieSupportedByBrowser()
) {
throw new Error('Cookies are not enabled in this browser.')
}
this.configureStorage()
// FilterBy logic
const filterByCookieValue = UtilsCookie.getCookie(this, UtilsCookie.cookieIds.filterBy)
if (typeof filterByCookieValue === 'boolean' && !filterByCookieValue) {
throw new Error('The cookie value of filterBy must be a json!')
}
let filterByCookie = {}
try {
filterByCookie = JSON.parse(filterByCookieValue)
} catch (e) {
throw new Error('Could not parse the json of the filterBy cookie!')
}
this.filterColumns = filterByCookie ? filterByCookie : {}
// FilterControl logic
this._filterControls = []
this._filterControlValuesLoaded = false
this.options.cookiesEnabled = typeof this.options.cookiesEnabled === 'string' ?
this.options.cookiesEnabled.replace('[', '').replace(']', '')
.replace(/'/g, '').replace(/ /g, '').split(',') :
this.options.cookiesEnabled
if (this.options.filterControl) {
const that = this
this.$el.on('column-search.bs.table', (e, field, text) => {
let isNewField = true
for (let i = 0; i < that._filterControls.length; i++) {
if (that._filterControls[i].field === field) {
that._filterControls[i].text = text
isNewField = false
break
}
}
if (isNewField) {
that._filterControls.push({
field,
text
})
}
UtilsCookie.setCookie(that, UtilsCookie.cookieIds.filterControl, JSON.stringify(that._filterControls))
}).on('created-controls.bs.table', UtilsCookie.initCookieFilters(that))
}
}
super.init()
}
initServer (...args) {
if (
this.options.cookie &&
this.options.filterControl &&
!this._filterControlValuesLoaded
) {
const cookie = JSON.parse(UtilsCookie.getCookie(this, UtilsCookie.cookieIds.filterControl))
if (cookie) {
return
}
}
super.initServer(...args)
}
initTable (...args) {
super.initTable(...args)
this.initCookie()
}
onSort (...args) {
super.onSort(...args)
if (!this.options.cookie) {
return
}
if (this.options.sortName === undefined || this.options.sortOrder === undefined) {
UtilsCookie.deleteCookie(this, UtilsCookie.cookieIds.sortName)
UtilsCookie.deleteCookie(this, UtilsCookie.cookieIds.sortOrder)
} else {
this.options.sortPriority = null
UtilsCookie.deleteCookie(this, UtilsCookie.cookieIds.sortPriority)
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.sortOrder, this.options.sortOrder)
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.sortName, this.options.sortName)
}
}
onMultipleSort (...args) {
super.onMultipleSort(...args)
if (!this.options.cookie) {
return
}
if (this.options.sortPriority === undefined) {
UtilsCookie.deleteCookie(this, UtilsCookie.cookieIds.sortPriority)
} else {
this.options.sortName = undefined
this.options.sortOrder = undefined
UtilsCookie.deleteCookie(this, UtilsCookie.cookieIds.sortName)
UtilsCookie.deleteCookie(this, UtilsCookie.cookieIds.sortOrder)
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.sortPriority, JSON.stringify(this.options.sortPriority))
}
}
onPageNumber (...args) {
super.onPageNumber(...args)
if (!this.options.cookie) {
return
}
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.pageNumber, this.options.pageNumber)
}
onPageListChange (...args) {
super.onPageListChange(...args)
if (!this.options.cookie) {
return
}
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.pageList,
this.options.pageSize === this.options.formatAllRows() ? 'all' : this.options.pageSize)
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.pageNumber, this.options.pageNumber)
}
onPagePre (...args) {
super.onPagePre(...args)
if (!this.options.cookie) {
return
}
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.pageNumber, this.options.pageNumber)
}
onPageNext (...args) {
super.onPageNext(...args)
if (!this.options.cookie) {
return
}
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.pageNumber, this.options.pageNumber)
}
_toggleColumn (...args) {
super._toggleColumn(...args)
if (!this.options.cookie) {
return
}
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.hiddenColumns, JSON.stringify(this.getHiddenColumns().map(column => column.field)))
}
_toggleAllColumns (...args) {
super._toggleAllColumns(...args)
if (!this.options.cookie) {
return
}
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.hiddenColumns, JSON.stringify(this.getHiddenColumns().map(column => column.field)))
}
toggleView () {
super.toggleView()
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.cardView, this.options.cardView)
}
toggleCustomView () {
super.toggleCustomView()
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.customView, this.customViewDefaultView)
}
selectPage (page) {
super.selectPage(page)
if (!this.options.cookie) {
return
}
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.pageNumber, page)
}
onSearch (event) {
super.onSearch(event, arguments.length > 1 ? arguments[1] : true)
if (!this.options.cookie) {
return
}
if (this.options.search) {
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.searchText, this.searchText)
}
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.pageNumber, this.options.pageNumber)
}
initHeader (...args) {
if (this.options.reorderableColumns && this.options.cookie) {
this.columnsSortOrder = JSON.parse(UtilsCookie.getCookie(this, UtilsCookie.cookieIds.reorderColumns))
}
super.initHeader(...args)
}
persistReorderColumnsState (that) {
UtilsCookie.setCookie(that, UtilsCookie.cookieIds.reorderColumns, JSON.stringify(that.columnsSortOrder))
}
filterBy (...args) {
super.filterBy(...args)
if (!this.options.cookie) {
return
}
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.filterBy, JSON.stringify(this.filterColumns))
}
initCookie () {
if (!this.options.cookie) {
return
}
if (this.options.cookieIdTable === '' || this.options.cookieExpire === '') {
console.error('Configuration error. Please review the cookieIdTable and the cookieExpire property. If the properties are correct, then this browser does not support cookies.')
this.options.cookie = false // Make sure that the cookie extension is disabled
return
}
const sortOrderCookie = UtilsCookie.getCookie(this, UtilsCookie.cookieIds.sortOrder)
const sortOrderNameCookie = UtilsCookie.getCookie(this, UtilsCookie.cookieIds.sortName)
let sortPriorityCookie = UtilsCookie.getCookie(this, UtilsCookie.cookieIds.sortPriority)
const pageNumberCookie = UtilsCookie.getCookie(this, UtilsCookie.cookieIds.pageNumber)
const pageListCookie = UtilsCookie.getCookie(this, UtilsCookie.cookieIds.pageList)
const searchTextCookie = UtilsCookie.getCookie(this, UtilsCookie.cookieIds.searchText)
const cardViewCookie = UtilsCookie.getCookie(this, UtilsCookie.cookieIds.cardView)
const customViewCookie = UtilsCookie.getCookie(this, UtilsCookie.cookieIds.customView)
const hiddenColumnsCookieValue = UtilsCookie.getCookie(this, UtilsCookie.cookieIds.hiddenColumns)
let hiddenColumnsCookie = {}
try {
hiddenColumnsCookie = JSON.parse(hiddenColumnsCookieValue)
} catch (e) {
throw new Error('Could not parse the json of the hidden columns cookie!', hiddenColumnsCookieValue)
}
try {
sortPriorityCookie = JSON.parse(sortPriorityCookie)
} catch (e) {
throw new Error('Could not parse the json of the sortPriority cookie!', sortPriorityCookie)
}
if (!sortPriorityCookie) {
// sortOrder
this.options.sortOrder = sortOrderCookie ? sortOrderCookie : this.options.sortOrder
// sortName
this.options.sortName = sortOrderNameCookie ? sortOrderNameCookie : this.options.sortName
} else {
this.options.sortOrder = undefined
this.options.sortName = undefined
}
// sortPriority
this.options.sortPriority = sortPriorityCookie ? sortPriorityCookie : this.options.sortPriority
if (this.options.sortOrder || this.options.sortName) {
// sortPriority
this.options.sortPriority = null
}
// pageNumber
this.options.pageNumber = pageNumberCookie ? +pageNumberCookie : this.options.pageNumber
// pageSize
this.options.pageSize = pageListCookie ? pageListCookie === 'all' ?
this.options.formatAllRows() : +pageListCookie : this.options.pageSize
// searchText
if (UtilsCookie.isCookieEnabled(this, UtilsCookie.cookieIds.searchText) && this.options.searchText === '') {
this.options.searchText = searchTextCookie ? searchTextCookie : ''
}
// cardView
if (cardViewCookie !== null) {
this.options.cardView = cardViewCookie === 'true' ? cardViewCookie : false
}
this.customViewDefaultView = customViewCookie === 'true'
if (hiddenColumnsCookie) {
for (const column of this.columns) {
if (!column.switchable) {
continue
}
column.visible = this.isSelectionColumn(column) ||
!hiddenColumnsCookie.includes(column.field)
}
}
}
getCookies () {
const bootstrapTable = this
const cookies = {}
for (const [key, value] of Object.entries(UtilsCookie.cookieIds)) {
cookies[key] = UtilsCookie.getCookie(bootstrapTable, value)
if (key === 'columns' || key === 'hiddenColumns' || key === 'sortPriority') {
cookies[key] = JSON.parse(cookies[key])
}
}
return cookies
}
deleteCookie (cookieName) {
if (!cookieName) {
return
}
UtilsCookie.deleteCookie(this, UtilsCookie.cookieIds[cookieName])
}
configureStorage () {
const that = this
this._storage = {}
switch (this.options.cookieStorage) {
case 'cookieStorage':
this._storage.setItem = function (cookieName, cookieValue) {
document.cookie = [
cookieName, '=', encodeURIComponent(cookieValue),
`; expires=${UtilsCookie.calculateExpiration(that.options.cookieExpire)}`,
that.options.cookiePath ? `; path=${that.options.cookiePath}` : '',
that.options.cookieDomain ? `; domain=${that.options.cookieDomain}` : '',
that.options.cookieSecure ? '; secure' : '',
`;SameSite=${that.options.cookieSameSite}`
].join('')
}
this._storage.getItem = function (cookieName) {
const value = `; ${document.cookie}`
const parts = value.split(`; ${cookieName}=`)
return parts.length === 2 ? decodeURIComponent(parts.pop().split(';').shift()) : null
}
this._storage.removeItem = function (cookieName) {
document.cookie = [
encodeURIComponent(cookieName), '=',
'; expires=Thu, 01 Jan 1970 00:00:00 GMT',
that.options.cookiePath ? `; path=${that.options.cookiePath}` : '',
that.options.cookieDomain ? `; domain=${that.options.cookieDomain}` : '',
`;SameSite=${that.options.cookieSameSite}`
].join('')
}
break
case 'localStorage':
this._storage.setItem = function (cookieName, cookieValue) {
localStorage.setItem(cookieName, cookieValue)
}
this._storage.getItem = function (cookieName) {
return localStorage.getItem(cookieName)
}
this._storage.removeItem = function (cookieName) {
localStorage.removeItem(cookieName)
}
break
case 'sessionStorage':
this._storage.setItem = function (cookieName, cookieValue) {
sessionStorage.setItem(cookieName, cookieValue)
}
this._storage.getItem = function (cookieName) {
return sessionStorage.getItem(cookieName)
}
this._storage.removeItem = function (cookieName) {
sessionStorage.removeItem(cookieName)
}
break
case 'customStorage':
if (
!this.options.cookieCustomStorageSet ||
!this.options.cookieCustomStorageGet ||
!this.options.cookieCustomStorageDelete
) {
throw new Error('The following options must be set while using the customStorage: cookieCustomStorageSet, cookieCustomStorageGet and cookieCustomStorageDelete')
}
this._storage.setItem = function (cookieName, cookieValue) {
Utils.calculateObjectValue(that.options, that.options.cookieCustomStorageSet, [cookieName, cookieValue], '')
}
this._storage.getItem = function (cookieName) {
return Utils.calculateObjectValue(that.options, that.options.cookieCustomStorageGet, [cookieName], '')
}
this._storage.removeItem = function (cookieName) {
Utils.calculateObjectValue(that.options, that.options.cookieCustomStorageDelete, [cookieName], '')
}
break
default:
throw new Error('Storage method not supported.')
}
}
}

View File

@ -5,65 +5,81 @@
var Utils = $.fn.bootstrapTable.utils
$.extend($.fn.bootstrapTable.defaults, {
Object.assign($.fn.bootstrapTable.defaults, {
customView: false,
showCustomView: false,
showCustomViewButton: false
customViewDefaultView: false
})
$.extend($.fn.bootstrapTable.defaults.icons, {
customView: {
Object.assign($.fn.bootstrapTable.defaults.icons, {
customViewOn: {
bootstrap3: 'glyphicon glyphicon-list',
bootstrap5: 'bi-list',
bootstrap4: 'fa fa-list',
semantic: 'fa fa-list',
foundation: 'fa fa-list',
bulma: 'fa fa-list',
materialize: 'list'
}[$.fn.bootstrapTable.theme] || 'fa-list',
customViewOff: {
bootstrap3: 'glyphicon glyphicon-eye-open',
bootstrap5: 'bi-eye',
bootstrap4: 'fa fa-eye',
semantic: 'fa fa-eye',
foundation: 'fa fa-eye',
bulma: 'fa fa-eye',
materialize: 'remove_red_eye'
}[$.fn.bootstrapTable.theme] || 'fa-eye'
bootstrap5: 'bi-grid',
bootstrap4: 'fa fa-th',
semantic: 'fa fa-th',
foundation: 'fa fa-th',
bulma: 'fa fa-th',
materialize: 'grid_on'
}[$.fn.bootstrapTable.theme] || 'fa-th'
})
$.extend($.fn.bootstrapTable.defaults, {
Object.assign($.fn.bootstrapTable.defaults, {
onCustomViewPostBody () {
return false
},
onCustomViewPreBody () {
return false
},
onToggleCustomView () {
return false
}
})
$.extend($.fn.bootstrapTable.locales, {
formatToggleCustomView () {
return 'Toggle custom view'
Object.assign($.fn.bootstrapTable.locales, {
formatToggleCustomViewOn () {
return 'Show custom view'
},
formatToggleCustomViewOff () {
return 'Hide custom view'
}
})
$.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales)
Object.assign($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales)
$.fn.bootstrapTable.methods.push('toggleCustomView')
$.extend($.fn.bootstrapTable.Constructor.EVENTS, {
Object.assign($.fn.bootstrapTable.events, {
'custom-view-post-body.bs.table': 'onCustomViewPostBody',
'custom-view-pre-body.bs.table': 'onCustomViewPreBody'
'custom-view-pre-body.bs.table': 'onCustomViewPreBody',
'toggle-custom-view.bs.table': 'onToggleCustomView'
})
$.BootstrapTable = class extends $.BootstrapTable {
init () {
this.showCustomView = this.options.showCustomView
this.customViewDefaultView = this.options.customViewDefaultView
super.init()
}
initToolbar (...args) {
if (this.options.customView && this.options.showCustomViewButton) {
if (this.options.customView && this.options.showCustomView) {
this.buttons = Object.assign(this.buttons, {
customView: {
text: this.options.formatToggleCustomView(),
icon: this.options.icons.customView,
text: this.options.customViewDefaultView ? this.options.formatToggleCustomViewOff() : this.options.formatToggleCustomViewOn(),
icon: this.options.customViewDefaultView ? this.options.icons.customViewOn : this.options.icons.customViewOff,
event: this.toggleCustomView,
attributes: {
'aria-label': this.options.formatToggleCustomView(),
title: this.options.formatToggleCustomView()
'aria-label': this.options.customViewDefaultView ? this.options.formatToggleCustomViewOff() : this.options.formatToggleCustomViewOn(),
title: this.options.customViewDefaultView ? this.options.formatToggleCustomViewOff() : this.options.formatToggleCustomViewOn()
}
}
})
@ -84,7 +100,7 @@ $.BootstrapTable = class extends $.BootstrapTable {
$table.hide()
$customViewContainer.hide()
if (!this.options.customView || !this.showCustomView) {
if (!this.options.customView || !this.customViewDefaultView) {
$table.show()
return
}
@ -103,7 +119,17 @@ $.BootstrapTable = class extends $.BootstrapTable {
}
toggleCustomView () {
this.showCustomView = !this.showCustomView
this.customViewDefaultView = !this.customViewDefaultView
const icon = this.options.showButtonIcons ? this.customViewDefaultView ? this.options.icons.customViewOn : this.options.icons.customViewOff : ''
const text = this.options.showButtonText ? this.customViewDefaultView ? this.options.formatToggleCustomViewOff() : this.options.formatToggleCustomViewOn() : ''
this.$toolbar.find('button[name="customView"]')
.html(`${Utils.sprintf(this.constants.html.icon, this.options.iconsPrefix, icon)} ${text}`)
.attr('aria-label', text)
.attr('title', text)
this.initBody()
this.trigger('toggle-custom-view', this.customViewDefaultView)
}
}

View File

@ -19,28 +19,20 @@ const TYPE_NAME = {
pdf: 'PDF'
}
$.extend($.fn.bootstrapTable.defaults, {
Object.assign($.fn.bootstrapTable.defaults, {
showExport: false,
exportDataType: 'basic', // basic, all, selected
exportTypes: ['json', 'xml', 'csv', 'txt', 'sql', 'excel'],
exportOptions: {
onCellHtmlData (cell, rowIndex, colIndex, htmlData) {
if (cell.is('th')) {
return cell.find('.th-inner').text()
}
return htmlData
}
},
exportOptions: {},
exportFooter: false
})
$.extend($.fn.bootstrapTable.columnDefaults, {
Object.assign($.fn.bootstrapTable.columnDefaults, {
forceExport: false,
forceHide: false
})
$.extend($.fn.bootstrapTable.defaults.icons, {
Object.assign($.fn.bootstrapTable.defaults.icons, {
export: {
bootstrap3: 'glyphicon-export icon-share',
bootstrap5: 'bi-download',
@ -49,24 +41,28 @@ $.extend($.fn.bootstrapTable.defaults.icons, {
}[$.fn.bootstrapTable.theme] || 'fa-download'
})
$.extend($.fn.bootstrapTable.locales, {
Object.assign($.fn.bootstrapTable.locales, {
formatExport () {
return 'Export data'
}
})
$.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales)
Object.assign($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales)
$.fn.bootstrapTable.methods.push('exportTable')
$.extend($.fn.bootstrapTable.defaults, {
Object.assign($.fn.bootstrapTable.defaults, {
// eslint-disable-next-line no-unused-vars
onExportSaved (exportedRows) {
return false
},
onExportStarted () {
return false
}
})
$.extend($.fn.bootstrapTable.Constructor.EVENTS, {
'export-saved.bs.table': 'onExportSaved'
Object.assign($.fn.bootstrapTable.events, {
'export-saved.bs.table': 'onExportSaved',
'export-started.bs.table': 'onExportStarted'
})
$.BootstrapTable = class extends $.BootstrapTable {
@ -84,6 +80,10 @@ $.BootstrapTable = class extends $.BootstrapTable {
exportTypes = types.map(t => t.slice(1, -1))
}
if (typeof o.exportOptions === 'string') {
o.exportOptions = Utils.calculateObjectValue(null, o.exportOptions)
}
this.$export = this.$toolbar.find('>.columns div.export')
if (this.$export.length) {
this.updateExportButton()
@ -93,13 +93,13 @@ $.BootstrapTable = class extends $.BootstrapTable {
this.buttons = Object.assign(this.buttons, {
export: {
html:
(() => {
() => {
if (exportTypes.length === 1) {
return `
<div class="export ${this.constants.classes.buttonsDropdown}"
data-type="${exportTypes[0]}">
<button class="${this.constants.buttonsClass}"
aria-label="Export"
aria-label="${o.formatExport()}"
type="button"
title="${o.formatExport()}">
${o.showButtonIcons ? Utils.sprintf(this.constants.html.icon, o.iconsPrefix, o.icons.export) : ''}
@ -114,7 +114,7 @@ $.BootstrapTable = class extends $.BootstrapTable {
html.push(`
<div class="export ${this.constants.classes.buttonsDropdown}">
<button class="${this.constants.buttonsClass} dropdown-toggle"
aria-label="Export"
aria-label="${o.formatExport()}"
${this.constants.dataToggle}="dropdown"
type="button"
title="${o.formatExport()}">
@ -136,7 +136,7 @@ $.BootstrapTable = class extends $.BootstrapTable {
html.push(this.constants.html.toolbarDropdown[1], '</div>')
return html.join('')
})
}
}
})
}
@ -152,19 +152,15 @@ $.BootstrapTable = class extends $.BootstrapTable {
let $exportButtons = this.$export.find('[data-type]')
if (exportTypes.length === 1) {
$exportButtons = this.$export.find('button')
$exportButtons = this.$export
}
$exportButtons.click(e => {
e.preventDefault()
const type = $(e.currentTarget).data('type')
const exportOptions = {
type,
escape: false
}
this.exportTable(exportOptions)
this.trigger('export-started')
this.exportTable({
type: $(e.currentTarget).data('type')
})
})
this.handleToolbar()
}
@ -206,7 +202,7 @@ $.BootstrapTable = class extends $.BootstrapTable {
o.exportOptions.ignoreColumn = [detailViewIndex].concat(o.exportOptions.ignoreColumn || [])
}
if (o.exportFooter) {
if (o.exportFooter && o.height) {
const $footerRow = this.$tableFooter.find('tr').first()
const footerData = {}
const footerHtml = []
@ -240,7 +236,7 @@ $.BootstrapTable = class extends $.BootstrapTable {
options.fileName = o.exportOptions.fileName()
}
this.$el.tableExport($.extend({
this.$el.tableExport(Utils.extend({
onAfterSaveToFile: () => {
if (o.exportFooter) {
this.load(data)
@ -277,15 +273,17 @@ $.BootstrapTable = class extends $.BootstrapTable {
this.$el.one(eventName, () => {
setTimeout(() => {
const data = this.getData()
doExport(() => {
this.options.virtualScroll = virtualScroll
this.togglePagination()
})
this.trigger('export-saved', data)
}, 0)
})
this.options.virtualScroll = false
this.togglePagination()
this.trigger('export-saved', this.getData())
} else if (o.exportDataType === 'selected') {
let data = this.getData()
let selectedData = this.getSelections()

View File

@ -1,6 +1,5 @@
/**
* @author: Dennis Hernández
* @webSite: http://djhvscf.github.io/Blog
* @update zhixin wen <wenzhixin2010@gmail.com>
*/
@ -18,7 +17,7 @@ const debounce = (func, wait) => {
}
}
$.extend($.fn.bootstrapTable.defaults, {
Object.assign($.fn.bootstrapTable.defaults, {
mobileResponsive: false,
minWidth: 562,
minHeight: undefined,
@ -108,9 +107,9 @@ $.BootstrapTable = class extends $.BootstrapTable {
changeView (width, height) {
if (this.options.minHeight) {
if ((width <= this.options.minWidth) && (height <= this.options.minHeight)) {
if (width <= this.options.minWidth && height <= this.options.minHeight) {
this.conditionCardView()
} else if ((width > this.options.minWidth) && (height > this.options.minHeight)) {
} else if (width > this.options.minWidth && height > this.options.minHeight) {
this.conditionFullView()
}
} else if (width <= this.options.minWidth) {

View File

@ -4,76 +4,80 @@
var Utils = $.fn.bootstrapTable.utils
function printPageBuilderDefault (table) {
function printPageBuilderDefault (table, styles) {
return `
<html>
<head>
<style type="text/css" media="print">
@page {
size: auto;
margin: 25px 0 25px 0;
}
</style>
<style type="text/css" media="all">
table {
border-collapse: collapse;
font-size: 12px;
}
table, th, td {
border: 1px solid grey;
}
th, td {
text-align: center;
vertical-align: middle;
}
p {
font-weight: bold;
margin-left:20px;
}
table {
width:94%;
margin-left:3%;
margin-right:3%;
}
div.bs-table-print {
text-align:center;
}
</style>
</head>
<title>Print Table</title>
<body>
<p>Printed on: ${new Date} </p>
<div class="bs-table-print">${table}</div>
</body>
</html>`
<html>
<head>
${styles}
<style type="text/css" media="print">
@page {
size: auto;
margin: 25px 0 25px 0;
}
</style>
<style type="text/css" media="all">
table {
border-collapse: collapse;
font-size: 12px;
}
table, th, td {
border: 1px solid grey;
}
th, td {
text-align: center;
vertical-align: middle;
}
p {
font-weight: bold;
margin-left:20px;
}
table {
width: 94%;
margin-left: 3%;
margin-right: 3%;
}
div.bs-table-print {
text-align: center;
}
</style>
</head>
<title>Print Table</title>
<body>
<p>Printed on: ${new Date} </p>
<div class="bs-table-print">${table}</div>
</body>
</html>
`
}
$.extend($.fn.bootstrapTable.locales, {
Object.assign($.fn.bootstrapTable.locales, {
formatPrint () {
return 'Print'
}
})
$.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales)
Object.assign($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales)
$.extend($.fn.bootstrapTable.defaults, {
Object.assign($.fn.bootstrapTable.defaults, {
showPrint: false,
printAsFilteredAndSortedOnUI: true,
printSortColumn: undefined,
printSortOrder: 'asc',
printPageBuilder (table) {
return printPageBuilderDefault(table)
printStyles: [],
printPageBuilder (table, styles) {
return printPageBuilderDefault(table, styles)
}
})
$.extend($.fn.bootstrapTable.COLUMN_DEFAULTS, {
Object.assign($.fn.bootstrapTable.columnDefaults, {
printFilter: undefined,
printIgnore: false,
printFormatter: undefined
})
$.extend($.fn.bootstrapTable.defaults.icons, {
Object.assign($.fn.bootstrapTable.defaults.icons, {
print: {
bootstrap3: 'glyphicon-print icon-share',
bootstrap5: 'bi-printer',
'bootstrap-table': 'icon-printer'
}[$.fn.bootstrapTable.theme] || 'fa-print'
})
@ -133,10 +137,15 @@ $.BootstrapTable = class extends $.BootstrapTable {
}
doPrint (data) {
const canPrint = column => {
return !column.printIgnore && column.visible
}
const formatValue = (row, i, column) => {
const value_ = Utils.getItemField(row, column.field, this.options.escape, column.escape)
const value = Utils.calculateObjectValue(column,
column.printFormatter || column.formatter,
[$.common.getItemField(row, column.field), row, i], $.common.getItemField(row, column.field))
[value_, row, i], value_)
return typeof value === 'undefined' || value === null ?
this.options.undefinedText : value
@ -149,7 +158,7 @@ $.BootstrapTable = class extends $.BootstrapTable {
for (const columns of columnsArray) {
html.push('<tr>')
for (let h = 0; h < columns.length; h++) {
if (!columns[h].printIgnore) {
if (canPrint(columns[h])) {
html.push(
`<th
${Utils.sprintf(' rowspan="%s"', columns[h].rowspan)}
@ -162,7 +171,7 @@ $.BootstrapTable = class extends $.BootstrapTable {
html.push('</thead><tbody>')
const dontRender = []
const notRender = []
if (this.mergedCells) {
for (let mc = 0; mc < this.mergedCells.length; mc++) {
@ -174,7 +183,7 @@ $.BootstrapTable = class extends $.BootstrapTable {
for (let cs = 0; cs < currentMergedCell.colspan; cs++) {
const col = currentMergedCell.col + cs
dontRender.push(`${row },${ col}`)
notRender.push(`${row},${col}`)
}
}
}
@ -207,11 +216,11 @@ $.BootstrapTable = class extends $.BootstrapTable {
}
if (
!columns[j].printIgnore && columns[j].field &&
(
!dontRender.includes(`${i },${ j}`) ||
(rowspan > 0 && colspan > 0)
)
canPrint(columns[j]) &&
(
!notRender.includes(`${i},${j}`) ||
rowspan > 0 && colspan > 0
)
) {
if (rowspan > 0 && colspan > 0) {
html.push(`<td ${Utils.sprintf(' rowspan="%s"', rowspan)} ${Utils.sprintf(' colspan="%s"', colspan)}>`, formatValue(data[i], i, columns[j]), '</td>')
@ -221,7 +230,6 @@ $.BootstrapTable = class extends $.BootstrapTable {
}
}
html.push('</tr>')
}
@ -231,7 +239,7 @@ $.BootstrapTable = class extends $.BootstrapTable {
for (const columns of columnsArray) {
for (let h = 0; h < columns.length; h++) {
if (!columns[h].printIgnore) {
if (canPrint(columns)) {
const footerData = Utils.trToData(columns, this.$el.find('>tfoot>tr'))
const footerValue = Utils.calculateObjectValue(columns[h], columns[h].footerFormatter, [data], footerData[0] && footerData[0][columns[h].field] || '')
@ -252,8 +260,8 @@ $.BootstrapTable = class extends $.BootstrapTable {
}
let reverse = sortOrder !== 'asc'
reverse = -((+reverse) || -1)
return data.sort((a, b) => reverse * (a[colName].localeCompare(b[colName])))
reverse = -(+reverse || -1)
return data.sort((a, b) => reverse * a[colName].localeCompare(b[colName]))
}
const filterRow = (row, filters) => {
@ -275,11 +283,30 @@ $.BootstrapTable = class extends $.BootstrapTable {
data = sortRows(data, this.options.printSortColumn, this.options.printSortOrder)
const table = buildTable(data, this.options.columns)
const newWin = window.open('')
const printStyles = typeof this.options.printStyles === 'string' ?
this.options.printStyles.replace(/\[|\]| /g, '').toLowerCase().split(',') :
this.options.printStyles
const styles = printStyles.map(it =>
`<link rel="stylesheet" href="${it}" />`).join('')
newWin.document.write(this.options.printPageBuilder.call(this, table))
const calculatedPrintPage = Utils.calculateObjectValue(this, this.options.printPageBuilder,
[table, styles], printPageBuilderDefault(table, styles))
const startPrint = () => {
newWin.focus()
newWin.print()
newWin.close()
}
newWin.document.write(calculatedPrintPage)
newWin.document.close()
newWin.focus()
newWin.print()
newWin.close()
if (printStyles.length) {
const links = document.getElementsByTagName('link')
const lastLink = links[links.length - 1]
lastLink.onload = startPrint
} else {
startPrint()
}
}
}

View File

@ -1,6 +1,5 @@
/**
* @author: Dennis Hernández
* @webSite: http://djhvscf.github.io/Blog
* @update: https://github.com/wenzhixin
* @version: v1.2.0
*/
@ -60,7 +59,7 @@ const filterFn = () => {
}
}
$.extend($.fn.bootstrapTable.defaults, {
Object.assign($.fn.bootstrapTable.defaults, {
reorderableColumns: false,
maxMovingRows: 10,
// eslint-disable-next-line no-unused-vars
@ -70,7 +69,7 @@ $.extend($.fn.bootstrapTable.defaults, {
dragaccept: null
})
$.extend($.fn.bootstrapTable.Constructor.EVENTS, {
Object.assign($.fn.bootstrapTable.events, {
'reorder-column.bs.table': 'onReorderColumn'
})
@ -84,7 +83,7 @@ $.BootstrapTable = class extends $.BootstrapTable {
return
}
this.makeRowsReorderable()
this.makeColumnsReorderable()
}
_toggleColumn (...args) {
@ -94,7 +93,7 @@ $.BootstrapTable = class extends $.BootstrapTable {
return
}
this.makeRowsReorderable()
this.makeColumnsReorderable()
}
toggleView (...args) {
@ -108,7 +107,7 @@ $.BootstrapTable = class extends $.BootstrapTable {
return
}
this.makeRowsReorderable()
this.makeColumnsReorderable()
}
resetView (...args) {
@ -118,10 +117,10 @@ $.BootstrapTable = class extends $.BootstrapTable {
return
}
this.makeRowsReorderable()
this.makeColumnsReorderable()
}
makeRowsReorderable (order = null) {
makeColumnsReorderable (order = null) {
try {
$(this.$el).dragtable('destroy')
} catch (e) {
@ -208,6 +207,6 @@ $.BootstrapTable = class extends $.BootstrapTable {
orderColumns (order) {
this.columnsSortOrder = order
this.makeRowsReorderable()
this.makeColumnsReorderable()
}
}

View File

@ -1,6 +1,5 @@
/**
* @author: Dennis Hernández
* @webSite: http://djhvscf.github.io/Blog
* @update zhixin wen <wenzhixin2010@gmail.com>
*/
@ -8,11 +7,11 @@ const rowAttr = (row, index) => ({
id: `customId_${index}`
})
$.extend($.fn.bootstrapTable.defaults, {
Object.assign($.fn.bootstrapTable.defaults, {
reorderableRows: false,
onDragStyle: null,
onDropStyle: null,
onDragClass: 'reorder_rows_onDragClass',
onDragClass: 'reorder-rows-on-drag-class',
dragHandle: '>tbody>tr>td:not(.bs-checkbox)',
useRowAttrFunc: false,
// eslint-disable-next-line no-unused-vars
@ -26,10 +25,14 @@ $.extend($.fn.bootstrapTable.defaults, {
// eslint-disable-next-line no-unused-vars
onReorderRow (newData) {
return false
},
onDragStop () {},
onAllowDrop () {
return true
}
})
$.extend($.fn.bootstrapTable.Constructor.EVENTS, {
Object.assign($.fn.bootstrapTable.events, {
'reorder-row.bs.table': 'onReorderRow'
})
@ -61,6 +64,8 @@ $.BootstrapTable = class extends $.BootstrapTable {
onDragStyle: this.options.onDragStyle,
onDropStyle: this.options.onDropStyle,
onDragClass: this.options.onDragClass,
onAllowDrop: (hoveredRow, draggedRow) => this.onAllowDrop(hoveredRow, draggedRow),
onDragStop: (table, draggedRow) => this.onDragStop(table, draggedRow),
onDragStart: (table, droppedRow) => this.onDropStart(table, droppedRow),
onDrop: (table, droppedRow) => this.onDrop(table, droppedRow),
dragHandle: this.options.dragHandle
@ -74,8 +79,26 @@ $.BootstrapTable = class extends $.BootstrapTable {
this.options.onReorderRowsDrag(this.data[this.draggingIndex])
}
onDragStop (table, draggedRow) {
const rowIndexDraggedRow = $(draggedRow).data('index')
const draggedRowItem = this.data[rowIndexDraggedRow]
this.options.onDragStop(table, draggedRowItem, draggedRow)
}
onAllowDrop (hoveredRow, draggedRow) {
const rowIndexDraggedRow = $(draggedRow).data('index')
const rowIndexHoveredRow = $(hoveredRow).data('index')
const draggedRowItem = this.data[rowIndexDraggedRow]
const hoveredRowItem = this.data[rowIndexHoveredRow]
return this.options.onAllowDrop(hoveredRowItem, draggedRowItem, hoveredRow, draggedRow)
}
onDrop (table) {
this.$draggingTd.css('cursor', '')
const pageNum = this.options.pageNumber
const pageSize = this.options.pageSize
const newData = []
for (let i = 0; i < table.tBodies[0].rows.length; i++) {
@ -88,13 +111,17 @@ $.BootstrapTable = class extends $.BootstrapTable {
const draggingRow = this.data[this.draggingIndex]
const droppedIndex = newData.indexOf(this.data[this.draggingIndex])
const droppedRow = this.data[droppedIndex]
const index = this.options.data.indexOf(this.data[droppedIndex])
const index = (pageNum - 1) * pageSize + this.options.data.indexOf(this.data[droppedIndex])
this.options.data.splice(this.options.data.indexOf(draggingRow), 1)
this.options.data.splice(index, 0, draggingRow)
this.initSearch()
if (this.options.sidePagination === 'server') {
this.data = [...this.options.data]
}
// Call the user defined function
this.options.onReorderRowsDrop(droppedRow)

View File

@ -1,6 +1,5 @@
/**
* @author: Dennis Hernández
* @webSite: http://djhvscf.github.io/Blog
* @version: v2.0.0
*/
@ -30,7 +29,7 @@ const reInitResizable = that => {
initResizable(that)
}
$.extend($.fn.bootstrapTable.defaults, {
Object.assign($.fn.bootstrapTable.defaults, {
resizable: false
})

View File

@ -131,6 +131,9 @@
$th = $('<th style="width:36px"></th>');
} else {
$th = $('<th style="' + ((column.width) ? ('width:' + column.width + ((column.widthUnit) ? column.widthUnit : 'px')) : '') + '" class="' + column.field + '_cls"></th>');
if (column.align) {
$th.css("text-align", column.align);
}
}
if((!target.isFixWidth)&& column.width){
target.isFixWidth = column.width.indexOf("px")>-1?true:false;
@ -686,6 +689,7 @@
if (_isExpanded || _isCollapsed) {
var tr = $(this).parent().parent();
var row_id = tr.attr("id");
var row_pid = tr.attr("pid");
var _id = tr.attr("data-id");
var _ls = target.find("tbody").find("tr[id^='" + row_id + "_']");
if (!options.pagination) {
@ -714,18 +718,36 @@
var _ls = target.find("tbody").find("tr[id^='" + row_id + "_']");
if (_ls && _ls.length > 0) {
if (_isExpanded) {
$.each(_ls, function(index, item) {
$(item).css("display", "none");
});
if (row_pid == "row_root") {
$('table tr[id^="' + row_id + '_"]').css("display", "none");
$('table tr[id^="' + row_id + '_"]').each(function(i,n) {
var _isExpanded = $(n).find(".treetable-expander").hasClass(options.expanderExpandedClass);
if (_isExpanded) {
$(n).find(".treetable-expander").trigger("click");
}
})
} else {
$.each(_ls, function(index, item) {
$(item).css("display", "none");
var _isExpanded = $(item).find(".treetable-expander").hasClass(options.expanderExpandedClass);
if (_isExpanded) {
$(item).find(".treetable-expander").trigger("click");
}
});
}
} else {
$.each(_ls, function(index, item) {
var _icon = $(item).eq(options.expandColumn).find(".treetable-expander");
if (_icon && _icon.hasClass(options.expanderExpandedClass)) {
$(item).css("display", "table");
} else {
$(item).css("display", "table");
}
});
if (row_pid == "row_root") {
$('table tr[pid="' + row_id + '"]').css("display", "table");
} else {
$.each(_ls, function(index, item) {
var _p_icon = $("#" + $(item).attr("pid")).children().eq(options.expandColumn).find(".treetable-expander");
var _isExpanded = _p_icon.hasClass(options.expanderExpandedClass);
var _isCollapsed = _p_icon.hasClass(options.expanderCollapsedClass);
if (row_id == $(item).attr("pid")) {
$(item).css("display", "table");
}
});
}
}
} else {
if (options.pagination) {

File diff suppressed because one or more lines are too long

View File

@ -1,49 +1,53 @@
/*this is basic form validation using for validation person's basic information author:Clara Guo data:2017/07/20*/
$(document).ready(function(){
$.validator.setDefaults({
submitHandler: function(form) {
form.submit();
submitHandler: function(form) {
form.submit();
}
});
//手机号码验证身份证正则合并:(^\d{15}$)|(^\d{17}([0-9]|X)$)
jQuery.validator.addMethod("isPhone",function(value,element){
// 非法字符验证
jQuery.validator.addMethod("specialSign",function(value,element) {
var char = /^[^<>"'|\\]+$/;
return this.optional(element) || (char.test(value));
},"不能包含非法字符:< > \" ' \\\ |");
// 手机号码验证身份证正则合并:(^\d{15}$)|(^\d{17}([0-9]|X)$)
jQuery.validator.addMethod("isPhone",function(value,element) {
var length = value.length;
var phone=/^1[3-9]\d{9}$/;
var phone = /^1[3-9]\d{9}$/;
return this.optional(element)||(length == 11 && phone.test(value));
},"请填写正确的11位手机号");
//电话号码验证
jQuery.validator.addMethod("isTel",function(value,element){
// 电话号码验证
jQuery.validator.addMethod("isTel",function(value,element) {
var tel = /^(0\d{2,3}-)?\d{7,8}$/g;//区号3,4位,号码7,8位
return this.optional(element) || (tel.test(value));
},"请填写正确的座机号码");
//姓名校验
jQuery.validator.addMethod("isName",function(value,element){
var name=/^[\u4e00-\u9fa5]{2,6}$/;
// 姓名校验
jQuery.validator.addMethod("isName",function(value,element) {
var name = /^[\u4e00-\u9fa5]{2,6}$/;
return this.optional(element) || (name.test(value));
},"姓名只能用汉字,长度2-4位");
//校验用户名
jQuery.validator.addMethod("isUserName",function(value,element){
var userName=/^[a-zA-Z0-9]{2,13}$/;
// 校验用户名
jQuery.validator.addMethod("isUserName",function(value,element) {
var userName = /^[a-zA-Z0-9]{2,13}$/;
return this.optional(element) || (userName).test(value);
},'请输入数字或者字母,不包含特殊字符');
//校验身份证
jQuery.validator.addMethod("isIdentity",function(value,element){
var id= /^(\d{15}$|^\d{18}$|^\d{17}(\d|X))$/;
// 校验身份证
jQuery.validator.addMethod("isIdentity",function(value,element) {
var id = /^(\d{15}$|^\d{18}$|^\d{17}(\d|X))$/;
return this.optional(element) || (id.test(value));
},"请输入正确的15或18位身份证号,末尾若为X请大写");
//校验二代身份证
jQuery.validator.addMethod("isIdentity18",function(value,element){
var id= /^(^\d{17}(\d|X))$/;
// 校验二代身份证
jQuery.validator.addMethod("isIdentity18",function(value,element) {
var id = /^(^\d{17}(\d|X))$/;
return this.optional(element) || (id.test(value));
},"请输入正确的18位身份证号末尾若为X请大写");
//校验出生日期
jQuery.validator.addMethod("isBirth",function(value,element){
// 校验出生日期
jQuery.validator.addMethod("isBirth",function(value,element) {
var birth = /^(19|20)\d{2}-(1[0-2]|0?[1-9])-(0?[1-9]|[1-2][0-9]|3[0-1])$/;
return this.optional(element) || (birth).test(value);
},"出生日期格式示例2000-01-01");
//校验IP地址
jQuery.validator.addMethod("isIp",function(value,element){
// 校验IP地址
jQuery.validator.addMethod("isIp",function(value,element) {
var ip = /^(?:(?:2[0-4][0-9]\.)|(?:25[0-5]\.)|(?:1[0-9][0-9]\.)|(?:[1-9][0-9]\.)|(?:[0-9]\.)){3}(?:(?:2[0-4][0-9])|(?:25[0-5])|(?:1[0-9][0-9])|(?:[1-9][0-9])|(?:[0-9]))$/;
return this.optional(element) || (ip).test(value);
},"IP地址格式示例127.0.0.1");
@ -53,7 +57,7 @@ $(document).ready(function(){
jQuery.validator.addMethod("gt", function(value, element, param) {
return value > param;
}, $.validator.format("输入值必须大于{0}"));
//校验新旧密码是否相同
// 校验新旧密码是否相同
jQuery.validator.addMethod("isdiff",function(){
var p1=$("#pwdOld").val();
var p2=$("#pwdNew").val();
@ -63,7 +67,7 @@ $(document).ready(function(){
return true;
}
});
//校验新密码和确认密码是否相同
// 校验新密码和确认密码是否相同
jQuery.validator.addMethod("issame",function(){
var p3=$("#confirm_password").val();
var p4=$("#pwdNew").val();
@ -73,7 +77,7 @@ $(document).ready(function(){
return false;
}
});
//校验基础信息表单
// 校验基础信息表单
$("#basicInfoForm").validate({
errorElement:'span',
errorClass:'help-block error-mes',
@ -133,11 +137,11 @@ $(document).ready(function(){
}
});
//校验修改密码表单
// 校验修改密码表单
$("#modifyPwd").validate({
onfocusout: function(element) { $(element).valid()},
debug:false, //表示校验通过后是否直接提交表单
onkeyup:false, //表示按键松开时候监听验证
debug:false, // 表示校验通过后是否直接提交表单
onkeyup:false, // 表示按键松开时候监听验证
rules:{
pwdOld:{
required:true,

View File

@ -150,12 +150,26 @@ body.signin {
box-shadow: none;
}
.form-control{
border-radius: 1px!important;
padding: 6px 12px!important;
height: 34px!important;
.form-control {
border-radius: 1px!important;
padding: 6px 12px!important;
height: 34px!important;
}
.form-control:focus{
.form-control:focus {
border-color: #1ab394 !important;
}
body .layer-ext-moon-msg[type="dialog"]{
min-width: 100px !important;
}
body .layer-ext-moon-msg {
background-color: rgba(0,0,0,0.6);
color: #fff;
border: none;
}
body .layer-ext-moon-msg .layui-layer-content{
padding: 12px 25px;
text-align: center;
}

View File

@ -1 +1 @@
html{height:100%}body.signin{height:auto;background:url(../img/login-background.jpg) no-repeat center fixed;-webkit-background-size:cover;-moz-background-size:cover;-o-background-size:cover;background-size:cover}.signinpanel{width:750px;margin:10% auto 0;color:rgba(255,255,255,.95)}.signinpanel .logopanel{float:none;width:auto;padding:0;background:0}.signinpanel .signin-info ul{list-style:none;padding:0;margin:20px 0}.signinpanel .form-control{display:block;margin-top:15px}.signinpanel .uname{background:#fff url(../img/user.png) no-repeat 95% center;color:#333}.signinpanel .pword{background:#fff url(../img/locked.png) no-repeat 95% center;color:#333}.signinpanel .code{background:#fff no-repeat 95% center;color:#333;margin:0 0 15px 0}.signinpanel .btn{margin-top:15px}.signinpanel form{background:rgba(255,255,255,.2);border:1px solid rgba(255,255,255,.3);-moz-box-shadow:0 3px 0 rgba(12,12,12,.03);-webkit-box-shadow:0 3px 0 rgba(12,12,12,.03);box-shadow:0 3px 0 rgba(12,12,12,.03);-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;padding:30px}.signup-footer{border-top:solid 1px rgba(255,255,255,.3);margin:20px 0;padding-top:15px}@media screen and (max-width:768px){.signinpanel,.signuppanel{margin:0 auto;width:380px!important;padding:20px}.signinpanel form{margin-top:20px}.signup-footer,.signuppanel .form-control{margin-bottom:10px}.signup-footer .pull-left,.signup-footer .pull-right{float:none!important;text-align:center}.signinpanel .signin-info ul{display:none}}@media screen and (max-width:320px){.signinpanel,.signuppanel{margin:0 20px;width:auto}}.checkbox-custom{position:relative;padding:0 15px 0 25px;margin-bottom:7px;display:inline-block}.checkbox-custom input[type="checkbox"]{opacity:0;position:absolute;cursor:pointer;z-index:2;margin:-6px 0 0 0;top:50%;left:3px}.checkbox-custom label:before{content:'';position:absolute;top:50%;left:0;margin-top:-9px;width:18px;height:17px;display:inline-block;border-radius:2px;border:1px solid #bbb;background:#fff}.checkbox-custom input[type="checkbox"]:checked+label:after{position:absolute;display:inline-block;font-family:'Glyphicons Halflings';content:"\e013";top:42%;left:3px;margin-top:-5px;font-size:11px;line-height:1;width:16px;height:16px;color:#333}.checkbox-custom label{cursor:pointer;line-height:1.2;font-weight:normal;margin-bottom:0;text-align:left}.form-control,.form-control:focus,.has-error .form-control:focus,.has-success .form-control:focus,.has-warning .form-control:focus,.navbar-collapse,.navbar-form,.navbar-form-custom .form-control:focus,.navbar-form-custom .form-control:hover,.open .btn.dropdown-toggle,.panel,.popover,.progress,.progress-bar{box-shadow:none}.form-control{border-radius:1px!important;padding:6px 12px!important;height:34px!important}.form-control:focus{border-color:#1ab394!important}
html{height:100%}body.signin{height:auto;background:url(../img/login-background.jpg) no-repeat center fixed;-webkit-background-size:cover;-moz-background-size:cover;-o-background-size:cover;background-size:cover}.signinpanel{width:750px;margin:10% auto 0;color:rgba(255,255,255,.95)}.signinpanel .logopanel{float:none;width:auto;padding:0;background:0}.signinpanel .signin-info ul{list-style:none;padding:0;margin:20px 0}.signinpanel .form-control{display:block;margin-top:15px}.signinpanel .uname{background:#fff url(../img/user.png) no-repeat 95% center;color:#333}.signinpanel .pword{background:#fff url(../img/locked.png) no-repeat 95% center;color:#333}.signinpanel .code{background:#fff no-repeat 95% center;color:#333;margin:0 0 15px 0}.signinpanel .btn{margin-top:15px}.signinpanel form{background:rgba(255,255,255,.2);border:1px solid rgba(255,255,255,.3);-moz-box-shadow:0 3px 0 rgba(12,12,12,.03);-webkit-box-shadow:0 3px 0 rgba(12,12,12,.03);box-shadow:0 3px 0 rgba(12,12,12,.03);-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;padding:30px}.signup-footer{border-top:solid 1px rgba(255,255,255,.3);margin:20px 0;padding-top:15px}@media screen and (max-width:768px){.signinpanel,.signuppanel{margin:0 auto;width:380px!important;padding:20px}.signinpanel form{margin-top:20px}.signup-footer,.signuppanel .form-control{margin-bottom:10px}.signup-footer .pull-left,.signup-footer .pull-right{float:none!important;text-align:center}.signinpanel .signin-info ul{display:none}}@media screen and (max-width:320px){.signinpanel,.signuppanel{margin:0 20px;width:auto}}.checkbox-custom{position:relative;padding:0 15px 0 25px;margin-bottom:7px;display:inline-block}.checkbox-custom input[type="checkbox"]{opacity:0;position:absolute;cursor:pointer;z-index:2;margin:-6px 0 0 0;top:50%;left:3px}.checkbox-custom label:before{content:'';position:absolute;top:50%;left:0;margin-top:-9px;width:18px;height:17px;display:inline-block;border-radius:2px;border:1px solid #bbb;background:#fff}.checkbox-custom input[type="checkbox"]:checked+label:after{position:absolute;display:inline-block;font-family:'Glyphicons Halflings';content:"\e013";top:42%;left:3px;margin-top:-5px;font-size:11px;line-height:1;width:16px;height:16px;color:#333}.checkbox-custom label{cursor:pointer;line-height:1.2;font-weight:normal;margin-bottom:0;text-align:left}.form-control,.form-control:focus,.has-error .form-control:focus,.has-success .form-control:focus,.has-warning .form-control:focus,.navbar-collapse,.navbar-form,.navbar-form-custom .form-control:focus,.navbar-form-custom .form-control:hover,.open .btn.dropdown-toggle,.panel,.popover,.progress,.progress-bar{box-shadow:none}.form-control{border-radius:1px!important;padding:6px 12px!important;height:34px!important}.form-control:focus{border-color:#1ab394!important}body .layer-ext-moon-msg[type="dialog"]{min-width:100px!important}body .layer-ext-moon-msg{background-color:rgba(0,0,0,0.6);color:#fff;border:0}body .layer-ext-moon-msg .layui-layer-content{padding:12px 25px;text-align:center}

View File

@ -384,12 +384,12 @@ label.error {
position: relative;
padding-left: 25px;
padding-right: 15px;
padding-top: 8px;
padding-top: 7px;
}
.icheckbox, .icheckbox-blue, .iradio, .iradio-blue, .iradio-purple {
position: absolute;
top: 8px;
top: 7px;
left: 0
}
@ -1067,7 +1067,7 @@ table.rc-table-resizing thead > th > a {
}
/** 表格行拖拽样式 **/
.reorder_rows_onDragClass td {
.reorder-rows-on-drag-class td {
color:yellow!important;
background-color:#999!important;
text-shadow:0 0 10px black,0 0 10px black,0 0 8px black,0 0 6px black,0 0 6px black;

View File

@ -2,11 +2,6 @@
* 首页方法封装处理
* Copyright (c) 2019 ruoyi
*/
layer.config({
extend: 'moon/style.css',
skin: 'layer-ext-moon'
});
var isMobile = false;
var sidebarHeight = isMobile ? '100%' : '96%';
@ -61,7 +56,7 @@ $(window).bind("load resize", function() {
$(".slimScrollDiv").css({ "overflow": "hidden" });
$('.navbar-static-side').fadeOut();
} else {
if($('body').hasClass('canvas-menu')) {
if ($('body').hasClass('canvas-menu')) {
$('body').addClass('fixed-sidebar');
$('body').removeClass('canvas-menu');
$("body").removeClass("mini-navbar");
@ -118,7 +113,7 @@ function fixedSidebar() {
// 设置锚点
function setIframeUrl(href) {
if($.common.equals("history", mode)) {
if ($.common.equals("history", mode)) {
storage.set('publicPath', href);
} else {
var nowUrl = window.location.href;
@ -353,7 +348,6 @@ $(function() {
var panelUrl = $(this).parents('.menuTab').data('panel');
// 当前元素处于活动状态
if ($(this).parents('.menuTab').hasClass('active')) {
syncMenuTab($('.page-tabs-content').find('.active').attr('data-id'));
// 当前元素后面有同辈元素,使后面的一个元素处于活动状态
if ($(this).parents('.menuTab').next('.menuTab').length) {
@ -407,7 +401,7 @@ $(function() {
}
});
if($.common.isNotEmpty(panelUrl)){
if ($.common.isNotEmpty(panelUrl)) {
$('.menuTab[data-id="' + panelUrl + '"]').addClass('active').siblings('.menuTab').removeClass('active');
$('.mainContent .RuoYi_iframe').each(function() {
if ($(this).data('id') == panelUrl) {
@ -432,7 +426,7 @@ $(function() {
});
}
scrollToTab($('.menuTab.active'));
syncMenuTab($('.page-tabs-content').find('.active').attr('data-id'));
syncMenuTab($.common.isNotEmpty(panelUrl) ? panelUrl : $('.page-tabs-content').find('.active').attr('data-id'));
return false;
}

View File

@ -5,15 +5,9 @@
var startLayDate;
var endLayDate;
$(function() {
var isScrollToTop = parent.isScrollToTop;
// layer扩展皮肤
if (window.layer !== undefined) {
layer.config({
extend: 'moon/style.css',
skin: 'layer-ext-moon'
});
}
$(function() {
// 回到顶部绑定
if ($.fn.toTop !== undefined) {
@ -48,7 +42,7 @@ $(function() {
});
// laydate 时间控件绑定
if ($(".select-time").length > 0) {
if ($(".select-time").length > 0 && $('#startTime').length > 0 && $('#endTime').length > 0) {
layui.use('laydate', function() {
var laydate = layui.laydate;
startLayDate = laydate.render({
@ -231,7 +225,7 @@ var refreshItem = function(){
/** 关闭选项卡 */
var closeItem = function(dataId){
var topWindow = $(window.parent.document);
if($.common.isNotEmpty(dataId)){
if ($.common.isNotEmpty(dataId)) {
window.parent.$.modal.closeLoading();
// 根据dataId关闭指定选项卡
$('.menuTab[data-id="' + dataId + '"]', topWindow).remove();
@ -241,7 +235,7 @@ var closeItem = function(dataId){
}
var panelUrl = window.frameElement.getAttribute('data-panel');
$('.page-tabs-content .active i', topWindow).click();
if($.common.isNotEmpty(panelUrl)){
if ($.common.isNotEmpty(panelUrl)) {
$('.menuTab[data-id="' + panelUrl + '"]', topWindow).addClass('active').siblings('.menuTab').removeClass('active');
$('.mainContent .RuoYi_iframe', topWindow).each(function() {
if ($(this).data('id') == panelUrl) {
@ -288,7 +282,7 @@ function createMenuItem(dataUrl, menuName, isRefresh) {
// 添加选项卡对应的iframe
var str1 = '<iframe class="RuoYi_iframe" name="iframe' + dataIndex + '" width="100%" height="100%" src="' + dataUrl + '" frameborder="0" data-id="' + dataUrl + '" data-panel="' + panelUrl + '" seamless></iframe>';
if (parent.isScrollToTop) {
if (isScrollToTop) {
$('.mainContent', topWindow).find('iframe.RuoYi_iframe').hide().parents('.mainContent').append(str1);
} else {
$('.mainContent', topWindow).find('iframe.RuoYi_iframe').css({"visibility": "hidden", "position": "absolute"}).parents('.mainContent').append(str1);
@ -363,7 +357,7 @@ function activeWindow() {
}
function openToCurrentTab(obj) {
if (parent.isScrollToTop) {
if (isScrollToTop) {
$(obj).show().siblings('.RuoYi_iframe').hide();
} else {
$(obj).css({"visibility": "visible", "position": "static"}).siblings('.RuoYi_iframe').css({"visibility": "hidden", "position": "absolute"});
@ -373,22 +367,22 @@ function openToCurrentTab(obj) {
/** 密码规则范围验证 */
function checkpwd(chrtype, password) {
if (chrtype == 1) {
if(!$.common.numValid(password)){
if (!$.common.numValid(password)) {
$.modal.alertWarning("密码只能为0-9数字");
return false;
}
} else if (chrtype == 2) {
if(!$.common.enValid(password)){
if (!$.common.enValid(password)) {
$.modal.alertWarning("密码只能为a-z和A-Z字母");
return false;
}
} else if (chrtype == 3) {
if(!$.common.enNumValid(password)){
if (!$.common.enNumValid(password)) {
$.modal.alertWarning("密码必须包含字母以及数字");
return false;
}
} else if (chrtype == 4) {
if(!$.common.charValid(password)){
if (!$.common.charValid(password)) {
$.modal.alertWarning("密码必须包含字母、数字、以及特殊符号<font color='red'>~!@#$%^&*()-=_+</font>");
return false;
}
@ -398,14 +392,14 @@ function checkpwd(chrtype, password) {
/** 开始时间/时分秒 */
function beginOfTime(date) {
if($.common.isNotEmpty(date)) {
if ($.common.isNotEmpty(date)) {
return $.common.sprintf("%s 00:00:00", date);
}
}
/** 结束时间/时分秒 */
function endOfTime(date) {
if($.common.isNotEmpty(date)) {
if ($.common.isNotEmpty(date)) {
return $.common.sprintf("%s 23:59:59", date);
}
}

View File

@ -48,11 +48,11 @@ var table = {
paginationLoop: false,
pageSize: 10,
pageNumber: 1,
pageList: [10, 25, 50],
pageList: [10, 25, 50, 100],
toolbar: "toolbar",
loadingFontSize: 13,
striped: false,
escape: false,
escape: true,
firstLoad: true,
showFooter: false,
search: false,
@ -103,6 +103,7 @@ var table = {
showFooter: options.showFooter, // 是否显示表尾
iconSize: 'outline', // 图标大小undefined默认的按钮尺寸 xs超小按钮sm小按钮lg大按钮
toolbar: '#' + options.toolbar, // 指定工作栏
virtualScroll: options.virtualScroll, // 是否启动虚拟滚动(大量数据纯展示时使用)
loadingFontSize: options.loadingFontSize, // 自定义加载文本的字体大小
sidePagination: options.sidePagination, // server启用服务端分页client客户端分页
search: options.search, // 是否显示搜索框功能
@ -318,7 +319,7 @@ var table = {
_value = _value.replace(/\'/g,"&apos;");
_value = _value.replace(/\"/g,"&quot;");
var actions = [];
actions.push($.common.sprintf('<input style="opacity: 0;position: absolute;width:5px;z-index:-1" type="text" value="%s"/>', _value));
actions.push($.common.sprintf('<input style="opacity: 0;position: absolute;z-index:-1" type="text" value="%s"/>', _value));
actions.push($.common.sprintf('<a href="###" class="tooltip-show" data-toggle="tooltip" data-target="%s" title="%s">%s</a>', _target, _value, _text));
return actions.join('');
} else {
@ -365,7 +366,7 @@ var table = {
if ($.common.isNotEmpty(pageSize)) {
params.pageSize = pageSize;
}
if ($.common.isNotEmpty(tableId)){
if ($.common.isNotEmpty(tableId)) {
$("#" + tableId).bootstrapTable('refresh', params);
} else{
$("#" + table.options.id).bootstrapTable('refresh', params);
@ -425,7 +426,7 @@ var table = {
shadeClose: true,
btn1: function(index, layero){
var file = layero.find('#file').val();
if (file == '' || (!$.common.endWith(file, '.xls') && !$.common.endWith(file, '.xlsx'))){
if (file == '' || (!$.common.endWith(file, '.xls') && !$.common.endWith(file, '.xlsx'))) {
$.modal.msgWarning("请选择后缀为 “xls”或“xlsx”的文件。");
return false;
}
@ -806,7 +807,7 @@ var table = {
},
// 获取iframe页的DOM
getChildFrame: function (index) {
if ($.common.isEmpty(index)){
if ($.common.isEmpty(index)) {
var index = parent.layer.getFrameIndex(window.name);
return parent.layer.getChildFrame('body', index);
} else {
@ -815,7 +816,7 @@ var table = {
},
// 关闭窗体
close: function (index) {
if ($.common.isEmpty(index)){
if ($.common.isEmpty(index)) {
var index = parent.layer.getFrameIndex(window.name);
parent.layer.close(index);
} else {
@ -919,6 +920,7 @@ var table = {
fix: false,
area: [_width + 'px', _height + 'px'],
content: _url,
closeBtn: $.common.isEmpty(options.closeBtn) ? 1 : options.closeBtn,
shadeClose: $.common.isEmpty(options.shadeClose) ? true : options.shadeClose,
skin: options.skin,
// options.btn设置为0表示不显示按钮

View File

@ -8,18 +8,15 @@ $(function() {
});
});
$.validator.setDefaults({
submitHandler: function() {
login();
}
});
function login() {
$.modal.loading($("#btnSubmit").data("loading"));
var username = $.common.trim($("input[name='username']").val());
var password = $.common.trim($("input[name='password']").val());
var validateCode = $("input[name='validateCode']").val();
var rememberMe = $("input[name='rememberme']").is(':checked');
if($.common.isEmpty(validateCode) && captchaEnabled) {
$.modal.msg("请输入验证码");
return false;
}
$.ajax({
type: "post",
url: ctx + "login",
@ -29,13 +26,16 @@ function login() {
"validateCode": validateCode,
"rememberMe": rememberMe
},
beforeSend: function () {
$.modal.loading($("#btnSubmit").data("loading"));
},
success: function(r) {
if (r.code == web_status.SUCCESS) {
location.href = ctx + 'index';
} else {
$('.imgcode').click();
$(".code").val("");
$.modal.msg(r.msg);
$('.imgcode').click();
$(".code").val("");
$.modal.msg(r.msg);
}
$.modal.closeLoading();
}
@ -60,6 +60,9 @@ function validateRule() {
password: {
required: icon + "请输入您的密码",
}
},
submitHandler: function(form) {
login();
}
})
}

View File

@ -7,17 +7,14 @@ $(function() {
});
});
$.validator.setDefaults({
submitHandler: function() {
register();
}
});
function register() {
$.modal.loading($("#btnSubmit").data("loading"));
var username = $.common.trim($("input[name='username']").val());
var password = $.common.trim($("input[name='password']").val());
var validateCode = $("input[name='validateCode']").val();
if($.common.isEmpty(validateCode) && captchaEnabled) {
$.modal.msg("请输入验证码");
return false;
}
$.ajax({
type: "post",
url: ctx + "register",
@ -26,6 +23,9 @@ function register() {
"password": password,
"validateCode": validateCode
},
beforeSend: function () {
$.modal.loading($("#btnSubmit").data("loading"));
},
success: function(r) {
if (r.code == web_status.SUCCESS) {
layer.alert("<font color='red'>恭喜你,您的账号 " + username + " 注册成功!</font>", {
@ -57,7 +57,8 @@ function validateRule() {
},
password: {
required: true,
minlength: 5
minlength: 5,
specialSign: true
},
confirmPassword: {
required: true,
@ -77,6 +78,9 @@ function validateRule() {
required: icon + "请再次输入您的密码",
equalTo: icon + "两次密码输入不一致"
}
},
submitHandler: function(form) {
register();
}
})
}

View File

@ -91,7 +91,7 @@
</div>
<div class="form-group">
<label class="font-noraml">分组</label>
<label class="font-noraml">分组</label>
<select class="form-control noselect2 selectpicker">
<optgroup label="--请选择开发语言--">
<option value="Java">Java</option>

View File

@ -17,7 +17,7 @@
<h2>若依后台管理系统</h2>
<p>ruoyi是一个完全响应式基于Bootstrap3.3.7最新版本开发的扁平化主题她采用了主流的左右两栏式布局使用了Html5+CSS3等现代技术她提供了诸多的强大的可以重新组合的UI组件并集成了最新的jQuery版本(v2.1.1)当然也集成了很多功能强大用途广泛的就jQuery插件她可以用于所有的Web应用程序<b>网站管理后台</b><b>网站会员中心</b><b>CMS</b><b>CRM</b><b>OA</b>等等,当然,您也可以对她进行深度定制,以做出更强系统。</p>
<p>
<b>当前版本:</b>v4.7.8
<b>当前版本:</b>v4.7.9
</p>
<p>
<span class="label label-warning">免费开源</span>
@ -56,7 +56,7 @@
<h3>你好,若依 </h3>
<p>H+是一个完全响应式基于Bootstrap3.3.7最新版本开发的扁平化主题她采用了主流的左右两栏式布局使用了Html5+CSS3等现代技术她提供了诸多的强大的可以重新组合的UI组件并集成了最新的jQuery版本(v2.1.1)当然也集成了很多功能强大用途广泛的就jQuery插件她可以用于所有的Web应用程序<b>网站管理后台</b><b>网站会员中心</b><b>CMS</b><b>CRM</b><b>OA</b>等等,当然,您也可以对她进行深度定制,以做出更强系统。</p>
<p>
<b>当前版本:</b>v4.7.8
<b>当前版本:</b>v4.7.9
</p>
<p>
<span class="label label-warning">开源免费</span>

View File

@ -0,0 +1,78 @@
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<th:block th:include="include :: header('表格保存状态')" />
</head>
<body class="gray-bg">
<div class="container-div">
<div class="row">
<div class="col-sm-12 select-table table-striped">
<table id="bootstrap-table" data-cookie="true" data-cookie-id-table="userTableId"></table>
</div>
</div>
</div>
<div th:include="include :: footer"></div>
<th:block th:include="include :: bootstrap-table-cookie-js" />
<script th:inline="javascript">
var prefix = ctx + "demo/table";
var datas = [[${@dict.getType('sys_normal_disable')}]];
$(function() {
var options = {
url: prefix + "/list",
search: true,
showSearch: false,
showRefresh: false,
showToggle: false,
columns: [{
field: 'state',
checkbox: true
},
{
field : 'userId',
title : '用户ID'
},
{
field : 'userCode',
title : '用户编号'
},
{
field : 'userName',
title : '用户姓名'
},
{
field : 'userPhone',
title : '用户手机'
},
{
field : 'userEmail',
title : '用户邮箱'
},
{
field : 'userBalance',
title : '用户余额'
},
{
field: 'status',
title: '用户状态',
align: 'center',
formatter: function(value, row, index) {
return $.table.selectDictLabel(datas, value);
}
},
{
title: '操作',
align: 'center',
formatter: function(value, row, index) {
var actions = [];
actions.push('<a class="btn btn-success btn-xs" href="javascript:;"><i class="fa fa-edit"></i>编辑</a> ');
actions.push('<a class="btn btn-danger btn-xs" href="javascript:;"><i class="fa fa-remove"></i>删除</a>');
return actions.join('');
}
}]
};
$.table.init(options);
});
</script>
</body>
</html>

View File

@ -9,7 +9,7 @@
<div class="col-sm-12 select-table table-striped">
<table id="bootstrap-table" data-page-size="10"
data-show-custom-view="true" data-custom-view="customViewFormatter"
data-show-custom-view-button="true">
data-custom-view-default-view="true">
</table>
</div>
</div>

View File

@ -0,0 +1,112 @@
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<th:block th:include="include :: header('全文搜索')" />
</head>
<body class="gray-bg">
<div class="container-div">
<div class="row">
<div class="col-sm-12 select-table table-striped">
<table id="bootstrap-table" data-page-size="10" data-search-align="left"
data-show-custom-view="true" data-custom-view="customViewFormatter"
data-custom-view-default-view="true">
</table>
</div>
</div>
</div>
<template id="textSearchTemplate">
<div class="col-sm-12">
<p style="margin-top:10px;line-height:1.5;">
<span style="color:blue;font-size:medium">%title%</span><br>
<span style="color:#000000;font-size:medium">%content%</span><br>
<span style="color:#008000;font-size:medium">
档号:<a href="javascript:void(0)" onclick="handleView('%tableId%', '%archiveNo%')">%archiveNo%</a>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;%docNo%
</span>
</p>
</div>
</template>
<div th:include="include :: footer"></div>
<th:block th:include="include :: bootstrap-table-custom-view-js" />
<script th:inline="javascript">
var prefix = ctx + "demo/table";
$(function() {
var options = {
url: prefix + "/text/list",
search: true,
showSearch: false,
showRefresh: false,
showToggle: false,
showColumns: false,
showExport: true,
columns: [{
checkbox: true
},
{
field : 'tableId',
title : '编号',
visible: false
},
{
field : 'archiveNo',
title : '档号'
},
{
field : 'docNo',
title : '文件编号'
},
{
field : 'title',
title : '标题',
formatter: function(value, row, index) {
return $.table.tooltip(value, 20, "open");
}
},
{
field : 'content',
title : '内容',
formatter: function(value, row, index) {
return $.table.tooltip(value, 30, "open");
}
}]
};
$.table.init(options);
});
function customViewFormatter (data) {
var template = $('#textSearchTemplate').html()
var view = ''
if(data.length > 0) {
$.each(data, function (i, row) {
view += template.replace('%tableId%', row.tableId)
.replaceAll('%archiveNo%', row.archiveNo)
.replace('%title%', row.title)
.replace('%docNo%', row.docNo)
.replace('%content%', truncateString(row.content, 500))
})
return `<div class="row mx-0">${view}</div>`;
} else {
return `<div class="row mx-0"><div class="col-sm-12"><p style="margin-top:10px;line-height:1.5;"><span style="font-size:medium">没有找到记录。请检查过滤条件。</span></p></div></div>`
}
}
function handleView(tableId, archiveNo) {
alert("tableId" + tableId + "archiveNo" + archiveNo);
}
function truncateString(str, maxLength) {
if (str.length > maxLength) {
return str.slice(0, maxLength) + '...';
}
return str;
}
$(document).ready(function() {
$('.search-input').css('width', '320px');
});
</script>
</body>
</html>

View File

@ -0,0 +1,98 @@
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<th:block th:include="include :: header('表格虚拟滚动')" />
</head>
<body class="gray-bg">
<div class="container-div">
<div class="btn-group-sm" id="toolbar" role="group">
<a class="btn btn-success" onclick="loadRows()">
<i class="fa fa-plus"></i> 加载10000行数据
</a>
<a class="btn btn-info" onclick="appendRows()">
<i class="fa fa-edit"></i> 追加10000行数据
</a>
&nbsp;&nbsp;<span id="total" class="badge badge-success"></span>
</div>
<div class="row">
<div class="col-sm-12 select-table table-striped">
<table id="bootstrap-table"></table>
</div>
</div>
</div>
<div th:include="include :: footer"></div>
<script>
var total = 0
$(function() {
var options = {
data: getData(20),
height: 400,
sidePagination: "client",
pagination: false,
showSearch: false,
virtualScroll: true,
columns: [{
field : 'userId',
title : '用户ID'
},
{
field : 'userCode',
title : '用户编号'
},
{
field : 'userName',
title : '用户姓名'
},
{
field : 'userPhone',
title : '用户手机'
},
{
field : 'userEmail',
title : '用户邮箱'
},
{
field : 'userBalance',
title : '用户余额'
}]
};
$.table.init(options);
});
function getData(number, isAppend) {
if (!isAppend) {
total = 0
}
var data = []
for (var i = total; i < total + number; i++) {
var randomId = 100 + ~~ (Math.random() * 100);
data.push({
userId: i + 1,
userCode: 2000000 + randomId,
userName: '测试' + randomId,
userPhone: '1588888888',
userEmail: 'ry1@qq.com',
userBalance: 10 + randomId,
})
}
if (isAppend) {
total += number
} else {
total = number
}
$('#total').text(total);
return data;
}
function loadRows() {
$('#bootstrap-table').bootstrapTable('load', getData(10000))
}
function appendRows() {
$('#bootstrap-table').bootstrapTable('append', getData(10000, true))
}
</script>
</body>
</html>

View File

@ -9,10 +9,10 @@
<link th:href="@{/css/bootstrap.min.css?v=3.3.7}" rel="stylesheet"/>
<link th:href="@{/css/font-awesome.min.css?v=4.7.0}" rel="stylesheet"/>
<!-- bootstrap-table 表格插件样式 -->
<link th:href="@{/ajax/libs/bootstrap-table/bootstrap-table.min.css?v=1.18.3}" rel="stylesheet"/>
<link th:href="@{/ajax/libs/bootstrap-table/bootstrap-table.min.css?v=1.22.6}" rel="stylesheet"/>
<link th:href="@{/css/animate.min.css?v=20210831}" rel="stylesheet"/>
<link th:href="@{/css/style.min.css?v=20210831}" rel="stylesheet"/>
<link th:href="@{/ruoyi/css/ry-ui.css?v=4.7.8}" rel="stylesheet"/>
<link th:href="@{/ruoyi/css/ry-ui.css?v=4.7.9}" rel="stylesheet"/>
</head>
<!-- 通用JS -->
@ -22,22 +22,22 @@
<script th:src="@{/js/jquery.min.js?v=3.6.3}"></script>
<script th:src="@{/js/bootstrap.min.js?v=3.3.7}"></script>
<!-- bootstrap-table 表格插件 -->
<script th:src="@{/ajax/libs/bootstrap-table/bootstrap-table.min.js?v=1.18.3}"></script>
<script th:src="@{/ajax/libs/bootstrap-table/locale/bootstrap-table-zh-CN.min.js?v=1.18.3}"></script>
<script th:src="@{/ajax/libs/bootstrap-table/extensions/mobile/bootstrap-table-mobile.js?v=1.18.3}"></script>
<script th:src="@{/ajax/libs/bootstrap-table/bootstrap-table.min.js?v=1.22.6}"></script>
<script th:src="@{/ajax/libs/bootstrap-table/locale/bootstrap-table-zh-CN.min.js?v=1.22.6}"></script>
<script th:src="@{/ajax/libs/bootstrap-table/extensions/mobile/bootstrap-table-mobile.js?v=1.22.6}"></script>
<!-- jquery-validate 表单验证插件 -->
<script th:src="@{/ajax/libs/validate/jquery.validate.min.js?v=1.19.3}"></script>
<script th:src="@{/ajax/libs/validate/jquery.validate.extend.js?v=1.19.3}"></script>
<script th:src="@{/ajax/libs/validate/messages_zh.js?v=1.19.3}"></script>
<!-- bootstrap-table 表格树插件 -->
<script th:src="@{/ajax/libs/bootstrap-table/extensions/tree/bootstrap-table-tree.min.js?v=1.18.3}"></script>
<script th:src="@{/ajax/libs/bootstrap-table/extensions/tree/bootstrap-table-tree.min.js?v=1.22.6}"></script>
<!-- 遮罩层 -->
<script th:src="@{/ajax/libs/blockUI/jquery.blockUI.js?v=2.70.0}"></script>
<script th:src="@{/ajax/libs/iCheck/icheck.min.js?v=1.0.3}"></script>
<script th:src="@{/ajax/libs/layer/layer.min.js?v=3.7.0}"></script>
<script th:src="@{/ajax/libs/layui/layui.min.js?v=2.8.18}"></script>
<script th:src="@{/ruoyi/js/common.js?v=4.7.8}"></script>
<script th:src="@{/ruoyi/js/ry-ui.js?v=4.7.8}"></script>
<script th:src="@{/ruoyi/js/common.js?v=4.7.9}"></script>
<script th:src="@{/ruoyi/js/ry-ui.js?v=4.7.9}"></script>
</div>
<!-- ztree树插件 -->
@ -170,20 +170,20 @@
<!-- 表格行拖拽插件 -->
<div th:fragment="bootstrap-table-reorder-rows-js">
<script th:src="@{/ajax/libs/bootstrap-table/extensions/reorder-rows/bootstrap-table-reorder-rows.js?v=1.18.3}"></script>
<script th:src="@{/ajax/libs/bootstrap-table/extensions/reorder-rows/bootstrap-table-reorder-rows.js?v=1.22.6}"></script>
<script th:src="@{/ajax/libs/bootstrap-table/extensions/reorder-rows/jquery.tablednd.js?v=1.0.3}"></script>
</div>
<!-- 表格列拖拽插件 -->
<div th:fragment="bootstrap-table-reorder-columns-js">
<script th:src="@{/ajax/libs/bootstrap-table/extensions/reorder-columns/jquery.dragtable.js?v=5.3.5}"></script>
<script th:src="@{/ajax/libs/bootstrap-table/extensions/reorder-columns/bootstrap-table-reorder-columns.js?v=1.18.3}"></script>
<script th:src="@{/ajax/libs/bootstrap-table/extensions/reorder-columns/bootstrap-table-reorder-columns.js?v=1.22.6}"></script>
</div>
<!-- 表格列宽拖动插件 -->
<div th:fragment="bootstrap-table-resizable-js">
<script th:src="@{/ajax/libs/bootstrap-table/extensions/resizable/jquery.resizableColumns.min.js?v=0.1.0}"></script>
<script th:src="@{/ajax/libs/bootstrap-table/extensions/resizable/bootstrap-table-resizable.js?v=1.18.3}"></script>
<script th:src="@{/ajax/libs/bootstrap-table/extensions/resizable/bootstrap-table-resizable.js?v=1.22.6}"></script>
</div>
<!-- 表格行内编辑插件 -->
@ -192,31 +192,36 @@
</div>
<div th:fragment="bootstrap-table-editable-js">
<script th:src="@{/ajax/libs/bootstrap-table/extensions/editable/bootstrap-editable.min.js?v=1.5.1}"></script>
<script th:src="@{/ajax/libs/bootstrap-table/extensions/editable/bootstrap-table-editable.js?v=1.18.3}"></script>
<script th:src="@{/ajax/libs/bootstrap-table/extensions/editable/bootstrap-table-editable.js?v=1.22.6}"></script>
</div>
<!-- 表格导出插件 -->
<div th:fragment="bootstrap-table-export-js">
<script th:src="@{/ajax/libs/bootstrap-table/extensions/export/bootstrap-table-export.js?v=1.18.3}"></script>
<script th:src="@{/ajax/libs/bootstrap-table/extensions/export/bootstrap-table-export.js?v=1.22.6}"></script>
<script th:src="@{/ajax/libs/bootstrap-table/extensions/export/tableExport.min.js?v=1.10.24}"></script>
</div>
<!-- 表格冻结列插件 -->
<div th:fragment="bootstrap-table-fixed-columns-js">
<script th:src="@{/ajax/libs/bootstrap-table/extensions/columns/bootstrap-table-fixed-columns.js?v=1.18.3}"></script>
<script th:src="@{/ajax/libs/bootstrap-table/extensions/columns/bootstrap-table-fixed-columns.js?v=1.22.6}"></script>
</div>
<!-- 表格自动刷新插件 -->
<div th:fragment="bootstrap-table-auto-refresh-js">
<script th:src="@{/ajax/libs/bootstrap-table/extensions/auto-refresh/bootstrap-table-auto-refresh.js?v=1.18.3}"></script>
<script th:src="@{/ajax/libs/bootstrap-table/extensions/auto-refresh/bootstrap-table-auto-refresh.js?v=1.22.6}"></script>
</div>
<!-- 表格打印插件 -->
<div th:fragment="bootstrap-table-print-js">
<script th:src="@{/ajax/libs/bootstrap-table/extensions/print/bootstrap-table-print.js?v=1.18.3}"></script>
<script th:src="@{/ajax/libs/bootstrap-table/extensions/print/bootstrap-table-print.js?v=1.22.6}"></script>
</div>
<!-- 表格视图分页插件 -->
<div th:fragment="bootstrap-table-custom-view-js">
<script th:src="@{/ajax/libs/bootstrap-table/extensions/custom-view/bootstrap-table-custom-view.js?v=1.18.3}"></script>
<script th:src="@{/ajax/libs/bootstrap-table/extensions/custom-view/bootstrap-table-custom-view.js?v=1.22.6}"></script>
</div>
<!-- 表格保存状态插件 -->
<div th:fragment="bootstrap-table-cookie-js">
<script th:src="@{/ajax/libs/bootstrap-table/extensions/cookie/bootstrap-table-cookie.js?v=1.22.6}"></script>
</div>

View File

@ -14,7 +14,7 @@
<link th:href="@{/css/animate.min.css}" rel="stylesheet"/>
<link th:href="@{/css/style.min.css}" rel="stylesheet"/>
<link th:href="@{/css/skins.css?v=20200902}" rel="stylesheet"/>
<link th:href="@{/ruoyi/css/ry-ui.css?v=4.7.8}" rel="stylesheet"/>
<link th:href="@{/ruoyi/css/ry-ui.css?v=4.7.9}" rel="stylesheet"/>
<style type="text/css">.fixed-sidebar .nav:not(.navbar-toolbar)>li.active{border-left:0px!important;}</style>
</head>
<body class="fixed-sidebar full-height-layout gray-bg" th:classappend="${isMobile} ? 'canvas-menu'" style="overflow: hidden">
@ -125,8 +125,10 @@
<li><a class="menuItem" th:href="@{/demo/table/footer}">数据汇总</a></li>
<li><a class="menuItem" th:href="@{/demo/table/groupHeader}">组合表头</a></li>
<li><a class="menuItem" th:href="@{/demo/table/export}">表格导出</a></li>
<li><a class="menuItem" th:href="@{/demo/table/textSearch}">全文索引</a></li>
<li><a class="menuItem" th:href="@{/demo/table/exportSelected}">导出选择列</a></li>
<li><a class="menuItem" th:href="@{/demo/table/remember}">翻页记住选择</a></li>
<li><a class="menuItem" th:href="@{/demo/table/cookie}">表格保存状态</a></li>
<li><a class="menuItem" th:href="@{/demo/table/pageGo}">跳转至指定页</a></li>
<li><a class="menuItem" th:href="@{/demo/table/params}">自定义查询参数</a></li>
<li><a class="menuItem" th:href="@{/demo/table/multi}">初始多表格</a></li>
@ -147,6 +149,7 @@
<li><a class="menuItem" th:href="@{/demo/table/refresh}">表格自动刷新</a></li>
<li><a class="menuItem" th:href="@{/demo/table/print}">表格打印配置</a></li>
<li><a class="menuItem" th:href="@{/demo/table/dynamicColumns}">表格动态列</a></li>
<li><a class="menuItem" th:href="@{/demo/table/virtualScroll}">表格虚拟滚动</a></li>
<li><a class="menuItem" th:href="@{/demo/table/customView}">自定义视图分页</a></li>
<li><a class="menuItem" th:href="@{/demo/table/asynTree}">异步加载表格树</a></li>
<li><a class="menuItem" th:href="@{/demo/table/other}">表格其他操作</a></li>
@ -318,8 +321,8 @@
<script th:src="@{/js/jquery.contextMenu.min.js}"></script>
<script th:src="@{/ajax/libs/blockUI/jquery.blockUI.js}"></script>
<script th:src="@{/ajax/libs/layer/layer.min.js}"></script>
<script th:src="@{/ruoyi/js/ry-ui.js?v=4.7.8}"></script>
<script th:src="@{/ruoyi/js/common.js?v=4.7.8}"></script>
<script th:src="@{/ruoyi/js/ry-ui.js?v=4.7.9}"></script>
<script th:src="@{/ruoyi/js/common.js?v=4.7.9}"></script>
<script th:src="@{/ruoyi/index.js?v=20201208}"></script>
<script th:src="@{/ajax/libs/fullscreen/jquery.fullscreen.js}"></script>
<script th:src="@{/js/resize-tabs.js}"></script>
@ -336,7 +339,7 @@ var mode = "history";
var historyPath = storage.get("historyPath");
// 是否页签与菜单联动
var isLinkage = true;
//是否页签切换滚动到顶部
// 是否页签切换滚动到顶部
var isScrollToTop = true;
// 本地主题优先,未设置取系统配置

View File

@ -14,7 +14,7 @@
<link th:href="@{/css/animate.min.css}" rel="stylesheet"/>
<link th:href="@{/css/style.min.css}" rel="stylesheet"/>
<link th:href="@{/css/skins.css}" rel="stylesheet"/>
<link th:href="@{/ruoyi/css/ry-ui.css?v=4.7.8}" rel="stylesheet"/>
<link th:href="@{/ruoyi/css/ry-ui.css?v=4.7.9}" rel="stylesheet"/>
</head>
<body class="fixed-sidebar full-height-layout gray-bg" th:classappend="${isMobile} ? 'canvas-menu'" style="overflow: hidden">
<div id="wrapper">
@ -105,8 +105,10 @@
<li><a class="menuItem" th:href="@{/demo/table/footer}">数据汇总</a></li>
<li><a class="menuItem" th:href="@{/demo/table/groupHeader}">组合表头</a></li>
<li><a class="menuItem" th:href="@{/demo/table/export}">表格导出</a></li>
<li><a class="menuItem" th:href="@{/demo/table/textSearch}">全文索引</a></li>
<li><a class="menuItem" th:href="@{/demo/table/exportSelected}">导出选择列</a></li>
<li><a class="menuItem" th:href="@{/demo/table/remember}">翻页记住选择</a></li>
<li><a class="menuItem" th:href="@{/demo/table/cookie}">表格保存状态</a></li>
<li><a class="menuItem" th:href="@{/demo/table/pageGo}">跳转至指定页</a></li>
<li><a class="menuItem" th:href="@{/demo/table/params}">自定义查询参数</a></li>
<li><a class="menuItem" th:href="@{/demo/table/multi}">初始多表格</a></li>
@ -127,6 +129,7 @@
<li><a class="menuItem" th:href="@{/demo/table/refresh}">表格自动刷新</a></li>
<li><a class="menuItem" th:href="@{/demo/table/print}">表格打印配置</a></li>
<li><a class="menuItem" th:href="@{/demo/table/dynamicColumns}">表格动态列</a></li>
<li><a class="menuItem" th:href="@{/demo/table/virtualScroll}">表格虚拟滚动</a></li>
<li><a class="menuItem" th:href="@{/demo/table/customView}">自定义视图分页</a></li>
<li><a class="menuItem" th:href="@{/demo/table/asynTree}">异步加载表格树</a></li>
<li><a class="menuItem" th:href="@{/demo/table/other}">表格其他操作</a></li>
@ -264,8 +267,8 @@
<script th:src="@{/js/jquery.contextMenu.min.js}"></script>
<script th:src="@{/ajax/libs/blockUI/jquery.blockUI.js}"></script>
<script th:src="@{/ajax/libs/layer/layer.min.js}"></script>
<script th:src="@{/ruoyi/js/ry-ui.js?v=4.7.8}"></script>
<script th:src="@{/ruoyi/js/common.js?v=4.7.8}"></script>
<script th:src="@{/ruoyi/js/ry-ui.js?v=4.7.9}"></script>
<script th:src="@{/ruoyi/js/common.js?v=4.7.9}"></script>
<script th:src="@{/ruoyi/index.js?v=20201208}"></script>
<script th:src="@{/ajax/libs/fullscreen/jquery.fullscreen.js}"></script>
<script th:inline="javascript">

View File

@ -38,7 +38,7 @@
<script src="../static/js/bootstrap.min.js" th:src="@{/js/bootstrap.min.js}"></script>
<script src="../static/js/three.min.js" th:src="@{/js/three.min.js}"></script>
<script src="../static/ajax/libs/layer/layer.min.js" th:src="@{/ajax/libs/layer/layer.min.js}"></script>
<script src="../static/ruoyi/js/ry-ui.js" th:src="@{/ruoyi/js/ry-ui.js?v=4.7.8}"></script>
<script src="../static/ruoyi/js/ry-ui.js" th:src="@{/ruoyi/js/ry-ui.js?v=4.7.9}"></script>
</body>
<script th:inline="javascript">
var ctx = [[@{/}]];

View File

@ -9,7 +9,7 @@
<link href="../static/css/font-awesome.min.css" th:href="@{/css/font-awesome.min.css}" rel="stylesheet"/>
<link href="../static/css/style.min.css" th:href="@{/css/style.min.css}" rel="stylesheet"/>
<link href="../static/css/login.min.css" th:href="@{/css/login.min.css}" rel="stylesheet"/>
<link href="../static/ruoyi/css/ry-ui.css" th:href="@{/ruoyi/css/ry-ui.css?v=4.7.8}" rel="stylesheet"/>
<link href="../static/ruoyi/css/ry-ui.css" th:href="@{/ruoyi/css/ry-ui.css?v=4.7.9}" rel="stylesheet"/>
<!-- 360浏览器急速模式 -->
<meta name="renderer" content="webkit">
<!-- 避免IE使用兼容模式 -->
@ -65,18 +65,18 @@
</div>
<div class="signup-footer">
<div class="pull-left">
Copyright © 2018-2023 ruoyi.vip All Rights Reserved. <br>
Copyright © 2018-2024 ruoyi.vip All Rights Reserved. <br>
</div>
</div>
</div>
<script th:inline="javascript"> var ctx = [[@{/}]]; var captchaType = [[${captchaType}]]; </script>
<script th:inline="javascript"> var ctx = [[@{/}]]; var captchaType = [[${captchaType}]]; var captchaEnabled = [[${captchaEnabled}]];</script>
<!--[if lte IE 8]><script>window.location.href=ctx+'html/ie.html';</script><![endif]-->
<!-- 全局js -->
<script src="../static/js/jquery.min.js" th:src="@{/js/jquery.min.js}"></script>
<script src="../static/ajax/libs/validate/jquery.validate.min.js" th:src="@{/ajax/libs/validate/jquery.validate.min.js}"></script>
<script src="../static/ajax/libs/layer/layer.min.js" th:src="@{/ajax/libs/layer/layer.min.js}"></script>
<script src="../static/ajax/libs/blockUI/jquery.blockUI.js" th:src="@{/ajax/libs/blockUI/jquery.blockUI.js}"></script>
<script src="../static/ruoyi/js/ry-ui.js" th:src="@{/ruoyi/js/ry-ui.js?v=4.7.8}"></script>
<script src="../static/ruoyi/js/ry-ui.js" th:src="@{/ruoyi/js/ry-ui.js?v=4.7.9}"></script>
<script src="../static/ruoyi/login.js" th:src="@{/ruoyi/login.js}"></script>
</body>
</html>

View File

@ -79,7 +79,7 @@
<div class="ibox-content">
<p><i class="fa fa-send-o"></i> 官网:<a href="http://www.ruoyi.vip" target="_blank">http://www.ruoyi.vip</a>
</p>
<p><i class="fa fa-qq"></i> QQ群<s>满1389287</s> <s>满1679294</s> <s>满1529866</s> <s>满1772718</s> <s>满1366522</s> <s>满1382251</s> <s>满1145125</s> <s>满86752435</s> <s>满134072510</s> <s>满210336300</s> <s>满339522636</s> <s>满130035985</s> <s>满143151071</s> <s>满158781320</s> <s>满201531282</s> <s>满101526938</s> <s>满264355400</s> <s>满298522656</s> <s>满139845794</s> <s>满185760789</s> <a href="https://jq.qq.com/?_wv=1027&k=7FplYUnR" target="_blank">175104288</a>
<p><i class="fa fa-qq"></i> QQ群<s>满1389287</s> <s>满1679294</s> <s>满1529866</s> <s>满1772718</s> <s>满1366522</s> <s>满1382251</s> <s>满1145125</s> <s>满86752435</s> <s>满134072510</s> <s>满210336300</s> <s>满339522636</s> <s>满130035985</s> <s>满143151071</s> <s>满158781320</s> <s>满201531282</s> <s>满101526938</s> <s>满264355400</s> <s>满298522656</s> <s>满139845794</s> <s>满185760789</s> <s>满175104288</s> <s>满174942938</s> <s>满287843737</s> <a href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=KTVAIhggR3rR3uZWK9A8kR4yYNREQ4jo&authKey=An4DUV9e7uK8I8VgBbp949z0ypQoDrOoqvVg%2FWOr2vuNNDMZUAMPvqHor6TFMIgz&noverify=0&group_code=232896766" target="_blank">232896766</a>
</p>
<p><i class="fa fa-weixin"></i> 微信:<a href="javascript:;">/ *若依</a>
</p>
@ -96,13 +96,60 @@
<div class="ibox-content no-padding">
<div class="panel-body">
<div class="panel-group" id="version">
<div class="panel panel-default">
<div class="panel-heading">
<h5 class="panel-title">
<a data-toggle="collapse" data-parent="#version" href="#v479">v4.7.9</a><code class="pull-right">2024.06.06</code>
</h5>
</div>
<div id="v479" class="panel-collapse collapse in">
<div class="panel-body">
<ol>
<li>通知公告新增详细显示</li>
<li>新增数据脱敏过滤注解</li>
<li>新增表格示例(虚拟滚动)</li>
<li>新增表格示例(全文检索)</li>
<li>新增表格示例(保存状态)</li>
<li>代码生成支持表单布局选项</li>
<li>限制用户操作数据权限范围</li>
<li>用户密码新增非法字符验证</li>
<li>默认加载layer扩展皮肤</li>
<li>未修改初始密码弹框提醒</li>
<li>定时任务白名单配置范围缩小</li>
<li>操作日志列表重置回第一页</li>
<li>定时任务日志默认按时间排序</li>
<li>Excel注解ColumnType类型新增文本</li>
<li>Excel注解新增属性comboReadDict</li>
<li>新增Anonymous匿名访问不鉴权注解</li>
<li>升级oshi到最新版本6.6.1</li>
<li>升级druid到最新版本1.2.23</li>
<li>升级commons.io到最新版本2.13.0</li>
<li>升级spring-framework到安全版本</li>
<li>升级bootstrap-table到最新版本1.22.6</li>
<li>修复重置日期时出现的异常问题</li>
<li>修复页签关闭后存在的跳转问题</li>
<li>修复tooltip单击复制文本不生效的问题</li>
<li>更新缓存管理键名排序方式</li>
<li>更新HttpUtils中的User-Agent</li>
<li>优化自定义XSS注解匹配方式</li>
<li>优化登录注册页面验证码验证</li>
<li>优化数据权限自定义匹配方式</li>
<li>优化高频率定时任务不执行问题</li>
<li>优化树表格align属性在标题生效</li>
<li>优化代码生成主子表关联查询方式</li>
<li>优化导入Excel时设置dictType属性重复查缓存问题</li>
<li>其他细节优化</li>
</ol>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h5 class="panel-title">
<a data-toggle="collapse" data-parent="#version" href="#v478">v4.7.8</a><code class="pull-right">2023.11.23</code>
</h5>
</div>
<div id="v478" class="panel-collapse collapse in">
<div id="v478" class="panel-collapse collapse">
<div class="panel-body">
<ol>
<li>用户列表新增抽屉效果详细信息</li>

View File

@ -181,7 +181,7 @@
resetDate();
$("#operlog-form")[0].reset();
$("#businessTypes").selectpicker('refresh');
$.table.search('operlog-form', 'bootstrap-table');
$.table.search('operlog-form', 'bootstrap-table', 1);
}
</script>
</body>

View File

@ -9,7 +9,7 @@
<link href="../static/css/font-awesome.min.css" th:href="@{/css/font-awesome.min.css}" rel="stylesheet"/>
<link href="../static/css/style.min.css" th:href="@{/css/style.min.css}" rel="stylesheet"/>
<link href="../static/css/login.min.css" th:href="@{/css/login.min.css}" rel="stylesheet"/>
<link href="../static/ruoyi/css/ry-ui.css" th:href="@{/ruoyi/css/ry-ui.css?v=4.7.8}" rel="stylesheet"/>
<link href="../static/ruoyi/css/ry-ui.css" th:href="@{/ruoyi/css/ry-ui.css?v=4.7.9}" rel="stylesheet"/>
<!-- 360浏览器急速模式 -->
<meta name="renderer" content="webkit">
<!-- 避免IE使用兼容模式 -->
@ -64,17 +64,18 @@
</div>
<div class="signup-footer">
<div class="pull-left">
&copy; 2018-2023 All Rights Reserved. RuoYi <br>
&copy; 2018-2024 All Rights Reserved. RuoYi <br>
</div>
</div>
</div>
<script th:inline="javascript"> var ctx = [[@{/}]]; var captchaType = [[${captchaType}]]; </script>
<script th:inline="javascript"> var ctx = [[@{/}]]; var captchaType = [[${captchaType}]]; var captchaEnabled = [[${captchaEnabled}]];</script>
<!-- 全局js -->
<script src="../static/js/jquery.min.js" th:src="@{/js/jquery.min.js}"></script>
<script src="../static/ajax/libs/validate/jquery.validate.min.js" th:src="@{/ajax/libs/validate/jquery.validate.min.js}"></script>
<script src="../static/ajax/libs/validate/jquery.validate.extend.js" th:src="@{/ajax/libs/validate/jquery.validate.extend.js}"></script>
<script src="../static/ajax/libs/layer/layer.min.js" th:src="@{/ajax/libs/layer/layer.min.js}"></script>
<script src="../static/ajax/libs/blockUI/jquery.blockUI.js" th:src="@{/ajax/libs/blockUI/jquery.blockUI.js}"></script>
<script src="../static/ruoyi/js/ry-ui.js" th:src="@{/ruoyi/js/ry-ui.js?v=4.7.8}"></script>
<script src="../static/ruoyi/js/ry-ui.js" th:src="@{/ruoyi/js/ry-ui.js?v=4.7.9}"></script>
<script src="../static/ruoyi/register.js" th:src="@{/ruoyi/register.js}"></script>
</body>
</html>

View File

@ -140,7 +140,7 @@
</ul>
</body>
<script th:src="@{/js/jquery.min.js}"></script>
<script th:src="@{/ruoyi/js/common.js?v=4.7.8}"></script>
<script th:src="@{/ruoyi/js/common.js?v=4.7.9}"></script>
<script type="text/javascript">
//皮肤样式列表
var skins = ["skin-blue", "skin-green", "skin-purple", "skin-red", "skin-yellow"];

View File

@ -59,6 +59,7 @@
$(function() {
var options = {
url: prefix + "/list",
viewUrl: prefix + "/view/{id}",
createUrl: prefix + "/add",
updateUrl: prefix + "/edit/{id}",
removeUrl: prefix + "/remove",
@ -71,9 +72,12 @@
title : '序号'
},
{
field : 'noticeTitle',
title : '公告标题'
},
field: 'noticeTitle',
title: '公告标题',
formatter: function (value, row, index) {
return '<a href="javascript:void(0)" onclick="$.operate.view(\'' + row.noticeId + '\')">' + value + '</a>';
}
},
{
field: 'noticeType',
title: '公告类型',

View File

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
<head>
<th:block th:include="include :: header('公告详细')" />
</head>
<body>
<div class="main-content">
<div class="row">
<div class="col-sm-12">
<div class="mail-box-header">
<div class="mail-tools tooltip-demo m-t-md">
<h3 style="text-align: center">[[${notice.noticeTitle}]]</h3>
<h5>
<span class="pull-right font-noraml">发送时间:[[${#dates.format(notice.createTime, 'yyyy-MM-dd HH:mm:ss')}]] </span>
<span class="font-noraml">发件人: </span>[[${notice.createBy}]]
</h5>
</div>
</div>
<div class="mail-box">
<div class="mail-body" th:utext="${notice.noticeContent}"></div>
</div>
</div>
</div>
</div>
<th:block th:include="include :: footer" />
</body>
</html>

View File

@ -168,7 +168,8 @@
},
password:{
minlength: 5,
maxlength: 20
maxlength: 20,
specialSign: true
},
email:{
email:true,

View File

@ -260,7 +260,8 @@
newPassword: {
required: true,
minlength: 6,
maxlength: 20
maxlength: 20,
specialSign: true
},
confirmPassword: {
required: true,

View File

@ -65,7 +65,8 @@
newPassword: {
required: true,
minlength: 5,
maxlength: 20
maxlength: 20,
specialSign: true
},
confirmPassword: {
required: true,

View File

@ -31,9 +31,10 @@
$("#form-user-resetPwd").validate({
rules:{
password:{
required:true,
required: true,
minlength: 5,
maxlength: 20
maxlength: 20,
specialSign: true
},
},
focusCleanup: true

View File

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
<head>
<th:block th:include="include :: header('用户详细')" />
<th:block th:include="include :: header('用户详细')" />
</head>
<body>
<div class="main-content">

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId>
<version>4.7.8</version>
<version>4.7.9</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -33,6 +33,7 @@
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<classifier>jakarta</classifier>
</dependency>
<!-- Shiro使用EhCache缓存框架 -->
@ -91,8 +92,8 @@
<!-- servlet包 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
</dependency>
</dependencies>

View File

@ -0,0 +1,19 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 匿名访问不鉴权注解
*
* @author ruoyi
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Anonymous
{
}

View File

@ -88,6 +88,11 @@ public @interface Excel
*/
public String[] combo() default {};
/**
* 是否从字典读数据到combo,默认不读取,如读取需要设置dictType注解.
*/
public boolean comboReadDict() default false;
/**
* 是否需要纵向合并单元格,应对需求:含有list集合单元格)
*/
@ -171,7 +176,7 @@ public @interface Excel
public enum ColumnType
{
NUMERIC(0), STRING(1), IMAGE(2);
NUMERIC(0), STRING(1), IMAGE(2), TEXT(3);
private final int value;
ColumnType(int value)

View File

@ -0,0 +1,24 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.ruoyi.common.config.serializer.SensitiveJsonSerializer;
import com.ruoyi.common.enums.DesensitizedType;
/**
* 数据脱敏注解
*
* @author ruoyi
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveJsonSerializer.class)
public @interface Sensitive
{
DesensitizedType desensitizedType();
}

View File

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

View File

@ -0,0 +1,64 @@
package com.ruoyi.common.config.serializer;
import java.io.IOException;
import java.util.Objects;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.ruoyi.common.annotation.Sensitive;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.enums.DesensitizedType;
import com.ruoyi.common.utils.ShiroUtils;
/**
* 数据脱敏序列化过滤
*
* @author ruoyi
*/
public class SensitiveJsonSerializer extends JsonSerializer<String> implements ContextualSerializer
{
private DesensitizedType desensitizedType;
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException
{
if (desensitization())
{
gen.writeString(desensitizedType.desensitizer().apply(value));
}
else
{
gen.writeString(value);
}
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
throws JsonMappingException
{
Sensitive annotation = property.getAnnotation(Sensitive.class);
if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass()))
{
this.desensitizedType = annotation.desensitizedType();
return this;
}
return prov.findValueSerializer(property.getType(), property);
}
/**
* 是否需要脱敏处理
*/
private boolean desensitization()
{
SysUser securityUser = ShiroUtils.getSysUser();
if (securityUser == null)
{
return true;
}
// 管理员不脱敏
return !securityUser.isAdmin();
}
}

View File

@ -1,5 +1,7 @@
package com.ruoyi.common.constant;
import java.util.Locale;
/**
* 通用常量信息
*
@ -17,6 +19,11 @@ public class Constants
*/
public static final String GBK = "GBK";
/**
* 系统语言
*/
public static final Locale DEFAULT_LOCALE = Locale.SIMPLIFIED_CHINESE;
/**
* http请求
*/
@ -105,11 +112,11 @@ public class Constants
/**
* 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)
*/
public static final String[] JOB_WHITELIST_STR = { "com.ruoyi" };
public static final String[] JOB_WHITELIST_STR = { "com.ruoyi.quartz.task" };
/**
* 定时任务违规的字符
*/
public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
"org.springframework", "org.apache", "com.ruoyi.common.utils.file", "com.ruoyi.common.config" };
"org.springframework", "org.apache", "com.ruoyi.common.utils.file", "com.ruoyi.common.config", "com.ruoyi.generator" };
}

View File

@ -70,7 +70,7 @@ public class ShiroConstants
/**
* 登录记录缓存
*/
public static final String LOGINRECORDCACHE = "loginRecordCache";
public static final String LOGIN_RECORD_CACHE = "loginRecordCache";
/**
* 系统活跃用户缓存

View File

@ -21,6 +21,9 @@ public class UserConstants
/** 用户封禁状态 */
public static final String USER_DISABLE = "1";
/** 角色正常状态 */
public static final String ROLE_NORMAL = "0";
/** 角色封禁状态 */
public static final String ROLE_DISABLE = "1";

View File

@ -3,9 +3,9 @@ package com.ruoyi.common.core.controller;
import java.beans.PropertyEditorSupport;
import java.util.Date;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.WebDataBinder;

View File

@ -25,14 +25,14 @@ public class BaseEntity implements Serializable
private String createBy;
/** 创建时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
/** 更新者 */
private String updateBy;
/** 更新时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updateTime;
/** 备注 */

View File

@ -1,9 +1,9 @@
package com.ruoyi.common.core.domain.entity;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.fasterxml.jackson.annotation.JsonIgnore;

View File

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

View File

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

View File

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

View File

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

View File

@ -2,7 +2,7 @@ package com.ruoyi.common.core.domain.entity;
import java.util.Date;
import java.util.List;
import javax.validation.constraints.*;
import jakarta.validation.constraints.*;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.fasterxml.jackson.annotation.JsonIgnore;
@ -23,7 +23,7 @@ public class SysUser extends BaseEntity
private static final long serialVersionUID = 1L;
/** 用户ID */
@Excel(name = "用户序号", cellType = ColumnType.NUMERIC, prompt = "用户编号")
@Excel(name = "用户序号", type = Type.EXPORT, cellType = ColumnType.NUMERIC, prompt = "用户编号")
private Long userId;
/** 部门ID */
@ -52,7 +52,7 @@ public class SysUser extends BaseEntity
private String email;
/** 手机号码 */
@Excel(name = "手机号码")
@Excel(name = "手机号码", cellType = ColumnType.TEXT)
private String phonenumber;
/** 用户性别 */

View File

@ -365,6 +365,10 @@ public class Convert
*/
public static String[] toStrArray(String str)
{
if (StringUtils.isEmpty(str))
{
return new String[] {};
}
return toStrArray(",", str);
}

View File

@ -0,0 +1,59 @@
package com.ruoyi.common.enums;
import java.util.function.Function;
import com.ruoyi.common.utils.DesensitizedUtil;
/**
* 脱敏类型
*
* @author ruoyi
*/
public enum DesensitizedType
{
/**
* 姓名第2位星号替换
*/
USERNAME(s -> s.replaceAll("(\\S)\\S(\\S*)", "$1*$2")),
/**
* 密码,全部字符都用*代替
*/
PASSWORD(DesensitizedUtil::password),
/**
* 身份证中间10位星号替换
*/
ID_CARD(s -> s.replaceAll("(\\d{4})\\d{10}(\\d{3}[Xx]|\\d{4})", "$1** **** ****$2")),
/**
* 手机号中间4位星号替换
*/
PHONE(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")),
/**
* 电子邮箱,仅显示第一个字母和@后面的地址显示,其他星号替换
*/
EMAIL(s -> s.replaceAll("(^.)[^@]*(@.*$)", "$1****$2")),
/**
* 银行卡号保留最后4位其他星号替换
*/
BANK_CARD(s -> s.replaceAll("\\d{15}(\\d{3})", "**** **** **** **** $1")),
/**
* 车牌号码,包含普通车辆、新能源车辆
*/
CAR_LICENSE(DesensitizedUtil::carLicense);
private final Function<String, String> desensitizer;
DesensitizedType(Function<String, String> desensitizer)
{
this.desensitizer = desensitizer;
}
public Function<String, String> desensitizer()
{
return desensitizer;
}
}

View File

@ -3,9 +3,9 @@ package com.ruoyi.common.utils;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/**
* Cookie工具类

View File

@ -0,0 +1,49 @@
package com.ruoyi.common.utils;
/**
* 脱敏工具类
*
* @author ruoyi
*/
public class DesensitizedUtil
{
/**
* 密码的全部字符都用*代替,比如:******
*
* @param password 密码
* @return 脱敏后的密码
*/
public static String password(String password)
{
if (StringUtils.isBlank(password))
{
return StringUtils.EMPTY;
}
return StringUtils.repeat('*', password.length());
}
/**
* 车牌中间用*代替,如果是错误的车牌,不处理
*
* @param carLicense 完整的车牌号
* @return 脱敏后的车牌
*/
public static String carLicense(String carLicense)
{
if (StringUtils.isBlank(carLicense))
{
return StringUtils.EMPTY;
}
// 普通车牌
if (carLicense.length() == 7)
{
carLicense = StringUtils.hide(carLicense, 3, 6);
}
else if (carLicense.length() == 8)
{
// 新能源车牌
carLicense = StringUtils.hide(carLicense, 3, 7);
}
return carLicense;
}
}

View File

@ -54,6 +54,10 @@ public class DictUtils
*/
public static String getDictLabel(String dictType, String dictValue)
{
if (StringUtils.isEmpty(dictValue))
{
return StringUtils.EMPTY;
}
return getDictLabel(dictType, dictValue, SEPARATOR);
}
@ -66,6 +70,10 @@ public class DictUtils
*/
public static String getDictValue(String dictType, String dictLabel)
{
if (StringUtils.isEmpty(dictLabel))
{
return StringUtils.EMPTY;
}
return getDictValue(dictType, dictLabel, SEPARATOR);
}
@ -81,8 +89,11 @@ public class DictUtils
{
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StringUtils.containsAny(dictValue, separator) && StringUtils.isNotEmpty(datas))
if (StringUtils.isNull(datas))
{
return StringUtils.EMPTY;
}
if (StringUtils.containsAny(dictValue, separator))
{
for (SysDictData dict : datas)
{
@ -121,8 +132,11 @@ public class DictUtils
{
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StringUtils.containsAny(dictLabel, separator) && StringUtils.isNotEmpty(datas))
if (StringUtils.isNull(datas))
{
return StringUtils.EMPTY;
}
if (StringUtils.containsAny(dictLabel, separator))
{
for (SysDictData dict : datas)
{
@ -149,6 +163,48 @@ public class DictUtils
return StringUtils.stripEnd(propertyString.toString(), separator);
}
/**
* 根据字典类型获取字典所有值
*
* @param dictType 字典类型
* @return 字典值
*/
public static String getDictValues(String dictType)
{
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StringUtils.isNull(datas))
{
return StringUtils.EMPTY;
}
for (SysDictData dict : datas)
{
propertyString.append(dict.getDictValue()).append(SEPARATOR);
}
return StringUtils.stripEnd(propertyString.toString(), SEPARATOR);
}
/**
* 根据字典类型获取字典所有标签
*
* @param dictType 字典类型
* @return 字典值
*/
public static String getDictLabels(String dictType)
{
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StringUtils.isNull(datas))
{
return StringUtils.EMPTY;
}
for (SysDictData dict : datas)
{
propertyString.append(dict.getDictLabel()).append(SEPARATOR);
}
return StringUtils.stripEnd(propertyString.toString(), SEPARATOR);
}
/**
* 删除指定字典缓存
*

View File

@ -36,4 +36,32 @@ public class ExceptionUtil
}
return StringUtils.defaultString(msg);
}
/**
* 检测异常e被触发的原因是不是因为异常cause。
*
* @param e 捕获的异常。
* @param cause 异常触发原因。
* @return 如果异常e是由cause类异常触发则返回true否则返回false。
*/
public static boolean isCausedBy(final Throwable e, final Class<? extends Throwable> cause)
{
if (cause.isAssignableFrom(e.getClass()))
{
return true;
}
else
{
Throwable t = e.getCause();
while (t != null && t != e)
{
if (cause.isAssignableFrom(t.getClass()))
{
return true;
}
t = t.getCause();
}
return false;
}
}
}

View File

@ -2,7 +2,7 @@ package com.ruoyi.common.utils;
import java.net.InetAddress;
import java.net.UnknownHostException;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
/**
* 获取IP方法

View File

@ -3,7 +3,7 @@ package com.ruoyi.common.utils;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.shiro.SecurityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -72,10 +72,10 @@ public class LogUtils
{
String username = getUsername();
Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
String message = (String) request.getAttribute("javax.servlet.error.message");
String uri = (String) request.getAttribute("javax.servlet.error.request_uri");
Throwable t = (Throwable) request.getAttribute("javax.servlet.error.exception");
Integer statusCode = (Integer) request.getAttribute("jakarta.servlet.error.status_code");
String message = (String) request.getAttribute("jakarta.servlet.error.message");
String uri = (String) request.getAttribute("jakarta.servlet.error.request_uri");
Throwable t = (Throwable) request.getAttribute("jakarta.servlet.error.exception");
if (statusCode == null)
{

View File

@ -4,7 +4,7 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
/**
* Map通用处理方法

View File

@ -4,9 +4,9 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

View File

@ -23,6 +23,9 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
/** 下划线 */
private static final char SEPARATOR = '_';
/** 星号 */
private static final char ASTERISK = '*';
/**
* 获取参数不为空值
*
@ -163,6 +166,49 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
return (str == null ? "" : str.trim());
}
/**
* 替换指定字符串的指定区间内字符为"*"
*
* @param str 字符串
* @param startInclude 开始位置(包含)
* @param endExclude 结束位置(不包含)
* @return 替换后的字符串
*/
public static String hide(CharSequence str, int startInclude, int endExclude)
{
if (isEmpty(str))
{
return NULLSTR;
}
final int strLength = str.length();
if (startInclude > strLength)
{
return NULLSTR;
}
if (endExclude > strLength)
{
endExclude = strLength;
}
if (startInclude > endExclude)
{
// 如果起始位置大于结束位置,不替换
return NULLSTR;
}
final char[] chars = new char[strLength];
for (int i = 0; i < strLength; i++)
{
if (i >= startInclude && i < endExclude)
{
chars[i] = ASTERISK;
}
else
{
chars[i] = str.charAt(i);
}
}
return new String(chars);
}
/**
* 截取字符串
*

View File

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

View File

@ -25,7 +25,7 @@ public class FileUploadUtils
/**
* 默认大小 50M
*/
public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024;
public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024L;
/**
* 默认的文件名最大长度 100

View File

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

View File

@ -74,7 +74,7 @@ public class HttpUtils
URLConnection connection = realUrl.openConnection();
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
connection.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
connection.connect();
in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
String line;
@ -136,7 +136,7 @@ public class HttpUtils
URLConnection conn = realUrl.openConnection();
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
conn.setRequestProperty("Accept-Charset", "utf-8");
conn.setRequestProperty("contentType", "utf-8");
conn.setDoOutput(true);
@ -202,7 +202,7 @@ public class HttpUtils
HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
conn.setRequestProperty("Accept-Charset", "utf-8");
conn.setRequestProperty("contentType", "utf-8");
conn.setDoOutput(true);

View File

@ -23,7 +23,7 @@ import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.RegExUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
@ -39,6 +39,7 @@ 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.DataFormat;
import org.apache.poi.ss.usermodel.DataValidation;
import org.apache.poi.ss.usermodel.DataValidationConstraint;
import org.apache.poi.ss.usermodel.DataValidationHelper;
@ -193,6 +194,11 @@ public class ExcelUtil<T>
*/
public Class<T> clazz;
/**
* 需要显示列属性
*/
public String[] includeFields;
/**
* 需要排除列属性
*/
@ -203,11 +209,20 @@ public class ExcelUtil<T>
this.clazz = clazz;
}
/**
* 仅在Excel中显示列属性
*
* @param fields 列属性名 示例[单个"name"/多个"id","name"]
*/
public void showColumn(String... fields)
{
this.includeFields = fields;
}
/**
* 隐藏Excel中列属性
*
* @param fields 列属性名 示例[单个"name"/多个"id","name"]
* @throws Exception
*/
public void hideColumn(String... fields)
{
@ -396,7 +411,7 @@ public class ExcelUtil<T>
Object val = this.getCellValue(row, entry.getKey());
// 如果不存在实例则新建.
entity = (entity == null ? clazz.newInstance() : entity);
entity = (entity == null ? clazz.getDeclaredConstructor().newInstance() : entity);
// 从map中得到对应列的field.
Field field = (Field) entry.getValue()[0];
Excel attr = (Excel) entry.getValue()[1];
@ -470,7 +485,12 @@ public class ExcelUtil<T>
}
else if (StringUtils.isNotEmpty(attr.dictType()))
{
val = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator());
if (!sysDictMap.containsKey(attr.dictType() + val))
{
String dictValue = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator());
sysDictMap.put(attr.dictType() + val, dictValue);
}
val = sysDictMap.get(attr.dictType() + val);
}
else if (!attr.handler().equals(ExcelHandlerAdapter.class))
{
@ -783,6 +803,8 @@ public class ExcelUtil<T>
titleFont.setFontHeightInPoints((short) 16);
titleFont.setBold(true);
style.setFont(titleFont);
DataFormat dataFormat = wb.createDataFormat();
style.setDataFormat(dataFormat.getFormat("@"));
styles.put("title", style);
style = wb.createCellStyle();
@ -845,6 +867,9 @@ public class ExcelUtil<T>
headerFont.setBold(true);
headerFont.setColor(excel.headerColor().index);
style.setFont(headerFont);
// 设置表格头单元格文本形式
DataFormat dataFormat = wb.createDataFormat();
style.setDataFormat(dataFormat.getFormat("@"));
headerStyles.put(key, style);
}
}
@ -862,34 +887,66 @@ public class ExcelUtil<T>
Map<String, CellStyle> styles = new HashMap<String, CellStyle>();
for (Object[] os : fields)
{
Field field = (Field) os[0];
Excel excel = (Excel) os[1];
String key = StringUtils.format("data_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor());
if (!styles.containsKey(key))
if (Collection.class.isAssignableFrom(field.getType()))
{
CellStyle style = wb.createCellStyle();
style.setAlignment(excel.align());
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setBorderRight(BorderStyle.THIN);
style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setBorderLeft(BorderStyle.THIN);
style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setBorderTop(BorderStyle.THIN);
style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setBorderBottom(BorderStyle.THIN);
style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
style.setFillForegroundColor(excel.backgroundColor().getIndex());
Font dataFont = wb.createFont();
dataFont.setFontName("Arial");
dataFont.setFontHeightInPoints((short) 10);
dataFont.setColor(excel.color().index);
style.setFont(dataFont);
styles.put(key, style);
ParameterizedType pt = (ParameterizedType) field.getGenericType();
Class<?> subClass = (Class<?>) pt.getActualTypeArguments()[0];
List<Field> subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class);
for (Field subField : subFields)
{
Excel subExcel = subField.getAnnotation(Excel.class);
annotationDataStyles(styles, subField, subExcel);
}
}
else
{
annotationDataStyles(styles, field, excel);
}
}
return styles;
}
/**
* 根据Excel注解创建表格列样式
*
* @param styles 自定义样式列表
* @param field 属性列信息
* @param excel 注解信息
*/
public void annotationDataStyles(Map<String, CellStyle> styles, Field field, Excel excel)
{
String key = StringUtils.format("data_{}_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor(), excel.cellType());
if (!styles.containsKey(key))
{
CellStyle style = wb.createCellStyle();
style.setAlignment(excel.align());
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setBorderRight(BorderStyle.THIN);
style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setBorderLeft(BorderStyle.THIN);
style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setBorderTop(BorderStyle.THIN);
style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setBorderBottom(BorderStyle.THIN);
style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
style.setFillForegroundColor(excel.backgroundColor().getIndex());
Font dataFont = wb.createFont();
dataFont.setFontName("Arial");
dataFont.setFontHeightInPoints((short) 10);
dataFont.setColor(excel.color().index);
style.setFont(dataFont);
if (ColumnType.TEXT == excel.cellType())
{
DataFormat dataFormat = wb.createDataFormat();
style.setDataFormat(dataFormat.getFormat("@"));
}
styles.put(key, style);
}
}
/**
* 创建单元格
*/
@ -904,7 +961,7 @@ public class ExcelUtil<T>
if (isSubList())
{
// 填充默认样式,防止合并单元格样式失效
sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor())));
sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType())));
if (attr.needMerge())
{
sheet.addMergedRegion(new CellRangeAddress(rownum - 1, rownum, column, column));
@ -922,7 +979,7 @@ public class ExcelUtil<T>
*/
public void setCellVo(Object value, Excel attr, Cell cell)
{
if (ColumnType.STRING == attr.cellType())
if (ColumnType.STRING == attr.cellType() || ColumnType.TEXT == attr.cellType())
{
String cellValue = Convert.toStr(value);
// 对于任何以表达式触发字符 =-+@开头的单元格直接使用tab字符作为前缀防止CSV注入。
@ -999,17 +1056,28 @@ public class ExcelUtil<T>
// 设置列宽
sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256));
}
if (StringUtils.isNotEmpty(attr.prompt()) || attr.combo().length > 0)
if (StringUtils.isNotEmpty(attr.prompt()) || attr.combo().length > 0 || attr.comboReadDict())
{
if (attr.combo().length > 15 || StringUtils.join(attr.combo()).length() > 255)
String[] comboArray = attr.combo();
if (attr.comboReadDict())
{
if (!sysDictMap.containsKey("combo_" + attr.dictType()))
{
String labels = DictUtils.getDictLabels(attr.dictType());
sysDictMap.put("combo_" + attr.dictType(), labels);
}
String val = sysDictMap.get("combo_" + attr.dictType());
comboArray = StringUtils.split(val, DictUtils.SEPARATOR);
}
if (comboArray.length > 15 || StringUtils.join(comboArray).length() > 255)
{
// 如果下拉数大于15或字符串长度大于255则使用一个新sheet存储避免生成的模板下拉值获取不到
setXSSFValidationWithHidden(sheet, attr.combo(), attr.prompt(), 1, 100, column, column);
setXSSFValidationWithHidden(sheet, comboArray, attr.prompt(), 1, 100, column, column);
}
else
{
// 提示信息或只能选择不能输入的列内容.
setPromptOrValidation(sheet, attr.combo(), attr.prompt(), 1, 100, column, column);
setPromptOrValidation(sheet, comboArray, attr.prompt(), 1, 100, column, column);
}
}
}
@ -1034,7 +1102,7 @@ public class ExcelUtil<T>
CellRangeAddress cellAddress = new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column);
sheet.addMergedRegion(cellAddress);
}
cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor())));
cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType())));
// 用于读取对象中的属性
Object value = getTargetValue(vo, field, attr);
@ -1282,7 +1350,7 @@ public class ExcelUtil<T>
{
try
{
Object instance = excel.handler().newInstance();
Object instance = excel.handler().getDeclaredConstructor().newInstance();
Method formatMethod = excel.handler().getMethod("format", new Class[] { Object.class, String[].class, Cell.class, Workbook.class });
value = formatMethod.invoke(instance, value, excel.args(), cell, this.wb);
}
@ -1434,46 +1502,86 @@ public class ExcelUtil<T>
List<Field> tempFields = new ArrayList<>();
tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
for (Field field : tempFields)
if (StringUtils.isNotEmpty(includeFields))
{
if (!ArrayUtils.contains(this.excludeFields, field.getName()))
for (Field field : tempFields)
{
// 单注解
if (field.isAnnotationPresent(Excel.class))
if (ArrayUtils.contains(this.includeFields, field.getName()) || field.isAnnotationPresent(Excels.class))
{
Excel attr = field.getAnnotation(Excel.class);
if (attr != null && (attr.type() == Type.ALL || attr.type() == type))
addField(fields, field);
}
}
}
else if (StringUtils.isNotEmpty(excludeFields))
{
for (Field field : tempFields)
{
if (!ArrayUtils.contains(this.excludeFields, field.getName()))
{
addField(fields, field);
}
}
}
else
{
for (Field field : tempFields)
{
addField(fields, field);
}
}
return fields;
}
/**
* 添加字段信息
*/
public void addField(List<Object[]> fields, Field field)
{
// 单注解
if (field.isAnnotationPresent(Excel.class))
{
Excel attr = field.getAnnotation(Excel.class);
if (attr != null && (attr.type() == Type.ALL || attr.type() == type))
{
field.setAccessible(true);
fields.add(new Object[] { field, attr });
}
if (Collection.class.isAssignableFrom(field.getType()))
{
subMethod = getSubMethod(field.getName(), clazz);
ParameterizedType pt = (ParameterizedType) field.getGenericType();
Class<?> subClass = (Class<?>) pt.getActualTypeArguments()[0];
this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class);
}
}
// 多注解
if (field.isAnnotationPresent(Excels.class))
{
Excels attrs = field.getAnnotation(Excels.class);
Excel[] excels = attrs.value();
for (Excel attr : excels)
{
if (StringUtils.isNotEmpty(includeFields))
{
if (ArrayUtils.contains(this.includeFields, field.getName() + "." + attr.targetAttr())
&& (attr != null && (attr.type() == Type.ALL || attr.type() == type)))
{
field.setAccessible(true);
fields.add(new Object[] { field, attr });
}
if (Collection.class.isAssignableFrom(field.getType()))
{
subMethod = getSubMethod(field.getName(), clazz);
ParameterizedType pt = (ParameterizedType) field.getGenericType();
Class<?> subClass = (Class<?>) pt.getActualTypeArguments()[0];
this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class);
}
}
// 多注解
if (field.isAnnotationPresent(Excels.class))
else
{
Excels attrs = field.getAnnotation(Excels.class);
Excel[] excels = attrs.value();
for (Excel attr : excels)
if (!ArrayUtils.contains(this.excludeFields, field.getName() + "." + attr.targetAttr())
&& (attr != null && (attr.type() == Type.ALL || attr.type() == type)))
{
if (!ArrayUtils.contains(this.excludeFields, field.getName() + "." + attr.targetAttr())
&& (attr != null && (attr.type() == Type.ALL || attr.type() == type)))
{
field.setAccessible(true);
fields.add(new Object[] { field, attr });
}
field.setAccessible(true);
fields.add(new Object[] { field, attr });
}
}
}
}
return fields;
}
/**

View File

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

View File

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

View File

@ -3,14 +3,14 @@ package com.ruoyi.common.xss;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import com.ruoyi.common.utils.StringUtils;
/**
@ -31,10 +31,10 @@ public class XssFilter implements Filter
String tempExcludes = filterConfig.getInitParameter("excludes");
if (StringUtils.isNotEmpty(tempExcludes))
{
String[] url = tempExcludes.split(",");
for (int i = 0; url != null && i < url.length; i++)
String[] urls = tempExcludes.split(",");
for (String url : urls)
{
excludes.add(url[i]);
excludes.add(url);
}
}
}

View File

@ -1,7 +1,7 @@
package com.ruoyi.common.xss;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import com.ruoyi.common.utils.html.EscapeUtil;
/**

View File

@ -1,8 +1,8 @@
package com.ruoyi.common.xss;
import com.ruoyi.common.utils.StringUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -27,8 +27,13 @@ public class XssValidator implements ConstraintValidator<Xss, String>
public static boolean containsHtml(String value)
{
StringBuilder sHtml = new StringBuilder();
Pattern pattern = Pattern.compile(HTML_PATTERN);
Matcher matcher = pattern.matcher(value);
return matcher.matches();
while (matcher.find())
{
sHtml.append(matcher.group());
}
return pattern.matcher(sHtml).matches();
}
}

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId>
<version>4.7.8</version>
<version>4.7.9</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -32,7 +32,7 @@
<!-- 阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<artifactId>druid-spring-boot-3-starter</artifactId>
</dependency>
<!-- 验证码 -->
@ -41,7 +41,7 @@
<artifactId>kaptcha</artifactId>
<exclusions>
<exclusion>
<artifactId>javax.servlet-api</artifactId>
<artifactId>servlet-api</artifactId>
<groupId>javax.servlet</groupId>
</exclusion>
</exclusions>
@ -51,6 +51,20 @@
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<classifier>jakarta</classifier>
</dependency>
<!-- Shiro核心框架 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<classifier>jakarta</classifier>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<classifier>jakarta</classifier>
</dependency>
<!-- thymeleaf模板引擎和shiro框架的整合 -->

View File

@ -7,6 +7,7 @@ import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import com.ruoyi.common.annotation.DataScope;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.context.PermissionContextHolder;
import com.ruoyi.common.core.domain.BaseEntity;
import com.ruoyi.common.core.domain.entity.SysRole;
@ -71,8 +72,7 @@ public class DataScopeAspect
if (!currentUser.isAdmin())
{
String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext());
dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
controllerDataScope.userAlias(), permission);
dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), controllerDataScope.userAlias(), permission);
}
}
}
@ -90,16 +90,22 @@ public class DataScopeAspect
{
StringBuilder sqlString = new StringBuilder();
List<String> conditions = new ArrayList<String>();
List<String> scopeCustomIds = new ArrayList<String>();
user.getRoles().forEach(role -> {
if (DATA_SCOPE_CUSTOM.equals(role.getDataScope()) && StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL) && StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
{
scopeCustomIds.add(Convert.toStr(role.getRoleId()));
}
});
for (SysRole role : user.getRoles())
{
String dataScope = role.getDataScope();
if (!DATA_SCOPE_CUSTOM.equals(dataScope) && conditions.contains(dataScope))
if (conditions.contains(dataScope) || StringUtils.equals(role.getStatus(), UserConstants.ROLE_DISABLE))
{
continue;
}
if (StringUtils.isNotEmpty(permission) && StringUtils.isNotEmpty(role.getPermissions())
&& !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
if (!StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
{
continue;
}
@ -111,9 +117,15 @@ public class DataScopeAspect
}
else if (DATA_SCOPE_CUSTOM.equals(dataScope))
{
sqlString.append(StringUtils.format(
" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias,
role.getRoleId()));
if (scopeCustomIds.size() > 1)
{
// 多个自定数据权限使用in查询避免多次拼接。
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id in ({}) ) ", deptAlias, String.join(",", scopeCustomIds)));
}
else
{
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, role.getRoleId()));
}
}
else if (DATA_SCOPE_DEPT.equals(dataScope))
{
@ -121,9 +133,7 @@ public class DataScopeAspect
}
else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))
{
sqlString.append(StringUtils.format(
" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
deptAlias, user.getDeptId(), user.getDeptId()));
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", deptAlias, user.getDeptId(), user.getDeptId()));
}
else if (DATA_SCOPE_SELF.equals(dataScope))
{
@ -140,7 +150,7 @@ public class DataScopeAspect
conditions.add(dataScope);
}
// 多角色情况下,所有角色都不包含传递过来的权限字符这个时候sqlString也会为空所以要限制一下,不查询任何数据
// 角色都不包含传递过来的权限字符这个时候sqlString也会为空所以要限制一下,不查询任何数据
if (StringUtils.isEmpty(conditions))
{
sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));

View File

@ -2,8 +2,8 @@ package com.ruoyi.framework.aspectj;
import java.util.Collection;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;

View File

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

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