diff --git a/README.md b/README.md index 7004f0e50..cbf2da393 100644 --- a/README.md +++ b/README.md @@ -4,26 +4,27 @@ [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/blob/master/LICENSE) [![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
-[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-3.4.0-success.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus) +[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-3.5.0-success.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus) [![Spring Boot](https://img.shields.io/badge/Spring%20Boot-2.5-blue.svg)]() [![JDK-8+](https://img.shields.io/badge/JDK-8-green.svg)]() [![JDK-11](https://img.shields.io/badge/JDK-11-green.svg)]() -[![JDK-17](https://img.shields.io/badge/JDK-17-green.svg)]() -> 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 @@ 更新日志
+ +
    +
  1. [重大更新] 重写数据权限实现
  2. +
  3. [重磅更新] 重构分页 简化使用
  4. +
  5. [重磅更新] 用户登录 支持校验错误次数锁定登录
  6. +
  7. [重磅更新] 增加 jdbc 批处理参数 大幅提升批量操作性能 对原生语句与 MP 均有效
  8. +
  9. update springboot 2.5.7 => 2.5.8 升级预防 log4j2 问题
  10. +
  11. update springboot-admin 2.5.4 => 2.5.5
  12. +
  13. update hutool 5.7.16 => 5.7.18
  14. +
  15. update redisson 3.16.4 => 3.16.7
  16. +
  17. update dynamic-ds 3.4.1 => 3.5.0
  18. +
  19. update qiniu 7.8.0 => 7.9.0
  20. +
  21. update minio 8.3.3 => 8.3.4
  22. +
  23. update tlog 1.3.4 => 1.3.6 启用 tlog 自动配置
  24. +
  25. update clipboard 2.0.6 => 2.0.8
  26. +
  27. update 多数据源切换标注过期 3.6.0 移除 推荐使用原生注解
  28. +
  29. update 通用权限服务 迁移回 ruoyi-framework 模块
  30. +
  31. update 使用 hutool-jwt 替换老旧 jjwt 依赖
  32. +
  33. update 调整 OSS 表字段内容长度
  34. +
  35. update LoginUser 增加角色缓存 优化角色权限代码
  36. +
  37. update 使用 Cglib 重构 BeanCopyUtils 性能优异
  38. +
  39. update 禁止所有工具类实例化 优化代码书写规范
  40. +
  41. update 优化查询用户的角色组、岗位组代码
  42. +
  43. update 更新 RedisUtils 返回客户端实例
  44. +
  45. update 修改 健康检查权限 改为用户放行 提高安全性
  46. +
  47. update hutool 工具 改为单包引入 减少无用依赖
  48. +
  49. update ServicePlusImpl 功能 下沉到 BaseMapperPlus
  50. +
  51. update 去除 jdk17 标签 由于很多组件还未适配 导致一些问题
  52. +
  53. udpate 代码生成预览支持复制内容
  54. +
  55. update 用户导入提示溢出则显示滚动条
  56. +
  57. update 路由支持单独配置菜单或角色权限
  58. +
  59. update 优化web拦截器 使用原生接口处理 默认非生产环境开启
  60. +
  61. update 调整监控依赖 从 common 迁移到 framework
  62. +
  63. add 新增 Vue3 分支 与 代码生成模板(由于组件还未完善 仅供学习)
  64. +
  65. add 增加 RedisUtils 注册监听器方法
  66. +
  67. add 增加 自定义 Xss 校验注解 用户导入增加 Bean 校验
  68. +
  69. add oss下载增加 loading 层
  70. +
  71. add 新增图片预览组件
  72. +
  73. add 集成compression-webpack-plugin插件实现打包Gzip压缩
  74. +
  75. add 新增 SqlUtils 检查关键字方法
  76. +
  77. fix 修复 集群雪花id重复问题 使用网卡信息绑定生成
  78. +
  79. fix 修复 count 语法异常
  80. +
  81. fix 修复更改密码问题
  82. +
  83. fix 修复sql关键字处理 防止解析器报错
  84. +
  85. fix 修复 TreeBuildUtils 顶节点不为 0 问题
  86. +
  87. fix 修复 SysOssConfig 主键类型错误
  88. +
  89. fix 修复代码生成 导出注解错误
  90. +
  91. fix 修复 redisson 集群模式 路径未匹配协议头问题
  92. +
  93. fix 修复打包后字体图标偶现的乱码问题
  94. +
  95. fix 修复版本差异导致的懒加载报错问题
  96. +
  97. fix 修复代码生成字典组重复问题
  98. +
  99. remove 删除 jjwt 无用依赖
  100. +
  101. remove 移除过期 用户导入
  102. +
  103. remove 移除过期工具 DictUtils
  104. +
+
  1. update [重磅更新] 重构 Excel 导入 支持 Validator 校验 支持自定义监听器
  2. @@ -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 @@ + + + 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