diff --git a/README.md b/README.md
index 7004f0e50..cbf2da393 100644
--- a/README.md
+++ b/README.md
@@ -4,26 +4,27 @@
[](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/blob/master/LICENSE)
[](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
-[](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus)
+[](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus)
[]()
[]()
[]()
-[]()
-> RuoYi-Vue-Plus 是基于 RuoYi-Vue 针对 `分布式集群` 场景升级(不兼容原框架)
+> RuoYi-Vue-Plus 是重写 RuoYi-Vue 针对 `分布式集群` 场景全方位升级(不兼容原框架)
> 系统演示: [传送门](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/系统演示?sort_id=4836388)
| 功能介绍 | 使用技术 | 文档地址 | 特性注意事项 |
|---|---|---|---|
| 当前框架 | RuoYi-Vue-Plus | [RuoYi-Vue-Plus文档](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages) | 重写RuoYi-Vue全方位升级(不兼容原框架) |
-| satoken分支 | RuoYi-Vue-Plus-satoken | [satoken分支地址](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/satoken/) | 使用satoken重构权限鉴权(公测 可尝试上生产) |
+| satoken分支 | RuoYi-Vue-Plus-satoken | [satoken分支地址](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/satoken/) | 高可读性 扩展性(推荐使用) |
| 单体分支 | RuoYi-Vue-Plus-fast | [fast分支地址](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/fast/) | 单体应用结构 |
+| Vue3分支 | RuoYi-Vue-Plus-UI | [UI地址](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus-UI) | 由于组件还未完善 仅供学习 |
| 原框架 | RuoYi-Vue | [RuoYi-Vue官网](http://ruoyi.vip/) | 定期同步需要的功能 |
| 前端开发框架 | Vue、Element UI | [Element UI官网](https://element.eleme.cn/#/zh-CN) | |
| 后端开发框架 | SpringBoot | [SpringBoot官网](https://spring.io/projects/spring-boot/#learn) | |
-| 容器框架 | Undertow | [Undertow官网](https://undertow.io/) | 基于 Netty 的高性能容器 |
+| 容器框架 | Undertow | [Undertow官网](https://undertow.io/) | 基于 XNIO 的高性能容器 |
| 权限认证框架 | Spring Security、Jwt | [SpringSecurity官网](https://spring.io/projects/spring-security#learn) | 支持多终端认证系统 |
+| 权限认证框架 | Sa-Token、Jwt | [Sa-Token官网](https://sa-token.dev33.cn/) | 强解耦、强扩展 |
| 关系数据库 | MySQL | [MySQL官网](https://dev.mysql.com/) | 适配 8.X 最低 5.7 |
| 缓存数据库 | Redis | [Redis官网](https://redis.io/) | 适配 6.X 最低 4.X |
| 数据库框架 | Mybatis-Plus | [Mybatis-Plus文档](https://baomidou.com/guide/) | 快速 CRUD 增加开发效率 |
diff --git a/pom.xml b/pom.xml
index 79a18f530..7361fac28 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,15 +6,15 @@
com.ruoyi
ruoyi-vue-plus
- 3.4.0
+ 3.5.0
RuoYi-Vue-Plus
https://gitee.com/JavaLionLi/RuoYi-Vue-Plus
RuoYi-Vue-Plus后台管理系统
- 3.4.0
- 2.5.7
+ 3.5.0
+ 2.5.8
UTF-8
UTF-8
1.8
@@ -24,27 +24,27 @@
1.5.22
4.1.2
2.2.11
+ 3.3.0
2.3
- 0.9.1
3.4.3.4
3.9.1
- 5.7.16
+ 5.7.18
4.9.2
- 2.5.4
- 3.16.4
+ 2.5.5
+ 3.16.7
2.2.1
- 3.4.1
- 1.3.4
+ 3.5.0
+ 1.3.6
2.3.0
3.0.1
- 7.8.0
+ 7.9.0
3.13.1
5.6.58
- 8.3.3
+ 8.3.4
localhost
@@ -115,6 +115,12 @@
+
+ cglib
+ cglib
+ ${cglib.version}
+
+
org.apache.velocity
@@ -122,13 +128,6 @@
${velocity.version}
-
-
- io.jsonwebtoken
- jjwt
- ${jwt.version}
-
-
com.sun.xml.bind
@@ -162,7 +161,31 @@
cn.hutool
- hutool-all
+ hutool-core
+ ${hutool.version}
+
+
+
+ cn.hutool
+ hutool-http
+ ${hutool.version}
+
+
+
+ cn.hutool
+ hutool-captcha
+ ${hutool.version}
+
+
+
+ cn.hutool
+ hutool-extra
+ ${hutool.version}
+
+
+
+ cn.hutool
+ hutool-jwt
${hutool.version}
@@ -204,29 +227,13 @@
com.yomahub
- tlog-spring-boot-configuration
+ tlog-web-spring-boot-starter
${tlog.version}
com.yomahub
- tlog-webroot
- ${tlog.version}
-
-
- javassist
- org.javassist
-
-
- guava
- com.google.guava
-
-
-
-
-
- com.yomahub
- tlog-xxl-job
+ tlog-xxljob-spring-boot-starter
${tlog.version}
diff --git a/ruoyi-extend/pom.xml b/ruoyi-extend/pom.xml
index ccd525cec..f0ae61d49 100644
--- a/ruoyi-extend/pom.xml
+++ b/ruoyi-extend/pom.xml
@@ -5,7 +5,7 @@
ruoyi-vue-plus
com.ruoyi
- 3.4.0
+ 3.5.0
4.0.0
ruoyi-extend
diff --git a/ruoyi-extend/ruoyi-monitor-admin/pom.xml b/ruoyi-extend/ruoyi-monitor-admin/pom.xml
index 177c9af3e..4b0d591c9 100644
--- a/ruoyi-extend/ruoyi-monitor-admin/pom.xml
+++ b/ruoyi-extend/ruoyi-monitor-admin/pom.xml
@@ -5,7 +5,7 @@
ruoyi-extend
com.ruoyi
- 3.4.0
+ 3.5.0
4.0.0
jar
diff --git a/ruoyi-extend/ruoyi-monitor-admin/src/main/java/com/ruoyi/monitor/admin/config/SecurityConfig.java b/ruoyi-extend/ruoyi-monitor-admin/src/main/java/com/ruoyi/monitor/admin/config/SecurityConfig.java
index 7335e2f25..04a0fdef0 100644
--- a/ruoyi-extend/ruoyi-monitor-admin/src/main/java/com/ruoyi/monitor/admin/config/SecurityConfig.java
+++ b/ruoyi-extend/ruoyi-monitor-admin/src/main/java/com/ruoyi/monitor/admin/config/SecurityConfig.java
@@ -2,7 +2,6 @@ package com.ruoyi.monitor.admin.config;
import de.codecentric.boot.admin.server.config.AdminServerProperties;
import org.springframework.context.annotation.Configuration;
-import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@@ -15,7 +14,6 @@ import org.springframework.security.web.authentication.SavedRequestAwareAuthenti
*/
@Configuration
@EnableWebSecurity
-@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, proxyTargetClass = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final String adminContextPath;
@@ -34,8 +32,8 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
//授予对所有静态资产和登录页面的公共访问权限。
.antMatchers(adminContextPath + "/assets/**").permitAll()
.antMatchers(adminContextPath + "/login").permitAll()
- .antMatchers("/actuator").anonymous()
- .antMatchers("/actuator/**").anonymous()
+ .antMatchers("/actuator").permitAll()
+ .antMatchers("/actuator/**").permitAll()
//必须对每个其他请求进行身份验证
.anyRequest().authenticated().and()
//配置登录和注销
diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/pom.xml b/ruoyi-extend/ruoyi-xxl-job-admin/pom.xml
index 29786ee06..640628e35 100644
--- a/ruoyi-extend/ruoyi-xxl-job-admin/pom.xml
+++ b/ruoyi-extend/ruoyi-xxl-job-admin/pom.xml
@@ -4,7 +4,7 @@
ruoyi-extend
com.ruoyi
- 3.4.0
+ 3.5.0
ruoyi-xxl-job-admin
jar
@@ -102,6 +102,7 @@
org.apache.maven.plugins
maven-resources-plugin
+ 2.6
ttf
diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/CookieInterceptor.java b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/CookieInterceptor.java
index 57c1c08d2..e55b890fa 100644
--- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/CookieInterceptor.java
+++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/CookieInterceptor.java
@@ -3,8 +3,8 @@ package com.xxl.job.admin.controller.interceptor;
import com.xxl.job.admin.core.util.FtlUtil;
import com.xxl.job.admin.core.util.I18nUtil;
import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.AsyncHandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
-import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
@@ -17,7 +17,7 @@ import java.util.HashMap;
* @author xuxueli 2015-12-12 18:09:04
*/
@Component
-public class CookieInterceptor extends HandlerInterceptorAdapter {
+public class CookieInterceptor implements AsyncHandlerInterceptor {
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@@ -36,8 +36,8 @@ public class CookieInterceptor extends HandlerInterceptorAdapter {
if (modelAndView != null) {
modelAndView.addObject("I18nUtil", FtlUtil.generateStaticModel(I18nUtil.class.getName()));
}
-
- super.postHandle(request, response, handler, modelAndView);
+
+ AsyncHandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
-
+
}
diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/PermissionInterceptor.java b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/PermissionInterceptor.java
index 5202c2903..19eac7355 100644
--- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/PermissionInterceptor.java
+++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/PermissionInterceptor.java
@@ -6,7 +6,7 @@ import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.admin.service.LoginService;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
-import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+import org.springframework.web.servlet.AsyncHandlerInterceptor;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
@@ -18,16 +18,16 @@ import javax.servlet.http.HttpServletResponse;
* @author xuxueli 2015-12-12 18:09:04
*/
@Component
-public class PermissionInterceptor extends HandlerInterceptorAdapter {
+public class PermissionInterceptor implements AsyncHandlerInterceptor {
@Resource
private LoginService loginService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
-
+
if (!(handler instanceof HandlerMethod)) {
- return super.preHandle(request, response, handler);
+ return AsyncHandlerInterceptor.super.preHandle(request, response, handler);
}
// if need login
@@ -53,7 +53,7 @@ public class PermissionInterceptor extends HandlerInterceptorAdapter {
request.setAttribute(LoginService.LOGIN_IDENTITY_KEY, loginUser);
}
- return super.preHandle(request, response, handler);
+ return AsyncHandlerInterceptor.super.preHandle(request, response, handler);
}
-
+
}
diff --git a/ruoyi-ui/package.json b/ruoyi-ui/package.json
index 2784eccb2..60529054f 100644
--- a/ruoyi-ui/package.json
+++ b/ruoyi-ui/package.json
@@ -1,6 +1,6 @@
{
"name": "ruoyi-vue-plus",
- "version": "3.4.0",
+ "version": "3.5.0",
"description": "RuoYi-Vue-Plus后台管理系统",
"author": "LionLi",
"license": "MIT",
@@ -38,7 +38,7 @@
"dependencies": {
"@riophae/vue-treeselect": "0.4.0",
"axios": "0.24.0",
- "clipboard": "2.0.6",
+ "clipboard": "2.0.8",
"core-js": "3.19.1",
"echarts": "4.9.0",
"element-ui": "2.15.6",
@@ -65,7 +65,9 @@
"@vue/cli-plugin-eslint": "4.4.6",
"@vue/cli-service": "4.4.6",
"babel-eslint": "10.1.0",
+ "babel-plugin-dynamic-import-node": "2.3.3",
"chalk": "4.1.0",
+ "compression-webpack-plugin": "5.0.2",
"connect": "3.6.6",
"eslint": "7.15.0",
"eslint-plugin-vue": "7.2.0",
diff --git a/ruoyi-ui/src/components/Crontab/day.vue b/ruoyi-ui/src/components/Crontab/day.vue
index bf9f5664e..fe3eaf0c4 100644
--- a/ruoyi-ui/src/components/Crontab/day.vue
+++ b/ruoyi-ui/src/components/Crontab/day.vue
@@ -2,7 +2,7 @@
- 日,允许的通配符[, - * / L M]
+ 日,允许的通配符[, - * ? / L W]
@@ -15,23 +15,23 @@
周期从
- -
- 日
+ -
+ 日
从
- 号开始,每
- 日执行一次
+ 号开始,每
+ 日执行一次
每月
- 号最近的那个工作日
+ 号最近的那个工作日
@@ -72,31 +72,22 @@ export default {
// 单选按钮值变化时
radioChange() {
('day rachange');
- if (this.radioValue === 1) {
- this.$emit('update', 'day', '*', 'day');
- this.$emit('update', 'week', '?', 'day');
- this.$emit('update', 'month', '*', 'day');
- } else {
- if (this.cron.hour === '*') {
- this.$emit('update', 'hour', '0', 'day');
- }
- if (this.cron.min === '*') {
- this.$emit('update', 'min', '0', 'day');
- }
- if (this.cron.second === '*') {
- this.$emit('update', 'second', '0', 'day');
- }
+ if (this.radioValue !== 2 && this.cron.week !== '?') {
+ this.$emit('update', 'week', '?', 'day')
}
switch (this.radioValue) {
+ case 1:
+ this.$emit('update', 'day', '*');
+ break;
case 2:
this.$emit('update', 'day', '?');
break;
case 3:
- this.$emit('update', 'day', this.cycle01 + '-' + this.cycle02);
+ this.$emit('update', 'day', this.cycleTotal);
break;
case 4:
- this.$emit('update', 'day', this.average01 + '/' + this.average02);
+ this.$emit('update', 'day', this.averageTotal);
break;
case 5:
this.$emit('update', 'day', this.workday + 'W');
@@ -125,7 +116,7 @@ export default {
// 最近工作日值变化时
workdayChange() {
if (this.radioValue == '5') {
- this.$emit('update', 'day', this.workday + 'W');
+ this.$emit('update', 'day', this.workdayCheck + 'W');
}
},
// checkbox值变化时
@@ -133,19 +124,10 @@ export default {
if (this.radioValue == '7') {
this.$emit('update', 'day', this.checkboxString);
}
- },
- // 父组件传递的week发生变化触发
- weekChange() {
- //判断week值与day不能同时为“?”
- if (this.cron.week == '?' && this.radioValue == '2') {
- this.radioValue = '1';
- } else if (this.cron.week !== '?' && this.radioValue != '2') {
- this.radioValue = '2';
- }
- },
+ }
},
watch: {
- "radioValue": "radioChange",
+ 'radioValue': 'radioChange',
'cycleTotal': 'cycleChange',
'averageTotal': 'averageChange',
'workdayCheck': 'workdayChange',
@@ -154,20 +136,20 @@ export default {
computed: {
// 计算两个周期值
cycleTotal: function () {
- this.cycle01 = this.checkNum(this.cycle01, 1, 31)
- this.cycle02 = this.checkNum(this.cycle02, 1, 31)
- return this.cycle01 + '-' + this.cycle02;
+ const cycle01 = this.checkNum(this.cycle01, 1, 30)
+ const cycle02 = this.checkNum(this.cycle02, cycle01 ? cycle01 + 1 : 2, 31, 31)
+ return cycle01 + '-' + cycle02;
},
// 计算平均用到的值
averageTotal: function () {
- this.average01 = this.checkNum(this.average01, 1, 31)
- this.average02 = this.checkNum(this.average02, 1, 31)
- return this.average01 + '/' + this.average02;
+ const average01 = this.checkNum(this.average01, 1, 30)
+ const average02 = this.checkNum(this.average02, 1, 31 - average01 || 0)
+ return average01 + '/' + average02;
},
// 计算工作日格式
workdayCheck: function () {
- this.workday = this.checkNum(this.workday, 1, 31)
- return this.workday;
+ const workday = this.checkNum(this.workday, 1, 31)
+ return workday;
},
// 计算勾选的checkbox值合集
checkboxString: function () {
diff --git a/ruoyi-ui/src/components/Crontab/hour.vue b/ruoyi-ui/src/components/Crontab/hour.vue
index 50833fc19..9c5c600dc 100644
--- a/ruoyi-ui/src/components/Crontab/hour.vue
+++ b/ruoyi-ui/src/components/Crontab/hour.vue
@@ -9,16 +9,16 @@
周期从
- -
- 小时
+ -
+ 小时
从
- 小时开始,每
- 小时执行一次
+ 小时开始,每
+ 小时执行一次
@@ -51,23 +51,15 @@ export default {
methods: {
// 单选按钮值变化时
radioChange() {
- if (this.radioValue === 1) {
- this.$emit('update', 'hour', '*', 'hour');
- this.$emit('update', 'day', '*', 'hour');
- } else {
- if (this.cron.min === '*') {
- this.$emit('update', 'min', '0', 'hour');
- }
- if (this.cron.second === '*') {
- this.$emit('update', 'second', '0', 'hour');
- }
- }
switch (this.radioValue) {
+ case 1:
+ this.$emit('update', 'hour', '*')
+ break;
case 2:
- this.$emit('update', 'hour', this.cycle01 + '-' + this.cycle02);
+ this.$emit('update', 'hour', this.cycleTotal);
break;
case 3:
- this.$emit('update', 'hour', this.average01 + '/' + this.average02);
+ this.$emit('update', 'hour', this.averageTotal);
break;
case 4:
this.$emit('update', 'hour', this.checkboxString);
@@ -94,7 +86,7 @@ export default {
}
},
watch: {
- "radioValue": "radioChange",
+ 'radioValue': 'radioChange',
'cycleTotal': 'cycleChange',
'averageTotal': 'averageChange',
'checkboxString': 'checkboxChange'
@@ -102,15 +94,15 @@ export default {
computed: {
// 计算两个周期值
cycleTotal: function () {
- this.cycle01 = this.checkNum(this.cycle01, 0, 23)
- this.cycle02 = this.checkNum(this.cycle02, 0, 23)
- return this.cycle01 + '-' + this.cycle02;
+ const cycle01 = this.checkNum(this.cycle01, 0, 22)
+ const cycle02 = this.checkNum(this.cycle02, cycle01 ? cycle01 + 1 : 1, 23)
+ return cycle01 + '-' + cycle02;
},
// 计算平均用到的值
averageTotal: function () {
- this.average01 = this.checkNum(this.average01, 0, 23)
- this.average02 = this.checkNum(this.average02, 1, 23)
- return this.average01 + '/' + this.average02;
+ const average01 = this.checkNum(this.average01, 0, 22)
+ const average02 = this.checkNum(this.average02, 1, 23 - average01 || 0)
+ return average01 + '/' + average02;
},
// 计算勾选的checkbox值合集
checkboxString: function () {
diff --git a/ruoyi-ui/src/components/Crontab/index.vue b/ruoyi-ui/src/components/Crontab/index.vue
index 27b4ab36c..3963df28e 100644
--- a/ruoyi-ui/src/components/Crontab/index.vue
+++ b/ruoyi-ui/src/components/Crontab/index.vue
@@ -2,7 +2,12 @@
-
+
@@ -268,7 +273,7 @@ export default {
insValue = 5;
} else {
this.$refs[refName].checkboxList = value.split(",");
- insValue = 7;
+ insValue = 6;
}
} else if (name == "year") {
if (value == "") {
diff --git a/ruoyi-ui/src/components/Crontab/min.vue b/ruoyi-ui/src/components/Crontab/min.vue
index bd12ab597..43cab900d 100644
--- a/ruoyi-ui/src/components/Crontab/min.vue
+++ b/ruoyi-ui/src/components/Crontab/min.vue
@@ -9,16 +9,16 @@
周期从
- -
- 分钟
+ -
+ 分钟
从
- 分钟开始,每
- 分钟执行一次
+ 分钟开始,每
+ 分钟执行一次
@@ -52,19 +52,15 @@ export default {
methods: {
// 单选按钮值变化时
radioChange() {
- if (this.radioValue !== 1 && this.cron.second === '*') {
- this.$emit('update', 'second', '0', 'min');
- }
switch (this.radioValue) {
case 1:
this.$emit('update', 'min', '*', 'min');
- this.$emit('update', 'hour', '*', 'min');
break;
case 2:
- this.$emit('update', 'min', this.cycle01 + '-' + this.cycle02, 'min');
+ this.$emit('update', 'min', this.cycleTotal, 'min');
break;
case 3:
- this.$emit('update', 'min', this.average01 + '/' + this.average02, 'min');
+ this.$emit('update', 'min', this.averageTotal, 'min');
break;
case 4:
this.$emit('update', 'min', this.checkboxString, 'min');
@@ -92,7 +88,7 @@ export default {
},
watch: {
- "radioValue": "radioChange",
+ 'radioValue': 'radioChange',
'cycleTotal': 'cycleChange',
'averageTotal': 'averageChange',
'checkboxString': 'checkboxChange',
@@ -100,15 +96,15 @@ export default {
computed: {
// 计算两个周期值
cycleTotal: function () {
- this.cycle01 = this.checkNum(this.cycle01, 0, 59)
- this.cycle02 = this.checkNum(this.cycle02, 0, 59)
- return this.cycle01 + '-' + this.cycle02;
+ const cycle01 = this.checkNum(this.cycle01, 0, 58)
+ const cycle02 = this.checkNum(this.cycle02, cycle01 ? cycle01 + 1 : 1, 59)
+ return cycle01 + '-' + cycle02;
},
// 计算平均用到的值
averageTotal: function () {
- this.average01 = this.checkNum(this.average01, 0, 59)
- this.average02 = this.checkNum(this.average02, 1, 59)
- return this.average01 + '/' + this.average02;
+ const average01 = this.checkNum(this.average01, 0, 58)
+ const average02 = this.checkNum(this.average02, 1, 59 - average01 || 0)
+ return average01 + '/' + average02;
},
// 计算勾选的checkbox值合集
checkboxString: function () {
diff --git a/ruoyi-ui/src/components/Crontab/month.vue b/ruoyi-ui/src/components/Crontab/month.vue
index 619d1e791..fd0ac384f 100644
--- a/ruoyi-ui/src/components/Crontab/month.vue
+++ b/ruoyi-ui/src/components/Crontab/month.vue
@@ -9,16 +9,16 @@
周期从
- -
- 月
+ -
+ 月
从
- 月开始,每
- 月月执行一次
+ 月开始,每
+ 月月执行一次
@@ -51,29 +51,15 @@ export default {
methods: {
// 单选按钮值变化时
radioChange() {
- if (this.radioValue === 1) {
- this.$emit('update', 'month', '*');
- this.$emit('update', 'year', '*');
- } else {
- if (this.cron.day === '*') {
- this.$emit('update', 'day', '0', 'month');
- }
- if (this.cron.hour === '*') {
- this.$emit('update', 'hour', '0', 'month');
- }
- if (this.cron.min === '*') {
- this.$emit('update', 'min', '0', 'month');
- }
- if (this.cron.second === '*') {
- this.$emit('update', 'second', '0', 'month');
- }
- }
switch (this.radioValue) {
+ case 1:
+ this.$emit('update', 'month', '*');
+ break;
case 2:
- this.$emit('update', 'month', this.cycle01 + '-' + this.cycle02);
+ this.$emit('update', 'month', this.cycleTotal);
break;
case 3:
- this.$emit('update', 'month', this.average01 + '/' + this.average02);
+ this.$emit('update', 'month', this.averageTotal);
break;
case 4:
this.$emit('update', 'month', this.checkboxString);
@@ -100,7 +86,7 @@ export default {
}
},
watch: {
- "radioValue": "radioChange",
+ 'radioValue': 'radioChange',
'cycleTotal': 'cycleChange',
'averageTotal': 'averageChange',
'checkboxString': 'checkboxChange'
@@ -108,15 +94,15 @@ export default {
computed: {
// 计算两个周期值
cycleTotal: function () {
- this.cycle01 = this.checkNum(this.cycle01, 1, 12)
- this.cycle02 = this.checkNum(this.cycle02, 1, 12)
- return this.cycle01 + '-' + this.cycle02;
+ const cycle01 = this.checkNum(this.cycle01, 1, 11)
+ const cycle02 = this.checkNum(this.cycle02, cycle01 ? cycle01 + 1 : 2, 12)
+ return cycle01 + '-' + cycle02;
},
// 计算平均用到的值
averageTotal: function () {
- this.average01 = this.checkNum(this.average01, 1, 12)
- this.average02 = this.checkNum(this.average02, 1, 12)
- return this.average01 + '/' + this.average02;
+ const average01 = this.checkNum(this.average01, 1, 11)
+ const average02 = this.checkNum(this.average02, 1, 12 - average01 || 0)
+ return average01 + '/' + average02;
},
// 计算勾选的checkbox值合集
checkboxString: function () {
diff --git a/ruoyi-ui/src/components/Crontab/result.vue b/ruoyi-ui/src/components/Crontab/result.vue
index 07b963b79..aea6e0e46 100644
--- a/ruoyi-ui/src/components/Crontab/result.vue
+++ b/ruoyi-ui/src/components/Crontab/result.vue
@@ -179,7 +179,7 @@ export default {
// 获取达到条件的日期是星期X
let thisWeek = this.formatDate(new Date(YY + '-' + MM + '-' + thisDD + ' 00:00:00'), 'week');
// 当星期日时
- if (thisWeek == 0) {
+ if (thisWeek == 1) {
// 先找下一个日,并判断是否为月底
DD++;
thisDD = DD < 10 ? '0' + DD : DD;
@@ -187,7 +187,7 @@ export default {
if (this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
DD -= 3;
}
- } else if (thisWeek == 6) {
+ } else if (thisWeek == 7) {
// 当星期6时只需判断不是1号就可进行操作
if (this.dayRuleSup !== 1) {
DD--;
@@ -200,7 +200,7 @@ export default {
// 获取当前日期是属于星期几
let thisWeek = this.formatDate(new Date(YY + '-' + MM + '-' + DD + ' 00:00:00'), 'week');
// 校验当前星期是否在星期池(dayRuleSup)中
- if (Array.indexOf(this.dayRuleSup, thisWeek) < 0) {
+ if (this.dayRuleSup.indexOf(thisWeek) < 0) {
// 如果到达最大值时
if (Di == DDate.length - 1) {
resetDay();
@@ -385,7 +385,7 @@ export default {
} else if (rule.indexOf('#') >= 0) {
this.dayRule = 'assWeek';
let matchRule = rule.match(/[0-9]{1}/g);
- this.dayRuleSup = [Number(matchRule[0]), Number(matchRule[1])];
+ this.dayRuleSup = [Number(matchRule[1]), Number(matchRule[0])];
this.dateArr[3] = [1];
if (this.dayRuleSup[1] == 7) {
this.dayRuleSup[1] = 0;
@@ -401,14 +401,6 @@ export default {
this.dayRule = 'weekDay';
this.dayRuleSup = this.getAssignArr(rule)
}
- // 如果weekDay时将7调整为0【week值0即是星期日】
- if (this.dayRule == 'weekDay') {
- for (let i = 0; i < this.dayRuleSup.length; i++) {
- if (this.dayRuleSup[i] == 7) {
- this.dayRuleSup[i] = 0;
- }
- }
- }
}
},
// 获取"日"数组-少量为日期规则
@@ -543,14 +535,15 @@ export default {
if (type == undefined) {
return Y + '-' + (M < 10 ? '0' + M : M) + '-' + (D < 10 ? '0' + D : D) + ' ' + (h < 10 ? '0' + h : h) + ':' + (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s);
} else if (type == 'week') {
- return week;
+ // 在quartz中 1为星期日
+ return week + 1;
}
},
// 检查日期是否存在
checkDate(value) {
let time = new Date(value);
let format = this.formatDate(time)
- return value == format ? true : false;
+ return value === format;
}
},
watch: {
diff --git a/ruoyi-ui/src/components/Crontab/second.vue b/ruoyi-ui/src/components/Crontab/second.vue
index 0fdf3386d..e7b776171 100644
--- a/ruoyi-ui/src/components/Crontab/second.vue
+++ b/ruoyi-ui/src/components/Crontab/second.vue
@@ -9,16 +9,16 @@
周期从
- -
- 秒
+ -
+ 秒
从
- 秒开始,每
- 秒执行一次
+ 秒开始,每
+ 秒执行一次
@@ -54,13 +54,12 @@ export default {
switch (this.radioValue) {
case 1:
this.$emit('update', 'second', '*', 'second');
- this.$emit('update', 'min', '*', 'second');
break;
case 2:
- this.$emit('update', 'second', this.cycle01 + '-' + this.cycle02);
+ this.$emit('update', 'second', this.cycleTotal);
break;
case 3:
- this.$emit('update', 'second', this.average01 + '/' + this.average02);
+ this.$emit('update', 'second', this.averageTotal);
break;
case 4:
this.$emit('update', 'second', this.checkboxString);
@@ -84,25 +83,10 @@ export default {
if (this.radioValue == '4') {
this.$emit('update', 'second', this.checkboxString);
}
- },
- othChange() {
- // 反解析
- let ins = this.cron.second
- ('反解析 second', ins);
- if (ins === '*') {
- this.radioValue = 1;
- } else if (ins.indexOf('-') > -1) {
- this.radioValue = 2
- } else if (ins.indexOf('/') > -1) {
- this.radioValue = 3
- } else {
- this.radioValue = 4
- this.checkboxList = ins.split(',')
- }
}
},
watch: {
- "radioValue": "radioChange",
+ 'radioValue': 'radioChange',
'cycleTotal': 'cycleChange',
'averageTotal': 'averageChange',
'checkboxString': 'checkboxChange',
@@ -113,15 +97,15 @@ export default {
computed: {
// 计算两个周期值
cycleTotal: function () {
- this.cycle01 = this.checkNum(this.cycle01, 0, 59)
- this.cycle02 = this.checkNum(this.cycle02, 0, 59)
- return this.cycle01 + '-' + this.cycle02;
+ const cycle01 = this.checkNum(this.cycle01, 0, 58)
+ const cycle02 = this.checkNum(this.cycle02, cycle01 ? cycle01 + 1 : 1, 59)
+ return cycle01 + '-' + cycle02;
},
// 计算平均用到的值
averageTotal: function () {
- this.average01 = this.checkNum(this.average01, 0, 59)
- this.average02 = this.checkNum(this.average02, 1, 59)
- return this.average01 + '/' + this.average02;
+ const average01 = this.checkNum(this.average01, 0, 58)
+ const average02 = this.checkNum(this.average02, 1, 59 - average01 || 0)
+ return average01 + '/' + average02;
},
// 计算勾选的checkbox值合集
checkboxString: function () {
diff --git a/ruoyi-ui/src/components/Crontab/week.vue b/ruoyi-ui/src/components/Crontab/week.vue
index 5ad949d6b..1cec700e8 100644
--- a/ruoyi-ui/src/components/Crontab/week.vue
+++ b/ruoyi-ui/src/components/Crontab/week.vue
@@ -2,7 +2,7 @@
- 周,允许的通配符[, - * / L #]
+ 周,允许的通配符[, - * ? / L #]
@@ -15,8 +15,25 @@
周期从星期
- -
-
+
+ {{item.value}}
+
+ -
+
+ {{item.value}}
+
@@ -24,14 +41,18 @@
第
周的星期
-
+
+ {{item.value}}
+
本月最后一个星期
-
+
+ {{item.value}}
+
@@ -39,7 +60,7 @@
指定
- {{item}}
+ {{item.value}}
@@ -52,13 +73,42 @@ export default {
data() {
return {
radioValue: 2,
- weekday: 1,
- cycle01: 1,
- cycle02: 2,
+ weekday: 2,
+ cycle01: 2,
+ cycle02: 3,
average01: 1,
- average02: 1,
+ average02: 2,
checkboxList: [],
- weekList: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
+ weekList: [
+ {
+ key: 2,
+ value: '星期一'
+ },
+ {
+ key: 3,
+ value: '星期二'
+ },
+ {
+ key: 4,
+ value: '星期三'
+ },
+ {
+ key: 5,
+ value: '星期四'
+ },
+ {
+ key: 6,
+ value: '星期五'
+ },
+ {
+ key: 7,
+ value: '星期六'
+ },
+ {
+ key: 1,
+ value: '星期日'
+ }
+ ],
checkNum: this.$options.propsData.check
}
},
@@ -67,45 +117,30 @@ export default {
methods: {
// 单选按钮值变化时
radioChange() {
- if (this.radioValue === 1) {
- this.$emit('update', 'week', '*');
- this.$emit('update', 'year', '*');
- } else {
- if (this.cron.month === '*') {
- this.$emit('update', 'month', '0', 'week');
- }
- if (this.cron.day === '*') {
- this.$emit('update', 'day', '0', 'week');
- }
- if (this.cron.hour === '*') {
- this.$emit('update', 'hour', '0', 'week');
- }
- if (this.cron.min === '*') {
- this.$emit('update', 'min', '0', 'week');
- }
- if (this.cron.second === '*') {
- this.$emit('update', 'second', '0', 'week');
- }
+ if (this.radioValue !== 2 && this.cron.day !== '?') {
+ this.$emit('update', 'day', '?', 'week');
}
switch (this.radioValue) {
+ case 1:
+ this.$emit('update', 'week', '*');
+ break;
case 2:
this.$emit('update', 'week', '?');
break;
case 3:
- this.$emit('update', 'week', this.cycle01 + '-' + this.cycle02);
+ this.$emit('update', 'week', this.cycleTotal);
break;
case 4:
- this.$emit('update', 'week', this.average01 + '#' + this.average02);
+ this.$emit('update', 'week', this.averageTotal);
break;
case 5:
- this.$emit('update', 'week', this.weekday + 'L');
+ this.$emit('update', 'week', this.weekdayCheck + 'L');
break;
case 6:
this.$emit('update', 'week', this.checkboxString);
break;
}
},
- // 根据互斥事件,更改radio的值
// 周期两个值变化时
cycleChange() {
@@ -133,7 +168,7 @@ export default {
},
},
watch: {
- "radioValue": "radioChange",
+ 'radioValue': 'radioChange',
'cycleTotal': 'cycleChange',
'averageTotal': 'averageChange',
'weekdayCheck': 'weekdayChange',
@@ -150,7 +185,7 @@ export default {
averageTotal: function () {
this.average01 = this.checkNum(this.average01, 1, 4)
this.average02 = this.checkNum(this.average02, 1, 7)
- return this.average01 + '#' + this.average02;
+ return this.average02 + '#' + this.average01;
},
// 最近的工作日(格式)
weekdayCheck: function () {
diff --git a/ruoyi-ui/src/components/Crontab/year.vue b/ruoyi-ui/src/components/Crontab/year.vue
index 800dfa522..5487a6c7f 100644
--- a/ruoyi-ui/src/components/Crontab/year.vue
+++ b/ruoyi-ui/src/components/Crontab/year.vue
@@ -15,16 +15,16 @@
周期从
- -
-
+ -
+
从
- 年开始,每
- 年执行一次
+ 年开始,每
+ 年执行一次
@@ -59,21 +59,6 @@ export default {
methods: {
// 单选按钮值变化时
radioChange() {
- if (this.cron.month === '*') {
- this.$emit('update', 'month', '0', 'year');
- }
- if (this.cron.day === '*') {
- this.$emit('update', 'day', '0', 'year');
- }
- if (this.cron.hour === '*') {
- this.$emit('update', 'hour', '0', 'year');
- }
- if (this.cron.min === '*') {
- this.$emit('update', 'min', '0', 'year');
- }
- if (this.cron.second === '*') {
- this.$emit('update', 'second', '0', 'year');
- }
switch (this.radioValue) {
case 1:
this.$emit('update', 'year', '');
@@ -82,10 +67,10 @@ export default {
this.$emit('update', 'year', '*');
break;
case 3:
- this.$emit('update', 'year', this.cycle01 + '-' + this.cycle02);
+ this.$emit('update', 'year', this.cycleTotal);
break;
case 4:
- this.$emit('update', 'year', this.average01 + '/' + this.average02);
+ this.$emit('update', 'year', this.averageTotal);
break;
case 5:
this.$emit('update', 'year', this.checkboxString);
@@ -112,7 +97,7 @@ export default {
}
},
watch: {
- "radioValue": "radioChange",
+ 'radioValue': 'radioChange',
'cycleTotal': 'cycleChange',
'averageTotal': 'averageChange',
'checkboxString': 'checkboxChange'
@@ -120,15 +105,15 @@ export default {
computed: {
// 计算两个周期值
cycleTotal: function () {
- this.cycle01 = this.checkNum(this.cycle01, this.fullYear, this.fullYear + 100)
- this.cycle02 = this.checkNum(this.cycle02, this.fullYear + 1, this.fullYear + 101)
- return this.cycle01 + '-' + this.cycle02;
+ const cycle01 = this.checkNum(this.cycle01, this.fullYear, 2098)
+ const cycle02 = this.checkNum(this.cycle02, cycle01 ? cycle01 + 1 : this.fullYear + 1, 2099)
+ return cycle01 + '-' + cycle02;
},
// 计算平均用到的值
averageTotal: function () {
- this.average01 = this.checkNum(this.average01, this.fullYear, this.fullYear + 100)
- this.average02 = this.checkNum(this.average02, 1, 10)
- return this.average01 + '/' + this.average02;
+ const average01 = this.checkNum(this.average01, this.fullYear, 2098)
+ const average02 = this.checkNum(this.average02, 1, 2099 - average01 || this.fullYear)
+ return average01 + '/' + average02;
},
// 计算勾选的checkbox值合集
checkboxString: function () {
@@ -139,6 +124,8 @@ export default {
mounted: function () {
// 仅获取当前年份
this.fullYear = Number(new Date().getFullYear());
+ this.cycle01 = this.fullYear
+ this.average01 = this.fullYear
}
}
diff --git a/ruoyi-ui/src/components/ImagePreview/index.vue b/ruoyi-ui/src/components/ImagePreview/index.vue
new file mode 100644
index 000000000..44e27aac2
--- /dev/null
+++ b/ruoyi-ui/src/components/ImagePreview/index.vue
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ruoyi-ui/src/directive/index.js b/ruoyi-ui/src/directive/index.js
index 5801640a1..b9b07da3c 100644
--- a/ruoyi-ui/src/directive/index.js
+++ b/ruoyi-ui/src/directive/index.js
@@ -3,10 +3,12 @@ import hasPermi from './permission/hasPermi'
import dialogDrag from './dialog/drag'
import dialogDragWidth from './dialog/dragWidth'
import dialogDragHeight from './dialog/dragHeight'
+import clipboard from './module/clipboard'
const install = function(Vue) {
Vue.directive('hasRole', hasRole)
Vue.directive('hasPermi', hasPermi)
+ Vue.directive('clipboard', clipboard)
Vue.directive('dialogDrag', dialogDrag)
Vue.directive('dialogDragWidth', dialogDragWidth)
Vue.directive('dialogDragHeight', dialogDragHeight)
diff --git a/ruoyi-ui/src/directive/module/clipboard.js b/ruoyi-ui/src/directive/module/clipboard.js
new file mode 100644
index 000000000..635315a84
--- /dev/null
+++ b/ruoyi-ui/src/directive/module/clipboard.js
@@ -0,0 +1,54 @@
+/**
+* v-clipboard 文字复制剪贴
+* Copyright (c) 2021 ruoyi
+*/
+
+import Clipboard from 'clipboard'
+export default {
+ bind(el, binding, vnode) {
+ switch (binding.arg) {
+ case 'success':
+ el._vClipBoard_success = binding.value;
+ break;
+ case 'error':
+ el._vClipBoard_error = binding.value;
+ break;
+ default: {
+ const clipboard = new Clipboard(el, {
+ text: () => binding.value,
+ action: () => binding.arg === 'cut' ? 'cut' : 'copy'
+ });
+ clipboard.on('success', e => {
+ const callback = el._vClipBoard_success;
+ callback && callback(e);
+ });
+ clipboard.on('error', e => {
+ const callback = el._vClipBoard_error;
+ callback && callback(e);
+ });
+ el._vClipBoard = clipboard;
+ }
+ }
+ },
+ update(el, binding) {
+ if (binding.arg === 'success') {
+ el._vClipBoard_success = binding.value;
+ } else if (binding.arg === 'error') {
+ el._vClipBoard_error = binding.value;
+ } else {
+ el._vClipBoard.text = function () { return binding.value; };
+ el._vClipBoard.action = () => binding.arg === 'cut' ? 'cut' : 'copy';
+ }
+ },
+ unbind(el, binding) {
+ if (!el._vClipboard) return
+ if (binding.arg === 'success') {
+ delete el._vClipBoard_success;
+ } else if (binding.arg === 'error') {
+ delete el._vClipBoard_error;
+ } else {
+ el._vClipBoard.destroy();
+ delete el._vClipBoard;
+ }
+ }
+}
diff --git a/ruoyi-ui/src/main.js b/ruoyi-ui/src/main.js
index 29c402265..13c6cf290 100644
--- a/ruoyi-ui/src/main.js
+++ b/ruoyi-ui/src/main.js
@@ -29,6 +29,8 @@ import Editor from "@/components/Editor"
import FileUpload from "@/components/FileUpload"
// 图片上传组件
import ImageUpload from "@/components/ImageUpload"
+// 图片预览组件
+import ImagePreview from "@/components/ImagePreview"
// 字典标签组件
import DictTag from '@/components/DictTag'
// 头部标签组件
@@ -54,6 +56,7 @@ Vue.component('RightToolbar', RightToolbar)
Vue.component('Editor', Editor)
Vue.component('FileUpload', FileUpload)
Vue.component('ImageUpload', ImageUpload)
+Vue.component('ImagePreview', ImagePreview)
Vue.use(directive)
Vue.use(plugins)
diff --git a/ruoyi-ui/src/plugins/download.js b/ruoyi-ui/src/plugins/download.js
index ac50dade2..726ef547f 100644
--- a/ruoyi-ui/src/plugins/download.js
+++ b/ruoyi-ui/src/plugins/download.js
@@ -1,14 +1,17 @@
import axios from 'axios'
-import { Message } from 'element-ui'
+import {Loading, Message} from 'element-ui'
import { saveAs } from 'file-saver'
import { getToken } from '@/utils/auth'
+import errorCode from '@/utils/errorCode'
import { blobValidate } from "@/utils/ruoyi";
const baseURL = process.env.VUE_APP_BASE_API
+let downloadLoadingInstance;
export default {
oss(ossId) {
var url = baseURL + '/system/oss/download/' + ossId
+ downloadLoadingInstance = Loading.service({ text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", })
axios({
method: 'get',
url: url,
@@ -20,8 +23,13 @@ export default {
const blob = new Blob([res.data], { type: 'application/octet-stream' })
this.saveAs(blob, decodeURI(res.headers['download-filename']))
} else {
- Message.error('无效的会话,或者会话已过期,请重新登录。');
+ this.printErrMsg(res.data);
}
+ downloadLoadingInstance.close();
+ }).catch((r) => {
+ console.error(r)
+ Message.error('下载文件出现错误,请联系管理员!')
+ downloadLoadingInstance.close();
})
},
zip(url, name) {
@@ -37,12 +45,18 @@ export default {
const blob = new Blob([res.data], { type: 'application/zip' })
this.saveAs(blob, name)
} else {
- Message.error('无效的会话,或者会话已过期,请重新登录。');
+ this.printErrMsg(res.data);
}
})
},
saveAs(text, name, opts) {
saveAs(text, name, opts);
+ },
+ async printErrMsg(data) {
+ const resText = await data.text();
+ const rspObj = JSON.parse(resText);
+ const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
+ Message.error(errMsg);
}
}
diff --git a/ruoyi-ui/src/plugins/modal.js b/ruoyi-ui/src/plugins/modal.js
index 7ad9d297c..b37ca1457 100644
--- a/ruoyi-ui/src/plugins/modal.js
+++ b/ruoyi-ui/src/plugins/modal.js
@@ -59,6 +59,14 @@ export default {
type: "warning",
})
},
+ // 提交内容
+ prompt(content) {
+ return MessageBox.prompt(content, "系统提示", {
+ confirmButtonText: '确定',
+ cancelButtonText: '取消',
+ type: "warning",
+ })
+ },
// 打开遮罩层
loading(content) {
loadingInstance = Loading.service({
diff --git a/ruoyi-ui/src/router/index.js b/ruoyi-ui/src/router/index.js
index cda6d4c3a..3fb685d06 100644
--- a/ruoyi-ui/src/router/index.js
+++ b/ruoyi-ui/src/router/index.js
@@ -17,6 +17,8 @@ import Layout from '@/layout'
* redirect: noRedirect // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击
* name:'router-name' // 设定路由的名字,一定要填写不然使用时会出现各种问题
* query: '{"id": 1, "name": "ry"}' // 访问路由的默认传递参数
+ * roles: ['admin', 'common'] // 访问路由的角色权限
+ * permissions: ['a:a:a', 'b:b:b'] // 访问路由的菜单权限
* meta : {
noCache: true // 如果设置为true,则不会被 缓存(默认 false)
title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字
@@ -35,28 +37,28 @@ export const constantRoutes = [
children: [
{
path: '/redirect/:path(.*)',
- component: (resolve) => require(['@/views/redirect'], resolve)
+ component: () => import('@/views/redirect')
}
]
},
{
path: '/login',
- component: (resolve) => require(['@/views/login'], resolve),
+ component: () => import('@/views/login'),
hidden: true
},
{
path: '/register',
- component: (resolve) => require(['@/views/register'], resolve),
+ component: () => import('@/views/register'),
hidden: true
},
{
path: '/404',
- component: (resolve) => require(['@/views/error/404'], resolve),
+ component: () => import('@/views/error/404'),
hidden: true
},
{
path: '/401',
- component: (resolve) => require(['@/views/error/401'], resolve),
+ component: () => import('@/views/error/401'),
hidden: true
},
{
@@ -66,7 +68,7 @@ export const constantRoutes = [
children: [
{
path: 'index',
- component: (resolve) => require(['@/views/index'], resolve),
+ component: () => import('@/views/index'),
name: 'Index',
meta: { title: '首页', icon: 'dashboard', affix: true }
}
@@ -80,20 +82,25 @@ export const constantRoutes = [
children: [
{
path: 'profile',
- component: (resolve) => require(['@/views/system/user/profile/index'], resolve),
+ component: () => import('@/views/system/user/profile/index'),
name: 'Profile',
meta: { title: '个人中心', icon: 'user' }
}
]
- },
+ }
+]
+
+// 动态路由,基于用户权限动态去加载
+export const dynamicRoutes = [
{
path: '/system/user-auth',
component: Layout,
hidden: true,
+ permissions: ['system:user:edit'],
children: [
{
path: 'role/:userId(\\d+)',
- component: (resolve) => require(['@/views/system/user/authRole'], resolve),
+ component: () => import('@/views/system/user/authRole'),
name: 'AuthRole',
meta: { title: '分配角色', activeMenu: '/system/user' }
}
@@ -103,10 +110,11 @@ export const constantRoutes = [
path: '/system/role-auth',
component: Layout,
hidden: true,
+ permissions: ['system:role:edit'],
children: [
{
path: 'user/:roleId(\\d+)',
- component: (resolve) => require(['@/views/system/role/authUser'], resolve),
+ component: () => import('@/views/system/role/authUser'),
name: 'AuthUser',
meta: { title: '分配用户', activeMenu: '/system/role' }
}
@@ -116,10 +124,11 @@ export const constantRoutes = [
path: '/system/dict-data',
component: Layout,
hidden: true,
+ permissions: ['system:dict:list'],
children: [
{
path: 'index/:dictId(\\d+)',
- component: (resolve) => require(['@/views/system/dict/data'], resolve),
+ component: () => import('@/views/system/dict/data'),
name: 'Data',
meta: { title: '字典数据', activeMenu: '/system/dict' }
}
@@ -129,12 +138,13 @@ export const constantRoutes = [
path: '/system/oss-config',
component: Layout,
hidden: true,
+ permissions: ['system:oss:list'],
children: [
{
path: 'index',
- component: (resolve) => require(['@/views/system/oss/config'], resolve),
+ component: () => import('@/views/system/oss/config'),
name: 'OssConfig',
- meta: { title: '配置管理', activeMenu: '/system/oss'}
+ meta: { title: '配置管理', activeMenu: '/system/oss' }
}
]
},
@@ -142,10 +152,11 @@ export const constantRoutes = [
path: '/tool/gen-edit',
component: Layout,
hidden: true,
+ permissions: ['tool:gen:edit'],
children: [
{
path: 'index',
- component: (resolve) => require(['@/views/tool/gen/editTable'], resolve),
+ component: () => import('@/views/tool/gen/editTable'),
name: 'GenEdit',
meta: { title: '修改生成配置', activeMenu: '/tool/gen' }
}
diff --git a/ruoyi-ui/src/store/modules/permission.js b/ruoyi-ui/src/store/modules/permission.js
index 8d84fff81..6f08c6f3b 100644
--- a/ruoyi-ui/src/store/modules/permission.js
+++ b/ruoyi-ui/src/store/modules/permission.js
@@ -1,4 +1,5 @@
-import { constantRoutes } from '@/router'
+import auth from '@/plugins/auth'
+import router, { constantRoutes, dynamicRoutes } from '@/router'
import { getRouters } from '@/api/menu'
import Layout from '@/layout/index'
import ParentView from '@/components/ParentView'
@@ -42,7 +43,9 @@ const permission = {
const rdata = JSON.parse(JSON.stringify(res.data))
const sidebarRoutes = filterAsyncRouter(sdata)
const rewriteRoutes = filterAsyncRouter(rdata, false, true)
+ const asyncRoutes = filterDynamicRoutes(dynamicRoutes);
rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true })
+ router.addRoutes(asyncRoutes);
commit('SET_ROUTES', rewriteRoutes)
commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes))
commit('SET_DEFAULT_ROUTES', sidebarRoutes)
@@ -106,6 +109,23 @@ function filterChildren(childrenMap, lastRouter = false) {
return children
}
+// 动态路由遍历,验证是否具备权限
+export function filterDynamicRoutes(routes) {
+ const res = []
+ routes.forEach(route => {
+ if (route.permissions) {
+ if (auth.hasPermiOr(route.permissions)) {
+ res.push(route)
+ }
+ } else if (route.roles) {
+ if (auth.hasRoleOr(route.roles)) {
+ res.push(route)
+ }
+ }
+ })
+ return res
+}
+
export const loadView = (view) => {
if (process.env.NODE_ENV === 'development') {
return (resolve) => require([`@/views/${view}`], resolve)
diff --git a/ruoyi-ui/src/utils/request.js b/ruoyi-ui/src/utils/request.js
index d376a76a5..a3917572f 100644
--- a/ruoyi-ui/src/utils/request.js
+++ b/ruoyi-ui/src/utils/request.js
@@ -110,7 +110,10 @@ export function download(url, params, filename) {
const blob = new Blob([data])
saveAs(blob, filename)
} else {
- Message.error('无效的会话,或者会话已过期,请重新登录。');
+ const resText = await data.text();
+ const rspObj = JSON.parse(resText);
+ const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
+ Message.error(errMsg);
}
downloadLoadingInstance.close();
}).catch((r) => {
diff --git a/ruoyi-ui/src/views/index.vue b/ruoyi-ui/src/views/index.vue
index 3fc731e74..dd34ed61d 100644
--- a/ruoyi-ui/src/views/index.vue
+++ b/ruoyi-ui/src/views/index.vue
@@ -101,6 +101,62 @@
更新日志
+
+
+ - [重大更新] 重写数据权限实现
+ - [重磅更新] 重构分页 简化使用
+ - [重磅更新] 用户登录 支持校验错误次数锁定登录
+ - [重磅更新] 增加 jdbc 批处理参数 大幅提升批量操作性能 对原生语句与 MP 均有效
+ - update springboot 2.5.7 => 2.5.8 升级预防 log4j2 问题
+ - update springboot-admin 2.5.4 => 2.5.5
+ - update hutool 5.7.16 => 5.7.18
+ - update redisson 3.16.4 => 3.16.7
+ - update dynamic-ds 3.4.1 => 3.5.0
+ - update qiniu 7.8.0 => 7.9.0
+ - update minio 8.3.3 => 8.3.4
+ - update tlog 1.3.4 => 1.3.6 启用 tlog 自动配置
+ - update clipboard 2.0.6 => 2.0.8
+ - update 多数据源切换标注过期 3.6.0 移除 推荐使用原生注解
+ - update 通用权限服务 迁移回 ruoyi-framework 模块
+ - update 使用 hutool-jwt 替换老旧 jjwt 依赖
+ - update 调整 OSS 表字段内容长度
+ - update LoginUser 增加角色缓存 优化角色权限代码
+ - update 使用 Cglib 重构 BeanCopyUtils 性能优异
+ - update 禁止所有工具类实例化 优化代码书写规范
+ - update 优化查询用户的角色组、岗位组代码
+ - update 更新 RedisUtils 返回客户端实例
+ - update 修改 健康检查权限 改为用户放行 提高安全性
+ - update hutool 工具 改为单包引入 减少无用依赖
+ - update ServicePlusImpl 功能 下沉到 BaseMapperPlus
+ - update 去除 jdk17 标签 由于很多组件还未适配 导致一些问题
+ - udpate 代码生成预览支持复制内容
+ - update 用户导入提示溢出则显示滚动条
+ - update 路由支持单独配置菜单或角色权限
+ - update 优化web拦截器 使用原生接口处理 默认非生产环境开启
+ - update 调整监控依赖 从 common 迁移到 framework
+ - add 新增 Vue3 分支 与 代码生成模板(由于组件还未完善 仅供学习)
+ - add 增加 RedisUtils 注册监听器方法
+ - add 增加 自定义 Xss 校验注解 用户导入增加 Bean 校验
+ - add oss下载增加 loading 层
+ - add 新增图片预览组件
+ - add 集成compression-webpack-plugin插件实现打包Gzip压缩
+ - add 新增 SqlUtils 检查关键字方法
+ - fix 修复 集群雪花id重复问题 使用网卡信息绑定生成
+ - fix 修复 count 语法异常
+ - fix 修复更改密码问题
+ - fix 修复sql关键字处理 防止解析器报错
+ - fix 修复 TreeBuildUtils 顶节点不为 0 问题
+ - fix 修复 SysOssConfig 主键类型错误
+ - fix 修复代码生成 导出注解错误
+ - fix 修复 redisson 集群模式 路径未匹配协议头问题
+ - fix 修复打包后字体图标偶现的乱码问题
+ - fix 修复版本差异导致的懒加载报错问题
+ - fix 修复代码生成字典组重复问题
+ - remove 删除 jjwt 无用依赖
+ - remove 移除过期 用户导入
+ - remove 移除过期工具 DictUtils
+
+
- update [重磅更新] 重构 Excel 导入 支持 Validator 校验 支持自定义监听器
@@ -588,7 +644,7 @@ export default {
data() {
return {
// 版本号
- version: "3.4.0",
+ version: "3.5.0",
};
},
methods: {
diff --git a/ruoyi-ui/src/views/monitor/online/index.vue b/ruoyi-ui/src/views/monitor/online/index.vue
index 509fbe5e2..dba6e06ee 100644
--- a/ruoyi-ui/src/views/monitor/online/index.vue
+++ b/ruoyi-ui/src/views/monitor/online/index.vue
@@ -111,7 +111,7 @@ export default {
},
/** 强退按钮操作 */
handleForceLogout(row) {
- this.$modal.confirm('是否确认强退名称为"' + row.userName + '"的数据项?').then(function() {
+ this.$modal.confirm('是否确认强退名称为"' + row.userName + '"的用户?').then(function() {
return forceLogout(row.tokenId);
}).then(() => {
this.getList();
diff --git a/ruoyi-ui/src/views/system/menu/index.vue b/ruoyi-ui/src/views/system/menu/index.vue
index d65773ad2..5c1f85a6f 100644
--- a/ruoyi-ui/src/views/system/menu/index.vue
+++ b/ruoyi-ui/src/views/system/menu/index.vue
@@ -128,7 +128,7 @@
-
+
-
+
diff --git a/ruoyi-ui/src/views/system/user/index.vue b/ruoyi-ui/src/views/system/user/index.vue
index 1ff9b906b..0eb8ddc7c 100644
--- a/ruoyi-ui/src/views/system/user/index.vue
+++ b/ruoyi-ui/src/views/system/user/index.vue
@@ -596,7 +596,7 @@ export default {
cancelButtonText: "取消",
closeOnClickModal: false,
inputPattern: /^.{5,20}$/,
- inputErrorMessage: "用户密码长度必须介于 5 和 20 之间",
+ inputErrorMessage: "用户密码长度必须介于 5 和 20 之间"
}).then(({ value }) => {
resetUserPwd(row.userId, value).then(response => {
this.$modal.msgSuccess("修改成功,新密码是:" + value);
@@ -663,7 +663,7 @@ export default {
this.upload.open = false;
this.upload.isUploading = false;
this.$refs.upload.clearFiles();
- this.$alert(response.msg, "导入结果", { dangerouslyUseHTMLString: true });
+ this.$alert("" + response.msg + "
", "导入结果", { dangerouslyUseHTMLString: true });
this.getList();
},
// 提交上传文件
diff --git a/ruoyi-ui/src/views/tool/gen/basicInfoForm.vue b/ruoyi-ui/src/views/tool/gen/basicInfoForm.vue
index 757962c44..70295298e 100644
--- a/ruoyi-ui/src/views/tool/gen/basicInfoForm.vue
+++ b/ruoyi-ui/src/views/tool/gen/basicInfoForm.vue
@@ -11,7 +11,6 @@
-
@@ -30,9 +29,9 @@
+
diff --git a/ruoyi/src/main/resources/vm/vue/v3/index.vue.vm b/ruoyi/src/main/resources/vm/vue/v3/index.vue.vm
new file mode 100644
index 000000000..6487f0ffc
--- /dev/null
+++ b/ruoyi/src/main/resources/vm/vue/v3/index.vue.vm
@@ -0,0 +1,577 @@
+
+
+
+#foreach($column in $columns)
+#if($column.query)
+#set($dictType=$column.dictType)
+#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if($column.htmlType == "input" || $column.htmlType == "textarea")
+
+
+
+#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
+
+
+
+
+
+#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType)
+
+
+
+
+
+#elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
+
+
+
+
+#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
+
+
+
+#end
+#end
+#end
+
+ 搜索
+ 重置
+
+
+
+
+
+ 新增
+
+
+ 修改
+
+
+ 删除
+
+
+ 导出
+
+
+
+
+
+
+#foreach($column in $columns)
+#set($javaField=$column.javaField)
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if($column.pk)
+
+#elseif($column.list && $column.htmlType == "datetime")
+
+
+ {{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}
+
+
+#elseif($column.list && $column.dictType && "" != $column.dictType)
+
+
+#if($column.htmlType == "checkbox")
+
+#else
+
+#end
+
+
+#elseif($column.list && "" != $javaField)
+
+#end
+#end
+
+
+ 修改
+ 删除
+
+
+
+
+
+
+
+
+
+#foreach($column in $columns)
+#set($field=$column.javaField)
+#if($column.insert && !$column.pk)
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#set($dictType=$column.dictType)
+#if($column.htmlType == "input")
+
+
+
+#elseif($column.htmlType == "imageUpload")
+
+
+
+#elseif($column.htmlType == "fileUpload")
+
+
+
+#elseif($column.htmlType == "editor")
+
+
+
+#elseif($column.htmlType == "select" && "" != $dictType)
+
+
+
+
+
+#elseif($column.htmlType == "select" && $dictType)
+
+
+
+
+
+#elseif($column.htmlType == "checkbox" && "" != $dictType)
+
+
+
+ {{dict.label}}
+
+
+
+#elseif($column.htmlType == "checkbox" && $dictType)
+
+
+ 请选择字典生成
+
+
+#elseif($column.htmlType == "radio" && "" != $dictType)
+
+
+ {{dict.label}}
+
+
+#elseif($column.htmlType == "radio" && $dictType)
+
+
+ 请选择字典生成
+
+
+#elseif($column.htmlType == "datetime")
+
+
+
+
+#elseif($column.htmlType == "textarea")
+
+
+
+#end
+#end
+#end
+#if($table.sub)
+ ${subTable.functionName}信息
+
+
+ 添加
+
+
+ 删除
+
+
+
+
+
+#foreach($column in $subTable.columns)
+#set($javaField=$column.javaField)
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if($column.pk || $javaField == ${subTableFkclassName})
+#elseif($column.list && "" != $javaField)
+
+
+
+
+
+#end
+#end
+
+#end
+
+
+
+
+
+
+
+
+
diff --git a/ruoyi/src/main/resources/vm/vue/v3/readme.txt b/ruoyi/src/main/resources/vm/vue/v3/readme.txt
new file mode 100644
index 000000000..99239bb53
--- /dev/null
+++ b/ruoyi/src/main/resources/vm/vue/v3/readme.txt
@@ -0,0 +1 @@
+ʹõRuoYi-Vue3ǰˣôҪһ´Ŀ¼ģindex.vue.vmindex-tree.vue.vmļϼvueĿ¼
\ No newline at end of file
diff --git a/script/docker/docker-compose.yml b/script/docker/docker-compose.yml
index 34c44b8d8..79739c383 100644
--- a/script/docker/docker-compose.yml
+++ b/script/docker/docker-compose.yml
@@ -102,7 +102,7 @@ services:
ipv4_address: 172.30.0.54
ruoyi-server1:
- image: "ruoyi/ruoyi-server:3.4.0"
+ image: "ruoyi/ruoyi-server:3.5.0"
container_name: ruoyi-server1
environment:
# 时区上海
@@ -117,7 +117,7 @@ services:
ipv4_address: 172.30.0.60
ruoyi-server2:
- image: "ruoyi/ruoyi-server:3.4.0"
+ image: "ruoyi/ruoyi-server:3.5.0"
container_name: ruoyi-server2
environment:
# 时区上海
@@ -132,7 +132,7 @@ services:
ipv4_address: 172.30.0.61
ruoyi-monitor-admin:
- image: "ruoyi/ruoyi-monitor-admin:3.4.0"
+ image: "ruoyi/ruoyi-monitor-admin:3.5.0"
container_name: ruoyi-monitor-admin
environment:
# 时区上海
@@ -147,7 +147,7 @@ services:
ipv4_address: 172.30.0.90
ruoyi-xxl-job-admin:
- image: "ruoyi/ruoyi-xxl-job-admin:3.4.0"
+ image: "ruoyi/ruoyi-xxl-job-admin:3.5.0"
container_name: ruoyi-xxl-job-admin
environment:
# 时区上海
diff --git a/script/docker/nginx/nginx.conf b/script/docker/nginx/nginx.conf
index b4ba03175..e77b6c7c6 100644
--- a/script/docker/nginx/nginx.conf
+++ b/script/docker/nginx/nginx.conf
@@ -52,6 +52,16 @@ http {
#ssl_prefer_server_ciphers on;
# https配置参考 end
+ # 演示环境配置 拦截除 GET POST 之外的所有请求
+ # if ($request_method !~* GET|POST) {
+ # rewrite ^/(.*)$ /403;
+ # }
+
+ # location = /403 {
+ # default_type application/json;
+ # return 200 '{"msg":"演示模式,不允许操作","code":500}';
+ # }
+
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
@@ -66,6 +76,9 @@ http {
proxy_pass http://server/;
}
+ # https 会拦截内链所有的 http 请求 造成功能无法使用
+ # 解决方案1 将 admin 服务 也配置成 https
+ # 解决方案2 将菜单配置为外链访问 走独立页面 http 访问
location /admin/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
@@ -74,6 +87,9 @@ http {
proxy_pass http://monitor-admin/admin/;
}
+ # https 会拦截内链所有的 http 请求 造成功能无法使用
+ # 解决方案1 将 xxljob 服务 也配置成 https
+ # 解决方案2 将菜单配置为外链访问 走独立页面 http 访问
location /xxl-job-admin/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
diff --git a/script/docker/redis/redis.conf b/script/docker/redis/redis.conf
index 7d550e149..a07c9d213 100644
--- a/script/docker/redis/redis.conf
+++ b/script/docker/redis/redis.conf
@@ -1,6 +1,9 @@
# redis 密码
# requirepass ruoyi123
+# key 监听器配置
+# notify-keyspace-events Ex
+
# 配置持久化文件存储路径
dir /redis/data
# 配置rdb