| @@ -34,6 +34,12 @@ | |||
| <logback.version>1.2.13</logback.version> | |||
| <spring-security.version>5.7.12</spring-security.version> | |||
| <spring-framework.version>5.3.39</spring-framework.version> | |||
| <cn.hutool.all.version>5.5.4</cn.hutool.all.version> | |||
| <com.itextpdf.version>5.5.13.2</com.itextpdf.version> | |||
| <itext-asian.version>5.2.0</itext-asian.version> | |||
| <de.odysseus.juel.version>2.1.3</de.odysseus.juel.version> | |||
| <httpclient.version>4.5.13</httpclient.version> | |||
| <net.coobird.version>0.4.8</net.coobird.version> | |||
| </properties> | |||
| <!-- 依赖声明 --> | |||
| @@ -182,6 +188,49 @@ | |||
| <version>${kaptcha.version}</version> | |||
| </dependency> | |||
| <!--hutool工具包--> | |||
| <dependency> | |||
| <groupId>cn.hutool</groupId> | |||
| <artifactId>hutool-all</artifactId> | |||
| <version>${cn.hutool.all.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>de.odysseus.juel</groupId> | |||
| <artifactId>juel</artifactId> | |||
| <version>${de.odysseus.juel.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.apache.httpcomponents</groupId> | |||
| <artifactId>httpclient</artifactId> | |||
| <version>${httpclient.version}</version> | |||
| </dependency> | |||
| <!--pdf--> | |||
| <dependency> | |||
| <groupId>com.itextpdf</groupId> | |||
| <artifactId>itextpdf</artifactId> | |||
| <version>${com.itextpdf.version}</version> | |||
| </dependency> | |||
| <!--pdf 字体--> | |||
| <dependency> | |||
| <groupId>com.itextpdf</groupId> | |||
| <artifactId>itext-asian</artifactId> | |||
| <version>${itext-asian.version}</version> | |||
| </dependency> | |||
| <!--thumbnailator图片处理--> | |||
| <dependency> | |||
| <groupId>net.coobird</groupId> | |||
| <artifactId>thumbnailator</artifactId> | |||
| <version>${net.coobird.version}</version> | |||
| </dependency> | |||
| <!-- 定时任务--> | |||
| <dependency> | |||
| <groupId>com.ruoyi</groupId> | |||
| @@ -61,6 +61,12 @@ | |||
| <artifactId>ruoyi-generator</artifactId> | |||
| </dependency> | |||
| <!-- 业务模块--> | |||
| <dependency> | |||
| <groupId>com.ruoyi</groupId> | |||
| <artifactId>ruoyi-business</artifactId> | |||
| </dependency> | |||
| </dependencies> | |||
| <build> | |||
| @@ -80,17 +86,17 @@ | |||
| </execution> | |||
| </executions> | |||
| </plugin> | |||
| <plugin> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-war-plugin</artifactId> | |||
| <version>3.1.0</version> | |||
| <plugin> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-war-plugin</artifactId> | |||
| <version>3.1.0</version> | |||
| <configuration> | |||
| <failOnMissingWebXml>false</failOnMissingWebXml> | |||
| <warName>${project.artifactId}</warName> | |||
| </configuration> | |||
| </plugin> | |||
| </configuration> | |||
| </plugin> | |||
| </plugins> | |||
| <finalName>${project.artifactId}</finalName> | |||
| </build> | |||
| </project> | |||
| </project> | |||
| @@ -100,6 +100,7 @@ | |||
| <orderEntry type="library" name="Maven: org.springframework.security:spring-security-core:5.7.12" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.security:spring-security-crypto:5.7.12" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.security:spring-security-web:5.7.12" level="project" /> | |||
| <orderEntry type="library" name="Maven: net.coobird:thumbnailator:0.4.8" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.github.pagehelper:pagehelper-spring-boot-starter:1.4.7" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.mybatis.spring.boot:mybatis-spring-boot-starter:2.3.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-jdbc:2.5.15" level="project" /> | |||
| @@ -115,6 +116,7 @@ | |||
| <orderEntry type="library" name="Maven: org.hibernate.validator:hibernate-validator:6.2.5.Final" level="project" /> | |||
| <orderEntry type="library" name="Maven: jakarta.validation:jakarta.validation-api:2.0.2" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.4.3.Final" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.projectlombok:lombok:1.18.26" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.12.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.12.7.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.12.7" level="project" /> | |||
| @@ -122,7 +124,6 @@ | |||
| <orderEntry type="library" name="Maven: commons-io:commons-io:2.19.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.poi:poi-ooxml:4.1.2" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.poi:poi:4.1.2" level="project" /> | |||
| <orderEntry type="library" name="Maven: commons-codec:commons-codec:1.15" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.commons:commons-collections4:4.4" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.commons:commons-math3:3.6.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.zaxxer:SparseBitSet:1.2" level="project" /> | |||
| @@ -132,6 +133,7 @@ | |||
| <orderEntry type="library" name="Maven: com.github.virtuald:curvesapi:1.06" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.28" level="project" /> | |||
| <orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt:0.9.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: javax.servlet:javax.servlet-api:4.0.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: javax.xml.bind:jaxb-api:2.3.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: javax.activation:javax.activation-api:1.2.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-data-redis:2.5.15" level="project" /> | |||
| @@ -152,8 +154,16 @@ | |||
| <orderEntry type="library" name="Maven: org.reactivestreams:reactive-streams:1.0.4" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.commons:commons-pool2:2.9.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: eu.bitwalker:UserAgentUtils:1.21" level="project" /> | |||
| <orderEntry type="library" name="Maven: javax.servlet:javax.servlet-api:4.0.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.itextpdf:itextpdf:5.5.13.2" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.itextpdf:itext-asian:5.2.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: cn.hutool:hutool-all:5.5.4" level="project" /> | |||
| <orderEntry type="module" module-name="ruoyi-generator" /> | |||
| <orderEntry type="library" name="Maven: org.apache.velocity:velocity-engine-core:2.3" level="project" /> | |||
| <orderEntry type="module" module-name="ruoyi-business" /> | |||
| <orderEntry type="library" name="Maven: de.odysseus.juel:juel:2.1.3" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpclient:4.5.13" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpcore:4.4.16" level="project" /> | |||
| <orderEntry type="library" name="Maven: commons-logging:commons-logging:1.2" level="project" /> | |||
| <orderEntry type="library" name="Maven: commons-codec:commons-codec:1.15" level="project" /> | |||
| </component> | |||
| </module> | |||
| @@ -1,9 +1,11 @@ | |||
| package com.ruoyi.web.controller.common; | |||
| import java.util.ArrayList; | |||
| import java.util.List; | |||
| import javax.servlet.http.HttpServletRequest; | |||
| import javax.servlet.http.HttpServletResponse; | |||
| import com.ruoyi.common.config.RuoYiConfig; | |||
| import com.ruoyi.common.core.domain.AjaxResult; | |||
| import com.ruoyi.common.utils.StringUtils; | |||
| import com.ruoyi.common.utils.file.FileUploadUtils; | |||
| import com.ruoyi.common.utils.file.FileUtils; | |||
| import com.ruoyi.framework.config.ServerConfig; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| @@ -13,22 +15,20 @@ import org.springframework.web.bind.annotation.PostMapping; | |||
| import org.springframework.web.bind.annotation.RequestMapping; | |||
| import org.springframework.web.bind.annotation.RestController; | |||
| import org.springframework.web.multipart.MultipartFile; | |||
| import com.ruoyi.common.config.RuoYiConfig; | |||
| import com.ruoyi.common.core.domain.AjaxResult; | |||
| import com.ruoyi.common.utils.StringUtils; | |||
| import com.ruoyi.common.utils.file.FileUploadUtils; | |||
| import com.ruoyi.common.utils.file.FileUtils; | |||
| import com.ruoyi.framework.config.ServerConfig; | |||
| import javax.servlet.http.HttpServletRequest; | |||
| import javax.servlet.http.HttpServletResponse; | |||
| import java.util.ArrayList; | |||
| import java.util.List; | |||
| /** | |||
| * 通用请求处理 | |||
| * | |||
| * | |||
| * @author ruoyi | |||
| */ | |||
| @RestController | |||
| @RequestMapping("/common") | |||
| public class CommonController | |||
| { | |||
| public class CommonController { | |||
| private static final Logger log = LoggerFactory.getLogger(CommonController.class); | |||
| @Autowired | |||
| @@ -38,17 +38,14 @@ public class CommonController | |||
| /** | |||
| * 通用下载请求 | |||
| * | |||
| * | |||
| * @param fileName 文件名称 | |||
| * @param delete 是否删除 | |||
| * @param delete 是否删除 | |||
| */ | |||
| @GetMapping("/download") | |||
| public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request) | |||
| { | |||
| try | |||
| { | |||
| if (!FileUtils.checkAllowDownload(fileName)) | |||
| { | |||
| public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request) { | |||
| try { | |||
| if (!FileUtils.checkAllowDownload(fileName)) { | |||
| throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName)); | |||
| } | |||
| String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1); | |||
| @@ -57,13 +54,10 @@ public class CommonController | |||
| response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); | |||
| FileUtils.setAttachmentResponseHeader(response, realFileName); | |||
| FileUtils.writeBytes(filePath, response.getOutputStream()); | |||
| if (delete) | |||
| { | |||
| if (delete) { | |||
| FileUtils.deleteFile(filePath); | |||
| } | |||
| } | |||
| catch (Exception e) | |||
| { | |||
| } catch (Exception e) { | |||
| log.error("下载文件失败", e); | |||
| } | |||
| } | |||
| @@ -72,10 +66,8 @@ public class CommonController | |||
| * 通用上传请求(单个) | |||
| */ | |||
| @PostMapping("/upload") | |||
| public AjaxResult uploadFile(MultipartFile file) throws Exception | |||
| { | |||
| try | |||
| { | |||
| public AjaxResult uploadFile(MultipartFile file) throws Exception { | |||
| try { | |||
| // 上传文件路径 | |||
| String filePath = RuoYiConfig.getUploadPath(); | |||
| // 上传并返回新文件名称 | |||
| @@ -87,9 +79,7 @@ public class CommonController | |||
| ajax.put("newFileName", FileUtils.getName(fileName)); | |||
| ajax.put("originalFilename", file.getOriginalFilename()); | |||
| return ajax; | |||
| } | |||
| catch (Exception e) | |||
| { | |||
| } catch (Exception e) { | |||
| return AjaxResult.error(e.getMessage()); | |||
| } | |||
| } | |||
| @@ -98,18 +88,15 @@ public class CommonController | |||
| * 通用上传请求(多个) | |||
| */ | |||
| @PostMapping("/uploads") | |||
| public AjaxResult uploadFiles(List<MultipartFile> files) throws Exception | |||
| { | |||
| try | |||
| { | |||
| public AjaxResult uploadFiles(List<MultipartFile> files) throws Exception { | |||
| try { | |||
| // 上传文件路径 | |||
| String filePath = RuoYiConfig.getUploadPath(); | |||
| List<String> urls = new ArrayList<String>(); | |||
| List<String> fileNames = new ArrayList<String>(); | |||
| List<String> newFileNames = new ArrayList<String>(); | |||
| List<String> originalFilenames = new ArrayList<String>(); | |||
| for (MultipartFile file : files) | |||
| { | |||
| for (MultipartFile file : files) { | |||
| // 上传并返回新文件名称 | |||
| String fileName = FileUploadUtils.upload(filePath, file); | |||
| String url = serverConfig.getUrl() + fileName; | |||
| @@ -124,9 +111,7 @@ public class CommonController | |||
| ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMETER)); | |||
| ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMETER)); | |||
| return ajax; | |||
| } | |||
| catch (Exception e) | |||
| { | |||
| } catch (Exception e) { | |||
| return AjaxResult.error(e.getMessage()); | |||
| } | |||
| } | |||
| @@ -136,12 +121,9 @@ public class CommonController | |||
| */ | |||
| @GetMapping("/download/resource") | |||
| public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response) | |||
| throws Exception | |||
| { | |||
| try | |||
| { | |||
| if (!FileUtils.checkAllowDownload(resource)) | |||
| { | |||
| throws Exception { | |||
| try { | |||
| if (!FileUtils.checkAllowDownload(resource)) { | |||
| throw new Exception(StringUtils.format("资源文件({})非法,不允许下载。 ", resource)); | |||
| } | |||
| // 本地资源路径 | |||
| @@ -153,9 +135,7 @@ public class CommonController | |||
| response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); | |||
| FileUtils.setAttachmentResponseHeader(response, downloadName); | |||
| FileUtils.writeBytes(downloadPath, response.getOutputStream()); | |||
| } | |||
| catch (Exception e) | |||
| { | |||
| } catch (Exception e) { | |||
| log.error("下载文件失败", e); | |||
| } | |||
| } | |||
| @@ -1,15 +1,5 @@ | |||
| package com.ruoyi.web.controller.system; | |||
| import java.util.Map; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.web.bind.annotation.GetMapping; | |||
| import org.springframework.web.bind.annotation.PostMapping; | |||
| import org.springframework.web.bind.annotation.PutMapping; | |||
| import org.springframework.web.bind.annotation.RequestBody; | |||
| import org.springframework.web.bind.annotation.RequestMapping; | |||
| import org.springframework.web.bind.annotation.RequestParam; | |||
| import org.springframework.web.bind.annotation.RestController; | |||
| import org.springframework.web.multipart.MultipartFile; | |||
| import com.ruoyi.common.annotation.Log; | |||
| import com.ruoyi.common.config.RuoYiConfig; | |||
| import com.ruoyi.common.core.controller.BaseController; | |||
| @@ -25,10 +15,15 @@ import com.ruoyi.common.utils.file.FileUtils; | |||
| import com.ruoyi.common.utils.file.MimeTypeUtils; | |||
| import com.ruoyi.framework.web.service.TokenService; | |||
| import com.ruoyi.system.service.ISysUserService; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.web.bind.annotation.*; | |||
| import org.springframework.web.multipart.MultipartFile; | |||
| import java.util.Map; | |||
| /** | |||
| * 个人信息 业务处理 | |||
| * | |||
| * | |||
| * @author ruoyi | |||
| */ | |||
| @RestController | |||
| @@ -127,7 +122,7 @@ public class SysProfileController extends BaseController | |||
| if (!file.isEmpty()) | |||
| { | |||
| LoginUser loginUser = getLoginUser(); | |||
| String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION, true); | |||
| String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION); | |||
| if (userService.updateUserAvatar(loginUser.getUserId(), avatar)) | |||
| { | |||
| String oldAvatar = loginUser.getUser().getAvatar(); | |||
| @@ -6,7 +6,7 @@ spring: | |||
| druid: | |||
| # 主库数据源 | |||
| master: | |||
| url: jdbc:mysql://192.168.0.119:3318/rongxin_base?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 | |||
| url: jdbc:mysql://localhost:3318/rongxin_base?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 | |||
| username: nsbjgkwh | |||
| password: ns61GK32x% | |||
| # 从库数据源 | |||
| @@ -1,7 +1,7 @@ | |||
| # 项目相关配置 | |||
| ruoyi: | |||
| # 名称 | |||
| name: RuoYi | |||
| name: RongXin | |||
| # 版本 | |||
| version: 3.9.0 | |||
| # 版权年份 | |||
| @@ -12,4 +12,39 @@ | |||
| <artifactId>ruoyi-business</artifactId> | |||
| <dependencies> | |||
| <!-- 系统模块--> | |||
| <dependency> | |||
| <groupId>com.ruoyi</groupId> | |||
| <artifactId>ruoyi-system</artifactId> | |||
| </dependency> | |||
| <!-- 通用工具--> | |||
| <dependency> | |||
| <groupId>com.ruoyi</groupId> | |||
| <artifactId>ruoyi-common</artifactId> | |||
| </dependency> | |||
| <!--hutool工具包--> | |||
| <!--<dependency> | |||
| <groupId>cn.hutool</groupId> | |||
| <artifactId>hutool-all</artifactId> | |||
| </dependency>--> | |||
| <dependency> | |||
| <groupId>de.odysseus.juel</groupId> | |||
| <artifactId>juel</artifactId> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.apache.httpcomponents</groupId> | |||
| <artifactId>httpclient</artifactId> | |||
| </dependency> | |||
| </dependencies> | |||
| </project> | |||
| @@ -0,0 +1,111 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4"> | |||
| <component name="FacetManager"> | |||
| <facet type="Spring" name="Spring"> | |||
| <configuration /> | |||
| </facet> | |||
| </component> | |||
| <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8"> | |||
| <output url="file://$MODULE_DIR$/target/classes" /> | |||
| <output-test url="file://$MODULE_DIR$/target/test-classes" /> | |||
| <content url="file://$MODULE_DIR$"> | |||
| <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> | |||
| <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" /> | |||
| <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> | |||
| <excludeFolder url="file://$MODULE_DIR$/target" /> | |||
| </content> | |||
| <orderEntry type="inheritedJdk" /> | |||
| <orderEntry type="sourceFolder" forTests="false" /> | |||
| <orderEntry type="module" module-name="ruoyi-system" /> | |||
| <orderEntry type="module" module-name="ruoyi-common" /> | |||
| <orderEntry type="library" name="Maven: org.springframework:spring-context-support:5.3.39" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework:spring-beans:5.3.39" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework:spring-context:5.3.39" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework:spring-expression:5.3.39" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework:spring-core:5.3.39" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.3.39" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework:spring-web:5.3.39" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-security:2.5.15" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter:2.5.15" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot:2.5.15" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.5.15" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-logging:2.5.15" level="project" /> | |||
| <orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.2.13" level="project" /> | |||
| <orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.2.13" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-to-slf4j:2.17.2" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-api:2.17.2" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.slf4j:jul-to-slf4j:1.7.36" level="project" /> | |||
| <orderEntry type="library" name="Maven: jakarta.annotation:jakarta.annotation-api:1.3.5" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework:spring-aop:5.3.39" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.security:spring-security-config:5.7.12" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.security:spring-security-core:5.7.12" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.security:spring-security-crypto:5.7.12" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.security:spring-security-web:5.7.12" level="project" /> | |||
| <orderEntry type="library" name="Maven: net.coobird:thumbnailator:0.4.8" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.github.pagehelper:pagehelper-spring-boot-starter:1.4.7" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.mybatis.spring.boot:mybatis-spring-boot-starter:2.3.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-jdbc:2.5.15" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.zaxxer:HikariCP:4.0.3" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework:spring-jdbc:5.3.39" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.mybatis.spring.boot:mybatis-spring-boot-autoconfigure:2.3.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.mybatis:mybatis:3.5.13" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.mybatis:mybatis-spring:2.1.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.github.pagehelper:pagehelper-spring-boot-autoconfigure:1.4.7" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.github.pagehelper:pagehelper:5.3.3" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.github.jsqlparser:jsqlparser:4.5" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-validation:2.5.15" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-el:9.0.106" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.hibernate.validator:hibernate-validator:6.2.5.Final" level="project" /> | |||
| <orderEntry type="library" name="Maven: jakarta.validation:jakarta.validation-api:2.0.2" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.4.3.Final" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.fasterxml:classmate:1.5.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.projectlombok:lombok:1.18.26" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.12.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.12.7.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.12.7" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.12.7" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.alibaba.fastjson2:fastjson2:2.0.57" level="project" /> | |||
| <orderEntry type="library" name="Maven: commons-io:commons-io:2.19.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.poi:poi-ooxml:4.1.2" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.poi:poi:4.1.2" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.commons:commons-collections4:4.4" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.commons:commons-math3:3.6.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.zaxxer:SparseBitSet:1.2" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.poi:poi-ooxml-schemas:4.1.2" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.xmlbeans:xmlbeans:3.1.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.commons:commons-compress:1.19" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.github.virtuald:curvesapi:1.06" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.28" level="project" /> | |||
| <orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt:0.9.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: javax.servlet:javax.servlet-api:4.0.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: javax.xml.bind:jaxb-api:2.3.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: javax.activation:javax.activation-api:1.2.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-data-redis:2.5.15" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.data:spring-data-redis:2.5.12" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.data:spring-data-keyvalue:2.5.12" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.data:spring-data-commons:2.5.12" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework:spring-tx:5.3.39" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework:spring-oxm:5.3.39" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.36" level="project" /> | |||
| <orderEntry type="library" name="Maven: io.lettuce:lettuce-core:6.1.10.RELEASE" level="project" /> | |||
| <orderEntry type="library" name="Maven: io.netty:netty-common:4.1.92.Final" level="project" /> | |||
| <orderEntry type="library" name="Maven: io.netty:netty-handler:4.1.92.Final" level="project" /> | |||
| <orderEntry type="library" name="Maven: io.netty:netty-resolver:4.1.92.Final" level="project" /> | |||
| <orderEntry type="library" name="Maven: io.netty:netty-buffer:4.1.92.Final" level="project" /> | |||
| <orderEntry type="library" name="Maven: io.netty:netty-transport-native-unix-common:4.1.92.Final" level="project" /> | |||
| <orderEntry type="library" name="Maven: io.netty:netty-codec:4.1.92.Final" level="project" /> | |||
| <orderEntry type="library" name="Maven: io.netty:netty-transport:4.1.92.Final" level="project" /> | |||
| <orderEntry type="library" name="Maven: io.projectreactor:reactor-core:3.4.29" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.reactivestreams:reactive-streams:1.0.4" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.commons:commons-pool2:2.9.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: eu.bitwalker:UserAgentUtils:1.21" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.itextpdf:itextpdf:5.5.13.2" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.itextpdf:itext-asian:5.2.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: cn.hutool:hutool-all:5.5.4" level="project" /> | |||
| <orderEntry type="library" name="Maven: de.odysseus.juel:juel:2.1.3" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpclient:4.5.13" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpcore:4.4.16" level="project" /> | |||
| <orderEntry type="library" name="Maven: commons-logging:commons-logging:1.2" level="project" /> | |||
| <orderEntry type="library" name="Maven: commons-codec:commons-codec:1.15" level="project" /> | |||
| </component> | |||
| </module> | |||
| @@ -35,6 +35,12 @@ | |||
| <artifactId>spring-boot-starter-security</artifactId> | |||
| </dependency> | |||
| <!--thumbnailator图片处理--> | |||
| <dependency> | |||
| <groupId>net.coobird</groupId> | |||
| <artifactId>thumbnailator</artifactId> | |||
| </dependency> | |||
| <!-- pagehelper 分页插件 --> | |||
| <dependency> | |||
| <groupId>com.github.pagehelper</groupId> | |||
| @@ -47,18 +53,24 @@ | |||
| <artifactId>spring-boot-starter-validation</artifactId> | |||
| </dependency> | |||
| <!-- lombok --> | |||
| <dependency> | |||
| <groupId>org.projectlombok</groupId> | |||
| <artifactId>lombok</artifactId> | |||
| </dependency> | |||
| <!--常用工具类 --> | |||
| <dependency> | |||
| <groupId>org.apache.commons</groupId> | |||
| <artifactId>commons-lang3</artifactId> | |||
| </dependency> | |||
| <!-- JSON工具类 --> | |||
| <dependency> | |||
| <groupId>com.fasterxml.jackson.core</groupId> | |||
| <artifactId>jackson-databind</artifactId> | |||
| </dependency> | |||
| <!-- 阿里JSON解析器 --> | |||
| <dependency> | |||
| <groupId>com.alibaba.fastjson2</groupId> | |||
| @@ -89,6 +101,12 @@ | |||
| <artifactId>jjwt</artifactId> | |||
| </dependency> | |||
| <!-- servlet包 --> | |||
| <dependency> | |||
| <groupId>javax.servlet</groupId> | |||
| <artifactId>javax.servlet-api</artifactId> | |||
| </dependency> | |||
| <!-- Jaxb --> | |||
| <dependency> | |||
| <groupId>javax.xml.bind</groupId> | |||
| @@ -119,6 +137,25 @@ | |||
| <artifactId>javax.servlet-api</artifactId> | |||
| </dependency> | |||
| <!--pdf--> | |||
| <dependency> | |||
| <groupId>com.itextpdf</groupId> | |||
| <artifactId>itextpdf</artifactId> | |||
| </dependency> | |||
| <!--pdf 字体--> | |||
| <dependency> | |||
| <groupId>com.itextpdf</groupId> | |||
| <artifactId>itext-asian</artifactId> | |||
| </dependency> | |||
| <!-- hutool --> | |||
| <dependency> | |||
| <groupId>cn.hutool</groupId> | |||
| <artifactId>hutool-all</artifactId> | |||
| </dependency> | |||
| </dependencies> | |||
| </project> | |||
| </project> | |||
| @@ -37,6 +37,7 @@ | |||
| <orderEntry type="library" name="Maven: org.springframework.security:spring-security-core:5.7.12" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.security:spring-security-crypto:5.7.12" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.security:spring-security-web:5.7.12" level="project" /> | |||
| <orderEntry type="library" name="Maven: net.coobird:thumbnailator:0.4.8" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.github.pagehelper:pagehelper-spring-boot-starter:1.4.7" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.mybatis.spring.boot:mybatis-spring-boot-starter:2.3.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-jdbc:2.5.15" level="project" /> | |||
| @@ -54,6 +55,7 @@ | |||
| <orderEntry type="library" name="Maven: jakarta.validation:jakarta.validation-api:2.0.2" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.4.3.Final" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.fasterxml:classmate:1.5.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.projectlombok:lombok:1.18.26" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.12.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.12.7.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.12.7" level="project" /> | |||
| @@ -72,6 +74,7 @@ | |||
| <orderEntry type="library" name="Maven: com.github.virtuald:curvesapi:1.06" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.28" level="project" /> | |||
| <orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt:0.9.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: javax.servlet:javax.servlet-api:4.0.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: javax.xml.bind:jaxb-api:2.3.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: javax.activation:javax.activation-api:1.2.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-data-redis:2.5.15" level="project" /> | |||
| @@ -93,6 +96,8 @@ | |||
| <orderEntry type="library" name="Maven: org.reactivestreams:reactive-streams:1.0.4" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.commons:commons-pool2:2.9.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: eu.bitwalker:UserAgentUtils:1.21" level="project" /> | |||
| <orderEntry type="library" name="Maven: javax.servlet:javax.servlet-api:4.0.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.itextpdf:itextpdf:5.5.13.2" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.itextpdf:itext-asian:5.2.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: cn.hutool:hutool-all:5.5.4" level="project" /> | |||
| </component> | |||
| </module> | |||
| @@ -5,7 +5,7 @@ import org.springframework.stereotype.Component; | |||
| /** | |||
| * 读取项目相关配置 | |||
| * | |||
| * | |||
| * @author ruoyi | |||
| */ | |||
| @Component | |||
| @@ -13,7 +13,7 @@ import org.springframework.stereotype.Component; | |||
| public class RuoYiConfig | |||
| { | |||
| /** 项目名称 */ | |||
| private String name; | |||
| private static String name; | |||
| /** 版本 */ | |||
| private String version; | |||
| @@ -30,8 +30,7 @@ public class RuoYiConfig | |||
| /** 验证码类型 */ | |||
| private static String captchaType; | |||
| public String getName() | |||
| { | |||
| public static String getName() { | |||
| return name; | |||
| } | |||
| @@ -0,0 +1,24 @@ | |||
| package com.ruoyi.common.core.domain.pdf; | |||
| import lombok.Data; | |||
| /** | |||
| * @description: | |||
| * @author: zzl | |||
| * @date: Created in 2024-04-30 10:57 | |||
| * @version: 1.0 | |||
| * @modified By: | |||
| */ | |||
| @Data | |||
| public class BottomItem { | |||
| private static final long serialVersionUID = 1L; | |||
| private String leftItem; | |||
| private String centerItem; | |||
| private String rightItem; | |||
| } | |||
| @@ -0,0 +1,44 @@ | |||
| package com.ruoyi.common.core.domain.pdf; | |||
| import lombok.Data; | |||
| /** | |||
| * @description: | |||
| * @author: zzl | |||
| * @date: Created in 2024-04-30 10:57 | |||
| * @version: 1.0 | |||
| * @modified By: | |||
| */ | |||
| @Data | |||
| public class PageSet { | |||
| private static final long serialVersionUID = 1L; | |||
| // 纸张类型 | |||
| private String paperType; | |||
| // 纸张宽度 默认 A4, 换算关系 1cm ~ 28.35f | |||
| private float paperWidth = 595.0f; | |||
| // 纸张高度 默认 A4 | |||
| private float paperHeight = 842.0f; | |||
| // 打印方向 1 纵 2横 0缺省 | |||
| private String printDirection = "1"; | |||
| private int tableTotalWidth = 520; | |||
| // 左边距 | |||
| private int marginLeft = 50; | |||
| // 右边距 | |||
| private int marginRight = 50; | |||
| // 上边距 | |||
| private int marginTop = 30; | |||
| // 下边距 | |||
| private int marginBottom = 20; | |||
| } | |||
| @@ -0,0 +1,46 @@ | |||
| package com.ruoyi.common.core.domain.pdf; | |||
| import lombok.Data; | |||
| import java.util.List; | |||
| /** | |||
| * @description: | |||
| * @author: zzl | |||
| * @date: Created in 2024-04-30 10:57 | |||
| * @version: 1.0 | |||
| * @modified By: | |||
| */ | |||
| @Data | |||
| public class PdfProperty { | |||
| private static final long serialVersionUID = 1L; | |||
| // 顶部大标题 | |||
| private String title; | |||
| // 纸张定义 | |||
| private PageSet pageSet; | |||
| // 肩部:左 中 右 | |||
| private ShoulderItem shoulder; | |||
| // 表行高 | |||
| private float rowHeight; | |||
| // 列宽 百分比 | |||
| private float[] columnWidth; | |||
| // 列标题 | |||
| private String[] header; | |||
| // 水平位置( 0:左 1:中 2:右) | |||
| private int[] aligns; | |||
| // 列表数据 | |||
| private List<String[]> contentList; | |||
| } | |||
| @@ -0,0 +1,24 @@ | |||
| package com.ruoyi.common.core.domain.pdf; | |||
| import lombok.Data; | |||
| /** | |||
| * @description: | |||
| * @author: zzl | |||
| * @date: Created in 2024-04-30 10:57 | |||
| * @version: 1.0 | |||
| * @modified By: | |||
| */ | |||
| @Data | |||
| public class ShoulderItem { | |||
| private static final long serialVersionUID = 1L; | |||
| private String leftItem; | |||
| private String centerItem; | |||
| private String rightItem; | |||
| } | |||
| @@ -2,7 +2,7 @@ package com.ruoyi.common.enums; | |||
| /** | |||
| * 业务操作类型 | |||
| * | |||
| * | |||
| * @author ruoyi | |||
| */ | |||
| public enum BusinessType | |||
| @@ -51,9 +51,14 @@ public enum BusinessType | |||
| * 生成代码 | |||
| */ | |||
| GENCODE, | |||
| /** | |||
| * 清空数据 | |||
| */ | |||
| CLEAN, | |||
| /** | |||
| * 打印 | |||
| */ | |||
| PRINT, | |||
| } | |||
| @@ -0,0 +1,490 @@ | |||
| package com.ruoyi.common.utils; | |||
| import java.math.BigDecimal; | |||
| import java.math.RoundingMode; | |||
| import java.text.NumberFormat; | |||
| import java.text.ParseException; | |||
| import java.util.Arrays; | |||
| import java.util.List; | |||
| import java.util.Locale; | |||
| /** | |||
| * 字符串工具类 | |||
| * | |||
| * @author ruoyi | |||
| */ | |||
| public class DecimalUtils { | |||
| private static final List<String> CN_NUMBERS = Arrays.asList("零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"); | |||
| private static final List<String> CN_UNITS = Arrays.asList("", "拾", "佰", "仟"); | |||
| private static final List<String> CN_BIG_UNITS = Arrays.asList("", "万", "亿", "兆"); | |||
| private static final String CN_NEGATIVE = "负"; | |||
| private static final String CN_INTEGER = "整"; | |||
| private static final String CN_FULL = "圆"; | |||
| private static final String CN_DECIMAL_UNIT = "角分"; | |||
| private static final int MAXIMUM_NUMBER = 999999999; | |||
| /** | |||
| * 将金额数字转换为中文大写 | |||
| * @param amount 金额数字,支持最大到999999999.99 | |||
| * @return 中文大写金额字符串 | |||
| */ | |||
| public static String bigDecimalToChinese(BigDecimal amount) { | |||
| if (amount == null) { | |||
| throw new IllegalArgumentException("金额不能为空"); | |||
| } | |||
| // 检查金额范围 | |||
| if (amount.abs().compareTo(new BigDecimal(MAXIMUM_NUMBER + ".99")) > 0) { | |||
| throw new IllegalArgumentException("金额超出最大范围(999999999.99)"); | |||
| } | |||
| StringBuilder result = new StringBuilder(); | |||
| // 处理负数 | |||
| if (amount.compareTo(BigDecimal.ZERO) < 0) { | |||
| result.append(CN_NEGATIVE); | |||
| amount = amount.abs(); | |||
| } | |||
| // 转换为字符串处理 | |||
| String amountStr = amount.setScale(2, BigDecimal.ROUND_HALF_UP).toString(); | |||
| String[] parts = amountStr.split("\\."); | |||
| String integerPart = parts[0]; | |||
| String decimalPart = parts.length > 1 ? parts[1] : ""; | |||
| // 处理整数部分 | |||
| boolean hasInteger = !"0".equals(integerPart); | |||
| if (hasInteger) { | |||
| result.append(convertIntegerPart(integerPart)).append(CN_FULL); | |||
| } | |||
| // 处理小数部分 | |||
| String decimalStr = convertDecimalPart(decimalPart); | |||
| if (!decimalStr.isEmpty()) { | |||
| // 如果没有整数部分且有小部分,则不显示"零圆" | |||
| if (!hasInteger && decimalStr.charAt(0) != CN_NUMBERS.get(0).charAt(0)) { | |||
| result.append(decimalStr); | |||
| } else { | |||
| // 如果有整数部分或小数部分以零开头,则正常拼接 | |||
| if (!hasInteger) { | |||
| result.append(CN_NUMBERS.get(0)).append(CN_FULL); | |||
| } | |||
| result.append(decimalStr); | |||
| } | |||
| } else { | |||
| // 如果没有小数部分,添加"整" | |||
| result.append(CN_INTEGER); | |||
| } | |||
| return result.toString(); | |||
| } | |||
| private static String convertIntegerPart(String integerPart) { | |||
| StringBuilder sb = new StringBuilder(); | |||
| int length = integerPart.length(); | |||
| int zeroCount = 0; // 连续零的个数 | |||
| for (int i = 0; i < length; i++) { | |||
| int digit = Character.getNumericValue(integerPart.charAt(i)); | |||
| int pos = length - i - 1; // 当前位数 | |||
| int unitPos = pos % 4; // 单位位置(个十百千) | |||
| int bigUnitPos = pos / 4; // 大单位位置(万,亿) | |||
| if (digit == 0) { | |||
| zeroCount++; | |||
| } else { | |||
| if (zeroCount > 0) { | |||
| sb.append(CN_NUMBERS.get(0)); | |||
| zeroCount = 0; | |||
| } | |||
| sb.append(CN_NUMBERS.get(digit)).append(CN_UNITS.get(unitPos)); | |||
| } | |||
| // 添加大单位(万,亿) | |||
| if (unitPos == 0 && bigUnitPos > 0) { | |||
| if (zeroCount < 4) { // 如果当前段不全为零 | |||
| sb.append(CN_BIG_UNITS.get(bigUnitPos)); | |||
| } | |||
| zeroCount = 0; | |||
| } | |||
| } | |||
| // 处理全零的情况 | |||
| if (sb.length() == 0) { | |||
| return CN_NUMBERS.get(0); | |||
| } | |||
| return sb.toString(); | |||
| } | |||
| private static String convertDecimalPart(String decimalPart) { | |||
| StringBuilder sb = new StringBuilder(); | |||
| boolean hasNonZero = false; | |||
| for (int i = 0; i < decimalPart.length() && i < CN_DECIMAL_UNIT.length(); i++) { | |||
| int digit = Character.getNumericValue(decimalPart.charAt(i)); | |||
| if (digit != 0) { | |||
| sb.append(CN_NUMBERS.get(digit)).append(CN_DECIMAL_UNIT.charAt(i)); | |||
| hasNonZero = true; | |||
| } else if (hasNonZero || (i > 0 && sb.length() > 0)) { | |||
| // 如果前面已经有非零数字,或者不是第一位,可以添加零 | |||
| sb.append(CN_NUMBERS.get(0)); | |||
| } | |||
| } | |||
| // 去除末尾的零 | |||
| while (sb.length() > 0 && sb.charAt(sb.length() - 1) == CN_NUMBERS.get(0).charAt(0)) { | |||
| sb.deleteCharAt(sb.length() - 1); | |||
| } | |||
| return sb.toString(); | |||
| } | |||
| //默认除法运算精度 | |||
| private static final int DEF_DIV_SCALE = 10; | |||
| /** | |||
| * 提供精确的加法运算 | |||
| * | |||
| * @param v1 被加数 | |||
| * @param v2 加数 | |||
| * @return 两个参数的和 | |||
| */ | |||
| public static double add(double v1, double v2) { | |||
| BigDecimal b1 = new BigDecimal(Double.toString(v1)); | |||
| BigDecimal b2 = new BigDecimal(Double.toString(v2)); | |||
| return b1.add(b2).doubleValue(); | |||
| } | |||
| /** | |||
| * 提供精确的加法运算 | |||
| * | |||
| * @param v1 被加数 | |||
| * @param v2 加数 | |||
| * @return 两个参数的和 | |||
| */ | |||
| public static BigDecimal add(String v1, String v2) { | |||
| BigDecimal b1 = new BigDecimal(v1); | |||
| BigDecimal b2 = new BigDecimal(v2); | |||
| return b1.add(b2); | |||
| } | |||
| /** | |||
| * 提供精确的加法运算 | |||
| * | |||
| * @param v1 被加数 | |||
| * @param v2 加数 | |||
| * @param scale 保留scale 位小数 | |||
| * @return 两个参数的和 | |||
| */ | |||
| public static String add(String v1, String v2, int scale) { | |||
| if (scale < 0) { | |||
| throw new IllegalArgumentException( | |||
| "The scale must be a positive integer or zero"); | |||
| } | |||
| BigDecimal b1 = new BigDecimal(v1); | |||
| BigDecimal b2 = new BigDecimal(v2); | |||
| return b1.add(b2).setScale(scale, RoundingMode.HALF_UP).toString(); | |||
| } | |||
| /** | |||
| * 提供精确的减法运算 | |||
| * | |||
| * @param v1 被减数 | |||
| * @param v2 减数 | |||
| * @return 两个参数的差 | |||
| */ | |||
| public static double sub(double v1, double v2) { | |||
| BigDecimal b1 = new BigDecimal(Double.toString(v1)); | |||
| BigDecimal b2 = new BigDecimal(Double.toString(v2)); | |||
| return b1.subtract(b2).doubleValue(); | |||
| } | |||
| /** | |||
| * 提供精确的减法运算。 | |||
| * | |||
| * @param v1 被减数 | |||
| * @param v2 减数 | |||
| * @return 两个参数的差 | |||
| */ | |||
| public static BigDecimal sub(String v1, String v2) { | |||
| BigDecimal b1 = new BigDecimal(v1); | |||
| BigDecimal b2 = new BigDecimal(v2); | |||
| return b1.subtract(b2); | |||
| } | |||
| /** | |||
| * 提供精确的减法运算 | |||
| * | |||
| * @param v1 被减数 | |||
| * @param v2 减数 | |||
| * @param scale 保留scale 位小数 | |||
| * @return 两个参数的差 | |||
| */ | |||
| public static String sub(String v1, String v2, int scale) { | |||
| if (scale < 0) { | |||
| throw new IllegalArgumentException( | |||
| "The scale must be a positive integer or zero"); | |||
| } | |||
| BigDecimal b1 = new BigDecimal(v1); | |||
| BigDecimal b2 = new BigDecimal(v2); | |||
| return b1.subtract(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString(); | |||
| } | |||
| /** | |||
| * 提供精确的乘法运算 | |||
| * | |||
| * @param v1 被乘数 | |||
| * @param v2 乘数 | |||
| * @return 两个参数的积 | |||
| */ | |||
| public static double mul(double v1, double v2) { | |||
| BigDecimal b1 = new BigDecimal(Double.toString(v1)); | |||
| BigDecimal b2 = new BigDecimal(Double.toString(v2)); | |||
| return b1.multiply(b2).doubleValue(); | |||
| } | |||
| /** | |||
| * 提供精确的乘法运算 | |||
| * | |||
| * @param v1 被乘数 | |||
| * @param v2 乘数 | |||
| * @return 两个参数的积 | |||
| */ | |||
| public static BigDecimal mul(String v1, String v2) { | |||
| BigDecimal b1 = new BigDecimal(v1); | |||
| BigDecimal b2 = new BigDecimal(v2); | |||
| return b1.multiply(b2); | |||
| } | |||
| /** | |||
| * 提供精确的乘法运算 | |||
| * | |||
| * @param v1 被乘数 | |||
| * @param v2 乘数 | |||
| * @param scale 保留scale 位小数 | |||
| * @return 两个参数的积 | |||
| */ | |||
| public static double mul(double v1, double v2, int scale) { | |||
| BigDecimal b1 = new BigDecimal(Double.toString(v1)); | |||
| BigDecimal b2 = new BigDecimal(Double.toString(v2)); | |||
| return round(b1.multiply(b2).doubleValue(), scale); | |||
| } | |||
| /** | |||
| * 提供精确的乘法运算 | |||
| * | |||
| * @param v1 被乘数 | |||
| * @param v2 乘数 | |||
| * @param scale 保留scale 位小数 | |||
| * @return 两个参数的积 | |||
| */ | |||
| public static String mul(String v1, String v2, int scale) { | |||
| if (scale < 0) { | |||
| throw new IllegalArgumentException( | |||
| "The scale must be a positive integer or zero"); | |||
| } | |||
| BigDecimal b1 = new BigDecimal(v1); | |||
| BigDecimal b2 = new BigDecimal(v2); | |||
| return b1.multiply(b2).setScale(scale, RoundingMode.HALF_UP).toString(); | |||
| } | |||
| /** | |||
| * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 | |||
| * 小数点以后10位,以后的数字四舍五入 | |||
| * | |||
| * @param v1 被除数 | |||
| * @param v2 除数 | |||
| * @return 两个参数的商 | |||
| */ | |||
| public static double div(double v1, double v2) { | |||
| return div(v1, v2, DEF_DIV_SCALE); | |||
| } | |||
| /** | |||
| * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 | |||
| * 定精度,以后的数字四舍五入 | |||
| * | |||
| * @param v1 被除数 | |||
| * @param v2 除数 | |||
| * @param scale 表示表示需要精确到小数点以后几位。 | |||
| * @return 两个参数的商 | |||
| */ | |||
| public static double div(double v1, double v2, int scale) { | |||
| if (scale < 0) { | |||
| throw new IllegalArgumentException("The scale must be a positive integer or zero"); | |||
| } | |||
| BigDecimal b1 = new BigDecimal(Double.toString(v1)); | |||
| BigDecimal b2 = new BigDecimal(Double.toString(v2)); | |||
| return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue(); | |||
| } | |||
| /** | |||
| * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 | |||
| * 定精度,以后的数字四舍五入 | |||
| * | |||
| * @param v1 被除数 | |||
| * @param v2 除数 | |||
| * @param scale 表示需要精确到小数点以后几位 | |||
| * @return 两个参数的商 | |||
| */ | |||
| public static String div(String v1, String v2, int scale) { | |||
| if (scale < 0) { | |||
| throw new IllegalArgumentException("The scale must be a positive integer or zero"); | |||
| } | |||
| BigDecimal b1 = new BigDecimal(v1); | |||
| BigDecimal b2 = new BigDecimal(v1); | |||
| return b1.divide(b2, scale, RoundingMode.HALF_UP).toString(); | |||
| } | |||
| /** | |||
| * 提供精确的小数位四舍五入处理 | |||
| * | |||
| * @param v 需要四舍五入的数字 | |||
| * @param scale 小数点后保留几位 | |||
| * @return 四舍五入后的结果 | |||
| */ | |||
| public static double round(double v, int scale) { | |||
| if (scale < 0) { | |||
| throw new IllegalArgumentException("The scale must be a positive integer or zero"); | |||
| } | |||
| BigDecimal b = new BigDecimal(Double.toString(v)); | |||
| return b.setScale(scale, RoundingMode.HALF_UP).doubleValue(); | |||
| } | |||
| /** | |||
| * 提供精确的小数位四舍五入处理 | |||
| * | |||
| * @param v 需要四舍五入的数字 | |||
| * @param scale 小数点后保留几位 | |||
| * @return 四舍五入后的结果 | |||
| */ | |||
| public static String round(String v, int scale) { | |||
| if (scale < 0) { | |||
| throw new IllegalArgumentException( | |||
| "The scale must be a positive integer or zero"); | |||
| } | |||
| BigDecimal b = new BigDecimal(v); | |||
| return b.setScale(scale, RoundingMode.HALF_UP).toString(); | |||
| } | |||
| /** | |||
| * 取余数 | |||
| * | |||
| * @param v1 被除数 | |||
| * @param v2 除数 | |||
| * @param scale 小数点后保留几位 | |||
| * @return 余数 | |||
| */ | |||
| public static String remainder(String v1, String v2, int scale) { | |||
| if (scale < 0) { | |||
| throw new IllegalArgumentException( | |||
| "The scale must be a positive integer or zero"); | |||
| } | |||
| BigDecimal b1 = new BigDecimal(v1); | |||
| BigDecimal b2 = new BigDecimal(v2); | |||
| return b1.remainder(b2).setScale(scale, RoundingMode.HALF_UP).toString(); | |||
| } | |||
| /** | |||
| * 取余数 BigDecimal | |||
| * | |||
| * @param v1 被除数 | |||
| * @param v2 除数 | |||
| * @param scale 小数点后保留几位 | |||
| * @return 余数 | |||
| */ | |||
| public static BigDecimal remainder(BigDecimal v1, BigDecimal v2, int scale) { | |||
| if (scale < 0) { | |||
| throw new IllegalArgumentException( | |||
| "The scale must be a positive integer or zero"); | |||
| } | |||
| return v1.remainder(v2).setScale(scale, RoundingMode.HALF_UP); | |||
| } | |||
| /** | |||
| * 比较大小 | |||
| * | |||
| * @param v1 被比较数 | |||
| * @param v2 比较数 | |||
| * @return 如果v1 大于v2 则 返回true 否则false | |||
| */ | |||
| public static boolean compare(String v1, String v2) { | |||
| BigDecimal b1 = new BigDecimal(v1); | |||
| BigDecimal b2 = new BigDecimal(v2); | |||
| int bj = b1.compareTo(b2); | |||
| boolean res; | |||
| res = bj > 0; | |||
| return res; | |||
| } | |||
| /** | |||
| * 将数字格式化为千分位 | |||
| * | |||
| * @param number | |||
| * @return 字符串 | |||
| */ | |||
| public static String convert(BigDecimal number) { | |||
| if (null == number || number.compareTo(BigDecimal.ZERO) == 0) { | |||
| return ""; | |||
| } | |||
| NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.ENGLISH); | |||
| // 设置最大分数位数,这里设置为0表示不使用分数 | |||
| numberFormat.setMaximumFractionDigits(2); | |||
| // 设置最小整数位数,这里设置为2表示整数部分至少2位 | |||
| //numberFormat.setMinimumIntegerDigits(2); | |||
| // 设置grouping used属性为false,表示不使用千分位分隔符 | |||
| numberFormat.setGroupingUsed(true); | |||
| return numberFormat.format(number); | |||
| } | |||
| /** | |||
| * 将千分位数字字符串格式化为 BigDecimal | |||
| * @param formattedNumber | |||
| * @return 字符串 | |||
| */ | |||
| public static BigDecimal convert(String formattedNumber) { | |||
| if (StringUtils.isEmpty(formattedNumber)) { | |||
| return BigDecimal.ZERO; | |||
| } | |||
| try { | |||
| NumberFormat format = NumberFormat.getInstance(Locale.US); | |||
| Number number = format.parse(formattedNumber); | |||
| BigDecimal bigDecimal = new BigDecimal(number.toString()); | |||
| return bigDecimal; | |||
| } catch (ParseException e) { | |||
| e.printStackTrace(); | |||
| return BigDecimal.ZERO; | |||
| } | |||
| } | |||
| public static boolean isZero_s(BigDecimal b) { | |||
| return null == b || b.compareTo(BigDecimal.ZERO) == 0; | |||
| } | |||
| /** | |||
| * 将是0的处理成空 | |||
| * @param number | |||
| * @return BigDecimal | |||
| */ | |||
| public static String zeroToNull(BigDecimal number) { | |||
| if(number.compareTo(BigDecimal.ZERO) == 0){ | |||
| return null; | |||
| }else{ | |||
| return number.toString(); | |||
| } | |||
| } | |||
| } | |||
| @@ -1,16 +1,21 @@ | |||
| package com.ruoyi.common.utils; | |||
| import java.util.Collection; | |||
| import java.util.List; | |||
| import cn.hutool.core.collection.CollectionUtil; | |||
| import com.alibaba.fastjson2.JSONArray; | |||
| import com.ruoyi.common.constant.CacheConstants; | |||
| import com.ruoyi.common.core.domain.entity.SysDictData; | |||
| import com.ruoyi.common.core.redis.RedisCache; | |||
| import com.ruoyi.common.utils.spring.SpringUtils; | |||
| import java.util.Collection; | |||
| import java.util.LinkedHashMap; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| import java.util.stream.Collectors; | |||
| /** | |||
| * 字典工具类 | |||
| * | |||
| * | |||
| * @author ruoyi | |||
| */ | |||
| public class DictUtils | |||
| @@ -22,7 +27,7 @@ public class DictUtils | |||
| /** | |||
| * 设置字典缓存 | |||
| * | |||
| * | |||
| * @param key 参数键 | |||
| * @param dictDatas 字典数据列表 | |||
| */ | |||
| @@ -33,7 +38,7 @@ public class DictUtils | |||
| /** | |||
| * 获取字典缓存 | |||
| * | |||
| * | |||
| * @param key 参数键 | |||
| * @return dictDatas 字典数据列表 | |||
| */ | |||
| @@ -49,7 +54,7 @@ public class DictUtils | |||
| /** | |||
| * 根据字典类型和字典值获取字典标签 | |||
| * | |||
| * | |||
| * @param dictType 字典类型 | |||
| * @param dictValue 字典值 | |||
| * @return 字典标签 | |||
| @@ -65,7 +70,7 @@ public class DictUtils | |||
| /** | |||
| * 根据字典类型和字典标签获取字典值 | |||
| * | |||
| * | |||
| * @param dictType 字典类型 | |||
| * @param dictLabel 字典标签 | |||
| * @return 字典值 | |||
| @@ -81,7 +86,7 @@ public class DictUtils | |||
| /** | |||
| * 根据字典类型和字典值获取字典标签 | |||
| * | |||
| * | |||
| * @param dictType 字典类型 | |||
| * @param dictValue 字典值 | |||
| * @param separator 分隔符 | |||
| @@ -124,7 +129,7 @@ public class DictUtils | |||
| /** | |||
| * 根据字典类型和字典标签获取字典值 | |||
| * | |||
| * | |||
| * @param dictType 字典类型 | |||
| * @param dictLabel 字典标签 | |||
| * @param separator 分隔符 | |||
| @@ -209,7 +214,7 @@ public class DictUtils | |||
| /** | |||
| * 删除指定字典缓存 | |||
| * | |||
| * | |||
| * @param key 字典键 | |||
| */ | |||
| public static void removeDictCache(String key) | |||
| @@ -228,7 +233,7 @@ public class DictUtils | |||
| /** | |||
| * 设置cache key | |||
| * | |||
| * | |||
| * @param configKey 参数键 | |||
| * @return 缓存键key | |||
| */ | |||
| @@ -236,4 +241,18 @@ public class DictUtils | |||
| { | |||
| return CacheConstants.SYS_DICT_KEY + configKey; | |||
| } | |||
| public static Map<String, String> dictCacheValueLabelMap(String key) { | |||
| List<SysDictData> dictCache = getDictCache(key); | |||
| if (CollectionUtil.isEmpty(dictCache)) | |||
| return new LinkedHashMap<>(); // safety | |||
| return dictCache.stream().collect(Collectors.toMap(SysDictData::getDictValue, SysDictData::getDictLabel, (a, b) -> a, LinkedHashMap::new)); | |||
| } | |||
| public static String getDictLabelElseOriginValue(String dictType, String dictValue) { | |||
| if (StringUtils.isEmpty(dictValue)) | |||
| return dictValue; | |||
| String res = getDictLabel(dictType, dictValue); | |||
| return StringUtils.isEmpty(res) ? dictValue : res; | |||
| } | |||
| } | |||
| @@ -0,0 +1,69 @@ | |||
| package com.ruoyi.common.utils.file; | |||
| import cn.hutool.core.codec.Base64; | |||
| import org.springframework.web.multipart.MultipartFile; | |||
| import java.io.*; | |||
| public class BASE64DecodedMultipartFile implements MultipartFile { | |||
| private final byte[] imgContent; | |||
| private final String header; | |||
| public BASE64DecodedMultipartFile(byte[] imgContent, String header) { | |||
| this.imgContent = imgContent; | |||
| this.header = header.split(";")[0]; | |||
| } | |||
| @Override | |||
| public String getName() { | |||
| return System.currentTimeMillis() + Math.random() + "." + header.split("/")[1]; | |||
| } | |||
| @Override | |||
| public String getOriginalFilename() { | |||
| return System.currentTimeMillis() + (int) Math.random() * 10000 + "." + header.split("/")[1]; | |||
| } | |||
| @Override | |||
| public String getContentType() { | |||
| return header.split(":")[1]; | |||
| } | |||
| @Override | |||
| public boolean isEmpty() { | |||
| return imgContent == null || imgContent.length == 0; | |||
| } | |||
| @Override | |||
| public long getSize() { | |||
| return imgContent.length; | |||
| } | |||
| @Override | |||
| public byte[] getBytes() throws IOException { | |||
| return imgContent; | |||
| } | |||
| @Override | |||
| public InputStream getInputStream() throws IOException { | |||
| return new ByteArrayInputStream(imgContent); | |||
| } | |||
| @Override | |||
| public void transferTo(File dest) throws IOException, IllegalStateException { | |||
| new FileOutputStream(dest).write(imgContent); | |||
| } | |||
| public static MultipartFile base64ToMultipart(String base64) { | |||
| String[] base64Array = base64.split(","); | |||
| String imageString = base64Array.length > 1 ? base64Array[1] : base64Array[0]; | |||
| byte[] imageBytes = Base64.decode(imageString); | |||
| return new BASE64DecodedMultipartFile(imageBytes, "image/png"); | |||
| } | |||
| } | |||
| @@ -0,0 +1,71 @@ | |||
| package com.ruoyi.common.utils.file; | |||
| import org.springframework.web.multipart.MultipartFile; | |||
| import java.io.*; | |||
| /** | |||
| * @description: | |||
| * @author: zzl | |||
| * @date: Created in 2021/8/5 18:38 | |||
| * @version: v1.0 | |||
| * @modified By: | |||
| */ | |||
| public class ConvertToMultipartFile implements MultipartFile { | |||
| private byte[] fileBytes; | |||
| String name; | |||
| String originalFilename; | |||
| String contentType; | |||
| boolean isEmpty; | |||
| long size; | |||
| public ConvertToMultipartFile(byte[] fileBytes, String name, String originalFilename, String contentType, long size) { | |||
| this.fileBytes = fileBytes; | |||
| this.name = name; | |||
| this.originalFilename = originalFilename; | |||
| this.contentType = contentType; | |||
| this.size = size; | |||
| this.isEmpty = false; | |||
| } | |||
| @Override | |||
| public String getName() { | |||
| return name; | |||
| } | |||
| @Override | |||
| public String getOriginalFilename() { | |||
| return originalFilename; | |||
| } | |||
| @Override | |||
| public String getContentType() { | |||
| return contentType; | |||
| } | |||
| @Override | |||
| public boolean isEmpty() { | |||
| return isEmpty; | |||
| } | |||
| @Override | |||
| public long getSize() { | |||
| return size; | |||
| } | |||
| @Override | |||
| public byte[] getBytes() throws IOException { | |||
| return fileBytes; | |||
| } | |||
| @Override | |||
| public InputStream getInputStream() throws IOException { | |||
| return new ByteArrayInputStream(fileBytes); | |||
| } | |||
| @Override | |||
| public void transferTo(File dest) throws IOException, IllegalStateException { | |||
| new FileOutputStream(dest).write(fileBytes); | |||
| } | |||
| } | |||
| @@ -1,11 +1,5 @@ | |||
| package com.ruoyi.common.utils.file; | |||
| import java.io.File; | |||
| import java.io.IOException; | |||
| import java.nio.file.Paths; | |||
| import java.util.Objects; | |||
| import org.apache.commons.io.FilenameUtils; | |||
| import org.springframework.web.multipart.MultipartFile; | |||
| import com.ruoyi.common.config.RuoYiConfig; | |||
| import com.ruoyi.common.constant.Constants; | |||
| import com.ruoyi.common.exception.file.FileNameLengthLimitExceededException; | |||
| @@ -13,21 +7,39 @@ import com.ruoyi.common.exception.file.FileSizeLimitExceededException; | |||
| import com.ruoyi.common.exception.file.InvalidExtensionException; | |||
| import com.ruoyi.common.utils.DateUtils; | |||
| import com.ruoyi.common.utils.StringUtils; | |||
| import com.ruoyi.common.utils.uuid.IdUtils; | |||
| import com.ruoyi.common.utils.uuid.Seq; | |||
| import org.apache.commons.io.FilenameUtils; | |||
| import org.springframework.web.multipart.MultipartFile; | |||
| import javax.imageio.ImageIO; | |||
| import java.awt.image.BufferedImage; | |||
| import java.io.File; | |||
| import java.io.FileOutputStream; | |||
| import java.io.IOException; | |||
| import java.io.InputStream; | |||
| import java.net.HttpURLConnection; | |||
| import java.net.URL; | |||
| import java.nio.file.Paths; | |||
| import java.util.Objects; | |||
| import static java.util.Arrays.binarySearch; | |||
| /** | |||
| * 文件上传工具类 | |||
| * | |||
| * | |||
| * @author ruoyi | |||
| */ | |||
| public class FileUploadUtils | |||
| { | |||
| public class FileUploadUtils { | |||
| /** | |||
| * 默认大小 50M | |||
| */ | |||
| public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024L; | |||
| /** | |||
| * 图片压缩红线 kb | |||
| */ | |||
| public static final long DES_FILE_SIZE = 1024; | |||
| /** | |||
| * 默认的文件名最大长度 100 | |||
| */ | |||
| @@ -38,16 +50,58 @@ public class FileUploadUtils | |||
| */ | |||
| private static String defaultBaseDir = RuoYiConfig.getProfile(); | |||
| public static void setDefaultBaseDir(String defaultBaseDir) | |||
| { | |||
| public static void setDefaultBaseDir(String defaultBaseDir) { | |||
| FileUploadUtils.defaultBaseDir = defaultBaseDir; | |||
| } | |||
| public static String getDefaultBaseDir() | |||
| { | |||
| public static String getDefaultBaseDir() { | |||
| return defaultBaseDir; | |||
| } | |||
| public static String base64Upload(String img64, String bizPath) { | |||
| try { | |||
| MultipartFile file = BASE64DecodedMultipartFile.base64ToMultipart(img64); | |||
| String filePath = RuoYiConfig.getProfile() + File.separator + bizPath; | |||
| return upload(filePath, file); | |||
| } catch (IOException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| return null; | |||
| } | |||
| /** | |||
| * 根据远程文件url 下载存储到本地 | |||
| * @param urlStr "https://example.com/image.jpg" 远程文件地址 | |||
| * @return bizPath 新的存储位置 | |||
| * @return fileName a.png | |||
| * @throws Exception | |||
| */ | |||
| public static String saveFile(String urlStr, String bizPath, String fileName) throws Exception { | |||
| // 读取远程文件 | |||
| URL url = new URL(urlStr); | |||
| HttpURLConnection conn = (HttpURLConnection) url.openConnection(); | |||
| conn.setRequestMethod("GET"); | |||
| conn.setConnectTimeout(5000); | |||
| InputStream is = conn.getInputStream(); | |||
| byte[] data = ImageUtils.readInputStream(is); | |||
| bizPath = bizPath + "/" + DateUtils.dateTime(); | |||
| // 创建文件路径 | |||
| getAbsoluteFile(bizPath, fileName).getAbsolutePath(); | |||
| // 创建文件 | |||
| File imageFile = new File(bizPath + "/" + fileName); | |||
| FileOutputStream outStream = new FileOutputStream(imageFile); | |||
| outStream.write(data); | |||
| outStream.close(); | |||
| return getPathFileName(bizPath, fileName); | |||
| } | |||
| /** | |||
| * 以默认配置进行文件上传 | |||
| * | |||
| @@ -55,14 +109,10 @@ public class FileUploadUtils | |||
| * @return 文件名称 | |||
| * @throws Exception | |||
| */ | |||
| public static final String upload(MultipartFile file) throws IOException | |||
| { | |||
| try | |||
| { | |||
| public static final String upload(MultipartFile file) throws IOException { | |||
| try { | |||
| return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); | |||
| } | |||
| catch (Exception e) | |||
| { | |||
| } catch (Exception e) { | |||
| throw new IOException(e.getMessage(), e); | |||
| } | |||
| } | |||
| @@ -71,18 +121,31 @@ public class FileUploadUtils | |||
| * 根据文件路径上传 | |||
| * | |||
| * @param baseDir 相对应用的基目录 | |||
| * @param file 上传的文件 | |||
| * @param file 上传的文件 | |||
| * @return 文件名称 | |||
| * @throws IOException | |||
| */ | |||
| public static final String upload(String baseDir, MultipartFile file) throws IOException | |||
| { | |||
| try | |||
| { | |||
| public static final String upload(String baseDir, MultipartFile file) throws IOException { | |||
| try { | |||
| return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); | |||
| } catch (Exception e) { | |||
| throw new IOException(e.getMessage(), e); | |||
| } | |||
| catch (Exception e) | |||
| { | |||
| } | |||
| /** | |||
| * 根据文件路径上传 | |||
| * | |||
| * @param baseDir 相对应用的基目录 | |||
| * @param file 上传的文件 | |||
| * @param isPng 是否转透明png | |||
| * @return 文件名称 | |||
| * @throws IOException | |||
| */ | |||
| public static final String upload(String baseDir, MultipartFile file, boolean isPng) throws IOException { | |||
| try { | |||
| return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); | |||
| } catch (Exception e) { | |||
| throw new IOException(e.getMessage(), e); | |||
| } | |||
| } | |||
| @@ -90,86 +153,161 @@ public class FileUploadUtils | |||
| /** | |||
| * 文件上传 | |||
| * | |||
| * @param baseDir 相对应用的基目录 | |||
| * @param file 上传的文件 | |||
| * @param baseDir 相对应用的基目录 | |||
| * @param file 上传的文件 | |||
| * @param allowedExtension 上传文件类型 | |||
| * @return 返回上传成功的文件名 | |||
| * @throws FileSizeLimitExceededException 如果超出最大大小 | |||
| * @throws FileSizeLimitExceededException 如果超出最大大小 | |||
| * @throws FileNameLengthLimitExceededException 文件名太长 | |||
| * @throws IOException 比如读写文件出错时 | |||
| * @throws InvalidExtensionException 文件校验异常 | |||
| * @throws IOException 比如读写文件出错时 | |||
| * @throws InvalidExtensionException 文件校验异常 | |||
| */ | |||
| public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension) | |||
| throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, | |||
| InvalidExtensionException | |||
| { | |||
| return upload(baseDir, file, allowedExtension, false); | |||
| InvalidExtensionException { | |||
| // 文件名过长 | |||
| int fileNameLength = Objects.requireNonNull(file.getOriginalFilename()).length(); | |||
| if (fileNameLength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) { | |||
| throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); | |||
| } | |||
| // 文件大小、文件后缀格式 | |||
| assertAllowed(file, allowedExtension); | |||
| // 大于1M的 图片文件 进行压缩 | |||
| String fileType = getExtension(file); | |||
| if (binarySearch(MimeTypeUtils.IMAGE_EXTENSION, fileType) > 0) { | |||
| String fileName = file.getOriginalFilename(); | |||
| // 压缩成字节数组 | |||
| byte[] imageByte = ImageUtils.compressPicForScale(file.getBytes(), FileUploadUtils.DES_FILE_SIZE); | |||
| // 恢复成MultipartFile,且不重命名 | |||
| file = new ConvertToMultipartFile(imageByte, fileName, fileName, fileType, imageByte.length); | |||
| } | |||
| // 文件重命名 | |||
| String fileName = extractFilename(file); | |||
| // 判断后缀名是否正确 | |||
| int splitIndex = fileName.lastIndexOf("."); | |||
| String fType = fileName.substring(splitIndex + 1); | |||
| if (fType == null || fType.equals("")) { | |||
| fileName = fileName + file.getContentType(); | |||
| } | |||
| if (splitIndex < 0) { | |||
| fileName = fileName + "." + fileType; | |||
| } | |||
| // 创建文件路径 | |||
| String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath(); | |||
| // 存储文件 | |||
| file.transferTo(Paths.get(absPath)); | |||
| // 返回文件路径 | |||
| return getPathFileName(baseDir, fileName); | |||
| } | |||
| /** | |||
| * 文件上传 | |||
| * | |||
| * @param baseDir 相对应用的基目录 | |||
| * @param file 上传的文件 | |||
| * @param useCustomNaming 系统自定义文件名 | |||
| * @param baseDir 相对应用的基目录 | |||
| * @param file 上传的文件 | |||
| * @param isPng 是否转透明png | |||
| * @param allowedExtension 上传文件类型 | |||
| * @return 返回上传成功的文件名 | |||
| * @throws FileSizeLimitExceededException 如果超出最大大小 | |||
| * @throws FileSizeLimitExceededException 如果超出最大大小 | |||
| * @throws FileNameLengthLimitExceededException 文件名太长 | |||
| * @throws IOException 比如读写文件出错时 | |||
| * @throws InvalidExtensionException 文件校验异常 | |||
| * @throws IOException 比如读写文件出错时 | |||
| * @throws InvalidExtensionException 文件校验异常 | |||
| */ | |||
| public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension, boolean useCustomNaming) | |||
| public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension, boolean isPng, boolean isAngle) | |||
| throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, | |||
| InvalidExtensionException | |||
| { | |||
| InvalidExtensionException { | |||
| // 文件名过长 | |||
| int fileNameLength = Objects.requireNonNull(file.getOriginalFilename()).length(); | |||
| if (fileNameLength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) | |||
| { | |||
| if (fileNameLength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) { | |||
| throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); | |||
| } | |||
| // 文件大小、文件后缀格式 | |||
| assertAllowed(file, allowedExtension); | |||
| String fileName = useCustomNaming ? uuidFilename(file) : extractFilename(file); | |||
| // 大于1M的 图片文件 进行压缩 | |||
| String fileType = getExtension(file); | |||
| if (binarySearch(MimeTypeUtils.IMAGE_EXTENSION, fileType) > 0) { | |||
| String fileName = file.getOriginalFilename(); | |||
| // 压缩成字节数组 | |||
| byte[] imageByte = ImageUtils.compressPicForScale(file.getBytes(), FileUploadUtils.DES_FILE_SIZE); | |||
| // 恢复成MultipartFile,且不重命名 | |||
| file = new ConvertToMultipartFile(imageByte, fileName, fileName, fileType, imageByte.length); | |||
| } | |||
| // 文件重命名 | |||
| String fileName = extractFilename(file); | |||
| // 判断后缀名是否正确 | |||
| int splitIndex = fileName.lastIndexOf("."); | |||
| String fType = fileName.substring(splitIndex + 1); | |||
| if (fType == null || fType.equals("")) { | |||
| fileName = fileName + file.getContentType(); | |||
| } | |||
| if (splitIndex < 0) { | |||
| fileName = fileName + "." + fileType; | |||
| } | |||
| // 创建文件路径 | |||
| String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath(); | |||
| file.transferTo(Paths.get(absPath)); | |||
| // 存储文件 | |||
| if(isAngle){ // 旋转保存 | |||
| // 创建文件路径 | |||
| File desc = getAbsoluteFile(baseDir, fileName); | |||
| String ex = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".")); | |||
| //根据图片角度自动识别翻转 | |||
| //int angle = ImageUtils.getAngle(ImageUtils.getExif(file.getInputStream())); | |||
| // 旋转可能是自动识别手机效果的,此处不是原生,所以写死 | |||
| int angle = 3; | |||
| BufferedImage bf = ImageUtils.getBufferedImg(ImageIO.read(file.getInputStream()), ImageUtils.getWidth(file.getInputStream()), ImageUtils.getHeight(file.getInputStream()), angle); | |||
| ImageIO.write(bf, ex.substring(1), desc); | |||
| }else { // 正常保存 | |||
| file.transferTo(Paths.get(absPath)); | |||
| } | |||
| // 是否有转换透明底的需求 | |||
| if (isPng) { | |||
| //BufferedImage image = ImageIO.read(new File(absPath)); | |||
| //BufferedImage transparentImage = ImageUtils.makeBackgroundTransparent(image); | |||
| //ImageIO.write(transparentImage, "PNG", new File(absPath + "png")); | |||
| ImageUtils.convertToTransparentPng(absPath, absPath); | |||
| } | |||
| // 返回文件路径 | |||
| return getPathFileName(baseDir, fileName); | |||
| } | |||
| /** | |||
| * 编码文件名(日期格式目录 + 原文件名 + 序列值 + 后缀) | |||
| */ | |||
| public static final String extractFilename(MultipartFile file) | |||
| { | |||
| return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(), FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file)); | |||
| } | |||
| /** | |||
| * 编编码文件名(日期格式目录 + UUID + 后缀) | |||
| * 编码文件名 | |||
| */ | |||
| public static final String uuidFilename(MultipartFile file) | |||
| { | |||
| return StringUtils.format("{}/{}.{}", DateUtils.datePath(), IdUtils.fastSimpleUUID(), getExtension(file)); | |||
| public static final String extractFilename(MultipartFile file) { | |||
| return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(), | |||
| FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file)); | |||
| } | |||
| public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException | |||
| { | |||
| public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException { | |||
| File desc = new File(uploadDir + File.separator + fileName); | |||
| if (!desc.exists()) | |||
| { | |||
| if (!desc.getParentFile().exists()) | |||
| { | |||
| if (!desc.exists()) { | |||
| if (!desc.getParentFile().exists()) { | |||
| desc.getParentFile().mkdirs(); | |||
| } | |||
| } | |||
| return desc; | |||
| } | |||
| public static final String getPathFileName(String uploadDir, String fileName) throws IOException | |||
| { | |||
| public static final String getPathFileName(String uploadDir, String fileName) throws IOException { | |||
| int dirLastIndex = RuoYiConfig.getProfile().length() + 1; | |||
| String currentDir = StringUtils.substring(uploadDir, dirLastIndex); | |||
| return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName; | |||
| @@ -184,40 +322,30 @@ public class FileUploadUtils | |||
| * @throws InvalidExtensionException | |||
| */ | |||
| public static final void assertAllowed(MultipartFile file, String[] allowedExtension) | |||
| throws FileSizeLimitExceededException, InvalidExtensionException | |||
| { | |||
| throws FileSizeLimitExceededException, InvalidExtensionException { | |||
| long size = file.getSize(); | |||
| if (size > DEFAULT_MAX_SIZE) | |||
| { | |||
| // 超过默认 50M | |||
| if (size > DEFAULT_MAX_SIZE) { | |||
| throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024); | |||
| } | |||
| String fileName = file.getOriginalFilename(); | |||
| String extension = getExtension(file); | |||
| if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) | |||
| { | |||
| if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) | |||
| { | |||
| // 文件格式校验 | |||
| if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) { | |||
| if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) { | |||
| throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension, | |||
| fileName); | |||
| } | |||
| else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) | |||
| { | |||
| } else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) { | |||
| throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension, | |||
| fileName); | |||
| } | |||
| else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) | |||
| { | |||
| } else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) { | |||
| throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension, | |||
| fileName); | |||
| } | |||
| else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) | |||
| { | |||
| } else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) { | |||
| throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension, | |||
| fileName); | |||
| } | |||
| else | |||
| { | |||
| } else { | |||
| throw new InvalidExtensionException(allowedExtension, extension, fileName); | |||
| } | |||
| } | |||
| @@ -230,12 +358,9 @@ public class FileUploadUtils | |||
| * @param allowedExtension | |||
| * @return | |||
| */ | |||
| public static final boolean isAllowedExtension(String extension, String[] allowedExtension) | |||
| { | |||
| for (String str : allowedExtension) | |||
| { | |||
| if (str.equalsIgnoreCase(extension)) | |||
| { | |||
| public static final boolean isAllowedExtension(String extension, String[] allowedExtension) { | |||
| for (String str : allowedExtension) { | |||
| if (str.equalsIgnoreCase(extension)) { | |||
| return true; | |||
| } | |||
| } | |||
| @@ -244,15 +369,13 @@ public class FileUploadUtils | |||
| /** | |||
| * 获取文件名的后缀 | |||
| * | |||
| * | |||
| * @param file 表单文件 | |||
| * @return 后缀名 | |||
| */ | |||
| public static final String getExtension(MultipartFile file) | |||
| { | |||
| public static final String getExtension(MultipartFile file) { | |||
| String extension = FilenameUtils.getExtension(file.getOriginalFilename()); | |||
| if (StringUtils.isEmpty(extension)) | |||
| { | |||
| if (StringUtils.isEmpty(extension)) { | |||
| extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType())); | |||
| } | |||
| return extension; | |||
| @@ -1,55 +1,58 @@ | |||
| package com.ruoyi.common.utils.file; | |||
| import java.io.ByteArrayInputStream; | |||
| import java.io.FileInputStream; | |||
| import java.io.InputStream; | |||
| import java.net.URL; | |||
| import java.net.URLConnection; | |||
| import java.util.Arrays; | |||
| import org.apache.poi.util.IOUtils; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import com.ruoyi.common.config.RuoYiConfig; | |||
| import com.ruoyi.common.constant.Constants; | |||
| import com.ruoyi.common.utils.StringUtils; | |||
| import net.coobird.thumbnailator.Thumbnails; | |||
| import org.apache.poi.util.IOUtils; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import javax.imageio.ImageIO; | |||
| import java.awt.*; | |||
| import java.awt.image.BufferedImage; | |||
| import java.io.*; | |||
| import java.net.URL; | |||
| import java.net.URLConnection; | |||
| import java.util.Arrays; | |||
| /** | |||
| * 图片处理工具类 | |||
| * | |||
| * @author ruoyi | |||
| */ | |||
| public class ImageUtils | |||
| { | |||
| public class ImageUtils { | |||
| private static final Logger log = LoggerFactory.getLogger(ImageUtils.class); | |||
| public static byte[] getImage(String imagePath) | |||
| { | |||
| //以下是常量,按照阿里代码开发规范,不允许代码中出现魔法值 | |||
| private static final Integer ZERO = 0; | |||
| private static final Integer ONE_ZERO_TWO_FOUR = 1024; | |||
| private static final Integer NINE_ZERO_ZERO = 900; | |||
| private static final Integer THREE_TWO_SEVEN_FIVE = 3275; | |||
| private static final Integer TWO_ZERO_FOUR_SEVEN = 2047; | |||
| private static final Double ZERO_EIGHT_FIVE = 0.85; | |||
| private static final Double ZERO_SIX = 0.6; | |||
| private static final Double ZERO_FOUR_FOUR = 0.44; | |||
| private static final Double ZERO_FOUR = 0.4; | |||
| public static byte[] getImage(String imagePath) { | |||
| InputStream is = getFile(imagePath); | |||
| try | |||
| { | |||
| try { | |||
| return IOUtils.toByteArray(is); | |||
| } | |||
| catch (Exception e) | |||
| { | |||
| } catch (Exception e) { | |||
| log.error("图片加载异常 {}", e); | |||
| return null; | |||
| } | |||
| finally | |||
| { | |||
| } finally { | |||
| IOUtils.closeQuietly(is); | |||
| } | |||
| } | |||
| public static InputStream getFile(String imagePath) | |||
| { | |||
| try | |||
| { | |||
| public static InputStream getFile(String imagePath) { | |||
| try { | |||
| byte[] result = readFile(imagePath); | |||
| result = Arrays.copyOf(result, result.length); | |||
| return new ByteArrayInputStream(result); | |||
| } | |||
| catch (Exception e) | |||
| { | |||
| } catch (Exception e) { | |||
| log.error("获取图片异常 {}", e); | |||
| } | |||
| return null; | |||
| @@ -57,17 +60,14 @@ public class ImageUtils | |||
| /** | |||
| * 读取文件为字节数据 | |||
| * | |||
| * | |||
| * @param url 地址 | |||
| * @return 字节数据 | |||
| */ | |||
| public static byte[] readFile(String url) | |||
| { | |||
| public static byte[] readFile(String url) { | |||
| InputStream in = null; | |||
| try | |||
| { | |||
| if (url.startsWith("http")) | |||
| { | |||
| try { | |||
| if (url.startsWith("http")) { | |||
| // 网络地址 | |||
| URL urlObj = new URL(url); | |||
| URLConnection urlConnection = urlObj.openConnection(); | |||
| @@ -75,24 +75,223 @@ public class ImageUtils | |||
| urlConnection.setReadTimeout(60 * 1000); | |||
| urlConnection.setDoInput(true); | |||
| in = urlConnection.getInputStream(); | |||
| } | |||
| else | |||
| { | |||
| } else { | |||
| // 本机地址 | |||
| String localPath = RuoYiConfig.getProfile(); | |||
| String downloadPath = localPath + StringUtils.substringAfter(url, Constants.RESOURCE_PREFIX); | |||
| in = new FileInputStream(downloadPath); | |||
| } | |||
| return IOUtils.toByteArray(in); | |||
| } | |||
| catch (Exception e) | |||
| { | |||
| } catch (Exception e) { | |||
| log.error("获取文件路径异常 {}", e); | |||
| return null; | |||
| } | |||
| finally | |||
| { | |||
| } finally { | |||
| IOUtils.closeQuietly(in); | |||
| } | |||
| } | |||
| /** | |||
| * 根据指定大小压缩图片 | |||
| * | |||
| * @param imageBytes 源图片字节数组 | |||
| * @param desFileSize 指定图片大小,单位kb | |||
| * @return 压缩质量后的图片字节数组 | |||
| */ | |||
| public static byte[] compressPicForScale(byte[] imageBytes, long desFileSize) { | |||
| if (imageBytes == null || imageBytes.length <= ZERO || imageBytes.length < desFileSize * ONE_ZERO_TWO_FOUR) { | |||
| // log.info("图片无需压缩"); | |||
| return imageBytes; | |||
| } | |||
| long srcSize = imageBytes.length; | |||
| double accuracy = getAccuracy(srcSize / ONE_ZERO_TWO_FOUR); | |||
| try { | |||
| while (imageBytes.length > desFileSize * ONE_ZERO_TWO_FOUR) { | |||
| ByteArrayInputStream inputStream = new ByteArrayInputStream(imageBytes); | |||
| ByteArrayOutputStream outputStream = new ByteArrayOutputStream(imageBytes.length); | |||
| Thumbnails.of(inputStream).scale(accuracy).outputQuality(accuracy).toOutputStream(outputStream); | |||
| imageBytes = outputStream.toByteArray(); | |||
| } | |||
| //log.info("图片原大小={}kb | 压缩后大小={}kb", srcSize / ONE_ZERO_TWO_FOUR, imageBytes.length / ONE_ZERO_TWO_FOUR); | |||
| } catch (Exception e) { | |||
| log.error("【图片压缩】msg=图片压缩失败!", e); | |||
| } | |||
| return imageBytes; | |||
| } | |||
| /** | |||
| * 自动调节精度(经验数值) | |||
| * | |||
| * @param size 源图片大小 | |||
| * @return 图片压缩质量比 | |||
| */ | |||
| private static double getAccuracy(long size) { | |||
| double accuracy; | |||
| if (size < NINE_ZERO_ZERO) { // 900 | |||
| accuracy = ZERO_EIGHT_FIVE; // 0.85 | |||
| } else if (size < TWO_ZERO_FOUR_SEVEN) { // 2047 | |||
| accuracy = ZERO_SIX; // 0.6 | |||
| } else if (size < THREE_TWO_SEVEN_FIVE) { // 3275 | |||
| accuracy = ZERO_FOUR_FOUR; // 0.44 | |||
| } else { | |||
| accuracy = ZERO_FOUR; // 0.4 | |||
| } | |||
| return accuracy; | |||
| } | |||
| /** | |||
| * 将图片转换为具有透明底的PNG图片 | |||
| * | |||
| * @param inputFile 输入图片文件路径 | |||
| * @param outputFile 输出图片文件路径 | |||
| * @throws IOException 如果读写文件时发生错误 | |||
| */ | |||
| public static void convertToTransparentPng(String inputFile, String outputFile) throws IOException { | |||
| // 读取图片 | |||
| BufferedImage image = ImageIO.read(new File(inputFile)); | |||
| // 创建一个新的图片对象,类型为 BufferedImage.TYPE_INT_ARGB | |||
| BufferedImage newImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB); | |||
| // 获取新图片的绘制上下文 | |||
| Graphics2D g2d = (Graphics2D) newImage.getGraphics(); | |||
| // 绘制原始图片到新图片上,同时处理每个像素 | |||
| for (int x = 0; x < image.getWidth(); x++) { | |||
| for (int y = 0; y < image.getHeight(); y++) { | |||
| // 获取原始图片的像素颜色 | |||
| int color = image.getRGB(x, y); | |||
| Color originalColor = new Color(color, true); | |||
| // 判断是否为白色,这里设置了一个阈值范围 | |||
| if (originalColor.getRed() > 102 && originalColor.getGreen() > 102 && originalColor.getBlue() > 102) { | |||
| // 如果是白色,则设置为透明 | |||
| g2d.setColor(new Color(0, 0, 0, 0)); | |||
| } else { | |||
| // 如果不是白色,则保持原始颜色 | |||
| g2d.setColor(originalColor); | |||
| } | |||
| // 绘制像素 | |||
| g2d.drawRect(x, y, 1, 1); | |||
| } | |||
| } | |||
| // 释放资源 | |||
| g2d.dispose(); | |||
| // 保存处理后的图片 | |||
| ImageIO.write(newImage, "PNG", new File(outputFile)); | |||
| } | |||
| /** | |||
| * 将图片转换为具有透明底的PNG图片 | |||
| * @param inputFile 输入图片文件 | |||
| * @param outputFile 输出图片文件路径 | |||
| * @throws IOException 如果读写文件时发生错误 | |||
| */ | |||
| public static void convertToTransparentPng(File inputFile, String outputFile) throws IOException { | |||
| // 读取输入图片 | |||
| BufferedImage image = ImageIO.read(inputFile); | |||
| // 创建一个具有透明背景的BufferedImage | |||
| BufferedImage transparentImage = new BufferedImage( | |||
| image.getWidth(), | |||
| image.getHeight(), | |||
| BufferedImage.TYPE_INT_ARGB | |||
| ); | |||
| // 绘制原始图片到透明背景上 | |||
| Graphics2D graphics = transparentImage.createGraphics(); | |||
| graphics.setComposite(AlphaComposite.Src); | |||
| graphics.drawImage(image, 0, 0, null); | |||
| graphics.dispose(); | |||
| // 写入到输出文件 | |||
| ImageIO.write(transparentImage, "PNG", new File(outputFile)); | |||
| } | |||
| public static BufferedImage makeBackgroundTransparent(BufferedImage image) { | |||
| int width = image.getWidth(); | |||
| int height = image.getHeight(); | |||
| BufferedImage transparentImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); | |||
| Graphics2D g2d = transparentImage.createGraphics(); | |||
| g2d.setComposite(AlphaComposite.Src); | |||
| g2d.drawImage(image, null, 0, 0); | |||
| g2d.dispose(); | |||
| for (int y = 0; y < height; y++) { | |||
| for (int x = 0; x < width; x++) { | |||
| int p = image.getRGB(x, y); | |||
| int a = (p >> 24) & 0xff; | |||
| int r = (p >> 16) & 0xff; | |||
| int g = (p >> 8) & 0xff; | |||
| int b = p & 0xff; | |||
| // Define your background color here | |||
| if (r > 255 && g > 255 && b > 255) { | |||
| // Set fully transparent for background color | |||
| p = (a << 24) | (0 << 16) | (0 << 8) | 0; | |||
| } | |||
| transparentImage.setRGB(x, y, p); | |||
| } | |||
| } | |||
| return transparentImage; | |||
| } | |||
| public static BufferedImage getBufferedImg(BufferedImage src, int width, int height, int ro) { | |||
| int angle = (int) (90 * ro); | |||
| int type = src.getColorModel().getTransparency(); | |||
| int wid = width; | |||
| int hei = height; | |||
| if (ro % 2 != 0) { | |||
| int temp = width; | |||
| width = height; | |||
| height = temp; | |||
| } | |||
| Rectangle re = new Rectangle(new Dimension(width, height)); | |||
| BufferedImage BfImg = null; | |||
| BfImg = new BufferedImage(re.width, re.height, type); | |||
| Graphics2D g2 = BfImg.createGraphics(); | |||
| g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); | |||
| g2.rotate(Math.toRadians(angle), re.width / 2, re.height / 2); | |||
| g2.drawImage(src, (re.width - wid) / 2, (re.height - hei) / 2, null); | |||
| g2.dispose(); | |||
| return BfImg; | |||
| } | |||
| //获得图片的高 | |||
| public static int getHeight(InputStream is) { | |||
| BufferedImage src = null; | |||
| int height = -1; | |||
| try { | |||
| src = ImageIO.read(is); | |||
| height = src.getHeight(); | |||
| } catch (Exception e) { | |||
| System.out.println(e.getMessage()); | |||
| } | |||
| return height; | |||
| } | |||
| //获得图片的宽 | |||
| public static int getWidth(InputStream is) { | |||
| BufferedImage src = null; | |||
| int width = -1; | |||
| try { | |||
| src = ImageIO.read(is); | |||
| width = src.getWidth(); | |||
| } catch (Exception e) { | |||
| System.out.println(e.getMessage()); | |||
| } | |||
| return width; | |||
| } | |||
| public static byte[] readInputStream(InputStream inStream) throws IOException { | |||
| ByteArrayOutputStream outStream = new ByteArrayOutputStream(); | |||
| byte[] buffer = new byte[1024]; | |||
| int len; | |||
| while ((len = inStream.read(buffer)) != -1) { | |||
| outStream.write(buffer, 0, len); | |||
| } | |||
| inStream.close(); | |||
| return outStream.toByteArray(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,72 @@ | |||
| package com.ruoyi.common.utils.pdf; | |||
| import com.itextpdf.text.*; | |||
| import com.itextpdf.text.pdf.*; | |||
| import com.ruoyi.common.config.RuoYiConfig; | |||
| import java.io.IOException; | |||
| /** | |||
| * @description: 显示页数 | |||
| * @author: zzl | |||
| * @date: Created in 2024-04-27 9:07 | |||
| * @version: 1.0 | |||
| * @modified By: | |||
| */ | |||
| public class MyHeaderFooter extends PdfPageEventHelper { | |||
| // 总页数 | |||
| PdfTemplate totalPage; | |||
| Font hfFont; | |||
| { | |||
| try { | |||
| hfFont = new Font(BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED), 8, Font.NORMAL); | |||
| } catch (DocumentException e) { | |||
| e.printStackTrace(); | |||
| } catch (IOException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| // 打开文档时,创建一个总页数的模版 | |||
| public void onOpenDocument(PdfWriter writer, Document document) { | |||
| PdfContentByte cb = writer.getDirectContent(); | |||
| totalPage = cb.createTemplate(30, 16); | |||
| } | |||
| // 一页加载完成触发,写入页眉和页脚 | |||
| public void onEndPage(PdfWriter writer, Document document) { | |||
| PdfPTable table = new PdfPTable(3); | |||
| try { | |||
| table.setTotalWidth(PageSize.A4.getWidth() - 100); | |||
| table.setWidths(new int[]{24, 24, 3}); | |||
| table.setLockedWidth(true); | |||
| table.getDefaultCell().setFixedHeight(-10); | |||
| table.getDefaultCell().setBorder(Rectangle.BOTTOM); | |||
| table.addCell(new Paragraph(RuoYiConfig.getName(), hfFont)); | |||
| table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_RIGHT); | |||
| table.addCell(new Paragraph("第" + writer.getPageNumber() + "页/", hfFont)); | |||
| // 总页数 | |||
| PdfPCell cell = new PdfPCell(Image.getInstance(totalPage)); | |||
| cell.setBorder(Rectangle.BOTTOM); | |||
| table.addCell(cell); | |||
| // 将页眉写到document中,位置可以指定,指定到下面就是页脚 | |||
| table.writeSelectedRows(0, -1, 50, PageSize.A4.getHeight() - 20, writer.getDirectContent()); | |||
| } catch (Exception de) { | |||
| throw new ExceptionConverter(de); | |||
| } | |||
| } | |||
| // 全部完成后,将总页数的pdf模版写到指定位置 | |||
| public void onCloseDocument(PdfWriter writer, Document document) { | |||
| String text = "总" + (writer.getPageNumber()) + "页" ; | |||
| ColumnText.showTextAligned(totalPage, Element.ALIGN_LEFT, new Paragraph(text, hfFont), 2, 2, 0); | |||
| } | |||
| } | |||
| @@ -0,0 +1,70 @@ | |||
| package com.ruoyi.common.utils.pdf; | |||
| import com.itextpdf.text.*; | |||
| import com.itextpdf.text.pdf.*; | |||
| import com.ruoyi.common.config.RuoYiConfig; | |||
| import java.io.IOException; | |||
| /** | |||
| * @description: 显示页数 | |||
| * @author: zzl | |||
| * @date: Created in 2024-04-27 9:07 | |||
| * @version: 1.0 | |||
| * @modified By: | |||
| */ | |||
| public class MyHeaderFooterRotate extends PdfPageEventHelper { | |||
| // 总页数 | |||
| PdfTemplate totalPage; | |||
| Font hfFont; | |||
| { | |||
| try { | |||
| hfFont = new Font(BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED), 8, Font.NORMAL); | |||
| } catch (DocumentException e) { | |||
| e.printStackTrace(); | |||
| } catch (IOException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| // 打开文档时,创建一个总页数的模版 | |||
| public void onOpenDocument(PdfWriter writer, Document document) { | |||
| PdfContentByte cb = writer.getDirectContent(); | |||
| totalPage = cb.createTemplate(30, 16); | |||
| } | |||
| // 一页加载完成触发,写入页眉和页脚 | |||
| public void onEndPage(PdfWriter writer, Document document) { | |||
| PdfPTable table = new PdfPTable(3); | |||
| try { | |||
| table.setTotalWidth(PageSize.A4.getHeight() - 100); | |||
| table.setWidths(new int[]{24, 24, 3}); | |||
| table.setLockedWidth(true); | |||
| table.getDefaultCell().setFixedHeight(-10); | |||
| table.getDefaultCell().setBorder(Rectangle.BOTTOM); | |||
| table.addCell(new Paragraph(RuoYiConfig.getName(), hfFont)); | |||
| table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_RIGHT); | |||
| table.addCell(new Paragraph("第" + writer.getPageNumber() + "页/", hfFont)); | |||
| // 总页数 | |||
| PdfPCell cell = new PdfPCell(Image.getInstance(totalPage)); | |||
| cell.setBorder(Rectangle.BOTTOM); | |||
| table.addCell(cell); | |||
| // 将页眉写到document中,位置可以指定,指定到下面就是页脚 | |||
| table.writeSelectedRows(0, -1, 50, PageSize.A4.getWidth() - 20, writer.getDirectContent()); | |||
| } catch (Exception de) { | |||
| throw new ExceptionConverter(de); | |||
| } | |||
| } | |||
| // 全部完成后,将总页数的pdf模版写到指定位置 | |||
| public void onCloseDocument(PdfWriter writer, Document document) { | |||
| String text = "总" + (writer.getPageNumber()) + "页" ; | |||
| ColumnText.showTextAligned(totalPage, Element.ALIGN_LEFT, new Paragraph(text, hfFont), 2, 2, 0); | |||
| } | |||
| } | |||
| @@ -0,0 +1,538 @@ | |||
| package com.ruoyi.common.utils.pdf; | |||
| import com.itextpdf.text.*; | |||
| import com.itextpdf.text.pdf.BaseFont; | |||
| import com.itextpdf.text.pdf.PdfPCell; | |||
| import com.itextpdf.text.pdf.PdfPTable; | |||
| import com.itextpdf.text.pdf.PdfWriter; | |||
| import com.itextpdf.text.pdf.draw.DottedLineSeparator; | |||
| import com.itextpdf.text.pdf.draw.LineSeparator; | |||
| import com.ruoyi.common.core.domain.pdf.PageSet; | |||
| import com.ruoyi.common.core.domain.pdf.PdfProperty; | |||
| import com.ruoyi.common.core.domain.pdf.ShoulderItem; | |||
| import com.ruoyi.common.utils.StringUtils; | |||
| import lombok.extern.slf4j.Slf4j; | |||
| import javax.servlet.http.HttpServletResponse; | |||
| import java.io.ByteArrayOutputStream; | |||
| import java.util.List; | |||
| /** | |||
| * pdf处理工具类 | |||
| * | |||
| * @author ruoyi | |||
| */ | |||
| @Slf4j | |||
| public class PdfUtils<statistic> { | |||
| // 定义全局的字体静态变量 | |||
| private static Font titlefontMax; | |||
| private static Font titlefont; | |||
| private static Font titlefontmin; | |||
| private static Font headfont; | |||
| private static Font keyfont; | |||
| private static Font textfont; | |||
| private static Font bottomfont; | |||
| // 最大宽度 | |||
| public static int maxWidth = 520; | |||
| // 表行高 | |||
| public static float rowHeight = 20f; | |||
| // 表格宽度百分比 | |||
| public static Integer widthPercentage = 100; | |||
| public static int leftspan = 3; | |||
| public static int centerspan = 3; | |||
| public static int rightspan = 3; | |||
| // 行线间距 | |||
| public static float dottedLineSpace = 50f; | |||
| // 静态代码块 | |||
| static { | |||
| try { | |||
| // 不同字体(这里定义为同一种字体:包含不同字号、不同style) | |||
| BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); | |||
| titlefontMax = new Font(bfChinese, 20, Font.BOLD); | |||
| titlefont = new Font(bfChinese, 15, Font.BOLD); | |||
| titlefontmin = new Font(bfChinese, 14, Font.BOLD); | |||
| headfont = new Font(bfChinese, 11, Font.NORMAL); | |||
| keyfont = new Font(bfChinese, 10, Font.BOLD); | |||
| textfont = new Font(bfChinese, 10, Font.NORMAL); | |||
| bottomfont = new Font(bfChinese, 11, Font.NORMAL); | |||
| } catch (Exception e) { | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| // 通用 初始化 PDF文件 | |||
| public static void initPdf(HttpServletResponse response, PdfProperty pdf) throws Exception { | |||
| // 1. 新建document对象 | |||
| Document document = null; | |||
| PageSet ps = pdf.getPageSet(); | |||
| if (StringUtils.isNull(ps)) { //默认A4纵向 | |||
| document = new Document(PageSize.A4); | |||
| } else { // 自定义 | |||
| if (ps.getPrintDirection().equals("1")) {// 纵向 | |||
| document = new Document(new Rectangle(ps.getPaperWidth(), ps.getPaperHeight()), ps.getMarginLeft(), ps.getMarginRight(), ps.getMarginTop(), ps.getMarginBottom()); | |||
| } else { //2 横向 | |||
| document = new Document(new Rectangle(ps.getPaperWidth(), ps.getPaperHeight()).rotate(), ps.getMarginLeft(), ps.getMarginRight(), ps.getMarginTop(), ps.getMarginBottom()); | |||
| } | |||
| } | |||
| // 2. 创建一个输出流,将输出流与PdfWriter绑定 | |||
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||
| PdfWriter writer = PdfWriter.getInstance(document, baos); | |||
| if (ps.getPrintDirection().equals("1")) {// 纵向 | |||
| writer.setPageEvent(new MyHeaderFooter());// 页眉/页脚 | |||
| } else { //2 横向 | |||
| writer.setPageEvent(new MyHeaderFooterRotate());// 页眉/页脚 | |||
| } | |||
| // 3. 打开文档 | |||
| document.open(); | |||
| document.addTitle(pdf.getTitle() + " PDF打印"); | |||
| // 4. 向文档中添加内容 | |||
| generateComPdf(document, pdf); | |||
| // 5. 关闭文档 | |||
| document.close(); | |||
| // 6. 将PDF输出到浏览器 | |||
| response.setContentType("application/pdf"); | |||
| response.setCharacterEncoding("utf-8"); | |||
| response.setContentLength(baos.size()); | |||
| response.setHeader("Content-Disposition", "inline; filename=example.pdf"); | |||
| response.getOutputStream().write(baos.toByteArray()); | |||
| response.getOutputStream().flush(); | |||
| } | |||
| // 通用 填充 PDF 内容 | |||
| public static void generateComPdf(Document document, PdfProperty pdf) throws Exception { | |||
| // 创建段落:表名 | |||
| createParagraph(document, pdf.getTitle()); | |||
| // 初始参数 | |||
| int columns = pdf.getHeader().length; | |||
| centerspan = columns / 3; | |||
| leftspan = centerspan + columns % 3; | |||
| rightspan = centerspan; | |||
| if (StringUtils.isNotNull(pdf.getPageSet())) { | |||
| maxWidth = pdf.getPageSet().getTableTotalWidth(); | |||
| } | |||
| if (StringUtils.isNotNull(pdf.getRowHeight())) { | |||
| rowHeight = pdf.getRowHeight(); | |||
| } | |||
| // 创建表 | |||
| PdfPTable table = createTable(pdf.getColumnWidth()); | |||
| // 肩部名称 | |||
| ShoulderItem shoulder = pdf.getShoulder(); | |||
| if (StringUtils.isNotNull(shoulder)) { | |||
| createShoulder(table, shoulder); | |||
| } | |||
| // 列标题 | |||
| String[] header = pdf.getHeader(); | |||
| for (String h : header) { | |||
| table.addCell(createCell(h, keyfont, Element.ALIGN_CENTER)); | |||
| } | |||
| // 设置为每页固定表头 < row | |||
| if (StringUtils.isNotNull(shoulder)) { | |||
| table.setHeaderRows(2); | |||
| } else { | |||
| table.setHeaderRows(1); | |||
| } | |||
| // 列水平位置 | |||
| int[] aligns = pdf.getAligns(); | |||
| // 行内容 | |||
| List<String[]> contentList = pdf.getContentList(); | |||
| for (String[] strs : contentList) { | |||
| int i = 0; | |||
| for (String str : strs) { | |||
| if (null == aligns) { | |||
| table.addCell(createCell(str, textfont)); | |||
| } else { | |||
| table.addCell(createCell(str, textfont, aligns[i])); | |||
| } | |||
| i++; | |||
| } | |||
| } | |||
| document.add(table); | |||
| } | |||
| /** | |||
| * 创建单元格(指定字体) | |||
| * | |||
| * @param value | |||
| * @param font | |||
| * @return | |||
| */ | |||
| public static PdfPCell createCell(String value, Font font) { | |||
| PdfPCell cell = new PdfPCell(); | |||
| cell.setVerticalAlignment(Element.ALIGN_MIDDLE); | |||
| cell.setHorizontalAlignment(Element.ALIGN_CENTER); | |||
| cell.setPhrase(new Phrase(value, font)); | |||
| cell.setFixedHeight(rowHeight); | |||
| cell.setNoWrap(false); // 自动换行 | |||
| return cell; | |||
| } | |||
| /** | |||
| * 创建单元格(指定字体、水平位置) | |||
| * | |||
| * @param value | |||
| * @param font | |||
| * @param align | |||
| * @return | |||
| */ | |||
| public static PdfPCell createCell(String value, Font font, int align) { | |||
| PdfPCell cell = new PdfPCell(); | |||
| cell.setVerticalAlignment(Element.ALIGN_MIDDLE); | |||
| cell.setHorizontalAlignment(align); | |||
| cell.setPhrase(new Phrase(value, font)); | |||
| cell.setMinimumHeight(rowHeight); | |||
| cell.setNoWrap(false); // 自动换行 | |||
| return cell; | |||
| } | |||
| /** | |||
| * 创建单元格(指定字体、水平位置、跨x列合并) | |||
| * | |||
| * @param value | |||
| * @param font | |||
| * @param align | |||
| * @param colspan | |||
| * @return | |||
| */ | |||
| public static PdfPCell createCell(String value, Font font, int align, int colspan) { | |||
| PdfPCell cell = new PdfPCell(); | |||
| cell.setVerticalAlignment(Element.ALIGN_MIDDLE); | |||
| cell.setHorizontalAlignment(align); | |||
| cell.setColspan(colspan); | |||
| cell.setPhrase(new Phrase(value, font)); | |||
| cell.setMinimumHeight(rowHeight); | |||
| cell.setNoWrap(false); // 自动换行 | |||
| return cell; | |||
| } | |||
| /** | |||
| * 创建单元格(指定字体、水平位置、跨x行合并) | |||
| * | |||
| * @param value | |||
| * @param font | |||
| * @param align | |||
| * @param rowspan | |||
| * @return | |||
| */ | |||
| public static PdfPCell createCell2(String value, Font font, int align, int rowspan) { | |||
| PdfPCell cell = new PdfPCell(); | |||
| cell.setVerticalAlignment(Element.ALIGN_MIDDLE); | |||
| cell.setHorizontalAlignment(align); | |||
| cell.setRowspan(rowspan); | |||
| cell.setPhrase(new Phrase(value, font)); | |||
| cell.setMinimumHeight(rowHeight); | |||
| cell.setNoWrap(false); // 自动换行 | |||
| return cell; | |||
| } | |||
| /** | |||
| * 创建单元格(指定字体、水平位置、跨x列合并、设置单元格内边距) | |||
| * | |||
| * @param value | |||
| * @param font | |||
| * @param align | |||
| * @param colspan | |||
| * @param boderFlag | |||
| * @return | |||
| */ | |||
| public static PdfPCell createCell(String value, Font font, int align, int colspan, boolean boderFlag) { | |||
| PdfPCell cell = new PdfPCell(); | |||
| cell.setVerticalAlignment(Element.ALIGN_MIDDLE); | |||
| cell.setHorizontalAlignment(align); | |||
| cell.setColspan(colspan); | |||
| cell.setPhrase(new Phrase(value, font)); | |||
| cell.setMinimumHeight(rowHeight); | |||
| cell.setNoWrap(false); // 自动换行 | |||
| cell.setPadding(3.0f); | |||
| if (!boderFlag) { | |||
| cell.setBorder(0); | |||
| cell.setPaddingTop(4.0f); | |||
| cell.setPaddingBottom(4.0f); | |||
| } else if (boderFlag) { | |||
| cell.setBorder(0); | |||
| cell.setPaddingTop(2.0f); | |||
| cell.setPaddingBottom(2.0f); | |||
| } | |||
| return cell; | |||
| } | |||
| public static PdfPCell createCellBottom(String value, Font font, int align, int colspan, boolean boderFlag) { | |||
| PdfPCell cell = new PdfPCell(); | |||
| cell.setVerticalAlignment(Element.ALIGN_MIDDLE); | |||
| cell.setHorizontalAlignment(align); | |||
| cell.setColspan(colspan); | |||
| cell.setPhrase(new Phrase(value, font)); | |||
| cell.setMinimumHeight(35f); | |||
| cell.setNoWrap(false); // 自动换行 | |||
| cell.setPadding(3.0f); | |||
| if (!boderFlag) { | |||
| cell.setBorder(0); | |||
| cell.setPaddingTop(4.0f); | |||
| cell.setPaddingBottom(4.0f); | |||
| } else if (boderFlag) { | |||
| cell.setBorder(0); | |||
| cell.setPaddingTop(2.0f); | |||
| cell.setPaddingBottom(2.0f); | |||
| } | |||
| return cell; | |||
| } | |||
| /** | |||
| * 创建单元格(指定字体、水平位置、边框宽度(0表示无边框)、内边距、是否合并2列) | |||
| * | |||
| * @param value | |||
| * @param font | |||
| * @param align | |||
| * @param borderWidth | |||
| * @param paddingSize | |||
| * @param flag | |||
| * @return | |||
| */ | |||
| public PdfPCell createCell(String value, Font font, int align, float[] borderWidth, float[] paddingSize, boolean flag) { | |||
| PdfPCell cell = new PdfPCell(); | |||
| cell.setVerticalAlignment(Element.ALIGN_MIDDLE); | |||
| cell.setHorizontalAlignment(align); | |||
| cell.setPhrase(new Phrase(value, font)); | |||
| cell.setBorderWidthLeft(borderWidth[0]); | |||
| cell.setBorderWidthRight(borderWidth[1]); | |||
| cell.setBorderWidthTop(borderWidth[2]); | |||
| cell.setBorderWidthBottom(borderWidth[3]); | |||
| cell.setPaddingTop(paddingSize[0]); | |||
| cell.setPaddingBottom(paddingSize[1]); | |||
| cell.setNoWrap(false); // 自动换行 | |||
| if (flag) { | |||
| cell.setColspan(2); | |||
| } | |||
| return cell; | |||
| } | |||
| /** | |||
| * 创建默认列宽,指定列数、水平(居中、右、左)的表格 | |||
| * | |||
| * @param colNumber | |||
| * @param align | |||
| * @return | |||
| */ | |||
| public static PdfPTable createTable(int colNumber, int align) { | |||
| PdfPTable table = new PdfPTable(colNumber); | |||
| try { | |||
| table.setTotalWidth(maxWidth); | |||
| table.setLockedWidth(true); | |||
| table.setHorizontalAlignment(align); | |||
| table.getDefaultCell().setBorder(1); | |||
| } catch (Exception e) { | |||
| e.printStackTrace(); | |||
| } | |||
| return table; | |||
| } | |||
| /** | |||
| * 创建指定列宽、列数的表格 | |||
| * | |||
| * @param widths | |||
| * @return | |||
| */ | |||
| public static PdfPTable createTable(float[] widths) { | |||
| PdfPTable table = new PdfPTable(widths.length); | |||
| try { | |||
| table.setTotalWidth(maxWidth); | |||
| table.setWidthPercentage(widthPercentage); | |||
| table.setWidths(widths); | |||
| table.setLockedWidth(true); | |||
| table.setHorizontalAlignment(Element.ALIGN_CENTER); | |||
| table.getDefaultCell().setBorder(1); | |||
| } catch (Exception e) { | |||
| e.printStackTrace(); | |||
| } | |||
| return table; | |||
| } | |||
| /** | |||
| * 创建段落 | |||
| * | |||
| * @return | |||
| */ | |||
| public static void createParagraph(Document document, String title) throws Exception { | |||
| Paragraph paragraph = new Paragraph(title, titlefont); | |||
| paragraph.setAlignment(1); //设置文字居中 0靠左 1,居中 2,靠右 | |||
| paragraph.setIndentationLeft(12); //设置左缩进 | |||
| paragraph.setIndentationRight(12); //设置右缩进 | |||
| paragraph.setFirstLineIndent(24); //设置首行缩进 | |||
| paragraph.setLeading(20f); //行间距 | |||
| paragraph.setSpacingBefore(5f); //设置段落上空白 | |||
| paragraph.setSpacingAfter(10f); //设置段落下空白 | |||
| document.add(paragraph); | |||
| } | |||
| /** | |||
| * 创建段落 | |||
| * | |||
| * @return | |||
| */ | |||
| public static void createParagraph(Document document, String title, Font titlefont) throws Exception { | |||
| Paragraph paragraph = new Paragraph(title, titlefont); | |||
| paragraph.setAlignment(1); //设置文字居中 0靠左 1,居中 2,靠右 | |||
| paragraph.setIndentationLeft(12); //设置左缩进 | |||
| paragraph.setIndentationRight(12); //设置右缩进 | |||
| paragraph.setFirstLineIndent(24); //设置首行缩进 | |||
| paragraph.setLeading(20f); //行间距 | |||
| paragraph.setSpacingBefore(5f); //设置段落上空白 | |||
| paragraph.setSpacingAfter(10f); //设置段落下空白 | |||
| document.add(paragraph); | |||
| } | |||
| /** | |||
| * 创建点线 | |||
| * | |||
| * @return | |||
| */ | |||
| public static void createDottedLine(Document document) throws Exception { | |||
| Paragraph dottedLine = new Paragraph(); | |||
| dottedLine.add(new Chunk(new DottedLineSeparator())); | |||
| dottedLine.setAlignment(1); //设置文字居中 0靠左 1,居中 2,靠右 | |||
| dottedLine.setIndentationLeft(12); //设置左缩进 | |||
| dottedLine.setIndentationRight(12); //设置右缩进 | |||
| dottedLine.setFirstLineIndent(24); //设置首行缩进 | |||
| dottedLine.setLeading(20f); //行间距 | |||
| dottedLine.setSpacingBefore(dottedLineSpace); //设置段落上空白 | |||
| dottedLine.setSpacingAfter(dottedLineSpace); //设置段落下空白 | |||
| document.add(dottedLine); | |||
| } | |||
| /** | |||
| * 创建直线 | |||
| * | |||
| * @return | |||
| */ | |||
| public static void createLine(Document document) throws Exception { | |||
| Paragraph line = new Paragraph(); | |||
| line.add(new Chunk(new LineSeparator())); | |||
| document.add(line); | |||
| } | |||
| /** | |||
| * 创建表格肩部:左 中 右 | |||
| * | |||
| * @return | |||
| */ | |||
| public static void createShoulder(PdfPTable table, ShoulderItem shoulder) { | |||
| table.addCell(createCell(shoulder.getLeftItem(), headfont, Element.ALIGN_LEFT, leftspan, false)); | |||
| table.addCell(createCell(shoulder.getCenterItem(), headfont, Element.ALIGN_CENTER, centerspan, false)); | |||
| table.addCell(createCell(shoulder.getRightItem(), headfont, Element.ALIGN_RIGHT, rightspan, false)); | |||
| } | |||
| /** | |||
| * 创建空白的表格 | |||
| * | |||
| * @return | |||
| */ | |||
| public static PdfPTable createBlankTable(float height) { | |||
| PdfPTable table = new PdfPTable(1); | |||
| table.getDefaultCell().setBorder(0); | |||
| table.addCell(createCell("", keyfont)); | |||
| table.setSpacingAfter(height); | |||
| table.setSpacingBefore(height); | |||
| return table; | |||
| } | |||
| /** | |||
| * 创建空白的段落 | |||
| * | |||
| * @return | |||
| */ | |||
| public static void createBlankParagraph(Document document, float height) { | |||
| Paragraph p3 = new Paragraph(); | |||
| p3.setSpacingBefore(height); | |||
| p3.setSpacingAfter(height); | |||
| try { | |||
| document.add(p3); | |||
| } catch (DocumentException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| /* DEMO 方法 */ | |||
| public static void generateDemoPDF(Document document) throws Exception { | |||
| // 创建段落:表名 | |||
| createParagraph(document, "这是DEMO标题"); | |||
| // 超链接 | |||
| Anchor anchor = new Anchor("打开百度"); | |||
| anchor.setReference("www.baidu.com"); | |||
| document.add(anchor); | |||
| // 点线 | |||
| Paragraph p2 = new Paragraph(); | |||
| p2.add(new Chunk(new DottedLineSeparator())); | |||
| document.add(p2); | |||
| // 直线 | |||
| Paragraph p1 = new Paragraph(); | |||
| p1.add(new Chunk(new LineSeparator())); | |||
| document.add(p1); | |||
| // 添加空白行 | |||
| //Paragraph p3 = new Paragraph(); | |||
| //p3.add(new Chunk(newLine())); | |||
| Paragraph p3 = new Paragraph(); | |||
| p3.setSpacingBefore(50f); | |||
| p3.setSpacingAfter(50f); | |||
| document.add(p3); | |||
| // 表格 | |||
| PdfPTable table = createTable(new float[]{40, 120, 120, 120, 80, 80}); | |||
| table.addCell(createCell("姓名:张三", headfont, Element.ALIGN_LEFT, 2, false)); | |||
| table.addCell(createCell("日期:2024-01-01", headfont, Element.ALIGN_CENTER, 2, false)); | |||
| table.addCell(createCell("单位:元", headfont, Element.ALIGN_RIGHT, 2, false)); | |||
| table.addCell(createCell("早上9:00", keyfont, Element.ALIGN_CENTER)); | |||
| table.addCell(createCell("中午11:00", keyfont, Element.ALIGN_CENTER)); | |||
| table.addCell(createCell("中午13:00", keyfont, Element.ALIGN_CENTER)); | |||
| table.addCell(createCell("下午15:00", keyfont, Element.ALIGN_CENTER)); | |||
| table.addCell(createCell("下午17:00", keyfont, Element.ALIGN_CENTER)); | |||
| table.addCell(createCell("晚上19:00", keyfont, Element.ALIGN_CENTER)); | |||
| Integer totalQuantity = 0; | |||
| for (int i = 0; i < 5; i++) { | |||
| table.addCell(createCell("起床", textfont)); | |||
| table.addCell(createCell("吃午饭", textfont)); | |||
| table.addCell(createCell("午休", textfont)); | |||
| table.addCell(createCell("下午茶", textfont)); | |||
| table.addCell(createCell("回家", textfont)); | |||
| table.addCell(createCell("吃晚饭", textfont)); | |||
| totalQuantity++; | |||
| } | |||
| table.addCell(createCell("总计", keyfont)); | |||
| table.addCell(createCell("", textfont)); | |||
| table.addCell(createCell("", textfont)); | |||
| table.addCell(createCell("", textfont)); | |||
| table.addCell(createCell(String.valueOf(totalQuantity) + "件事", textfont)); | |||
| table.addCell(createCell("", textfont)); | |||
| document.add(table); | |||
| // 添加图片 | |||
| Image image = Image.getInstance("https://img-blog.csdn.net/20180801174617455?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zNzg0ODcxMA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70"); | |||
| image.setAlignment(Image.ALIGN_CENTER); | |||
| image.scalePercent(40); //依照比例缩放 | |||
| document.add(image); | |||
| // 定位 | |||
| Anchor gotoP = new Anchor("back top"); | |||
| gotoP.setReference("#top"); | |||
| document.add(gotoP); | |||
| } | |||
| } | |||
| @@ -0,0 +1,44 @@ | |||
| package com.ruoyi.common.utils.pdf; | |||
| import com.itextpdf.text.Document; | |||
| import com.itextpdf.text.Element; | |||
| import com.itextpdf.text.Font; | |||
| import com.itextpdf.text.Phrase; | |||
| import com.itextpdf.text.pdf.ColumnText; | |||
| import com.itextpdf.text.pdf.GrayColor; | |||
| import com.itextpdf.text.pdf.PdfPageEventHelper; | |||
| import com.itextpdf.text.pdf.PdfWriter; | |||
| /** | |||
| * @description: 水印文案 | |||
| * @author: zzl | |||
| * @date: Created in 2024-04-27 9:07 | |||
| * @version: 1.0 | |||
| * @modified By: | |||
| */ | |||
| public class Watermark extends PdfPageEventHelper { | |||
| Font FONT = new Font(Font.FontFamily.HELVETICA, 30, Font.BOLD, new GrayColor(0.95f)); | |||
| private String waterCont;//水印内容 | |||
| public Watermark() { | |||
| } | |||
| public Watermark(String waterCont) { | |||
| this.waterCont = waterCont; | |||
| } | |||
| @Override | |||
| public void onEndPage(PdfWriter writer, Document document) { | |||
| for (int i = 0; i < 5; i++) { | |||
| for (int j = 0; j < 5; j++) { | |||
| ColumnText.showTextAligned(writer.getDirectContentUnder(), | |||
| Element.ALIGN_CENTER, | |||
| new Phrase(this.waterCont == null ? "HELLO WORLD" : this.waterCont, FONT), | |||
| (50.5f + i * 350), | |||
| (40.0f + j * 150), | |||
| writer.getPageNumber() % 2 == 1 ? 45 : -45); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,110 @@ | |||
| package com.ruoyi.common.utils.poi; | |||
| import org.apache.poi.ss.usermodel.*; | |||
| import org.apache.poi.ss.util.CellRangeAddressList; | |||
| import org.apache.poi.xssf.usermodel.XSSFDataValidation; | |||
| // Excel 字典sheet助手 | |||
| public final class ExcelDictSheetHelper | |||
| { | |||
| private final Workbook workbook; | |||
| private Sheet sheet; | |||
| private int currentColumn; | |||
| public ExcelDictSheetHelper(Workbook workbook) | |||
| { | |||
| this.workbook = workbook; | |||
| } | |||
| private Sheet Sheet() | |||
| { | |||
| if(null == sheet) | |||
| { | |||
| int numberOfSheets = workbook.getNumberOfSheets(); | |||
| sheet = workbook.createSheet("__HIDDEN_DICT__"); | |||
| workbook.setSheetHidden(numberOfSheets, true); | |||
| } | |||
| return sheet; | |||
| } | |||
| // 检查下拉列表是否超过字符限制 | |||
| public static boolean CheckValidationLimit(String[] textlist) | |||
| { | |||
| if(null == textlist || textlist.length == 0) | |||
| return false; | |||
| String str = String.join(",", textlist); | |||
| return str.length() >= 255; | |||
| } | |||
| public void AddValidation(Sheet sheet, String dictType, String[] textlist, int firstRow, int endRow, int firstCol, int endCol) | |||
| { | |||
| if(null == textlist || textlist.length == 0) | |||
| return; | |||
| try | |||
| { | |||
| String name = "__HIDDEN_" + dictType; | |||
| currentColumn++; | |||
| // 创建隐藏sheet, 将数组放在一列 | |||
| Sheet hidden = Sheet(); | |||
| for (int i = 0, length = textlist.length; i < length; i++) | |||
| { | |||
| Row row = hidden.getRow(i); | |||
| if(null == row) | |||
| row = hidden.createRow(i); | |||
| Cell cell = row.createCell(currentColumn - 1); | |||
| cell.setCellValue(textlist[i]); | |||
| } | |||
| String columnNum = GenerateColumnName(currentColumn); | |||
| Name namedCell = workbook.createName(); | |||
| namedCell.setNameName(name); | |||
| // 设置名称引用的公式 | |||
| //System.err.println(String.format("%s!$%s$1:$%s$%d", hidden.getSheetName(), columnNum, columnNum, textlist.length)); | |||
| namedCell.setRefersToFormula(String.format("%s!$%s$1:$%s$%d", hidden.getSheetName(), columnNum, columnNum, textlist.length)); | |||
| DataValidationHelper helper = sheet.getDataValidationHelper(); | |||
| DataValidationConstraint constraint = helper.createFormulaListConstraint(name); | |||
| // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列 | |||
| CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); | |||
| // 数据有效性对象 | |||
| DataValidation dataValidation = helper.createValidation(constraint, regions); | |||
| // 处理Excel兼容性问题 | |||
| if (dataValidation instanceof XSSFDataValidation) { | |||
| dataValidation.setSuppressDropDownArrow(true); | |||
| dataValidation.setShowErrorBox(true); | |||
| } else { | |||
| dataValidation.setSuppressDropDownArrow(false); | |||
| } | |||
| sheet.addValidationData(dataValidation); | |||
| } | |||
| catch(Exception e) | |||
| { | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| public static String GenerateColumnName(int i) | |||
| { | |||
| String strResult = ""; | |||
| int intRound = i / 26; | |||
| int intMod = i % 26; | |||
| if (intRound != 0) { | |||
| strResult = String.valueOf(((char) (intRound + 64))); | |||
| } | |||
| strResult += String.valueOf(((char) (intMod + 64))); | |||
| return strResult; | |||
| } | |||
| public static int ParseColumnName(String name) | |||
| { | |||
| int column = -1; | |||
| for (int i = 0; i < name.length(); ++i) | |||
| { | |||
| int c = name.charAt(i); | |||
| column = (column + 1) * 26 + c - 'A'; | |||
| } | |||
| return column; | |||
| } | |||
| } | |||
| @@ -0,0 +1,66 @@ | |||
| package com.ruoyi.common.utils.translation; | |||
| import java.lang.annotation.ElementType; | |||
| import java.lang.annotation.Retention; | |||
| import java.lang.annotation.RetentionPolicy; | |||
| import java.lang.annotation.Target; | |||
| /** | |||
| * 额外字典翻译字段注解, 用来代替无法使用Excel注解的情况, 优先级大于Excel注解 | |||
| * 优先级(5种): | |||
| * sql [+ referenceProperty]: 单条SQL语句: 建议加上LIMIT 1, 多条时取第一条, 必须指定占位符?. 例如: @Translate(sql = "SELECT * FROM t_finance_book WHERE id = ? LIMIT 1") | |||
| * table + tableKeyColumn + tableValueColumn [+ referenceProperty]: 拼接为 SELECT `tableValueColumn` FROM `table` WHERE `tableKeyColumn` = ? LIMIT 1. 例如: @Translate(table = "t_finance_book", tableValueColumn = "book_name", tableKeyColumn = "id") | |||
| * <p> | |||
| * dictSql + tableKeyColumn + tableValueColumn: 执行查询SQL后, 取tableKeyColumn(), tableValueColumn()作为字典键值. 例如: @Translate(dictSql = "SELECT * FROM t_finance_book", tableValueColumn = "book_name", tableKeyColumn = "id") | |||
| * readConverterExp | |||
| * dictType | |||
| * <p> | |||
| * 前两种方式使用待翻译字段值直查 | |||
| * 后三种都是加载对应键值字典, 然后用待翻译字段值匹配. 如果都指定了, 会按优先级混合在一起, 优先级高的覆盖优先级低的 | |||
| * <p> | |||
| * 关于referenceProperty | |||
| * 如果带翻译字段为Long的其他表ID, 如: | |||
| * private Long bookId; | |||
| * 如果需要翻译bookId | |||
| * 一种方法是将bookId设为String类型 | |||
| * 还有一种方法是声明String类型的属性辅助翻译bookId, 该新属性不需要原来本身就有bookId的值, 如 | |||
| * | |||
| * @author zhao | |||
| * @Translate(table = "t_finance_book", tableKeyColumn = "id", tableValueColumn = "book_name", referenceProperty = "bookId") // 引用bookId属性的值 | |||
| * // @Translate(sql = "SELECT * FROM t_finance_book WHERE id = ? LIMIT 1", referenceProperty = "bookId") // 也可以写成这样 | |||
| * private String bookName; // bookName的值不需要读取, 只写入 | |||
| * private Long typeId; | |||
| * @Translate(dictType = "dict_xxx_type", referenceProperty = "typeId") // 引用typeId属性的值 | |||
| * private String typeName; // typeName的值不需要读取, 只写入 | |||
| */ | |||
| @Retention(RetentionPolicy.RUNTIME) | |||
| @Target(ElementType.FIELD) | |||
| public @interface Translate { | |||
| /** | |||
| * 如果是字典类型,请设置字典的type值 (如: sys_user_sex) | |||
| */ | |||
| public String dictType() default "" ; | |||
| /** | |||
| * 读取内容转表达式 (如: 0=男,1=女,2=未知) | |||
| */ | |||
| public String readConverterExp() default "" ; | |||
| // 查询数据库字典, 字段必须为String, 不能为Long/Integer等数字型 | |||
| // 方式1: SQL, ?号占位 | |||
| public String sql() default "" ; | |||
| // 方式2: 表名 + 对应列名 + 名称列 | |||
| // 拼接为 SELECT tableValueColumn() FROM table() WHERE tableKeyColumn() = ? LIMIT 1 | |||
| public String table() default "" ; // 表名 | |||
| public String tableKeyColumn() default "id" ; // 键的列名 | |||
| public String tableValueColumn() default "name" ; // 值的列名 | |||
| public String referenceProperty() default "" ; // 引用其他类成员属性 | |||
| // 方式3: SQL + 对应列名 + 名称列: 无占位符, 从数据库加载键值对 | |||
| // 执行查询SQL后, 取tableKeyColumn(), tableValueColumn()作为字典键值 | |||
| public String dictSql() default "" ; | |||
| } | |||
| @@ -0,0 +1,58 @@ | |||
| package com.ruoyi.common.utils.translation; | |||
| import com.ruoyi.common.core.domain.BaseEntity; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| /** | |||
| * zhao: 字段自动字典翻译 | |||
| * 需要翻译的字段必须使用Translate注解或Excel注解标记 | |||
| * 优先级: | |||
| * 注解: Translate优先级大于Excel. 只会使用Translate或Excel其中的一种, 不会混合 | |||
| * 注解属性: readConverterExp优先级大于dictType. readConverterExp和dictType的键值对最终会混合在一起 | |||
| * 字段类型: | |||
| * 如果是String类型, 必须赋值注解的dictType或readConverterExp | |||
| * 如果是其他复合类型/List, 无需赋值dictType或readConverterExp, 仅标记注解即可, 此时将递归翻译(注意不要循环嵌套对象) | |||
| * <p> | |||
| * 数据库表数据字典: 仅Translate注解支持 | |||
| * 字段必须为String类型 | |||
| */ | |||
| public final class TranslateUtils { | |||
| private TranslateUtils() { | |||
| } | |||
| public static void translateList(List<?> list, Object... args) { | |||
| new Translator().translateList(list, args); | |||
| } | |||
| public static void translate(Object obj, Object... args) { | |||
| new Translator().translate(obj, args); | |||
| } | |||
| public static void translateMapList(List<Map<String, Object>> list, Map<String, String> dictMap) { | |||
| new Translator().translateMapList(list, dictMap); | |||
| } | |||
| public static void translateMap(Map<String, Object> map, Map<String, String> dictMap) { | |||
| new Translator().translateMap(map, dictMap); | |||
| } | |||
| public static <T> void translateObjectList(List<T> list) { | |||
| new Translator().translateObjectList(list); | |||
| } | |||
| public static <T> void translateObject(T object) { | |||
| new Translator().translateObject(object); | |||
| } | |||
| public static <T extends BaseEntity> void translateEntityList(List<T> list, boolean... keepRawField) { | |||
| boolean b = null != keepRawField && keepRawField.length > 0 ? keepRawField[0] : false; | |||
| new Translator().translateEntityList(list, b); | |||
| } | |||
| public static <T extends BaseEntity> void translateEntity(T object, boolean... keepRawField) { | |||
| boolean b = null != keepRawField && keepRawField.length > 0 ? keepRawField[0] : false; | |||
| new Translator().translateEntity(object, b); | |||
| } | |||
| } | |||
| @@ -0,0 +1,465 @@ | |||
| package com.ruoyi.common.utils.translation; | |||
| import cn.hutool.core.collection.CollectionUtil; | |||
| import cn.hutool.core.util.StrUtil; | |||
| import com.ruoyi.common.annotation.Excel; | |||
| import com.ruoyi.common.core.domain.BaseEntity; | |||
| import com.ruoyi.common.utils.DictUtils; | |||
| import com.ruoyi.common.utils.StringUtils; | |||
| import com.ruoyi.common.utils.reflect.ReflectUtils; | |||
| import com.ruoyi.common.utils.spring.SpringUtils; | |||
| import org.springframework.jdbc.core.JdbcTemplate; | |||
| import java.lang.reflect.Field; | |||
| import java.lang.reflect.Modifier; | |||
| import java.util.*; | |||
| public final class Translator { | |||
| private static final String KEY_SQL_KEY = "$__SQL__" ; | |||
| private final List<Object> handledObjects = new ArrayList<>(); | |||
| public Translator() { | |||
| } | |||
| public void translateList(List<?> list, Object... args) { | |||
| if (CollectionUtil.isEmpty(list)) | |||
| return; | |||
| if (startTranslate(list)) | |||
| list.forEach((x) -> translate(x, args)); | |||
| } | |||
| @SuppressWarnings("unchecked") | |||
| public void translate(Object obj, Object... args) { | |||
| if (null == obj) | |||
| return; | |||
| if (obj instanceof BaseEntity) { | |||
| Object o = DEF_PARM(args); | |||
| boolean b = o instanceof Boolean ? (Boolean) o : false; | |||
| translateEntity((BaseEntity) obj, b); | |||
| } else if (obj instanceof Map) { | |||
| Object o = DEF_PARM(args); | |||
| Map<String, String> dictMap = o instanceof Map ? (Map<String, String>) args[0] : null; | |||
| translateMap((Map<String, Object>) obj, dictMap); | |||
| } else if (obj instanceof List) { | |||
| translateList((List<?>) obj, args); | |||
| } else { | |||
| translateObject(obj); | |||
| } | |||
| } | |||
| public <T extends BaseEntity> void translateEntityList(List<T> list, boolean keepRawField) { | |||
| if (CollectionUtil.isEmpty(list)) | |||
| return; | |||
| list.forEach((x) -> translateEntity(x, keepRawField)); | |||
| } | |||
| public <T extends BaseEntity> void translateEntity(T object, boolean keepRawField) { | |||
| if (null == object) | |||
| return; | |||
| translateEntity_r(object, object.getClass(), keepRawField); | |||
| } | |||
| public void translateMapList(List<Map<String, Object>> list, Map<String, String> dictMap) { | |||
| if (CollectionUtil.isEmpty(list)) | |||
| return; | |||
| list.forEach((x) -> translateMap(x, dictMap)); | |||
| } | |||
| public void translateMap(Map<String, Object> map, Map<String, String> dictMap) { | |||
| if (CollectionUtil.isEmpty(map) || CollectionUtil.isEmpty(dictMap)) | |||
| return; | |||
| if (!startTranslate(map)) | |||
| return; | |||
| dictMap.forEach((k, v) -> { | |||
| if (!map.containsKey(k)) | |||
| return; | |||
| Object o = map.get(k); | |||
| if (null == o) | |||
| return; | |||
| Class<?> clazz = o.getClass(); | |||
| if (clazz.equals(String.class)) { | |||
| String str = (String) o; | |||
| if (StringUtils.isEmpty(str)) | |||
| return; | |||
| map.put(k, DictUtils.getDictLabelElseOriginValue(v, str)); | |||
| } else if (canRecursionClassObjectField(clazz)) | |||
| translate(o, dictMap); | |||
| }); | |||
| } | |||
| // 判断是否翻译对象属性 | |||
| private boolean canRecursionClassObjectField(Class<?> clazz) { | |||
| /* if(Object.class.equals(clazz)) // is Object class | |||
| return false; | |||
| if(!Object.class.isAssignableFrom(clazz)) // is internal class, e.g. int long boolean | |||
| return false; | |||
| if(clazz.getPackage().getName().startsWith("java.lang")) // class in java.lang.**, e.g. Long Integer | |||
| return false; | |||
| return true;*/ | |||
| return BaseEntity.class.isAssignableFrom(clazz) // BaseEntity | |||
| || List.class.isAssignableFrom(clazz) // List | |||
| || Map.class.isAssignableFrom(clazz) // Map | |||
| ; | |||
| } | |||
| public <T> void translateObjectList(List<T> list) { | |||
| if (CollectionUtil.isEmpty(list)) | |||
| return; | |||
| list.forEach(this::translateObject); | |||
| } | |||
| public <T> void translateObject(T object) { | |||
| if (null == object) | |||
| return; | |||
| translateObject_r(object, object.getClass()); | |||
| } | |||
| private <T> void translateObject_r(T object, Class<?> clazz) { | |||
| if (!startTranslate(object)) | |||
| return; | |||
| Field[] declaredFields = clazz.getDeclaredFields(); | |||
| for (Field field : declaredFields) { | |||
| boolean trans = hasTranslateFlag(field); // check translate flag | |||
| if (!trans) | |||
| continue; | |||
| translateField(field, object); | |||
| } | |||
| Class<?> superclass = getSuperClass(clazz); | |||
| if (null != superclass) | |||
| translateObject_r(object, superclass); | |||
| } | |||
| private <T extends BaseEntity> void translateEntity_r(T object, Class<?> clazz, boolean keepRawField) { | |||
| if (!startTranslate(object)) | |||
| return; | |||
| Field[] declaredFields = clazz.getDeclaredFields(); | |||
| for (Field field : declaredFields) { | |||
| boolean trans = hasTranslateFlag(field); // check translate flag | |||
| if (!trans) | |||
| continue; | |||
| translateField(field, object, keepRawField); | |||
| } | |||
| Class<?> superclass = getSuperClass(clazz); | |||
| if (null != superclass) | |||
| translateEntity_r(object, superclass, keepRawField); | |||
| } | |||
| // 解析readConverterExp | |||
| private Map<String, String> parseReadConverterExp(String str) { | |||
| Map<String, String> dict = new LinkedHashMap<>(); | |||
| if (StringUtils.isNotEmpty(str)) { | |||
| List<String> split = StrUtil.split(str, ',', true, true); | |||
| split.forEach((x) -> { | |||
| List<String> arr = StrUtil.split(x, '=', 2, true, true); | |||
| if (CollectionUtil.isEmpty(arr)) | |||
| return; | |||
| dict.put(arr.get(0), arr.size() > 1 ? arr.get(1) : null); | |||
| }); | |||
| } | |||
| return dict; | |||
| } | |||
| // 读取数据库数据作为字典 | |||
| private Map<String, String> loadDBDict(String sql, String keyColumn, String nameColumn) { | |||
| Map<String, String> dict = new LinkedHashMap<>(); | |||
| if (StringUtils.isNotEmpty(sql) && StringUtils.isNotEmpty(keyColumn) && StringUtils.isNotEmpty(nameColumn)) { | |||
| JdbcTemplate jdbcTemplate = SpringUtils.getBean(JdbcTemplate.class); | |||
| try { | |||
| List<Map<String, Object>> list = jdbcTemplate.queryForList(sql); | |||
| if (CollectionUtil.isNotEmpty(list)) { | |||
| for (Map<String, Object> map : list) { | |||
| String key = toString_s(map.get(keyColumn)); | |||
| if (StringUtils.isEmpty(key)) | |||
| continue; | |||
| dict.put(key, toString_s(map.get(nameColumn))); | |||
| } | |||
| } | |||
| } catch (Exception e) { | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| return dict; | |||
| } | |||
| // 生成查询SQL | |||
| private String genRelationSQL(String table, String keyColumn, String nameColumn) { | |||
| if (StringUtils.isNotEmpty(table) && StringUtils.isNotEmpty(keyColumn) && StringUtils.isNotEmpty(nameColumn)) { | |||
| return String.format("SELECT `%s` FROM `%s` WHERE `%s` = ? LIMIT 1", nameColumn, table, keyColumn); | |||
| } | |||
| return null; | |||
| } | |||
| // 直接取SQL值 | |||
| private String readDBValue(String sql, String id) { | |||
| if (StringUtils.isNotEmpty(sql) && StringUtils.isNotEmpty(id)) { | |||
| JdbcTemplate jdbcTemplate = SpringUtils.getBean(JdbcTemplate.class); | |||
| try { | |||
| List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, id); | |||
| if (CollectionUtil.isNotEmpty(list)) { | |||
| Map<String, Object> map = list.get(0); | |||
| return toString_s(map.get(map.keySet().iterator().next())); | |||
| } | |||
| } catch (Exception e) { | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| return null; | |||
| } | |||
| // 检查是否需要翻译 | |||
| private boolean hasTranslateFlag(Field field) { | |||
| // 静态/不可重赋值 不翻译 | |||
| int modifiers = field.getModifiers(); | |||
| if (Modifier.isStatic(modifiers) || Modifier.isFinal(modifiers)) | |||
| return false; | |||
| return null != field.getAnnotation(Translate.class) || null != field.getAnnotation(Excel.class); | |||
| } | |||
| // 获取翻译来源 | |||
| // 返回: dictType对应的字典键值对和readConverterExp解析后的键值对的组合 | |||
| // 优先级: Translate > Excel | readConverterExp > dictType | |||
| private Map<String, String> getTranslateDict(Field field) { | |||
| Map<String, String> dict = new LinkedHashMap<>(); | |||
| String str; | |||
| Translate translate = field.getAnnotation(Translate.class); // First check Translate annotation | |||
| if (null != translate) { | |||
| // 1. SQL 直查 | |||
| str = translate.sql(); | |||
| if (StringUtils.isNotEmpty(str)) { | |||
| dict.put(KEY_SQL_KEY, str); | |||
| } | |||
| // 2. table + tableKeyColumn + tableValueColumn 直查 | |||
| str = translate.table(); | |||
| if (StringUtils.isNotEmpty(str)) { | |||
| str = genRelationSQL(str, translate.tableKeyColumn(), translate.tableValueColumn()); | |||
| dict.put(KEY_SQL_KEY, str); | |||
| } | |||
| // 3. dict缓存 | |||
| str = translate.dictType(); | |||
| if (StringUtils.isNotEmpty(str)) { | |||
| dict.putAll(DictUtils.dictCacheValueLabelMap(str)); | |||
| } | |||
| // 4. readConverterExp配置 | |||
| str = translate.readConverterExp(); | |||
| if (StringUtils.isNotEmpty(str)) { | |||
| dict.putAll(parseReadConverterExp(str)); | |||
| } | |||
| // 5. 数据库任意表数据 | |||
| str = translate.dictSql(); | |||
| if (StringUtils.isNotEmpty(str)) { | |||
| dict.putAll(loadDBDict(str, translate.tableKeyColumn(), translate.tableValueColumn())); | |||
| } | |||
| } | |||
| if (StringUtils.isNotEmpty(dict)) | |||
| return dict; | |||
| Excel excel = field.getAnnotation(Excel.class); // And then check Excel annotation | |||
| if (null != excel) { | |||
| str = excel.dictType(); | |||
| if (StringUtils.isNotEmpty(str)) { | |||
| dict.putAll(DictUtils.dictCacheValueLabelMap(str)); | |||
| } | |||
| str = excel.readConverterExp(); | |||
| if (StringUtils.isNotEmpty(str)) { | |||
| dict.putAll(parseReadConverterExp(str)); | |||
| } | |||
| } | |||
| return dict; | |||
| } | |||
| private boolean translateField(Field field, Object object) { | |||
| if (field.getType().equals(String.class)) | |||
| return translateField_String(field, object); | |||
| else | |||
| return translateField_Object(field, object); | |||
| } | |||
| private boolean translateField_Object(Field field, Object object) { | |||
| if (field.getType().equals(String.class)) | |||
| return false; | |||
| if (!canRecursionClassObjectField(field.getType())) | |||
| return false; | |||
| Object val = null; | |||
| if (!field.isAccessible()) | |||
| field.setAccessible(true); | |||
| try { | |||
| val = field.get(object); | |||
| } catch (IllegalAccessException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| if (null != val) { | |||
| translate(val); | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| private boolean translateField_String(Field field, Object object) { | |||
| boolean trans = false; | |||
| if (!field.getType().equals(String.class)) | |||
| return false; | |||
| Map<String, String> dict = getTranslateDict(field); | |||
| if (CollectionUtil.isEmpty(dict)) | |||
| return false; | |||
| if (!field.isAccessible()) | |||
| field.setAccessible(true); | |||
| try { | |||
| Object val = getField(object, field); | |||
| if (null != val) { | |||
| String str = (String) val; | |||
| if (StringUtils.isNotEmpty(str)) { | |||
| if (dict.containsKey(KEY_SQL_KEY)) { | |||
| String dbVal = readDBValue(dict.get(KEY_SQL_KEY), str); | |||
| if (StringUtils.isNotEmpty(dbVal)) { | |||
| field.set(object, dbVal); | |||
| trans = true; | |||
| } | |||
| } | |||
| if (!trans && dict.containsKey(str)) { | |||
| field.set(object, dict.get(str)); | |||
| trans = true; | |||
| } | |||
| } | |||
| } | |||
| } catch (IllegalAccessException e) { | |||
| e.printStackTrace(); | |||
| return false; | |||
| } | |||
| return trans; | |||
| } | |||
| private <T extends BaseEntity> boolean translateField(Field field, T entity, boolean keepRawField) { | |||
| if (field.getType().equals(String.class)) | |||
| return translateField_String(field, entity, keepRawField); | |||
| else | |||
| return translateField_Object(field, entity, keepRawField); | |||
| } | |||
| private <T extends BaseEntity> boolean translateField_Object(Field field, T entity, boolean keepRawField) { | |||
| if (field.getType().equals(String.class)) | |||
| return false; | |||
| if (!canRecursionClassObjectField(field.getType())) | |||
| return false; | |||
| Object val = null; | |||
| try { | |||
| val = getField(entity, field); | |||
| } catch (IllegalAccessException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| if (null != val) { | |||
| translate(val, keepRawField); | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| private <T extends BaseEntity> boolean translateField_String(Field field, T entity, boolean keepRawField) { | |||
| boolean trans = false; | |||
| if (!field.getType().equals(String.class)) | |||
| return false; | |||
| Map<String, String> dict = getTranslateDict(field); | |||
| if (CollectionUtil.isEmpty(dict)) | |||
| return false; | |||
| try { | |||
| Object val = getField(entity, field); | |||
| if (keepRawField) | |||
| entity.getParams().put(field.getName(), val); // backup raw value to params | |||
| if (null != val) { | |||
| String str = (String) val; | |||
| if (StringUtils.isNotEmpty(str)) { | |||
| if (dict.containsKey(KEY_SQL_KEY)) { | |||
| String dbVal = readDBValue(dict.get(KEY_SQL_KEY), str); | |||
| if (StringUtils.isNotEmpty(dbVal)) { | |||
| field.set(entity, dbVal); | |||
| trans = true; | |||
| } | |||
| } | |||
| if (!trans && dict.containsKey(str)) { | |||
| field.set(entity, dict.get(str)); | |||
| trans = true; | |||
| } | |||
| } | |||
| } | |||
| } catch (IllegalAccessException e) { | |||
| e.printStackTrace(); | |||
| return false; | |||
| } | |||
| return trans; | |||
| } | |||
| private Object getField(Object obj, Field field) throws IllegalAccessException { | |||
| if (!field.isAccessible()) | |||
| field.setAccessible(true); | |||
| Translate annotation = field.getAnnotation(Translate.class); | |||
| if (null != annotation) { | |||
| String refProp = annotation.referenceProperty(); | |||
| if (StringUtils.isNotEmpty(refProp)) { | |||
| Field refField = ReflectUtils.getAccessibleField(obj, refProp); | |||
| if (null != refField) | |||
| return toString_s(refField.get(obj)); // 转换为String | |||
| } | |||
| } | |||
| return field.get(obj); | |||
| } | |||
| private Class<?> getSuperClass(Class<?> clazz) { | |||
| if (null == clazz) | |||
| return null; | |||
| Class<?> superclass = clazz.getSuperclass(); // translate parent class | |||
| if (null != superclass && !superclass.equals(Object.class)) // check is top or is Object | |||
| return superclass; | |||
| return null; | |||
| } | |||
| // 允许翻译则返回true | |||
| private boolean startTranslate(Object o) { | |||
| if (null == o) | |||
| return false; | |||
| if (o instanceof String) // String类型总是重新翻译 | |||
| return true; | |||
| if (contains_ptr(handledObjects, o)) // 其他类型防止循环递归 | |||
| { | |||
| return false; | |||
| } | |||
| handledObjects.add(o); | |||
| return true; | |||
| } | |||
| public void Reset() { | |||
| handledObjects.clear(); | |||
| } | |||
| public static <T> boolean contains_ptr(Collection<T> list, T target) { | |||
| if (CollectionUtil.isEmpty(list)) | |||
| return false; | |||
| return list.stream().anyMatch((x) -> x == target); | |||
| } | |||
| public static String toString_s(Object obj) { | |||
| if (null == obj) | |||
| return "" ; | |||
| if (obj instanceof String) | |||
| return (String) obj; | |||
| else | |||
| return obj.toString(); | |||
| } | |||
| public static <T> T DEF_PARM(T... args) { | |||
| return null != args && args.length > 0 ? args[0] : null; | |||
| } | |||
| } | |||
| @@ -73,6 +73,7 @@ | |||
| <orderEntry type="library" name="Maven: org.springframework.security:spring-security-core:5.7.12" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.security:spring-security-crypto:5.7.12" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.security:spring-security-web:5.7.12" level="project" /> | |||
| <orderEntry type="library" name="Maven: net.coobird:thumbnailator:0.4.8" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.github.pagehelper:pagehelper-spring-boot-starter:1.4.7" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.mybatis.spring.boot:mybatis-spring-boot-starter:2.3.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-jdbc:2.5.15" level="project" /> | |||
| @@ -89,6 +90,7 @@ | |||
| <orderEntry type="library" name="Maven: jakarta.validation:jakarta.validation-api:2.0.2" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.4.3.Final" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.fasterxml:classmate:1.5.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.projectlombok:lombok:1.18.26" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.12.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.alibaba.fastjson2:fastjson2:2.0.57" level="project" /> | |||
| <orderEntry type="library" name="Maven: commons-io:commons-io:2.19.0" level="project" /> | |||
| @@ -103,6 +105,7 @@ | |||
| <orderEntry type="library" name="Maven: org.apache.commons:commons-compress:1.19" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.github.virtuald:curvesapi:1.06" level="project" /> | |||
| <orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt:0.9.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: javax.servlet:javax.servlet-api:4.0.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: javax.xml.bind:jaxb-api:2.3.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: javax.activation:javax.activation-api:1.2.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-data-redis:2.5.15" level="project" /> | |||
| @@ -123,6 +126,8 @@ | |||
| <orderEntry type="library" name="Maven: org.reactivestreams:reactive-streams:1.0.4" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.commons:commons-pool2:2.9.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: eu.bitwalker:UserAgentUtils:1.21" level="project" /> | |||
| <orderEntry type="library" name="Maven: javax.servlet:javax.servlet-api:4.0.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.itextpdf:itextpdf:5.5.13.2" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.itextpdf:itext-asian:5.2.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: cn.hutool:hutool-all:5.5.4" level="project" /> | |||
| </component> | |||
| </module> | |||
| @@ -40,6 +40,7 @@ | |||
| <orderEntry type="library" name="Maven: org.springframework.security:spring-security-core:5.7.12" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.security:spring-security-crypto:5.7.12" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.security:spring-security-web:5.7.12" level="project" /> | |||
| <orderEntry type="library" name="Maven: net.coobird:thumbnailator:0.4.8" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.github.pagehelper:pagehelper-spring-boot-starter:1.4.7" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.mybatis.spring.boot:mybatis-spring-boot-starter:2.3.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-jdbc:2.5.15" level="project" /> | |||
| @@ -57,6 +58,7 @@ | |||
| <orderEntry type="library" name="Maven: jakarta.validation:jakarta.validation-api:2.0.2" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.4.3.Final" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.fasterxml:classmate:1.5.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.projectlombok:lombok:1.18.26" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.12.7.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.12.7" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.12.7" level="project" /> | |||
| @@ -74,6 +76,7 @@ | |||
| <orderEntry type="library" name="Maven: com.github.virtuald:curvesapi:1.06" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.28" level="project" /> | |||
| <orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt:0.9.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: javax.servlet:javax.servlet-api:4.0.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: javax.xml.bind:jaxb-api:2.3.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: javax.activation:javax.activation-api:1.2.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-data-redis:2.5.15" level="project" /> | |||
| @@ -94,7 +97,9 @@ | |||
| <orderEntry type="library" name="Maven: org.reactivestreams:reactive-streams:1.0.4" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.commons:commons-pool2:2.9.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: eu.bitwalker:UserAgentUtils:1.21" level="project" /> | |||
| <orderEntry type="library" name="Maven: javax.servlet:javax.servlet-api:4.0.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.itextpdf:itextpdf:5.5.13.2" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.itextpdf:itext-asian:5.2.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: cn.hutool:hutool-all:5.5.4" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.alibaba:druid-spring-boot-starter:1.2.23" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.alibaba:druid:1.2.23" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.5.15" level="project" /> | |||
| @@ -74,25 +74,22 @@ public class ${ClassName}Controller extends BaseController | |||
| /** | |||
| * ${functionName}导入模板 | |||
| */ | |||
| @RequiresPermissions("${permissionPrefix}:view") | |||
| @GetMapping("/importTemplate") | |||
| @ResponseBody | |||
| public AjaxResult importTemplate() { | |||
| @PostMapping("/importTemplate") | |||
| public void importTemplate(HttpServletResponse response) { | |||
| ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}.class); | |||
| return util.importTemplateExcel("${functionName}数据"); | |||
| util.importTemplateExcel(response, "${functionName}数据"); | |||
| } | |||
| /** | |||
| * ${functionName}导入 | |||
| */ | |||
| @Log(title = "${functionName}", businessType = BusinessType.IMPORT) | |||
| @RequiresPermissions("${permissionPrefix}:import") | |||
| @PreAuthorize("@ss.hasPermi('${permissionPrefix}:import')") | |||
| @PostMapping("/importData") | |||
| @ResponseBody | |||
| public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception { | |||
| ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}.class); | |||
| List<${ClassName}> list = util.importExcel(file.getInputStream(), 0); | |||
| String message = ${className}Service.import${ClassName}(list, updateSupport, getSysUser()); | |||
| String message = ${className}Service.import${ClassName}(list, updateSupport, getUsername()); | |||
| return AjaxResult.success(message); | |||
| } | |||
| @@ -142,11 +139,11 @@ public class ${ClassName}Controller extends BaseController | |||
| /** | |||
| * 打印${functionName} | |||
| */ | |||
| @RequiresPermissions("${permissionPrefix}:print") | |||
| @PreAuthorize("@ss.hasPermi('${permissionPrefix}:print')") | |||
| @Log(title = "${functionName}", businessType = BusinessType.PRINT) | |||
| @GetMapping("/print") | |||
| public void printPdf(${ClassName} ${className}, HttpServletResponse response) throws Exception{ | |||
| ${className}.setBookId(getSysUser().getLoginBookid()); | |||
| List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); | |||
| TranslateUtils.translateList(list, false); | |||
| @@ -171,12 +168,12 @@ public class ${ClassName}Controller extends BaseController | |||
| List<String[]> contentList = Lists.newArrayList(); | |||
| list.forEach(a ->{ | |||
| String[] str = new String[6]; | |||
| str[0] = a.getBookName(); | |||
| str[1] = a.getOrgCodeCertNum(); | |||
| str[2] = a.getDeptName(); | |||
| str[3] = a.getAccountant(); | |||
| str[4] = a.getAccountantPhone(); | |||
| str[5] = a.getCurrentDay(); | |||
| str[0] = ""; | |||
| str[1] = ""; | |||
| str[2] = ""; | |||
| str[3] = ""; | |||
| str[4] = ""; | |||
| str[5] = ""; | |||
| contentList.add(str); | |||
| }); | |||
| pdf.setContentList(contentList); | |||
| @@ -32,10 +32,10 @@ public interface I${ClassName}Service | |||
| * | |||
| * @param list ${functionName}数据列表 | |||
| * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据 | |||
| * @param user 操作用户 | |||
| * @param userName 操作用户 | |||
| * @return 结果 | |||
| */ | |||
| public String import${ClassName}(List<${ClassName}> list, Boolean isUpdateSupport, SysUser user); | |||
| public String import${ClassName}(List<${ClassName}> list, Boolean isUpdateSupport, String userName); | |||
| /** | |||
| * 新增${functionName} | |||
| @@ -60,16 +60,15 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service | |||
| * | |||
| * @param list ${functionName}数据列表 | |||
| * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据 | |||
| * @param user 操作用户 | |||
| * @param operName 操作用户 | |||
| * @return 结果 | |||
| */ | |||
| @Override | |||
| @Transactional | |||
| public String import${ClassName}(List<${ClassName}> list, Boolean isUpdateSupport, SysUser user) { | |||
| public String import${ClassName}(List<${ClassName}> list, Boolean isUpdateSupport, String operName) { | |||
| if (StringUtils.isEmpty(list)) { | |||
| throw new ServiceException("导入${functionName}数据不能为空!"); | |||
| } | |||
| String operName = user.getLoginName(); | |||
| int successNum = 0; | |||
| int failureNum = 0; | |||
| @@ -1,3 +1,39 @@ | |||
| #macro(GET_CHAR_COLUMN_LENGTH $column)## * 如果表列是char/varchar, 获取其最大字符长度, 否则为空字符串 * 参数: 列 * 结果保存在变量名为 $_char_column_length 字符串型 | |||
| #set($_char_column_length="") | |||
| #if($column.columnType.startsWith("char") || $column.columnType.startsWith("varchar")) | |||
| #set($startLeft=$column.columnType.indexOf("(")) | |||
| #if($startLeft != -1) | |||
| #set($endRight=$column.columnType.indexOf(")", $startLeft)) | |||
| #if($endRight != -1) | |||
| #set($startLeft=$startLeft+1) | |||
| #set($_char_column_length=$column.columnType.substring($startLeft, $endRight)) | |||
| #end | |||
| #end | |||
| #end | |||
| #end## GET_CHAR_COLUMN_LENGTH | |||
| #macro(COLUMN_IS_NUMBER $column)## * 检查表列是否是数字型 * 参数: 列 * 结果保存在变量名为 $_column_is_number bool型 | |||
| #set($_column_is_number=$column.columnType.startsWith("decimal") || $column.columnType.startsWith("tinyint") || $column.columnType.startsWith("mediumint") || $column.columnType.startsWith("int") || $column.columnType.startsWith("bigint") || $column.columnType.startsWith("smallint")) | |||
| #end## COLUMN_IS_NUMBER | |||
| #macro(GET_NUMBER_COLUMN_MIN_AND_PRECISION $column)## * 如果表列是数字型, 获取其最小值和浮点数部分精度, 否则为空字符串 * 参数: 列 * 最小值结果保存在变量名为 $_number_column_min 字符串型 * 浮点数部分精度结果保存在变量名为 $_number_column_precision 字符串型 | |||
| #set($_number_column_min="") | |||
| #set($_number_column_precision="") | |||
| #if($column.columnType.contains("unsigned")) | |||
| #set($_number_column_min="0") | |||
| #end | |||
| #set($startLeft=$column.columnType.indexOf("(")) | |||
| #if($startLeft != -1) | |||
| #set($endRight=$column.columnType.indexOf(")", $startLeft)) | |||
| #if($endRight != -1) | |||
| #set($startLeft=$startLeft+1) | |||
| #set($internalText=$column.columnType.substring($startLeft, $endRight)) | |||
| #set($splitIndex=$internalText.indexOf(",")) | |||
| #if($splitIndex != -1) | |||
| #set($splitIndex=$splitIndex+1) | |||
| #set($_number_column_precision=$internalText.substring($splitIndex)) | |||
| #end | |||
| #end | |||
| #end | |||
| #end## GET_NUMBER_COLUMN_MIN_AND_PRECISION | |||
| <template> | |||
| <div class="app-container"> | |||
| <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px"> | |||
| @@ -48,20 +84,23 @@ | |||
| <el-col :span="1.5"> | |||
| <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['${permissionPrefix}:add']">新增</el-button> | |||
| </el-col> | |||
| <!-- | |||
| <el-col :span="1.5"> | |||
| <el-button type="success" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate" v-hasPermi="['${permissionPrefix}:edit']">修改</el-button> | |||
| </el-col> | |||
| <el-col :span="1.5"> | |||
| <el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete" v-hasPermi="['${permissionPrefix}:remove']">删除</el-button> | |||
| </el-col> | |||
| --> | |||
| <el-col :span="1.5"> | |||
| <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" v-hasPermi="['${permissionPrefix}:export']">导出</el-button> | |||
| <el-button type="info" plain icon="el-icon-upload2" size="mini" @click="handleImport" v-hasPermi="['${permissionPrefix}:import']">导入</el-button> | |||
| </el-col> | |||
| <el-col :span="1.5"> | |||
| <el-button type="info" plain icon="el-icon-upload2" size="mini" @click="handleImport" v-hasPermi="['${permissionPrefix}:import']">导入</el-button> | |||
| <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" v-hasPermi="['${permissionPrefix}:export']">导出</el-button> | |||
| </el-col> | |||
| <el-col :span="1.5"> | |||
| <el-button type="warning" plain icon="el-icon-printer" size="mini" @click="handlePrint()" v-hasPermi="['${permissionPrefix}:export']">打印</el-button> | |||
| <el-button type="success" plain icon="el-icon-printer" size="mini" @click="handlePrint()" v-hasPermi="['${permissionPrefix}:print']">打印</el-button> | |||
| </el-col> | |||
| <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> | |||
| </el-row> | |||
| @@ -80,21 +119,17 @@ | |||
| #if($column.pk) | |||
| <el-table-column label="${comment}" align="center" prop="${javaField}" /> | |||
| #elseif($column.list && $column.htmlType == "datetime") | |||
| <el-table-column label="${comment}" align="center" prop="${javaField}" width="180"> | |||
| <template slot-scope="scope"> | |||
| <span>{{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}</span> | |||
| </template> | |||
| </el-table-column> | |||
| <el-table-column label="${comment}" align="center" prop="${javaField}" /> | |||
| #elseif($column.list && $column.htmlType == "imageUpload") | |||
| <el-table-column label="${comment}" align="center" prop="${javaField}" width="100"> | |||
| <template slot-scope="scope"> | |||
| <div v-if="!!scope.row.${javaField}"><el-tooltip effect="light" :content="item" placement="bottom" v-for="(item, index) in scope.row.${javaField}.split(',')" :key="index"><el-image style="height: 48px; width: 48px; margin: 2px; display: inline-block;" fit="scale-down" :src="$store.getters.baseRoutingUrl + item" :preview-src-list="scope.row.${javaField}.split(',').map((x) => $store.getters.baseRoutingUrl + x)"/></el-tooltip></div> | |||
| <div v-if="!!scope.row.${javaField}"><el-tooltip effect="light" :content="item" placement="bottom" v-for="(item, index) in scope.row.${javaField}.split(',')" :key="index"><el-image style="height: 48px; width: 48px; margin: 2px; display: inline-block;" fit="scale-down" :src="baseRoutingUrll + item" :preview-src-list="scope.row.${javaField}.split(',').map((x) => baseRoutingUrll + x)"/></el-tooltip></div> | |||
| </template> | |||
| </el-table-column> | |||
| #elseif($column.list && $column.htmlType == "fileUpload") | |||
| <el-table-column label="${comment}" header-align="center" align="left" prop="${javaField}" > | |||
| <template slot-scope="scope"> | |||
| <div v-if="!!scope.row.${javaField}"><el-tooltip effect="light" :content="item.substr(item.lastIndexOf('/') + 1)" placement="bottom" v-for="(item, index) in scope.row.${javaField}.split(',')" :key="index"><a :href="$store.getters.baseRoutingUrl + item" target="_blank" style="height: 32px; width: 32px; margin: 2px; display: inline-block; text-align: center;"><img :src="getFileIcon(item)" style="height: 100%;"/></a></el-tooltip></div> | |||
| <div v-if="!!scope.row.${javaField}"><el-tooltip effect="light" :content="item.substr(item.lastIndexOf('/') + 1)" placement="bottom" v-for="(item, index) in scope.row.${javaField}.split(',')" :key="index"><a :href="baseRoutingUrll + item" target="_blank" style="height: 32px; width: 32px; margin: 2px; display: inline-block; text-align: center;"><img :src="getFileIcon(item)" style="height: 100%;"/></a></el-tooltip></div> | |||
| </template> | |||
| </el-table-column> | |||
| #elseif($column.list && "" != $column.dictType) | |||
| @@ -124,11 +159,27 @@ | |||
| <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/> | |||
| <!-- 导入EXCEL组件 --> | |||
| <importExcel :visible="importExcelData.visible" :importExcelDatas="importExcelData" :datas="importExcelData.data" @improtExcelChildFn="improtExcelChildFun"></importExcel> | |||
| <el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body> | |||
| <el-upload ref="upload" :limit="1" accept=".xlsx, .xls" :headers="upload.headers" :action="upload.url + '?updateSupport=' + upload.updateSupport" :disabled="upload.isUploading" :on-progress="handleFileUploadProgress" :on-success="handleFileSuccess" :auto-upload="false" drag> | |||
| <i class="el-icon-upload"></i> | |||
| <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div> | |||
| <div class="el-upload__tip text-center" slot="tip"> | |||
| <div class="el-upload__tip" slot="tip"> | |||
| <el-checkbox v-model="upload.updateSupport" />是否更新已经存在的${functionName} | |||
| </div> | |||
| <span>仅允许导入xls、xlsx格式文件。</span> | |||
| <el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">下载模板</el-link> | |||
| </div> | |||
| </el-upload> | |||
| <div slot="footer" class="dialog-footer"> | |||
| <el-button type="primary" @click="submitFileForm">确 定</el-button> | |||
| <el-button @click="upload.open = false">取 消</el-button> | |||
| </div> | |||
| </el-dialog> | |||
| <!-- 查看${functionName}对话框 --> | |||
| <el-dialog :title="title" :visible.sync="viewOpen" width="800px" append-to-body> | |||
| <el-descriptions :column="descColumn" border :labelStyle="{width: `${descLabelWidth}%`}" :contentStyle="{width: `${(100 / descColumn) - descLabelWidth}%`}"> | |||
| <el-descriptions id="printDetail" :column="descColumn" border :labelStyle="{width: `${descLabelWidth}%`}" :contentStyle="{width: `${(100 / descColumn) - descLabelWidth}%`}"> | |||
| #foreach($column in $columns) | |||
| #set($field=$column.javaField) | |||
| #if($column.insert && !$column.pk) | |||
| @@ -140,13 +191,14 @@ | |||
| #set($comment=$column.columnComment) | |||
| #end | |||
| #set($dictType=$column.dictType) | |||
| <el-descriptions-item label="${comment}">#if(($column.htmlType == "select" || $column.htmlType == "checkbox" || $column.htmlType == "radio") && "" != $dictType)<el-tooltip effect="light" :content="form.${field}" placement="right"><dict-tag :options="${field}Options" :value="form.${field}"/></el-tooltip>#elseif($column.htmlType == "imageUpload")<div v-if="!!form.${field}"><el-tooltip effect="light" :content="item" placement="bottom" v-for="(item, index) in form.${field}.split(',')" :key="index"><el-image style="height: 64px; width: 64px; margin: 2px; display: inline-block;" fit="scale-down" :src="$store.getters.baseRoutingUrl + item" :preview-src-list="form.${field}.split(',').map((x) => $store.getters.baseRoutingUrl + x)"/></el-tooltip></div>#elseif($column.htmlType == "editor")<el-tooltip placement="bottom" effect="light"><div slot="content" v-html="form.${field}"/><div style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 230px;">{{ form.${field} }}</div></el-tooltip>#elseif($column.htmlType == "fileUpload")<div v-if="!!form.${field}"><el-tooltip effect="light" :content="item.substr(item.lastIndexOf('/') + 1)" placement="bottom" v-for="(item, index) in form.${field}.split(',')" :key="index"><a :href="$store.getters.baseRoutingUrl + item" target="_blank" style="height: 48px; width: 48px; margin: 2px; display: inline-block; text-align: center;"><img :src="getFileIcon(item)" style="height: 100%;"/></a></el-tooltip></div>#else{{ form.${field} }}#end</el-descriptions-item> | |||
| <el-descriptions-item label="${comment}">#if(($column.htmlType == "select" || $column.htmlType == "checkbox" || $column.htmlType == "radio") && "" != $dictType)<el-tooltip effect="light" :content="form.${field}" placement="right"><dict-tag :options="dict.type.dictType" :value="form.${field}"/></el-tooltip>#elseif($column.htmlType == "imageUpload")<div v-if="!!form.${field}"><el-tooltip effect="light" :content="item" placement="bottom" v-for="(item, index) in form.${field}.split(',')" :key="index"><el-image style="height: 64px; width: 64px; margin: 2px; display: inline-block;" fit="scale-down" :src="baseRoutingUrll + item" :preview-src-list="form.${field}.split(',').map((x) => baseRoutingUrll + x)"/></el-tooltip></div>#elseif($column.htmlType == "editor")<el-tooltip placement="bottom" effect="light"><div slot="content" v-html="form.${field}"/><div style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 230px;">{{ form.${field} }}</div></el-tooltip>#elseif($column.htmlType == "fileUpload")<div v-if="!!form.${field}"><el-tooltip effect="light" :content="item.substr(item.lastIndexOf('/') + 1)" placement="bottom" v-for="(item, index) in form.${field}.split(',')" :key="index"><a :href="baseRoutingUrll + item" target="_blank" style="height: 48px; width: 48px; margin: 2px; display: inline-block; text-align: center;"><img :src="getFileIcon(item)" style="height: 100%;"/></a></el-tooltip></div>#else{{ form.${field} }}#end</el-descriptions-item> | |||
| #end | |||
| #end | |||
| #end | |||
| </el-descriptions> | |||
| <!-- 弹框操作按钮 --> | |||
| <div slot="footer" class="dialog-footer"> | |||
| <el-button @click="doPrint">打 印</el-button> | |||
| <el-button @click="cancel">关 闭</el-button> | |||
| </div> | |||
| </el-dialog> | |||
| @@ -166,9 +218,18 @@ | |||
| #end | |||
| #set($dictType=$column.dictType) | |||
| #if($column.htmlType == "input") | |||
| #COLUMN_IS_NUMBER($column) | |||
| #if($_column_is_number) | |||
| #GET_NUMBER_COLUMN_MIN_AND_PRECISION($column) | |||
| <el-form-item label="${comment}" prop="${field}"> | |||
| <el-input v-model="form.${field}" placeholder="请输入${comment}" /> | |||
| <el-input-number v-model="form.${field}" placeholder="请输入${comment}" controls-position="right" #if($_number_column_min != "") :min="${_number_column_min}"#end #if($_number_column_precision != "") :precision="${_number_column_precision}"#end/> | |||
| </el-form-item> | |||
| #else | |||
| #GET_CHAR_COLUMN_LENGTH($column) | |||
| <el-form-item label="${comment}" prop="${field}"> | |||
| <el-input v-model="form.${field}" placeholder="请输入${comment}" #if($_char_column_length != "") show-word-limit :maxlength="${_char_column_length}"#end/> | |||
| </el-form-item> | |||
| #end | |||
| #elseif($column.htmlType == "imageUpload") | |||
| <el-form-item label="${comment}" prop="${field}"> | |||
| <image-upload v-model="form.${field}"/> | |||
| @@ -235,7 +296,7 @@ | |||
| </el-form-item> | |||
| #elseif($column.htmlType == "textarea") | |||
| <el-form-item label="${comment}" prop="${field}"> | |||
| <el-input v-model="form.${field}" type="textarea" placeholder="请输入内容" /> | |||
| <el-input v-model="form.${field}" type="textarea" :autosize="{ minRows: 2, maxRows: 3}" #if($_char_column_length != "") :maxlength="${_char_column_length}"#end show-word-limit placeholder="请输入内容" /> | |||
| </el-form-item> | |||
| #end | |||
| #end | |||
| @@ -306,7 +367,7 @@ | |||
| <script> | |||
| import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName}, print${BusinessName} } from "@/api/${moduleName}/${businessName}" | |||
| import importExcel from "@/components/importExcel/importExcel"; | |||
| import { getToken } from "@/utils/auth" | |||
| export default { | |||
| name: "${BusinessName}", | |||
| #if(${dicts} != '') | |||
| @@ -348,6 +409,8 @@ export default { | |||
| descLabelWidth: 15, | |||
| // 对话框显示只读的详情 | |||
| viewOpen: false, | |||
| // 项目路径 | |||
| baseRoutingUrl: process.env.VUE_APP_BASE_API, | |||
| #foreach ($column in $columns) | |||
| #if($column.htmlType == "datetime" && $column.queryType == "BETWEEN") | |||
| #set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) | |||
| @@ -363,7 +426,7 @@ export default { | |||
| //orderByColumn: "id", | |||
| //isAsc: "desc", | |||
| // 翻译字典 | |||
| //toDict: "1", | |||
| //toTranslateDict: "1", | |||
| #foreach ($column in $columns) | |||
| #if($column.query) | |||
| $column.javaField: null#if($foreach.count != $columns.size()),#end | |||
| @@ -389,22 +452,21 @@ export default { | |||
| #end | |||
| }, | |||
| // EXCEL导入 | |||
| importExcelData: { | |||
| //弹窗显示隐藏 | |||
| visible: false, | |||
| //标题 | |||
| title: "${functionName}导入", | |||
| //上传时附带的额外参数 | |||
| data: {}, | |||
| upload: { | |||
| // 是否显示弹出层(用户导入) | |||
| open: false, | |||
| // 弹出层标题(用户导入) | |||
| title: "", | |||
| // 是否禁用上传 | |||
| isUploading: false, | |||
| // 是否更新已经存在的用户数据 | |||
| updateSupport: 0, | |||
| // 设置上传的请求头部 | |||
| headers: { Authorization: "Bearer " + getToken() }, | |||
| // 上传的地址 | |||
| url: process.env.VUE_APP_BASE_API + "/${moduleName}/${businessName}/importData", | |||
| // 下载模板接口地址 空不显示按钮 | |||
| DownloadTemplateUrl: "/${moduleName}/${businessName}/importTemplate", | |||
| //更新数据文案 空不显示更新按钮 | |||
| updateText: "是否更新已经存在的${functionName}数据", | |||
| // 导入规则说明 (可为空) | |||
| promptText: "1、模板表头不允许变动!<br>2、仅允许导入“xls”或“xlsx”格式文件!", | |||
| url: process.env.VUE_APP_BASE_API + "/${moduleName}/${businessName}/importData" | |||
| }, | |||
| } | |||
| }, | |||
| created() { | |||
| @@ -597,17 +659,96 @@ export default { | |||
| ...this.queryParams | |||
| }, `${businessName}_#[[${new Date().getTime()}]]#.xlsx`) | |||
| }, | |||
| /** 打印表单 */ | |||
| doPrint() { | |||
| const originalTitle = document.title; | |||
| try { | |||
| document.title = this.title || '通用附件详情'; | |||
| const printElement = document.getElementById('printDetail'); | |||
| const printFrame = document.createElement('iframe'); | |||
| printFrame.style.position = 'absolute'; | |||
| printFrame.style.width = '0'; | |||
| printFrame.style.height = '0'; | |||
| printFrame.style.border = 'none'; | |||
| printFrame.style.left = '-9999px'; | |||
| printFrame.onload = function() { | |||
| try { | |||
| const frameDoc = printFrame.contentDocument || printFrame.contentWindow.document; | |||
| const contentClone = printElement.cloneNode(true); | |||
| const style = document.createElement('style'); | |||
| style.innerHTML = ` | |||
| @page { | |||
| size: auto; | |||
| margin: 10mm; | |||
| } | |||
| body { | |||
| font-family: Arial, sans-serif; | |||
| line-height: 1.5; | |||
| margin: 0; | |||
| padding: 0; | |||
| } | |||
| .el-descriptions { | |||
| width: 100% !important; | |||
| } | |||
| .el-descriptions-item__label { | |||
| width: ${this.descLabelWidth}% !important; | |||
| } | |||
| .el-descriptions-item__content { | |||
| width: ${(100 / this.descColumn) - this.descLabelWidth}% !important; | |||
| } | |||
| /* 确保图片在打印时显示完整 */ | |||
| img, .el-image { | |||
| max-width: 100% !important; | |||
| height: auto !important; | |||
| } | |||
| `; | |||
| frameDoc.head.appendChild(style); | |||
| frameDoc.body.appendChild(contentClone); | |||
| setTimeout(() => { | |||
| printFrame.contentWindow.focus(); | |||
| printFrame.contentWindow.print(); | |||
| setTimeout(() => { | |||
| document.body.removeChild(printFrame); | |||
| document.title = originalTitle; | |||
| }, 1000); | |||
| }, 500); | |||
| } catch (e) { | |||
| document.body.removeChild(printFrame); | |||
| document.title = originalTitle; | |||
| this.$message.error('打印过程中发生错误'); | |||
| } | |||
| }; | |||
| document.body.appendChild(printFrame); | |||
| } catch (e) { | |||
| document.title = originalTitle; | |||
| this.$message.error('打印过程中发生错误'); | |||
| } | |||
| }, | |||
| /** 打印按钮操作 */ | |||
| handlePrint() { | |||
| print${BusinessName}(this.queryParams).then(response => {}) | |||
| }, | |||
| /* 导入EXCEL组件 */ | |||
| improtExcelChildFun(payload) { | |||
| this.$set(this.importExcelData, "visible", payload); | |||
| this.getList(); | |||
| }, | |||
| handleImport() { | |||
| this.$set(this.importExcelData, "visible", true); | |||
| this.upload.title = "${functionName}导入" | |||
| this.upload.open = true | |||
| }, | |||
| /** 下载模板操作 */ | |||
| importTemplate() { | |||
| this.download('${moduleName}/${businessName}/importTemplate', { | |||
| }, `${businessName}_template_${new Date().getTime()}.xlsx`) | |||
| }, | |||
| // 文件上传中处理 | |||
| handleFileUploadProgress(event, file, fileList) { | |||
| this.upload.isUploading = true | |||
| }, | |||
| // 文件上传成功处理 | |||
| handleFileSuccess(response, file, fileList) { | |||
| this.upload.open = false | |||
| this.upload.isUploading = false | |||
| this.$refs.upload.clearFiles() | |||
| this.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true }) | |||
| this.getList() | |||
| }, | |||
| #foreach($column in $columns) | |||
| #if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "fileUpload") | |||
| @@ -615,7 +756,7 @@ export default { | |||
| if(!file) return ''; | |||
| switch(file.toLowerCase().substr(file.lastIndexOf('.') + 1)) { | |||
| case 'jpg': case 'png': case 'jpeg': case 'bmp': case 'gif': | |||
| return this.$store.getters.baseRoutingUrl + file; | |||
| return this.baseRoutingUrll + file; | |||
| case 'doc': case 'docx': | |||
| return require('@/assets/images/icon_word.jpg'); | |||
| case 'xls': case 'xlsx': | |||
| @@ -625,7 +766,7 @@ export default { | |||
| case 'zip': case 'rar': case '7z': case 'bz2': case 'gz': | |||
| return require('@/assets/images/icon_zip.jpg'); | |||
| default: | |||
| return require('@/assets/images/file.png'); | |||
| return require('@/assets/images/icon_rest.jpg'); | |||
| } | |||
| }, | |||
| #break | |||
| @@ -42,6 +42,7 @@ | |||
| <orderEntry type="library" name="Maven: org.springframework.security:spring-security-core:5.7.12" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.security:spring-security-crypto:5.7.12" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.security:spring-security-web:5.7.12" level="project" /> | |||
| <orderEntry type="library" name="Maven: net.coobird:thumbnailator:0.4.8" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.github.pagehelper:pagehelper-spring-boot-starter:1.4.7" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.mybatis.spring.boot:mybatis-spring-boot-starter:2.3.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-jdbc:2.5.15" level="project" /> | |||
| @@ -59,6 +60,7 @@ | |||
| <orderEntry type="library" name="Maven: jakarta.validation:jakarta.validation-api:2.0.2" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.4.3.Final" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.fasterxml:classmate:1.5.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.projectlombok:lombok:1.18.26" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.12.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.12.7.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.12.7" level="project" /> | |||
| @@ -77,6 +79,7 @@ | |||
| <orderEntry type="library" name="Maven: com.github.virtuald:curvesapi:1.06" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.28" level="project" /> | |||
| <orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt:0.9.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: javax.servlet:javax.servlet-api:4.0.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: javax.xml.bind:jaxb-api:2.3.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: javax.activation:javax.activation-api:1.2.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-data-redis:2.5.15" level="project" /> | |||
| @@ -97,6 +100,8 @@ | |||
| <orderEntry type="library" name="Maven: org.reactivestreams:reactive-streams:1.0.4" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.commons:commons-pool2:2.9.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: eu.bitwalker:UserAgentUtils:1.21" level="project" /> | |||
| <orderEntry type="library" name="Maven: javax.servlet:javax.servlet-api:4.0.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.itextpdf:itextpdf:5.5.13.2" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.itextpdf:itext-asian:5.2.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: cn.hutool:hutool-all:5.5.4" level="project" /> | |||
| </component> | |||
| </module> | |||
| @@ -39,6 +39,7 @@ | |||
| <orderEntry type="library" name="Maven: org.springframework.security:spring-security-core:5.7.12" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.security:spring-security-crypto:5.7.12" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.security:spring-security-web:5.7.12" level="project" /> | |||
| <orderEntry type="library" name="Maven: net.coobird:thumbnailator:0.4.8" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.github.pagehelper:pagehelper-spring-boot-starter:1.4.7" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.mybatis.spring.boot:mybatis-spring-boot-starter:2.3.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-jdbc:2.5.15" level="project" /> | |||
| @@ -56,6 +57,7 @@ | |||
| <orderEntry type="library" name="Maven: jakarta.validation:jakarta.validation-api:2.0.2" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.4.3.Final" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.fasterxml:classmate:1.5.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.projectlombok:lombok:1.18.26" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.12.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.12.7.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.12.7" level="project" /> | |||
| @@ -74,6 +76,7 @@ | |||
| <orderEntry type="library" name="Maven: com.github.virtuald:curvesapi:1.06" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.28" level="project" /> | |||
| <orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt:0.9.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: javax.servlet:javax.servlet-api:4.0.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: javax.xml.bind:jaxb-api:2.3.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: javax.activation:javax.activation-api:1.2.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-data-redis:2.5.15" level="project" /> | |||
| @@ -95,6 +98,8 @@ | |||
| <orderEntry type="library" name="Maven: org.reactivestreams:reactive-streams:1.0.4" level="project" /> | |||
| <orderEntry type="library" name="Maven: org.apache.commons:commons-pool2:2.9.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: eu.bitwalker:UserAgentUtils:1.21" level="project" /> | |||
| <orderEntry type="library" name="Maven: javax.servlet:javax.servlet-api:4.0.1" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.itextpdf:itextpdf:5.5.13.2" level="project" /> | |||
| <orderEntry type="library" name="Maven: com.itextpdf:itext-asian:5.2.0" level="project" /> | |||
| <orderEntry type="library" name="Maven: cn.hutool:hutool-all:5.5.4" level="project" /> | |||
| </component> | |||
| </module> | |||