mirror of
https://github.com/yangzongzhuan/RuoYi-Vue.git
synced 2025-12-24 22:17:12 +00:00
Compare commits
27 Commits
dependabot
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e83011d56 | ||
|
|
b12dab2d2e | ||
|
|
7b75f9ac0b | ||
|
|
4615293be9 | ||
|
|
ba5cf9de6e | ||
|
|
6de392bac2 | ||
|
|
49f62e565a | ||
|
|
5579b57bef | ||
|
|
88609b3b24 | ||
|
|
03f3f47397 | ||
|
|
b5400d962b | ||
|
|
e7ef3241c5 | ||
|
|
eb6878e18f | ||
|
|
8499225192 | ||
|
|
4a5e45d160 | ||
|
|
188e50ff1c | ||
|
|
bd66cc7260 | ||
|
|
866b47000c | ||
|
|
f38f8b2c3e | ||
|
|
faa86ac946 | ||
|
|
ad280e824c | ||
|
|
6e1aa42ebe | ||
|
|
315901041f | ||
|
|
91263711d4 | ||
|
|
9372d3401f | ||
|
|
0eaa090f4b | ||
|
|
a5adee3c5f |
@@ -1,11 +1,11 @@
|
||||
<p align="center">
|
||||
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-d3d0a9303e11d522a06cd263f3079027715.png">
|
||||
</p>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.9.0</h1>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.9.1</h1>
|
||||
<h4 align="center">基于SpringBoot+Vue前后端分离的Java快速开发框架</h4>
|
||||
<p align="center">
|
||||
<a href="https://gitee.com/y_project/RuoYi-Vue/stargazers"><img src="https://gitee.com/y_project/RuoYi-Vue/badge/star.svg?theme=dark"></a>
|
||||
<a href="https://gitee.com/y_project/RuoYi-Vue"><img src="https://img.shields.io/badge/RuoYi-v3.9.0-brightgreen.svg"></a>
|
||||
<a href="https://gitee.com/y_project/RuoYi-Vue"><img src="https://img.shields.io/badge/RuoYi-v3.9.1-brightgreen.svg"></a>
|
||||
<a href="https://gitee.com/y_project/RuoYi-Vue/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
|
||||
</p>
|
||||
|
||||
|
||||
22
pom.xml
22
pom.xml
@@ -6,32 +6,32 @@
|
||||
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.1</version>
|
||||
|
||||
<name>ruoyi</name>
|
||||
<url>http://www.ruoyi.vip</url>
|
||||
<description>若依管理系统</description>
|
||||
|
||||
<properties>
|
||||
<ruoyi.version>3.9.0</ruoyi.version>
|
||||
<ruoyi.version>3.9.1</ruoyi.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<java.version>1.8</java.version>
|
||||
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
|
||||
<spring-boot.version>2.5.15</spring-boot.version>
|
||||
<druid.version>1.2.23</druid.version>
|
||||
<bitwalker.version>1.21</bitwalker.version>
|
||||
<druid.version>1.2.27</druid.version>
|
||||
<yauaa.version>7.32.0</yauaa.version>
|
||||
<swagger.version>3.0.0</swagger.version>
|
||||
<kaptcha.version>2.3.3</kaptcha.version>
|
||||
<pagehelper.boot.version>1.4.7</pagehelper.boot.version>
|
||||
<fastjson.version>2.0.58</fastjson.version>
|
||||
<oshi.version>6.8.3</oshi.version>
|
||||
<commons.io.version>2.19.0</commons.io.version>
|
||||
<fastjson.version>2.0.60</fastjson.version>
|
||||
<oshi.version>6.9.1</oshi.version>
|
||||
<commons.io.version>2.21.0</commons.io.version>
|
||||
<poi.version>4.1.2</poi.version>
|
||||
<velocity.version>2.3</velocity.version>
|
||||
<jwt.version>0.9.1</jwt.version>
|
||||
<!-- override dependency version -->
|
||||
<tomcat.version>9.0.108</tomcat.version>
|
||||
<tomcat.version>9.0.112</tomcat.version>
|
||||
<logback.version>1.2.13</logback.version>
|
||||
<spring-security.version>5.7.14</spring-security.version>
|
||||
<spring-framework.version>5.3.39</spring-framework.version>
|
||||
@@ -109,9 +109,9 @@
|
||||
|
||||
<!-- 解析客户端操作系统、浏览器等 -->
|
||||
<dependency>
|
||||
<groupId>eu.bitwalker</groupId>
|
||||
<artifactId>UserAgentUtils</artifactId>
|
||||
<version>${bitwalker.version}</version>
|
||||
<groupId>nl.basjes.parse.useragent</groupId>
|
||||
<artifactId>yauaa</artifactId>
|
||||
<version>${yauaa.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- pagehelper 分页插件 -->
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>ruoyi</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.1</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -34,7 +34,7 @@ public class CommonController
|
||||
@Autowired
|
||||
private ServerConfig serverConfig;
|
||||
|
||||
private static final String FILE_DELIMETER = ",";
|
||||
private static final String FILE_DELIMITER = ",";
|
||||
|
||||
/**
|
||||
* 通用下载请求
|
||||
@@ -119,10 +119,10 @@ public class CommonController
|
||||
originalFilenames.add(file.getOriginalFilename());
|
||||
}
|
||||
AjaxResult ajax = AjaxResult.success();
|
||||
ajax.put("urls", StringUtils.join(urls, FILE_DELIMETER));
|
||||
ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMETER));
|
||||
ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMETER));
|
||||
ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMETER));
|
||||
ajax.put("urls", StringUtils.join(urls, FILE_DELIMITER));
|
||||
ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMITER));
|
||||
ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMITER));
|
||||
ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMITER));
|
||||
return ajax;
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
@@ -96,7 +96,8 @@ public class SysProfileController extends BaseController
|
||||
String newPassword = params.get("newPassword");
|
||||
LoginUser loginUser = getLoginUser();
|
||||
Long userId = loginUser.getUserId();
|
||||
String password = loginUser.getPassword();
|
||||
SysUser user = userService.selectUserById(userId);
|
||||
String password = user.getPassword();
|
||||
if (!SecurityUtils.matchesPassword(oldPassword, password))
|
||||
{
|
||||
return error("修改密码失败,旧密码错误");
|
||||
|
||||
@@ -3,7 +3,7 @@ ruoyi:
|
||||
# 名称
|
||||
name: RuoYi
|
||||
# 版本
|
||||
version: 3.9.0
|
||||
version: 3.9.1
|
||||
# 版权年份
|
||||
copyrightYear: 2025
|
||||
# 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>ruoyi</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.1</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -77,12 +77,6 @@
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- yml解析器 -->
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Token生成与解析-->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
@@ -109,8 +103,8 @@
|
||||
|
||||
<!-- 解析客户端操作系统、浏览器等 -->
|
||||
<dependency>
|
||||
<groupId>eu.bitwalker</groupId>
|
||||
<artifactId>UserAgentUtils</artifactId>
|
||||
<groupId>nl.basjes.parse.useragent</groupId>
|
||||
<artifactId>yauaa</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- servlet包 -->
|
||||
|
||||
@@ -83,12 +83,12 @@ public class Constants
|
||||
/**
|
||||
* 角色权限分隔符
|
||||
*/
|
||||
public static final String ROLE_DELIMETER = ",";
|
||||
public static final String ROLE_DELIMITER = ",";
|
||||
|
||||
/**
|
||||
* 权限标识分隔符
|
||||
*/
|
||||
public static final String PERMISSION_DELIMETER = ",";
|
||||
public static final String PERMISSION_DELIMITER = ",";
|
||||
|
||||
/**
|
||||
* 验证码有效期(分钟)
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.util.List;
|
||||
import javax.validation.constraints.*;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.ruoyi.common.annotation.Excel;
|
||||
import com.ruoyi.common.annotation.Excel.ColumnType;
|
||||
import com.ruoyi.common.annotation.Excel.Type;
|
||||
@@ -200,6 +201,7 @@ public class SysUser extends BaseEntity
|
||||
this.avatar = avatar;
|
||||
}
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
|
||||
public String getPassword()
|
||||
{
|
||||
return password;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.ruoyi.common.utils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.ruoyi.common.constant.CacheConstants;
|
||||
import com.ruoyi.common.core.domain.entity.SysDictData;
|
||||
@@ -89,37 +91,25 @@ public class DictUtils
|
||||
*/
|
||||
public static String getDictLabel(String dictType, String dictValue, String separator)
|
||||
{
|
||||
StringBuilder propertyString = new StringBuilder();
|
||||
List<SysDictData> datas = getDictCache(dictType);
|
||||
if (StringUtils.isNull(datas))
|
||||
if (StringUtils.isNull(datas) || StringUtils.isEmpty(dictValue))
|
||||
{
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
if (StringUtils.containsAny(separator, dictValue))
|
||||
Map<String, String> dictMap = datas.stream().collect(HashMap::new, (map, dict) -> map.put(dict.getDictValue(), dict.getDictLabel()), Map::putAll);
|
||||
if (!StringUtils.contains(dictValue, separator))
|
||||
{
|
||||
for (SysDictData dict : datas)
|
||||
return dictMap.getOrDefault(dictValue, StringUtils.EMPTY);
|
||||
}
|
||||
StringBuilder labelBuilder = new StringBuilder();
|
||||
for (String seperatedValue : dictValue.split(separator))
|
||||
{
|
||||
if (dictMap.containsKey(seperatedValue))
|
||||
{
|
||||
for (String value : dictValue.split(separator))
|
||||
{
|
||||
if (value.equals(dict.getDictValue()))
|
||||
{
|
||||
propertyString.append(dict.getDictLabel()).append(separator);
|
||||
break;
|
||||
}
|
||||
}
|
||||
labelBuilder.append(dictMap.get(seperatedValue)).append(separator);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (SysDictData dict : datas)
|
||||
{
|
||||
if (dictValue.equals(dict.getDictValue()))
|
||||
{
|
||||
return dict.getDictLabel();
|
||||
}
|
||||
}
|
||||
}
|
||||
return StringUtils.stripEnd(propertyString.toString(), separator);
|
||||
return StringUtils.removeEnd(labelBuilder.toString(), separator);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,37 +122,25 @@ public class DictUtils
|
||||
*/
|
||||
public static String getDictValue(String dictType, String dictLabel, String separator)
|
||||
{
|
||||
StringBuilder propertyString = new StringBuilder();
|
||||
List<SysDictData> datas = getDictCache(dictType);
|
||||
if (StringUtils.isNull(datas))
|
||||
if (StringUtils.isNull(datas) || StringUtils.isEmpty(dictLabel))
|
||||
{
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
if (StringUtils.containsAny(separator, dictLabel))
|
||||
Map<String, String> dictMap = datas.stream().collect(HashMap::new, (map, dict) -> map.put(dict.getDictLabel(), dict.getDictValue()), Map::putAll);
|
||||
if (!StringUtils.contains(dictLabel, separator))
|
||||
{
|
||||
for (SysDictData dict : datas)
|
||||
return dictMap.getOrDefault(dictLabel, StringUtils.EMPTY);
|
||||
}
|
||||
StringBuilder valueBuilder = new StringBuilder();
|
||||
for (String seperatedValue : dictLabel.split(separator))
|
||||
{
|
||||
if (dictMap.containsKey(seperatedValue))
|
||||
{
|
||||
for (String label : dictLabel.split(separator))
|
||||
{
|
||||
if (label.equals(dict.getDictLabel()))
|
||||
{
|
||||
propertyString.append(dict.getDictValue()).append(separator);
|
||||
break;
|
||||
}
|
||||
}
|
||||
valueBuilder.append(dictMap.get(seperatedValue)).append(separator);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (SysDictData dict : datas)
|
||||
{
|
||||
if (dictLabel.equals(dict.getDictLabel()))
|
||||
{
|
||||
return dict.getDictValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
return StringUtils.stripEnd(propertyString.toString(), separator);
|
||||
return StringUtils.removeEnd(valueBuilder.toString(), separator);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
package com.ruoyi.common.utils.http;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import nl.basjes.parse.useragent.UserAgent;
|
||||
import nl.basjes.parse.useragent.UserAgentAnalyzer;
|
||||
|
||||
/**
|
||||
* UserAgent解析工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class UserAgentUtils
|
||||
{
|
||||
public static final String UNKNOWN = "";
|
||||
|
||||
// 浏览器正则表达式模式
|
||||
private static final Pattern CHROME_PATTERN = Pattern.compile("Chrome/(\\d+)(?:\\.\\d+)*");
|
||||
private static final Pattern FIREFOX_PATTERN = Pattern.compile("Firefox/(\\d+)(?:\\.\\d+)*");
|
||||
private static final Pattern EDGE_PATTERN = Pattern.compile("Edg(?:e)?/(\\d+)(?:\\.\\d+)*");
|
||||
private static final Pattern SAFARI_PATTERN = Pattern.compile("Version/(\\d+)(?:\\.\\d+)*");
|
||||
private static final Pattern OPERA_PATTERN = Pattern.compile("Opera/(\\d+)(?:\\.\\d+)*");
|
||||
private static final Pattern IE_PATTERN = Pattern.compile("(?:MSIE |Trident/.*rv:)(\\d+)(?:\\.\\d+)*");
|
||||
private static final Pattern SAMSUNG_PATTERN = Pattern.compile("SamsungBrowser/(\\d+)(?:\\.\\d+)*");
|
||||
private static final Pattern UC_PATTERN = Pattern.compile("UCBrowser/(\\d+)(?:\\.\\d+)*");
|
||||
private static final Pattern QQ_PATTERN = Pattern.compile("QQBrowser/(\\d+)(?:\\.\\d+)*");
|
||||
private static final Pattern WECHAT_PATTERN = Pattern.compile("MicroMessenger/(\\d+)(?:\\.\\d+)*");
|
||||
private static final Pattern BAIDU_PATTERN = Pattern.compile("baidubrowser/(\\d+)(?:\\.\\d+)*");
|
||||
|
||||
// 操作系统正则表达式模式
|
||||
private static final Pattern WINDOWS_PATTERN = Pattern.compile("Windows NT (\\d+\\.\\d+)");
|
||||
private static final Pattern MACOS_PATTERN = Pattern.compile("Mac OS X (\\d+[_\\d]*)");
|
||||
private static final Pattern ANDROID_PATTERN = Pattern.compile("Android (\\d+)(?:\\.\\d+)*");
|
||||
private static final Pattern IOS_PATTERN = Pattern.compile("OS[\\s_](\\d+)(?:_\\d+)*");
|
||||
private static final Pattern LINUX_PATTERN = Pattern.compile("Linux");
|
||||
private static final Pattern CHROMEOS_PATTERN = Pattern.compile("CrOS");
|
||||
|
||||
private static final UserAgentAnalyzer userAgentAnalyzer = UserAgentAnalyzer
|
||||
.newBuilder().hideMatcherLoadStats()
|
||||
.withCache(5000)
|
||||
.showMinimalVersion()
|
||||
.withField(UserAgent.AGENT_NAME_VERSION)
|
||||
.withField(UserAgent.OPERATING_SYSTEM_NAME_VERSION)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* 获取客户端浏览器
|
||||
*/
|
||||
public static String getBrowser(String userAgent)
|
||||
{
|
||||
UserAgent.ImmutableUserAgent iua = userAgentAnalyzer.parse(userAgent);
|
||||
String agentNameVersion = iua.get(UserAgent.AGENT_NAME_VERSION).getValue();
|
||||
if (StringUtils.isBlank(agentNameVersion) || agentNameVersion.contains("??"))
|
||||
{
|
||||
return formatBrowser(userAgent);
|
||||
}
|
||||
return agentNameVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端操作系统
|
||||
*/
|
||||
public static String getOperatingSystem(String userAgent)
|
||||
{
|
||||
UserAgent.ImmutableUserAgent iua = userAgentAnalyzer.parse(userAgent);
|
||||
String operatingSystemNameVersion = iua.get(UserAgent.OPERATING_SYSTEM_NAME_VERSION).getValue();
|
||||
if (StringUtils.isBlank(operatingSystemNameVersion) || operatingSystemNameVersion.contains("??"))
|
||||
{
|
||||
return formatOperatingSystem(userAgent);
|
||||
}
|
||||
return operatingSystemNameVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* 全面浏览器检测
|
||||
*/
|
||||
private static String formatBrowser(String browser)
|
||||
{
|
||||
// Chrome系列浏览器
|
||||
Matcher chromeMatcher = CHROME_PATTERN.matcher(browser);
|
||||
if (chromeMatcher.find() && (browser.contains("Chrome") || browser.contains("CriOS")))
|
||||
{
|
||||
return "Chrome" + chromeMatcher.group(1);
|
||||
}
|
||||
// Firefox
|
||||
Matcher firefoxMatcher = FIREFOX_PATTERN.matcher(browser);
|
||||
if (firefoxMatcher.find())
|
||||
{
|
||||
return "Firefox" + firefoxMatcher.group(1);
|
||||
}
|
||||
// Edge浏览器
|
||||
Matcher edgeMatcher = EDGE_PATTERN.matcher(browser);
|
||||
if (edgeMatcher.find())
|
||||
{
|
||||
return "Edge" + edgeMatcher.group(1);
|
||||
}
|
||||
// Safari浏览器(需排除Chrome)
|
||||
Matcher safariMatcher = SAFARI_PATTERN.matcher(browser);
|
||||
if (safariMatcher.find() && !browser.contains("Chrome"))
|
||||
{
|
||||
return "Safari" + safariMatcher.group(1);
|
||||
}
|
||||
// 微信内置浏览器
|
||||
Matcher wechatMatcher = WECHAT_PATTERN.matcher(browser);
|
||||
if (wechatMatcher.find())
|
||||
{
|
||||
return "WeChat" + wechatMatcher.group(1);
|
||||
}
|
||||
// UC浏览器
|
||||
Matcher ucMatcher = UC_PATTERN.matcher(browser);
|
||||
if (ucMatcher.find())
|
||||
{
|
||||
return "UC Browser" + ucMatcher.group(1);
|
||||
}
|
||||
// QQ浏览器
|
||||
Matcher qqMatcher = QQ_PATTERN.matcher(browser);
|
||||
if (qqMatcher.find())
|
||||
{
|
||||
return "QQ Browser" + qqMatcher.group(1);
|
||||
}
|
||||
// 百度浏览器
|
||||
Matcher baiduMatcher = BAIDU_PATTERN.matcher(browser);
|
||||
if (baiduMatcher.find())
|
||||
{
|
||||
return "Baidu Browser" + baiduMatcher.group(1);
|
||||
}
|
||||
// Samsung浏览器
|
||||
Matcher samsungMatcher = SAMSUNG_PATTERN.matcher(browser);
|
||||
if (samsungMatcher.find())
|
||||
{
|
||||
return "Samsung Browser" + samsungMatcher.group(1);
|
||||
}
|
||||
// Opera浏览器
|
||||
Matcher operaMatcher = OPERA_PATTERN.matcher(browser);
|
||||
if (operaMatcher.find())
|
||||
{
|
||||
return "Opera" + operaMatcher.group(1);
|
||||
}
|
||||
// IE浏览器
|
||||
Matcher ieMatcher = IE_PATTERN.matcher(browser);
|
||||
if (ieMatcher.find())
|
||||
{
|
||||
return "Internet Explorer" + ieMatcher.group(1);
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测操作系统
|
||||
*/
|
||||
private static String formatOperatingSystem(String operatingSystem)
|
||||
{
|
||||
// Windows系统
|
||||
Matcher windowsMatcher = WINDOWS_PATTERN.matcher(operatingSystem);
|
||||
if (windowsMatcher.find())
|
||||
{
|
||||
return "Windows" + getWindowsVersionDisplay(windowsMatcher.group(1));
|
||||
}
|
||||
// macOS系统
|
||||
Matcher macMatcher = MACOS_PATTERN.matcher(operatingSystem);
|
||||
if (macMatcher.find())
|
||||
{
|
||||
String version = macMatcher.group(1).replace("_", ".");
|
||||
return "macOS" + extractMajorVersion(version);
|
||||
}
|
||||
// Android系统
|
||||
Matcher androidMatcher = ANDROID_PATTERN.matcher(operatingSystem);
|
||||
if (androidMatcher.find())
|
||||
{
|
||||
return "Android" + extractMajorVersion(androidMatcher.group(1));
|
||||
}
|
||||
// iOS系统
|
||||
Matcher iosMatcher = IOS_PATTERN.matcher(operatingSystem);
|
||||
if (iosMatcher.find() && (operatingSystem.contains("iPhone") || operatingSystem.contains("iPad")))
|
||||
{
|
||||
return "iOS" + extractMajorVersion(iosMatcher.group(1));
|
||||
}
|
||||
// Linux系统
|
||||
if (LINUX_PATTERN.matcher(operatingSystem).find() && !operatingSystem.contains("Android"))
|
||||
{
|
||||
return "Linux";
|
||||
}
|
||||
// Chrome OS
|
||||
if (CHROMEOS_PATTERN.matcher(operatingSystem).find())
|
||||
{
|
||||
return "Chrome OS";
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取优化的主版本号
|
||||
*/
|
||||
private static String extractMajorVersion(String fullVersion)
|
||||
{
|
||||
if (StringUtils.isEmpty(fullVersion))
|
||||
{
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
try
|
||||
{
|
||||
// 清理版本号中的非数字字符
|
||||
String cleanVersion = fullVersion.replaceAll("[^0-9.]", "");
|
||||
String[] parts = cleanVersion.split("\\.");
|
||||
if (parts.length > 0)
|
||||
{
|
||||
String firstPart = parts[0];
|
||||
if (firstPart.matches("\\d+"))
|
||||
{
|
||||
int version = Integer.parseInt(firstPart);
|
||||
|
||||
// 处理三位数版本号(如142 -> 14)
|
||||
if (version >= 100)
|
||||
{
|
||||
return String.valueOf(version / 10);
|
||||
}
|
||||
return firstPart;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
// 版本号解析失败,返回原始值
|
||||
}
|
||||
return fullVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Windows版本号显示优化
|
||||
*/
|
||||
private static String getWindowsVersionDisplay(String version)
|
||||
{
|
||||
switch (version)
|
||||
{
|
||||
case "10.0":
|
||||
return "10";
|
||||
case "6.3":
|
||||
return "8.1";
|
||||
case "6.2":
|
||||
return "8";
|
||||
case "6.1":
|
||||
return "7";
|
||||
case "6.0":
|
||||
return "Vista";
|
||||
case "5.1":
|
||||
return "XP";
|
||||
case "5.0":
|
||||
return "2000";
|
||||
default:
|
||||
return extractMajorVersion(version);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -174,12 +174,12 @@ public class ExcelUtil<T>
|
||||
/**
|
||||
* 对象的子列表方法
|
||||
*/
|
||||
private Method subMethod;
|
||||
private Map<String, Method> subMethods;
|
||||
|
||||
/**
|
||||
* 对象的子列表属性
|
||||
*/
|
||||
private List<Field> subFields;
|
||||
private Map<String, List<Field>> subFieldsMap;
|
||||
|
||||
/**
|
||||
* 统计列表
|
||||
@@ -252,7 +252,10 @@ public class ExcelUtil<T>
|
||||
int titleLastCol = this.fields.size() - 1;
|
||||
if (isSubList())
|
||||
{
|
||||
titleLastCol = titleLastCol + subFields.size() - 1;
|
||||
for (List<Field> currentSubFields : subFieldsMap.values())
|
||||
{
|
||||
titleLastCol = titleLastCol + currentSubFields.size() - 1;
|
||||
}
|
||||
}
|
||||
Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0);
|
||||
titleRow.setHeightInPoints(30);
|
||||
@@ -272,16 +275,17 @@ public class ExcelUtil<T>
|
||||
{
|
||||
Row subRow = sheet.createRow(rownum);
|
||||
int column = 0;
|
||||
int subFieldSize = subFields != null ? subFields.size() : 0;
|
||||
for (Object[] objects : fields)
|
||||
{
|
||||
Field field = (Field) objects[0];
|
||||
Excel attr = (Excel) objects[1];
|
||||
CellStyle cellStyle = styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()));
|
||||
if (Collection.class.isAssignableFrom(field.getType()))
|
||||
{
|
||||
Cell cell = subRow.createCell(column);
|
||||
cell.setCellValue(attr.name());
|
||||
cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));
|
||||
cell.setCellStyle(cellStyle);
|
||||
int subFieldSize = subFieldsMap != null ? subFieldsMap.get(field.getName()).size() : 0;
|
||||
if (subFieldSize > 1)
|
||||
{
|
||||
CellRangeAddress cellAddress = new CellRangeAddress(rownum, rownum, column, column + subFieldSize - 1);
|
||||
@@ -293,7 +297,7 @@ public class ExcelUtil<T>
|
||||
{
|
||||
Cell cell = subRow.createCell(column++);
|
||||
cell.setCellValue(attr.name());
|
||||
cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));
|
||||
cell.setCellStyle(cellStyle);
|
||||
}
|
||||
}
|
||||
rownum++;
|
||||
@@ -374,7 +378,11 @@ public class ExcelUtil<T>
|
||||
Map<String, Integer> cellMap = new HashMap<String, Integer>();
|
||||
// 获取表头
|
||||
Row heard = sheet.getRow(titleNum);
|
||||
for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++)
|
||||
if (heard == null)
|
||||
{
|
||||
throw new UtilException("文件标题行为空,请检查Excel文件格式");
|
||||
}
|
||||
for (int i = 0; i < heard.getLastCellNum(); i++)
|
||||
{
|
||||
Cell cell = heard.getCell(i);
|
||||
if (StringUtils.isNotNull(cell))
|
||||
@@ -382,10 +390,6 @@ public class ExcelUtil<T>
|
||||
String value = this.getCellValue(heard, i).toString();
|
||||
cellMap.put(value, i);
|
||||
}
|
||||
else
|
||||
{
|
||||
cellMap.put(null, i);
|
||||
}
|
||||
}
|
||||
// 有数据时才处理 得到类的所有field.
|
||||
List<Object[]> fields = this.getFields();
|
||||
@@ -697,7 +701,8 @@ public class ExcelUtil<T>
|
||||
Excel excel = (Excel) os[1];
|
||||
if (Collection.class.isAssignableFrom(field.getType()))
|
||||
{
|
||||
for (Field subField : subFields)
|
||||
List<Field> currentSubFields = subFieldsMap.get(field.getName());
|
||||
for (Field subField : currentSubFields)
|
||||
{
|
||||
Excel subExcel = subField.getAnnotation(Excel.class);
|
||||
this.createHeadCell(subExcel, row, column++);
|
||||
@@ -710,7 +715,7 @@ public class ExcelUtil<T>
|
||||
}
|
||||
if (Type.EXPORT.equals(type))
|
||||
{
|
||||
fillExcelData(index, row);
|
||||
fillExcelData(index);
|
||||
addStatisticsRow();
|
||||
}
|
||||
}
|
||||
@@ -720,10 +725,9 @@ public class ExcelUtil<T>
|
||||
* 填充excel数据
|
||||
*
|
||||
* @param index 序号
|
||||
* @param row 单元格行
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void fillExcelData(int index, Row row)
|
||||
public void fillExcelData(int index)
|
||||
{
|
||||
int startNo = index * sheetSize;
|
||||
int endNo = Math.min(startNo + sheetSize, list.size());
|
||||
@@ -731,7 +735,7 @@ public class ExcelUtil<T>
|
||||
|
||||
for (int i = startNo; i < endNo; i++)
|
||||
{
|
||||
row = sheet.createRow(currentRowNum);
|
||||
Row row = sheet.createRow(currentRowNum);
|
||||
T vo = (T) list.get(i);
|
||||
int column = 0;
|
||||
int maxSubListSize = getCurrentMaxSubListSize(vo);
|
||||
@@ -744,6 +748,7 @@ public class ExcelUtil<T>
|
||||
try
|
||||
{
|
||||
Collection<?> subList = (Collection<?>) getTargetValue(vo, field, excel);
|
||||
List<Field> currentSubFields = subFieldsMap.get(field.getName());
|
||||
if (subList != null && !subList.isEmpty())
|
||||
{
|
||||
int subIndex = 0;
|
||||
@@ -756,15 +761,15 @@ public class ExcelUtil<T>
|
||||
}
|
||||
|
||||
int subColumn = column;
|
||||
for (Field subField : subFields)
|
||||
for (Field subField : currentSubFields)
|
||||
{
|
||||
Excel subExcel = subField.getAnnotation(Excel.class);
|
||||
addCell(subExcel, subRow, (T) subVo, subField, subColumn++);
|
||||
}
|
||||
subIndex++;
|
||||
}
|
||||
column += subFields.size();
|
||||
}
|
||||
column += currentSubFields.size();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -1131,7 +1136,7 @@ public class ExcelUtil<T>
|
||||
{
|
||||
// 创建cell
|
||||
cell = row.createCell(column);
|
||||
if (isSubListValue(vo) && getListCellValue(vo).size() > 1 && attr.needMerge())
|
||||
if (isSubListValue(vo) && getListCellValue(vo) > 1 && attr.needMerge())
|
||||
{
|
||||
if (subMergedLastRowNum >= subMergedFirstRowNum)
|
||||
{
|
||||
@@ -1238,18 +1243,36 @@ public class ExcelUtil<T>
|
||||
public void setXSSFValidationWithHidden(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, int firstCol, int endCol)
|
||||
{
|
||||
String hideSheetName = "combo_" + firstCol + "_" + endCol;
|
||||
Sheet hideSheet = wb.createSheet(hideSheetName); // 用于存储 下拉菜单数据
|
||||
for (int i = 0; i < textlist.length; i++)
|
||||
Sheet hideSheet = null;
|
||||
String hideSheetDataName = hideSheetName + "_data";
|
||||
Name name = wb.getName(hideSheetDataName);
|
||||
if (name != null)
|
||||
{
|
||||
hideSheet.createRow(i).createCell(0).setCellValue(textlist[i]);
|
||||
// 名称已存在,尝试从名称的引用中找到sheet名称
|
||||
String refersToFormula = name.getRefersToFormula();
|
||||
if (StringUtils.isNotEmpty(refersToFormula) && refersToFormula.contains("!"))
|
||||
{
|
||||
String sheetNameFromFormula = refersToFormula.substring(0, refersToFormula.indexOf("!"));
|
||||
hideSheet = wb.getSheet(sheetNameFromFormula);
|
||||
}
|
||||
}
|
||||
// 创建名称,可被其他单元格引用
|
||||
Name name = wb.createName();
|
||||
name.setNameName(hideSheetName + "_data");
|
||||
name.setRefersToFormula(hideSheetName + "!$A$1:$A$" + textlist.length);
|
||||
|
||||
if (hideSheet == null)
|
||||
{
|
||||
hideSheet = wb.createSheet(hideSheetName); // 用于存储 下拉菜单数据
|
||||
for (int i = 0; i < textlist.length; i++)
|
||||
{
|
||||
hideSheet.createRow(i).createCell(0).setCellValue(textlist[i]);
|
||||
}
|
||||
// 创建名称,可被其他单元格引用
|
||||
name = wb.createName();
|
||||
name.setNameName(hideSheetDataName);
|
||||
name.setRefersToFormula(hideSheetName + "!$A$1:$A$" + textlist.length);
|
||||
}
|
||||
|
||||
DataValidationHelper helper = sheet.getDataValidationHelper();
|
||||
// 加载下拉列表内容
|
||||
DataValidationConstraint constraint = helper.createFormulaListConstraint(hideSheetName + "_data");
|
||||
DataValidationConstraint constraint = helper.createFormulaListConstraint(hideSheetDataName);
|
||||
// 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列
|
||||
CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
|
||||
// 数据有效性对象
|
||||
@@ -1537,6 +1560,8 @@ public class ExcelUtil<T>
|
||||
{
|
||||
List<Object[]> fields = new ArrayList<Object[]>();
|
||||
List<Field> tempFields = new ArrayList<>();
|
||||
subFieldsMap = new HashMap<>();
|
||||
subMethods = new HashMap<>();
|
||||
tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
|
||||
tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
|
||||
if (StringUtils.isNotEmpty(includeFields))
|
||||
@@ -1584,10 +1609,11 @@ public class ExcelUtil<T>
|
||||
}
|
||||
if (Collection.class.isAssignableFrom(field.getType()))
|
||||
{
|
||||
subMethod = getSubMethod(field.getName(), clazz);
|
||||
String fieldName = field.getName();
|
||||
subMethods.put(fieldName, getSubMethod(fieldName, clazz));
|
||||
ParameterizedType pt = (ParameterizedType) field.getGenericType();
|
||||
Class<?> subClass = (Class<?>) pt.getActualTypeArguments()[0];
|
||||
this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class);
|
||||
subFieldsMap.put(fieldName, FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1656,7 +1682,8 @@ public class ExcelUtil<T>
|
||||
{
|
||||
this.sheet = wb.createSheet();
|
||||
this.createTitle();
|
||||
wb.setSheetName(index, sheetName + index);
|
||||
int actualIndex = wb.getSheetIndex(this.sheet);
|
||||
wb.setSheetName(actualIndex, sheetName + index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1839,7 +1866,7 @@ public class ExcelUtil<T>
|
||||
*/
|
||||
public boolean isSubList()
|
||||
{
|
||||
return StringUtils.isNotNull(subFields) && subFields.size() > 0;
|
||||
return !StringUtils.isEmpty(subFieldsMap);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1847,24 +1874,32 @@ public class ExcelUtil<T>
|
||||
*/
|
||||
public boolean isSubListValue(T vo)
|
||||
{
|
||||
return StringUtils.isNotNull(subFields) && subFields.size() > 0 && StringUtils.isNotNull(getListCellValue(vo)) && getListCellValue(vo).size() > 0;
|
||||
return !StringUtils.isEmpty(subFieldsMap) && getListCellValue(vo) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取集合的值
|
||||
*/
|
||||
public Collection<?> getListCellValue(Object obj)
|
||||
public int getListCellValue(Object obj)
|
||||
{
|
||||
Object value;
|
||||
Collection<?> value;
|
||||
int max = 0;
|
||||
try
|
||||
{
|
||||
value = subMethod.invoke(obj, new Object[] {});
|
||||
for (String s : subMethods.keySet())
|
||||
{
|
||||
value = (Collection<?>) subMethods.get(s).invoke(obj);
|
||||
if (value.size() > max)
|
||||
{
|
||||
max = value.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return new ArrayList<Object>();
|
||||
return 0;
|
||||
}
|
||||
return (Collection<?>) value;
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,7 +13,7 @@ public class SqlUtil
|
||||
/**
|
||||
* 定义常用的 sql关键字
|
||||
*/
|
||||
public static String SQL_REGEX = "\u000B|and |extractvalue|updatexml|sleep|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()";
|
||||
public static String SQL_REGEX = "\u000B|and |extractvalue|updatexml|sleep|information_schema|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()";
|
||||
|
||||
/**
|
||||
* 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>ruoyi</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.1</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ public class DataScopeAspect
|
||||
List<String> conditions = new ArrayList<String>();
|
||||
List<String> scopeCustomIds = new ArrayList<String>();
|
||||
user.getRoles().forEach(role -> {
|
||||
if (DATA_SCOPE_CUSTOM.equals(role.getDataScope()) && StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL) && StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
|
||||
if (DATA_SCOPE_CUSTOM.equals(role.getDataScope()) && StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL) && (StringUtils.isEmpty(permission) || StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))))
|
||||
{
|
||||
scopeCustomIds.add(Convert.toStr(role.getRoleId()));
|
||||
}
|
||||
@@ -107,7 +107,7 @@ public class DataScopeAspect
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
|
||||
if (StringUtils.isNotEmpty(permission) && !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -50,6 +50,9 @@ public class LogAspect
|
||||
/** 计算操作消耗时间 */
|
||||
private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<Long>("Cost Time");
|
||||
|
||||
/** 参数最大长度限制 */
|
||||
private static final int PARAM_MAX_LENGTH = 2000;
|
||||
|
||||
/**
|
||||
* 处理请求前执行
|
||||
*/
|
||||
@@ -172,16 +175,16 @@ public class LogAspect
|
||||
*/
|
||||
private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames) throws Exception
|
||||
{
|
||||
Map<?, ?> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
|
||||
String requestMethod = operLog.getRequestMethod();
|
||||
Map<?, ?> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
|
||||
if (StringUtils.isEmpty(paramsMap) && StringUtils.equalsAny(requestMethod, HttpMethod.PUT.name(), HttpMethod.POST.name(), HttpMethod.DELETE.name()))
|
||||
{
|
||||
String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
|
||||
operLog.setOperParam(StringUtils.substring(params, 0, 2000));
|
||||
operLog.setOperParam(params);
|
||||
}
|
||||
else
|
||||
{
|
||||
operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, 2000));
|
||||
operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, PARAM_MAX_LENGTH));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +193,7 @@ public class LogAspect
|
||||
*/
|
||||
private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames)
|
||||
{
|
||||
String params = "";
|
||||
StringBuilder params = new StringBuilder();
|
||||
if (paramsArray != null && paramsArray.length > 0)
|
||||
{
|
||||
for (Object o : paramsArray)
|
||||
@@ -200,15 +203,20 @@ public class LogAspect
|
||||
try
|
||||
{
|
||||
String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames));
|
||||
params += jsonObj.toString() + " ";
|
||||
params.append(jsonObj).append(" ");
|
||||
if (params.length() >= PARAM_MAX_LENGTH)
|
||||
{
|
||||
return StringUtils.substring(params.toString(), 0, PARAM_MAX_LENGTH);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("请求参数拼装异常 msg:{}, 参数:{}", e.getMessage(), paramsArray, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return params.trim();
|
||||
return params.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.ruoyi.common.constant.Constants;
|
||||
import com.ruoyi.common.utils.LogUtils;
|
||||
import com.ruoyi.common.utils.ServletUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.http.UserAgentUtils;
|
||||
import com.ruoyi.common.utils.ip.AddressUtils;
|
||||
import com.ruoyi.common.utils.ip.IpUtils;
|
||||
import com.ruoyi.common.utils.spring.SpringUtils;
|
||||
@@ -14,7 +15,6 @@ import com.ruoyi.system.domain.SysLogininfor;
|
||||
import com.ruoyi.system.domain.SysOperLog;
|
||||
import com.ruoyi.system.service.ISysLogininforService;
|
||||
import com.ruoyi.system.service.ISysOperLogService;
|
||||
import eu.bitwalker.useragentutils.UserAgent;
|
||||
|
||||
/**
|
||||
* 异步工厂(产生任务用)
|
||||
@@ -37,7 +37,7 @@ public class AsyncFactory
|
||||
public static TimerTask recordLogininfor(final String username, final String status, final String message,
|
||||
final Object... args)
|
||||
{
|
||||
final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
|
||||
final String userAgent = ServletUtils.getRequest().getHeader("User-Agent");
|
||||
final String ip = IpUtils.getIpAddr();
|
||||
return new TimerTask()
|
||||
{
|
||||
@@ -54,9 +54,9 @@ public class AsyncFactory
|
||||
// 打印信息到日志
|
||||
sys_user_logger.info(s.toString(), args);
|
||||
// 获取客户端操作系统
|
||||
String os = userAgent.getOperatingSystem().getName();
|
||||
String os = UserAgentUtils.getOperatingSystem(userAgent);
|
||||
// 获取客户端浏览器
|
||||
String browser = userAgent.getBrowser().getName();
|
||||
String browser = UserAgentUtils.getBrowser(userAgent);
|
||||
// 封装对象
|
||||
SysLogininfor logininfor = new SysLogininfor();
|
||||
logininfor.setUserName(username);
|
||||
|
||||
@@ -53,7 +53,7 @@ public class PermissionService
|
||||
/**
|
||||
* 验证用户是否具有以下任意一个权限
|
||||
*
|
||||
* @param permissions 以 PERMISSION_DELIMETER 为分隔符的权限列表
|
||||
* @param permissions 以 PERMISSION_DELIMITER 为分隔符的权限列表
|
||||
* @return 用户是否具有以下任意一个权限
|
||||
*/
|
||||
public boolean hasAnyPermi(String permissions)
|
||||
@@ -69,7 +69,7 @@ public class PermissionService
|
||||
}
|
||||
PermissionContextHolder.setContext(permissions);
|
||||
Set<String> authorities = loginUser.getPermissions();
|
||||
for (String permission : permissions.split(Constants.PERMISSION_DELIMETER))
|
||||
for (String permission : permissions.split(Constants.PERMISSION_DELIMITER))
|
||||
{
|
||||
if (permission != null && hasPermissions(authorities, permission))
|
||||
{
|
||||
@@ -121,7 +121,7 @@ public class PermissionService
|
||||
/**
|
||||
* 验证用户是否具有以下任意一个角色
|
||||
*
|
||||
* @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表
|
||||
* @param roles 以 ROLE_DELIMITER 为分隔符的角色列表
|
||||
* @return 用户是否具有以下任意一个角色
|
||||
*/
|
||||
public boolean hasAnyRoles(String roles)
|
||||
@@ -135,7 +135,7 @@ public class PermissionService
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (String role : roles.split(Constants.ROLE_DELIMETER))
|
||||
for (String role : roles.split(Constants.ROLE_DELIMITER))
|
||||
{
|
||||
if (hasRole(role))
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.util.Set;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import com.ruoyi.common.constant.Constants;
|
||||
import com.ruoyi.common.constant.UserConstants;
|
||||
import com.ruoyi.common.core.domain.entity.SysRole;
|
||||
import com.ruoyi.common.core.domain.entity.SysUser;
|
||||
@@ -39,7 +40,7 @@ public class SysPermissionService
|
||||
// 管理员拥有所有权限
|
||||
if (user.isAdmin())
|
||||
{
|
||||
roles.add("admin");
|
||||
roles.add(Constants.SUPER_ADMIN);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -60,7 +61,7 @@ public class SysPermissionService
|
||||
// 管理员拥有所有权限
|
||||
if (user.isAdmin())
|
||||
{
|
||||
perms.add("*:*:*");
|
||||
perms.add(Constants.ALL_PERMISSION);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -15,10 +15,10 @@ import com.ruoyi.common.core.domain.model.LoginUser;
|
||||
import com.ruoyi.common.core.redis.RedisCache;
|
||||
import com.ruoyi.common.utils.ServletUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.http.UserAgentUtils;
|
||||
import com.ruoyi.common.utils.ip.AddressUtils;
|
||||
import com.ruoyi.common.utils.ip.IpUtils;
|
||||
import com.ruoyi.common.utils.uuid.IdUtils;
|
||||
import eu.bitwalker.useragentutils.UserAgent;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
@@ -161,12 +161,12 @@ public class TokenService
|
||||
*/
|
||||
public void setUserAgent(LoginUser loginUser)
|
||||
{
|
||||
UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
|
||||
String userAgent = ServletUtils.getRequest().getHeader("User-Agent");
|
||||
String ip = IpUtils.getIpAddr();
|
||||
loginUser.setIpaddr(ip);
|
||||
loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
|
||||
loginUser.setBrowser(userAgent.getBrowser().getName());
|
||||
loginUser.setOs(userAgent.getOperatingSystem().getName());
|
||||
loginUser.setBrowser(UserAgentUtils.getBrowser(userAgent));
|
||||
loginUser.setOs(UserAgentUtils.getOperatingSystem(userAgent));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>ruoyi</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.1</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -334,7 +334,7 @@ function getList() {
|
||||
#foreach ($column in $columns)
|
||||
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
||||
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
||||
if (null != daterange${AttrName} && '' != daterange${AttrName}) {
|
||||
if (null != daterange${AttrName}.value && '' != daterange${AttrName}.value) {
|
||||
queryParams.value.params["begin${AttrName}"] = daterange${AttrName}.value[0]
|
||||
queryParams.value.params["end${AttrName}"] = daterange${AttrName}.value[1]
|
||||
}
|
||||
|
||||
@@ -415,7 +415,7 @@ function getList() {
|
||||
#foreach ($column in $columns)
|
||||
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
||||
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
||||
if (null != daterange${AttrName} && '' != daterange${AttrName}) {
|
||||
if (null != daterange${AttrName}.value && '' != daterange${AttrName}.value) {
|
||||
queryParams.value.params["begin${AttrName}"] = daterange${AttrName}.value[0]
|
||||
queryParams.value.params["end${AttrName}"] = daterange${AttrName}.value[1]
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>ruoyi</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.1</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>ruoyi</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.1</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ruoyi",
|
||||
"version": "3.9.0",
|
||||
"version": "3.9.1",
|
||||
"description": "若依管理系统",
|
||||
"author": "若依",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -130,6 +130,16 @@
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* horizontal el menu */
|
||||
.el-menu--horizontal .el-menu-item .svg-icon + span,
|
||||
.el-menu--horizontal .el-submenu__title .svg-icon + span {
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
.el-menu--horizontal .el-menu--popup {
|
||||
min-width: 120px !important;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.pagination-container .el-pagination > .el-pagination__jump {
|
||||
display: none !important;
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
margin-right: 16px;
|
||||
margin-right: 10px !important;
|
||||
}
|
||||
|
||||
.el-menu {
|
||||
@@ -116,18 +116,20 @@
|
||||
margin-left: 54px;
|
||||
}
|
||||
|
||||
.submenu-title-noDropdown {
|
||||
padding: 0 !important;
|
||||
position: relative;
|
||||
|
||||
.el-tooltip {
|
||||
.el-menu:not(.el-menu--horizontal) {
|
||||
.submenu-title-noDropdown {
|
||||
padding: 0 !important;
|
||||
position: relative;
|
||||
|
||||
.svg-icon {
|
||||
margin-left: 20px;
|
||||
.el-tooltip {
|
||||
padding: 0 !important;
|
||||
|
||||
.svg-icon {
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-submenu {
|
||||
overflow: hidden;
|
||||
|
||||
@@ -94,7 +94,6 @@ export default {
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
line-height: 50px;
|
||||
margin-left: 8px;
|
||||
.no-redirect {
|
||||
color: #97a8be;
|
||||
cursor: text;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-for="(item, index) in options">
|
||||
<template v-if="values.includes(item.value)">
|
||||
<template v-if="isValueMatch(item.value)">
|
||||
<span
|
||||
v-if="(item.raw.listClass == 'default' || item.raw.listClass == '') && (item.raw.cssClass == '' || item.raw.cssClass == null)"
|
||||
:key="item.value"
|
||||
@@ -36,7 +36,6 @@ export default {
|
||||
default: null,
|
||||
},
|
||||
value: [Number, String, Array],
|
||||
// 当未找到匹配的数据时,显示value
|
||||
showValue: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
@@ -54,6 +53,7 @@ export default {
|
||||
computed: {
|
||||
values() {
|
||||
if (this.value === null || typeof this.value === 'undefined' || this.value === '') return []
|
||||
if (typeof this.value === 'number' || typeof this.value === 'boolean') return [this.value]
|
||||
return Array.isArray(this.value) ? this.value.map(item => '' + item) : String(this.value).split(this.separator)
|
||||
},
|
||||
unmatch() {
|
||||
@@ -63,14 +63,18 @@ export default {
|
||||
// 传入值为数组
|
||||
let unmatch = false // 添加一个标志来判断是否有未匹配项
|
||||
this.values.forEach(item => {
|
||||
if (!this.options.some(v => v.value === item)) {
|
||||
if (!this.options.some(v => v.value == item)) {
|
||||
this.unmatchArray.push(item)
|
||||
unmatch = true // 如果有未匹配项,将标志设置为true
|
||||
}
|
||||
})
|
||||
return unmatch // 返回标志的值
|
||||
},
|
||||
|
||||
},
|
||||
methods: {
|
||||
isValueMatch(itemValue) {
|
||||
return this.values.some(val => val == itemValue)
|
||||
}
|
||||
},
|
||||
filters: {
|
||||
handleArray(array) {
|
||||
|
||||
@@ -162,7 +162,7 @@ export default {
|
||||
this.$store.dispatch('app/toggleSideBarHide', true)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -171,7 +171,7 @@ export default {
|
||||
float: left;
|
||||
height: 50px !important;
|
||||
line-height: 50px !important;
|
||||
color: #999093 !important;
|
||||
color: #303133 !important;
|
||||
padding: 0 5px !important;
|
||||
margin: 0 10px !important;
|
||||
}
|
||||
@@ -186,7 +186,7 @@ export default {
|
||||
float: left;
|
||||
height: 50px !important;
|
||||
line-height: 50px !important;
|
||||
color: #999093 !important;
|
||||
color: #303133 !important;
|
||||
padding: 0 5px !important;
|
||||
margin: 0 10px !important;
|
||||
}
|
||||
|
||||
@@ -80,6 +80,39 @@ export default {
|
||||
min-height: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 移动端fixed-header优化 */
|
||||
@media screen and (max-width: 991px) {
|
||||
.fixed-header + .app-main {
|
||||
padding-bottom: max(60px, calc(constant(safe-area-inset-bottom) + 40px));
|
||||
padding-bottom: max(60px, calc(env(safe-area-inset-bottom) + 40px));
|
||||
overscroll-behavior-y: none;
|
||||
}
|
||||
|
||||
.hasTagsView .fixed-header + .app-main {
|
||||
padding-bottom: max(60px, calc(constant(safe-area-inset-bottom) + 40px));
|
||||
padding-bottom: max(60px, calc(env(safe-area-inset-bottom) + 40px));
|
||||
overscroll-behavior-y: none;
|
||||
}
|
||||
}
|
||||
|
||||
@supports (-webkit-touch-callout: none) {
|
||||
@media screen and (max-width: 991px) {
|
||||
.fixed-header + .app-main {
|
||||
padding-bottom: max(17px, calc(constant(safe-area-inset-bottom) + 10px));
|
||||
padding-bottom: max(17px, calc(env(safe-area-inset-bottom) + 10px));
|
||||
height: calc(100svh - 50px);
|
||||
height: calc(100dvh - 50px);
|
||||
}
|
||||
|
||||
.hasTagsView .fixed-header + .app-main {
|
||||
padding-bottom: max(17px, calc(constant(safe-area-inset-bottom) + 10px));
|
||||
padding-bottom: max(17px, calc(env(safe-area-inset-bottom) + 10px));
|
||||
height: calc(100svh - 84px);
|
||||
height: calc(100dvh - 84px);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
<template>
|
||||
<div class="navbar">
|
||||
<div class="navbar" :class="'nav' + navType">
|
||||
<hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
|
||||
|
||||
<breadcrumb v-if="!topNav" id="breadcrumb-container" class="breadcrumb-container" />
|
||||
<top-nav v-if="topNav" id="topmenu-container" class="topmenu-container" />
|
||||
|
||||
<breadcrumb v-if="navType == 1" id="breadcrumb-container" class="breadcrumb-container" />
|
||||
<top-nav v-if="navType == 2" id="topmenu-container" class="topmenu-container" />
|
||||
<template v-if="navType == 3">
|
||||
<logo v-show="showLogo" :collapse="false"></logo>
|
||||
<top-bar id="topbar-container" class="topbar-container" />
|
||||
</template>
|
||||
<div class="right-menu">
|
||||
<template v-if="device!=='mobile'">
|
||||
<search id="header-search" class="right-menu-item" />
|
||||
@@ -50,6 +53,8 @@
|
||||
import { mapGetters } from 'vuex'
|
||||
import Breadcrumb from '@/components/Breadcrumb'
|
||||
import TopNav from '@/components/TopNav'
|
||||
import TopBar from './TopBar'
|
||||
import Logo from './Sidebar/Logo'
|
||||
import Hamburger from '@/components/Hamburger'
|
||||
import Screenfull from '@/components/Screenfull'
|
||||
import SizeSelect from '@/components/SizeSelect'
|
||||
@@ -61,7 +66,9 @@ export default {
|
||||
emits: ['setLayout'],
|
||||
components: {
|
||||
Breadcrumb,
|
||||
Logo,
|
||||
TopNav,
|
||||
TopBar,
|
||||
Hamburger,
|
||||
Screenfull,
|
||||
SizeSelect,
|
||||
@@ -81,9 +88,14 @@ export default {
|
||||
return this.$store.state.settings.showSettings
|
||||
}
|
||||
},
|
||||
topNav: {
|
||||
navType: {
|
||||
get() {
|
||||
return this.$store.state.settings.topNav
|
||||
return this.$store.state.settings.navType
|
||||
}
|
||||
},
|
||||
showLogo: {
|
||||
get() {
|
||||
return this.$store.state.settings.sidebarLogo
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -110,20 +122,33 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.navbar.nav3 {
|
||||
.hamburger-container {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar {
|
||||
height: 50px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 4px rgba(0,21,41,.08);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// padding: 0 8px;
|
||||
box-sizing: border-box;
|
||||
|
||||
.hamburger-container {
|
||||
line-height: 46px;
|
||||
height: 100%;
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
transition: background .3s;
|
||||
-webkit-tap-highlight-color:transparent;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
margin-right: 8px;
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, .025)
|
||||
@@ -131,7 +156,7 @@ export default {
|
||||
}
|
||||
|
||||
.breadcrumb-container {
|
||||
float: left;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.topmenu-container {
|
||||
@@ -139,15 +164,26 @@ export default {
|
||||
left: 50px;
|
||||
}
|
||||
|
||||
.topbar-container {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.errLog-container {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.right-menu {
|
||||
float: right;
|
||||
height: 100%;
|
||||
line-height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: auto;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
|
||||
@@ -3,6 +3,27 @@
|
||||
<div class="drawer-container">
|
||||
<div>
|
||||
<div class="setting-drawer-content">
|
||||
<div class="setting-drawer-title">
|
||||
<h3 class="drawer-title">菜单导航设置</h3>
|
||||
</div>
|
||||
<div class="nav-wrap">
|
||||
<el-tooltip content="左侧菜单" placement="bottom">
|
||||
<div class="item left" @click="handleNavType(1)" :style="{'--theme': theme}" :class="{ activeItem: navType == 1 }">
|
||||
<b></b><b></b>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip content="混合菜单" placement="bottom">
|
||||
<div class="item mix" @click="handleNavType(2)" :style="{'--theme': theme}" :class="{ activeItem: navType == 2 }">
|
||||
<b></b><b></b>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="顶部菜单" placement="bottom">
|
||||
<div class="item top" @click="handleNavType(3)" :style="{'--theme': theme}" :class="{ activeItem: navType == 3 }">
|
||||
<b></b><b></b>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="setting-drawer-title">
|
||||
<h3 class="drawer-title">主题风格设置</h3>
|
||||
</div>
|
||||
@@ -39,11 +60,6 @@
|
||||
|
||||
<h3 class="drawer-title">系统布局配置</h3>
|
||||
|
||||
<div class="drawer-item">
|
||||
<span>开启 TopNav</span>
|
||||
<el-switch v-model="topNav" class="drawer-switch" />
|
||||
</div>
|
||||
|
||||
<div class="drawer-item">
|
||||
<span>开启 Tags-Views</span>
|
||||
<el-switch v-model="tagsView" class="drawer-switch" />
|
||||
@@ -93,6 +109,7 @@ export default {
|
||||
return {
|
||||
theme: this.$store.state.settings.theme,
|
||||
sideTheme: this.$store.state.settings.sideTheme,
|
||||
navType: this.$store.state.settings.navType,
|
||||
showSettings: false
|
||||
}
|
||||
},
|
||||
@@ -108,21 +125,6 @@ export default {
|
||||
})
|
||||
}
|
||||
},
|
||||
topNav: {
|
||||
get() {
|
||||
return this.$store.state.settings.topNav
|
||||
},
|
||||
set(val) {
|
||||
this.$store.dispatch('settings/changeSetting', {
|
||||
key: 'topNav',
|
||||
value: val
|
||||
})
|
||||
if (!val) {
|
||||
this.$store.dispatch('app/toggleSideBarHide', false)
|
||||
this.$store.commit("SET_SIDEBAR_ROUTERS", this.$store.state.permission.defaultRoutes)
|
||||
}
|
||||
}
|
||||
},
|
||||
tagsView: {
|
||||
get() {
|
||||
return this.$store.state.settings.tagsView
|
||||
@@ -180,6 +182,25 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
navType: {
|
||||
handler(val) {
|
||||
if (val == 1) {
|
||||
this.$store.dispatch("app/toggleSideBarHide", false)
|
||||
}
|
||||
if (val == 2) {
|
||||
}
|
||||
if (val == 3) {
|
||||
this.$store.dispatch("app/toggleSideBarHide", true)
|
||||
}
|
||||
if ([1, 3].includes(val)) {
|
||||
this.$store.commit("SET_SIDEBAR_ROUTERS",this.$store.state.permission.defaultRoutes)
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
themeChange(val) {
|
||||
this.$store.dispatch('settings/changeSetting', {
|
||||
@@ -195,6 +216,13 @@ export default {
|
||||
})
|
||||
this.sideTheme = val
|
||||
},
|
||||
handleNavType(val) {
|
||||
this.$store.dispatch('settings/changeSetting', {
|
||||
key: 'navType',
|
||||
value: val
|
||||
})
|
||||
this.navType = val
|
||||
},
|
||||
openSetting() {
|
||||
this.showSettings = true
|
||||
},
|
||||
@@ -206,7 +234,7 @@ export default {
|
||||
this.$cache.local.set(
|
||||
"layout-setting",
|
||||
`{
|
||||
"topNav":${this.topNav},
|
||||
"navType":${this.navType},
|
||||
"tagsView":${this.tagsView},
|
||||
"tagsIcon":${this.tagsIcon},
|
||||
"fixedHeader":${this.fixedHeader},
|
||||
@@ -229,70 +257,133 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.setting-drawer-content {
|
||||
.setting-drawer-title {
|
||||
margin-bottom: 12px;
|
||||
color: rgba(0, 0, 0, .85);
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.setting-drawer-content {
|
||||
.setting-drawer-title {
|
||||
margin-bottom: 12px;
|
||||
color: rgba(0, 0, 0, .85);
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.setting-drawer-block-checbox {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
.setting-drawer-block-checbox {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.setting-drawer-block-checbox-item {
|
||||
position: relative;
|
||||
margin-right: 16px;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
.setting-drawer-block-checbox-item {
|
||||
position: relative;
|
||||
margin-right: 16px;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
img {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.setting-drawer-block-checbox-selectIcon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding-top: 15px;
|
||||
padding-left: 24px;
|
||||
color: #1890ff;
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
}
|
||||
.setting-drawer-block-checbox-selectIcon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding-top: 15px;
|
||||
padding-left: 24px;
|
||||
color: #1890ff;
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.drawer-container {
|
||||
padding: 20px;
|
||||
.drawer-container {
|
||||
padding: 20px;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
word-wrap: break-word;
|
||||
|
||||
.drawer-title {
|
||||
margin-bottom: 12px;
|
||||
color: rgba(0, 0, 0, .85);
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
word-wrap: break-word;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.drawer-title {
|
||||
margin-bottom: 12px;
|
||||
color: rgba(0, 0, 0, .85);
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
.drawer-item {
|
||||
color: rgba(0, 0, 0, .65);
|
||||
font-size: 14px;
|
||||
padding: 12px 0;
|
||||
}
|
||||
|
||||
.drawer-switch {
|
||||
float: right
|
||||
}
|
||||
}
|
||||
|
||||
// 导航模式
|
||||
.nav-wrap {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.activeItem {
|
||||
border: 2px solid #{'var(--theme)'} !important;
|
||||
}
|
||||
|
||||
.item {
|
||||
position: relative;
|
||||
margin-right: 16px;
|
||||
cursor: pointer;
|
||||
width: 56px;
|
||||
height: 48px;
|
||||
border-radius: 4px;
|
||||
background: #f0f2f5;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.left {
|
||||
b:first-child {
|
||||
display: block;
|
||||
height: 30%;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.drawer-item {
|
||||
color: rgba(0, 0, 0, .65);
|
||||
font-size: 14px;
|
||||
padding: 12px 0;
|
||||
}
|
||||
|
||||
.drawer-switch {
|
||||
float: right
|
||||
b:last-child {
|
||||
width: 30%;
|
||||
background: #1b2a47;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
}
|
||||
.mix {
|
||||
b:first-child {
|
||||
border-radius: 4px 4px 0 0;
|
||||
display: block;
|
||||
height: 30%;
|
||||
background: #1b2a47;
|
||||
}
|
||||
b:last-child {
|
||||
width: 30%;
|
||||
background: #1b2a47;
|
||||
position: absolute;
|
||||
height: 70%;
|
||||
border-radius: 0 0 0 4px;
|
||||
}
|
||||
}
|
||||
.top {
|
||||
b:first-child {
|
||||
display: block;
|
||||
height: 30%;
|
||||
background: #1b2a47;
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<div class="sidebar-logo-container" :class="{'collapse':collapse}" :style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }">
|
||||
<div class="sidebar-logo-container" :class="{'collapse':collapse}" :style="{ backgroundColor: sideTheme === 'theme-dark' && navType !== 3 ? variables.menuBackground : variables.menuLightBackground }">
|
||||
<transition name="sidebarLogoFade">
|
||||
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
|
||||
<img v-if="logo" :src="logo" class="sidebar-logo" />
|
||||
<h1 v-else class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }} </h1>
|
||||
<h1 v-else class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' && navType !== 3 ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }} </h1>
|
||||
</router-link>
|
||||
<router-link v-else key="expand" class="sidebar-logo-link" to="/">
|
||||
<img v-if="logo" :src="logo" class="sidebar-logo" />
|
||||
<h1 class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }} </h1>
|
||||
<h1 class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' && navType !== 3 ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }} </h1>
|
||||
</router-link>
|
||||
</transition>
|
||||
</div>
|
||||
@@ -31,6 +31,9 @@ export default {
|
||||
},
|
||||
sideTheme() {
|
||||
return this.$store.state.settings.sideTheme
|
||||
},
|
||||
navType() {
|
||||
return this.$store.state.settings.navType
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -54,7 +57,6 @@ export default {
|
||||
|
||||
.sidebar-logo-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
background: #2b2f3a;
|
||||
|
||||
98
ruoyi-ui/src/layout/components/TopBar/index.vue
Normal file
98
ruoyi-ui/src/layout/components/TopBar/index.vue
Normal file
@@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<el-menu class="topbar-menu" :default-active="activeMenu" :active-text-color="theme" mode="horizontal">
|
||||
<sidebar-item :key="route.path + index" v-for="(route, index) in topMenus" :item="route" :base-path="route.path" />
|
||||
|
||||
<el-submenu index="more" class="el-submenu__hide-arrow" v-if="moreRoutes.length > 0">
|
||||
<template slot="title">更多菜单</template>
|
||||
<sidebar-item :key="route.path + index" v-for="(route, index) in moreRoutes" :item="route" :base-path="route.path" />
|
||||
</el-submenu>
|
||||
</el-menu>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SidebarItem from '../Sidebar/SidebarItem'
|
||||
|
||||
export default {
|
||||
components: { SidebarItem },
|
||||
data() {
|
||||
return {
|
||||
// 顶部栏初始数
|
||||
visibleNumber: 5
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
theme() {
|
||||
return this.$store.state.settings.theme
|
||||
},
|
||||
topMenus() {
|
||||
return this.$store.state.permission.sidebarRouters.filter((f) => !f.hidden).slice(0, this.visibleNumber)
|
||||
},
|
||||
moreRoutes() {
|
||||
const sidebarRouters = this.$store.state.permission.sidebarRouters;
|
||||
return sidebarRouters.filter((f) => !f.hidden).slice(this.visibleNumber, sidebarRouters.length - this.visibleNumber)
|
||||
},
|
||||
// 默认激活的菜单
|
||||
activeMenu() {
|
||||
const { meta, path } = this.$route
|
||||
if (meta.activeMenu) {
|
||||
return meta.activeMenu
|
||||
}
|
||||
return path
|
||||
},
|
||||
},
|
||||
beforeMount() {
|
||||
window.addEventListener('resize', this.setVisibleNumber)
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.setVisibleNumber)
|
||||
},
|
||||
mounted() {
|
||||
this.setVisibleNumber()
|
||||
},
|
||||
methods: {
|
||||
// 根据宽度计算设置显示栏数
|
||||
setVisibleNumber() {
|
||||
const width = document.body.getBoundingClientRect().width / 3
|
||||
this.visibleNumber = parseInt(width / 85)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
/* menu item */
|
||||
.topbar-menu.el-menu--horizontal .el-submenu__title, .topbar-menu.el-menu--horizontal .el-menu-item {
|
||||
padding: 0 10px !important;
|
||||
}
|
||||
|
||||
.el-menu--horizontal .el-menu--popup .el-menu-item:hover {
|
||||
background-color: #f5f7fa !important;
|
||||
}
|
||||
|
||||
/* submenu item */
|
||||
.topbar-menu.el-menu--horizontal > .el-submenu .el-submenu__title {
|
||||
float: left;
|
||||
height: 47px !important;
|
||||
line-height: 50px !important;
|
||||
color: #303133;
|
||||
margin: 0 15px !important;
|
||||
}
|
||||
|
||||
/* topbar more arrow */
|
||||
.topbar-menu .el-submenu .el-submenu__icon-arrow {
|
||||
position: static;
|
||||
vertical-align: middle;
|
||||
margin-left: 8px;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
/* menu__title el-menu-item */
|
||||
.topbar-menu.el-menu--horizontal .el-submenu__title, .topbar-menu.el-menu--horizontal .el-menu-item {
|
||||
height: 55px;
|
||||
}
|
||||
|
||||
.el-menu--horizontal .el-menu .el-menu-item, .el-menu--horizontal .el-menu .el-submenu__title{
|
||||
color: #303133;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -15,9 +15,9 @@ module.exports = {
|
||||
showSettings: true,
|
||||
|
||||
/**
|
||||
* 是否显示顶部导航
|
||||
* 菜单导航模式 1、纯左侧 2、混合(左侧+顶部) 3、纯顶部
|
||||
*/
|
||||
topNav: false,
|
||||
navType: 1,
|
||||
|
||||
/**
|
||||
* 是否显示 tagsView
|
||||
@@ -32,7 +32,7 @@ module.exports = {
|
||||
/**
|
||||
* 是否固定头部
|
||||
*/
|
||||
fixedHeader: false,
|
||||
fixedHeader: true,
|
||||
|
||||
/**
|
||||
* 是否显示logo
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import defaultSettings from '@/settings'
|
||||
import { useDynamicTitle } from '@/utils/dynamicTitle'
|
||||
|
||||
const { sideTheme, showSettings, topNav, tagsView, tagsIcon, fixedHeader, sidebarLogo, dynamicTitle, footerVisible, footerContent } = defaultSettings
|
||||
const { sideTheme, showSettings, navType, tagsView, tagsIcon, fixedHeader, sidebarLogo, dynamicTitle, footerVisible, footerContent } = defaultSettings
|
||||
|
||||
const storageSetting = JSON.parse(localStorage.getItem('layout-setting')) || ''
|
||||
const state = {
|
||||
@@ -9,7 +9,7 @@ const state = {
|
||||
theme: storageSetting.theme || '#409EFF',
|
||||
sideTheme: storageSetting.sideTheme || sideTheme,
|
||||
showSettings: showSettings,
|
||||
topNav: storageSetting.topNav === undefined ? topNav : storageSetting.topNav,
|
||||
navType: storageSetting.navType === undefined ? navType : storageSetting.navType,
|
||||
tagsView: storageSetting.tagsView === undefined ? tagsView : storageSetting.tagsView,
|
||||
tagsIcon: storageSetting.tagsIcon === undefined ? tagsIcon : storageSetting.tagsIcon,
|
||||
fixedHeader: storageSetting.fixedHeader === undefined ? fixedHeader : storageSetting.fixedHeader,
|
||||
@@ -23,6 +23,9 @@ const mutations = {
|
||||
if (state.hasOwnProperty(key)) {
|
||||
state[key] = value
|
||||
}
|
||||
},
|
||||
SET_TITLE: (state, title) => {
|
||||
state.title = title
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +36,7 @@ const actions = {
|
||||
},
|
||||
// 设置网页标题
|
||||
setTitle({ commit }, title) {
|
||||
state.title = title
|
||||
commit('SET_TITLE', title)
|
||||
useDynamicTitle()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,37 @@
|
||||
export default [
|
||||
{
|
||||
layout: 'colFormItem',
|
||||
tagIcon: 'input',
|
||||
label: '手机号',
|
||||
vModel: 'mobile',
|
||||
formId: 6,
|
||||
tag: 'el-input',
|
||||
placeholder: '请输入手机号',
|
||||
defaultValue: '',
|
||||
span: 24,
|
||||
style: { width: '100%' },
|
||||
clearable: true,
|
||||
prepend: '',
|
||||
append: '',
|
||||
'prefix-icon': 'el-icon-mobile',
|
||||
'suffix-icon': '',
|
||||
maxlength: 11,
|
||||
'show-word-limit': true,
|
||||
readonly: false,
|
||||
disabled: false,
|
||||
required: true,
|
||||
changeTag: true,
|
||||
regList: [{
|
||||
pattern: '/^1(3|4|5|7|8|9)\\d{9}$/',
|
||||
message: '手机号格式错误'
|
||||
}]
|
||||
export const drawingDefaultValue = []
|
||||
|
||||
export function initDrawingDefaultValue() {
|
||||
if (drawingDefaultValue.length === 0) {
|
||||
drawingDefaultValue.push({
|
||||
layout: 'colFormItem',
|
||||
tagIcon: 'input',
|
||||
label: '手机号',
|
||||
vModel: 'mobile',
|
||||
formId: 6,
|
||||
tag: 'el-input',
|
||||
placeholder: '请输入手机号',
|
||||
defaultValue: '',
|
||||
span: 24,
|
||||
style: {width: '100%'},
|
||||
clearable: true,
|
||||
prepend: '',
|
||||
append: '',
|
||||
'prefix-icon': 'el-icon-mobile',
|
||||
'suffix-icon': '',
|
||||
maxlength: 11,
|
||||
'show-word-limit': true,
|
||||
readonly: false,
|
||||
disabled: false,
|
||||
required: true,
|
||||
changeTag: true,
|
||||
regList: [{
|
||||
pattern: '/^1(3|4|5|7|8|9)\\d{9}$/',
|
||||
message: '手机号格式错误'
|
||||
}]
|
||||
})
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export function cleanDrawingDefaultValue() {
|
||||
drawingDefaultValue.splice(0, drawingDefaultValue.length)
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
<s> 满101046199 </s> <s> 满136919097 </s> <s> 满143961921 </s> <s> 满174951577 </s>
|
||||
<s> 满161281055 </s> <s> 满138988063 </s> <s> 满151450850 </s> <s> 满224622315 </s>
|
||||
<s> 满287842588 </s> <s> 满187944233 </s> <s> 满228578329 </s> <s> 满191164766 </s>
|
||||
<a href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=EeCBXu51I1zPWRia2uskpjDRx6VrbnFN&authKey=Xm8yDxk0%2FyYGI11oxhXaQnTn4K7UwCk7Kn2MZTh3P1JxLctollAkyeySjnaILDkb&noverify=0&group_code=174569686" target="_blank">174569686</a>
|
||||
<a href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=EeCBXu51I1zPWRia2uskpjDRx6VrbnFN&authKey=Xm8yDxk0%2FyYGI11oxhXaQnTn4K7UwCk7Kn2MZTh3P1JxLctollAkyeySjnaILDkb&noverify=0&group_code=174569686" target="_blank">174569686</a>
|
||||
</p>
|
||||
<p>
|
||||
<i class="el-icon-chat-dot-round"></i> 微信:<a
|
||||
@@ -112,6 +112,42 @@
|
||||
<span>更新日志</span>
|
||||
</div>
|
||||
<el-collapse accordion>
|
||||
<el-collapse-item title="v3.9.1 - 2025-12-18">
|
||||
<ol>
|
||||
<li>支持防盗链功能</li>
|
||||
<li>菜单导航设置支持纯顶部</li>
|
||||
<li>使用yauaa代替bitwalker</li>
|
||||
<li>用户头像更换后移除旧头像文件</li>
|
||||
<li>支持Excel导出对象的多个子列表</li>
|
||||
<li>升级oshi到最新版本6.9.1</li>
|
||||
<li>升级druid到最新版本1.2.27</li>
|
||||
<li>升级fastjson到最新版2.0.60</li>
|
||||
<li>升级spring-security到5.7.14</li>
|
||||
<li>升级tomcat到最新版本9.0.112</li>
|
||||
<li>升级commons.io到最新版本2.21.0</li>
|
||||
<li>用户导入添加验证提示</li>
|
||||
<li>显示列信息支持对象格式</li>
|
||||
<li>忽略用户密码字段的JSON序列化</li>
|
||||
<li>网页标题设置新增SET_TITLE方法</li>
|
||||
<li>自动识别json对象白名单配置范围缩小</li>
|
||||
<li>登录/注册页面底部版权信息修改为读取配置</li>
|
||||
<li>修复用户归属部门无法修改为空问题</li>
|
||||
<li>修复固定头部时出现的导航栏偏移问题</li>
|
||||
<li>修复v3时间控件between选择后清空报错问题</li>
|
||||
<li>修复comboReadDict属性下多个sheet出现的报错</li>
|
||||
<li>修复表单构建移除所有控件后切换路由回来空白问题</li>
|
||||
<li>优化布局设置显示</li>
|
||||
<li>优化字典组件值宽松匹配</li>
|
||||
<li>优化获取字典类型值的方法</li>
|
||||
<li>优化生成代码下载的zip文件名</li>
|
||||
<li>优化日志记录参数拼装提升效率</li>
|
||||
<li>优化导入文件检查标题行不能为空</li>
|
||||
<li>优化表单构建关闭页签销毁复制插件</li>
|
||||
<li>优化Excel统计行数值的单元格样式显示</li>
|
||||
<li>优化数据权限控制逻辑,放开permission限制</li>
|
||||
<li>其他细节优化</li>
|
||||
</ol>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="v3.9.0 - 2025-05-28">
|
||||
<ol>
|
||||
<li>优化菜单搜索查询页</li>
|
||||
@@ -1060,7 +1096,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
// 版本号
|
||||
version: "3.9.0"
|
||||
version: "3.9.1"
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
</el-form>
|
||||
<!-- 底部 -->
|
||||
<div class="el-login-footer">
|
||||
<span>Copyright © 2018-2025 ruoyi.vip All Rights Reserved.</span>
|
||||
<span>{{ footerContent }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -65,12 +65,14 @@
|
||||
import { getCodeImg } from "@/api/login"
|
||||
import Cookies from "js-cookie"
|
||||
import { encrypt, decrypt } from '@/utils/jsencrypt'
|
||||
import defaultSettings from '@/settings'
|
||||
|
||||
export default {
|
||||
name: "Login",
|
||||
data() {
|
||||
return {
|
||||
title: process.env.VUE_APP_TITLE,
|
||||
footerContent: defaultSettings.footerContent,
|
||||
codeUrl: "",
|
||||
loginForm: {
|
||||
username: "admin",
|
||||
@@ -156,7 +158,7 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss">
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.login {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
@@ -61,13 +61,14 @@
|
||||
</el-form>
|
||||
<!-- 底部 -->
|
||||
<div class="el-register-footer">
|
||||
<span>Copyright © 2018-2025 ruoyi.vip All Rights Reserved.</span>
|
||||
<span>{{ footerContent }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getCodeImg, register } from "@/api/login"
|
||||
import defaultSettings from '@/settings'
|
||||
|
||||
export default {
|
||||
name: "Register",
|
||||
@@ -81,6 +82,7 @@ export default {
|
||||
}
|
||||
return {
|
||||
title: process.env.VUE_APP_TITLE,
|
||||
footerContent: defaultSettings.footerContent,
|
||||
codeUrl: "",
|
||||
registerForm: {
|
||||
username: "",
|
||||
@@ -147,7 +149,7 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss">
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.register {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
@@ -146,13 +146,14 @@ import { beautifierConf, titleCase } from '@/utils/index'
|
||||
import { makeUpHtml, vueTemplate, vueScript, cssStyle } from '@/utils/generator/html'
|
||||
import { makeUpJs } from '@/utils/generator/js'
|
||||
import { makeUpCss } from '@/utils/generator/css'
|
||||
import drawingDefault from '@/utils/generator/drawingDefault'
|
||||
import { drawingDefaultValue, initDrawingDefaultValue, cleanDrawingDefaultValue } from '@/utils/generator/drawingDefault'
|
||||
import logo from '@/assets/logo/logo.png'
|
||||
import CodeTypeDialog from './CodeTypeDialog'
|
||||
import DraggableItem from './DraggableItem'
|
||||
|
||||
let oldActiveId
|
||||
let tempActiveData
|
||||
let clipboard = null
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -171,17 +172,20 @@ export default {
|
||||
selectComponents,
|
||||
layoutComponents,
|
||||
labelWidth: 100,
|
||||
drawingList: drawingDefault,
|
||||
drawingList: drawingDefaultValue,
|
||||
drawingData: {},
|
||||
activeId: drawingDefault[0].formId,
|
||||
activeId: drawingDefaultValue[0].formId,
|
||||
drawerVisible: false,
|
||||
formData: {},
|
||||
dialogVisible: false,
|
||||
generateConf: null,
|
||||
showFileName: false,
|
||||
activeData: drawingDefault[0]
|
||||
activeData: drawingDefaultValue[0]
|
||||
}
|
||||
},
|
||||
beforeCreate() {
|
||||
initDrawingDefaultValue()
|
||||
},
|
||||
created() {
|
||||
// 防止 firefox 下 拖拽 会新打卡一个选项卡
|
||||
document.body.ondrop = event => {
|
||||
@@ -208,7 +212,7 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const clipboard = new ClipboardJS('#copyNode', {
|
||||
clipboard = new ClipboardJS('#copyNode', {
|
||||
text: trigger => {
|
||||
const codeStr = this.generateCode()
|
||||
this.$notify({
|
||||
@@ -223,6 +227,9 @@ export default {
|
||||
this.$message.error('代码复制失败')
|
||||
})
|
||||
},
|
||||
beforeDestroy() {
|
||||
clipboard.destroy()
|
||||
},
|
||||
methods: {
|
||||
activeFormItem(element) {
|
||||
this.activeData = element
|
||||
@@ -284,6 +291,7 @@ export default {
|
||||
this.$confirm('确定要清空所有组件吗?', '提示', { type: 'warning' }).then(
|
||||
() => {
|
||||
this.drawingList = []
|
||||
cleanDrawingDefaultValue()
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
@@ -266,7 +266,8 @@ export default {
|
||||
this.$modal.msgSuccess("成功生成到自定义路径:" + row.genPath)
|
||||
})
|
||||
} else {
|
||||
this.$download.zip("/tool/gen/batchGenCode?tables=" + tableNames, "ruoyi.zip")
|
||||
const zipName = Array.isArray(tableNames) ? "ruoyi.zip" : tableNames + ".zip"
|
||||
this.$download.zip("/tool/gen/batchGenCode?tables=" + tableNames, zipName)
|
||||
}
|
||||
},
|
||||
/** 同步数据库操作 */
|
||||
|
||||
Reference in New Issue
Block a user