diff --git a/ruoyi-site/pom.xml b/ruoyi-site/pom.xml index d0e964e5b..ad4c3cbd3 100644 --- a/ruoyi-site/pom.xml +++ b/ruoyi-site/pom.xml @@ -12,6 +12,7 @@ ruoyi-sso-server ruoyi-h5 + ruoyi-tpl ruoyi-gateway ruoyi-tams diff --git a/ruoyi-site/ruoyi-tpl/Dockerfile b/ruoyi-site/ruoyi-tpl/Dockerfile new file mode 100644 index 000000000..2f8023f52 --- /dev/null +++ b/ruoyi-site/ruoyi-tpl/Dockerfile @@ -0,0 +1,20 @@ +FROM bellsoft/liberica-openjdk-debian:17.0.11-cds + +LABEL maintainer="lizhw" + +RUN mkdir -p /ruoyi/resource/logs \ + /ruoyi/resource/temp \ + /ruoyi/skywalking/agent + +WORKDIR /ruoyi/resource + +ENV SERVER_PORT=19201 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS="" + +EXPOSE ${SERVER_PORT} + +ADD ./target/ruoyi-tpl.jar ./app.jar + +ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \ + -XX:+HeapDumpOnOutOfMemoryError -XX:+UseZGC ${JAVA_OPTS} \ + -jar app.jar + diff --git a/ruoyi-site/ruoyi-tpl/README.md b/ruoyi-site/ruoyi-tpl/README.md new file mode 100644 index 000000000..5c8e2019b --- /dev/null +++ b/ruoyi-site/ruoyi-tpl/README.md @@ -0,0 +1 @@ +ruoyi-tpl 文件模板打印后台 \ No newline at end of file diff --git a/ruoyi-site/ruoyi-tpl/pom.xml b/ruoyi-site/ruoyi-tpl/pom.xml new file mode 100644 index 000000000..06a9e9349 --- /dev/null +++ b/ruoyi-site/ruoyi-tpl/pom.xml @@ -0,0 +1,90 @@ + + + + org.dromara + ruoyi-site + ${revision} + + 4.0.0 + + ruoyi-tpl + + + ruoyi-tpl 文件模板打印后台 + + + + + + org.dromara + ruoyi-common-core + + + + + org.dromara + ruoyi-common-tenant + + + + + org.dromara + ruoyi-common-mybatis + + + + org.dromara + ruoyi-common-doc + + + + org.dromara + ruoyi-common-log + + + + + com.mysql + mysql-connector-j + + + + org.dromara + ruoyi-common-encrypt + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.apache.poi + poi-ooxml + 4.1.2 + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + + diff --git a/ruoyi-site/ruoyi-tpl/sql/init_ddl.sql b/ruoyi-site/ruoyi-tpl/sql/init_ddl.sql new file mode 100644 index 000000000..e69de29bb diff --git a/ruoyi-site/ruoyi-tpl/sql/init_dml.sql b/ruoyi-site/ruoyi-tpl/sql/init_dml.sql new file mode 100644 index 000000000..e69de29bb diff --git a/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/TplApplication.java b/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/TplApplication.java new file mode 100644 index 000000000..dd6d72ae8 --- /dev/null +++ b/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/TplApplication.java @@ -0,0 +1,13 @@ +package com.lizhw.tpl; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class TplApplication { + + public static void main(String[] args) { + SpringApplication.run(TplApplication.class, args); + } + +} diff --git a/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/config/JacksonConfig.java b/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/config/JacksonConfig.java new file mode 100644 index 000000000..b06c34eaf --- /dev/null +++ b/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/config/JacksonConfig.java @@ -0,0 +1,52 @@ +package com.lizhw.tpl.config; + +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.LinkedHashMap; +import java.util.Map; + +@Configuration +public class JacksonConfig { + + public static final String TIME_FORMATTER = "HH:mm:ss"; + public static final String DATE_FORMATTER = "yyyy-MM-dd"; + public static final String DATETIME_FORMATTER = "yyyy-MM-dd HH:mm:ss"; + public final static Map SERIALIZER_MAP; + public final static Map DESERIALIZER_MAP; + + static { + SERIALIZER_MAP = new LinkedHashMap<>(); + SERIALIZER_MAP.put(Long.class, ToStringSerializer.instance); + SERIALIZER_MAP.put(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(JacksonConfig.TIME_FORMATTER))); + SERIALIZER_MAP.put(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(JacksonConfig.DATE_FORMATTER))); + SERIALIZER_MAP.put(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(JacksonConfig.DATETIME_FORMATTER))); + + DESERIALIZER_MAP = new LinkedHashMap<>(); + DESERIALIZER_MAP.put(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(JacksonConfig.TIME_FORMATTER))); + DESERIALIZER_MAP.put(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(JacksonConfig.DATE_FORMATTER))); + DESERIALIZER_MAP.put(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(JacksonConfig.DATETIME_FORMATTER))); + } + + @Bean + public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() { + return (builder) -> { + SERIALIZER_MAP.forEach(builder::serializerByType); + DESERIALIZER_MAP.forEach(builder::deserializerByType); + }; + } +} diff --git a/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/config/MybatisPlusAutoConfigTenant.java b/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/config/MybatisPlusAutoConfigTenant.java new file mode 100644 index 000000000..4f9207c98 --- /dev/null +++ b/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/config/MybatisPlusAutoConfigTenant.java @@ -0,0 +1,61 @@ +package com.lizhw.tpl.config; + +import cn.hutool.core.collection.ListUtil; +import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler; +import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; +import lombok.extern.slf4j.Slf4j; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.NullValue; +import net.sf.jsqlparser.expression.StringValue; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.config.MybatisPlusConfig; +import org.dromara.common.tenant.helper.TenantHelper; +import org.dromara.common.tenant.properties.TenantProperties; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.List; + +@Configuration +@ConditionalOnBean(MybatisPlusConfig.class) +@AutoConfiguration(after = {MybatisPlusConfig.class}) +@Slf4j +public class MybatisPlusAutoConfigTenant { + + @Bean + public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties tenantProperties) { + return new TenantLineInnerInterceptor(new TenantLineHandler() { + + @Override + public Expression getTenantId() { + String tenantId = TenantHelper.getTenantId(); + if (StringUtils.isBlank(tenantId)) { + log.error("无法获取有效的租户id -> Null"); + return new NullValue(); + } + // 返回固定租户 + return new StringValue(tenantId); + } + + @Override + public boolean ignoreTable(String tableName) { + String tenantId = TenantHelper.getTenantId(); + // 判断是否有租户 + if (StringUtils.isNotBlank(tenantId)) { + // 不需要过滤租户的表 + List excludes = tenantProperties.getExcludes(); + // 非业务表 + List tables = ListUtil.toList( + "gen_table", + "gen_table_column" + ); + tables.addAll(excludes); + return tables.contains(tableName); + } + return true; + } + }); + } +} diff --git a/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/config/WebConfiguration.java b/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/config/WebConfiguration.java new file mode 100644 index 000000000..18f441b3f --- /dev/null +++ b/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/config/WebConfiguration.java @@ -0,0 +1,27 @@ +package com.lizhw.tpl.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpHeaders; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfiguration implements WebMvcConfigurer { + @Bean + public WebMvcConfigurer corsConfigurer() { + return new WebMvcConfigurer() { + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**"). + allowedMethods(CorsConfiguration.ALL). //允许任何方法(post、get等) + allowedHeaders(CorsConfiguration.ALL). //允许任何请求头 + allowCredentials(true). + allowedOriginPatterns(CorsConfiguration.ALL).//带上cookie信息 + exposedHeaders(HttpHeaders.SET_COOKIE).maxAge(3600L); //maxAge(3600)表明在3600秒内,不需要再发送预检验请求,可以缓存该结果 + } + }; + } + +} diff --git a/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/module/TenantRpc.java b/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/module/TenantRpc.java new file mode 100644 index 000000000..808a6f931 --- /dev/null +++ b/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/module/TenantRpc.java @@ -0,0 +1,24 @@ +package com.lizhw.tpl.module; + +//import com.lizhw.tpl.config.FeignInterceptor; +//import org.springframework.cloud.openfeign.FeignClient; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; + +//@FeignClient( +// name = "saas-tenant-admin", // 服务名称 +// configuration = FeignInterceptor.class // 请求拦截器 (关键代码) + +/// / fallbackFactory = SpCfgInterfaceFallback.class // 服务降级处理 +//) +public interface TenantRpc { + + @GetMapping(value = "/auth/code") + Object emailCode(); + + + @GetMapping(value = "/system/client/{id}") + Object getInfo(@PathVariable Long id); + +} diff --git a/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/module/docx/service/ClassroomService.java b/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/module/docx/service/ClassroomService.java new file mode 100644 index 000000000..aa2331901 --- /dev/null +++ b/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/module/docx/service/ClassroomService.java @@ -0,0 +1,23 @@ +//package com.lizhw.tpl.module.classroom.service; +// +//import com.baomidou.mybatisplus.core.metadata.IPage; +//import com.lizhw.tpl.module.classroom.model.dto.ClassroomPageQuery; +//import com.lizhw.tpl.module.classroom.model.dto.ClassroomSaveDTO; +//import com.lizhw.tpl.module.classroom.model.vo.ClassroomListVO; +// +//import java.util.List; +// +//public interface ClassroomService { +// +// IPage pageCourse(ClassroomPageQuery pageQuery); +// +// List refList(); +// +// ClassroomListVO getCourseById(Long id); +// +// boolean saveCourse(ClassroomSaveDTO saveDTO); +// +// boolean updateCourseById(Long id, ClassroomSaveDTO saveDTO); +// +// boolean updateCourseEnableStateById(Long id, Integer enableState); +//} diff --git a/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/module/docx/ttt/Main.java b/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/module/docx/ttt/Main.java new file mode 100644 index 000000000..9e3847a22 --- /dev/null +++ b/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/module/docx/ttt/Main.java @@ -0,0 +1,24 @@ +package com.lizhw.tpl.module.docx.ttt; + +/** + * 基于Apache POI开的快速模板填充生成word,excel文档工具 + * by miracleren@gmail.com + */ + +public class Main { + + public static void main(String[] args) { + System.out.println(" _ _ _ _____ "); + System.out.println("| \\ | (_) | __ \\ "); + System.out.println("| \\| |_ ___ ___| | | | ___ ___ "); + System.out.println("| . ` | |/ __/ _ \\ | | |/ _ \\ / __|"); + System.out.println("| |\\ | | (_| __/ |__| | (_) | (__ "); + System.out.println("|_| \\_|_|\\___\\___|_____/ \\___/ \\___|"); + + //测试示例模板生成word + TestTemplate.buildTestDocx(); + + //测试示例模板生成xlsx + TestTemplate.buildTestXlsx(); + } +} diff --git a/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/module/docx/ttt/NiceDoc.java b/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/module/docx/ttt/NiceDoc.java new file mode 100644 index 000000000..4e330349a --- /dev/null +++ b/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/module/docx/ttt/NiceDoc.java @@ -0,0 +1,554 @@ +package com.lizhw.tpl.module.docx.ttt; + +import org.apache.poi.poifs.crypt.HashAlgorithm; +import org.apache.poi.util.Units; +import org.apache.poi.xwpf.usermodel.*; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.*; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.regex.Matcher; + +/** + * 基于模板快速生成word文档 + * 目前只支持docx文件 + *

+ * by miracleren@gmail.com + */ + +public class NiceDoc { + //private HWPFDocument doc; + private XWPFDocument docx; + private int status = 0; + private final List allTables = new ArrayList<>(); + + /** + * 根据路径初始化word模板 + * + * @param path + */ + public NiceDoc(String path) { + if (!path.endsWith(".docx")) System.out.println("无效文档后缀,当前只支持docx格式word文档模板。"); + + FileInputStream in; + try { + in = new FileInputStream(path); + docx = new XWPFDocument(in); + + //遍历段落生加载表格列表 + this.allTables.addAll(docx.getTables()); + pushLabels(new HashMap<>()); + status = 1; + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (docx == null) docx = new XWPFDocument(); + } + } + + /** + * 往模板填充标签值 + * {{labelName}} + * + * @param labels 标签值 + * @return + */ + public void pushLabels(Map labels) { + //遍历普通段落内容对像,填充标签值 + List paragraphs = docx.getParagraphs(); + replaceLabelsInParagraphs(paragraphs, labels); + + //遍历表格内容,并填充标签值 + List tables = status == 0 ? docx.getTables() : this.allTables; + for (XWPFTable table : tables) { + //表格行 + List rows = table.getRows(); + for (XWPFTableRow row : rows) { + //表格单元格 + List cells = row.getTableCells(); + for (XWPFTableCell cell : cells) { + //表格段落 + List cellParagraphs = cell.getParagraphs(); + replaceLabelsInParagraphs(cellParagraphs, labels); + } + } + } + + //页眉标签值填充 + List headers = docx.getHeaderList(); + for (XWPFHeader header : headers) { + List headerParagraphs = header.getListParagraph(); + replaceLabelsInParagraphs(headerParagraphs, labels); + } + + //页脚填充 + List footers = docx.getFooterList(); + for (XWPFFooter footer : footers) { + List footerParagraphs = footer.getListParagraph(); + replaceLabelsInParagraphs(footerParagraphs, labels); + } + } + + /** + * 往模板填充标签值实体类 + * + * @param entity + */ + public void pushLabels(Object entity) { + pushLabels(NiceUtils.entityToMap(entity)); + } + + + /** + * 填充表格内容到文档 + * {{tableName:colName}} + * + * @param tableName + * @param list + */ + public void pushTable(String tableName, List> list) { + List tables = this.allTables; + for (XWPFTable table : tables) { + boolean isFind = false; + XWPFTableRow baseRow = null; + + List rows = table.getRows(); + int rowCount = rows.size(); + for (int i = 0; i < rowCount; i++) { + List cells = rows.get(i).getTableCells(); + for (XWPFTableCell cell : cells) { + List cellParagraphs = cell.getParagraphs(); + for (XWPFParagraph cellParagraph : cellParagraphs) { + //查找表格标识名称 + if (!isFind) { + if (cellParagraph.getText().contains(NiceUtils.labelFormat("table#" + tableName))) { + isFind = true; + } else { + isFind = false; + break; + } + } + + //记录开始数据行 + if (cellParagraph.getText().contains("{{col#")) { + baseRow = rows.get(i); + break; + } + } + if (!isFind) break; + } + if (!isFind) break; + + //已知数据行,开始填充数据 + if (baseRow != null) { + int addRowIndex = 1; + for (Map listRow : list) { + CTRow ctRow = table.getCTTbl().insertNewTr(i + addRowIndex); + XWPFTableRow newRow = new XWPFTableRow(ctRow, table); + copyRowAndPushLabels(newRow, baseRow, listRow); + //table.addRow(newRow, i + addRowIndex); + addRowIndex++; + } + + //baseRow = null; + table.removeRow(i); + break; + } + } + //删除table标识行 + if (isFind) table.removeRow(0); + } + } + + /** + * 拷贝行,并填充相关值 + * + * @param newRow + * @param baseRow + * @param params + */ + private void copyRowAndPushLabels(XWPFTableRow newRow, XWPFTableRow baseRow, Map params) { + newRow.getCtRow().setTrPr(baseRow.getCtRow().getTrPr()); + for (XWPFTableCell cell : baseRow.getTableCells()) { + XWPFTableCell newCell = newRow.addNewTableCell(); + newCell.getCTTc().setTcPr(cell.getCTTc().getTcPr()); + boolean isFirst = true; + //newCell.setParagraph(cell.getParagraphs().get(0)); + for (XWPFParagraph paragraph : cell.getParagraphs()) { + XWPFParagraph newParagraph = isFirst ? newCell.getParagraphs().get(0) : newCell.addParagraph(); + isFirst = false; + newParagraph.getCTP().setPPr(paragraph.getCTP().getPPr()); + for (XWPFRun run : paragraph.getRuns()) { + XWPFRun newRun = newParagraph.createRun(); + newRun.getCTR().setRPr(run.getCTR().getRPr()); + + String text = run.getText(0); + if (text == null) continue; + else newRun.setText(text); + + Matcher labels = NiceUtils.getMatchingLabels(text); + while (labels.find()) { + String label = labels.group(); + String[] key = label.split("#"); + if (params.containsKey(key[key.length - 1])) { + newRun.setText(text.replace(NiceUtils.labelFormat(label), params.get(key[key.length - 1]).toString()), 0); + } + } + } + } + + } + } + + /** + * 段落列表填充标签 + * + * @param paragraphs + * @param params + */ + private void replaceLabelsInParagraphs(List paragraphs, Map params) { + for (int i = 0; i < paragraphs.size(); i++) { + XWPFParagraph paragraph = paragraphs.get(i); + + //获取doc表格,包括子表格,docx.getTables()无法获取子表格 + if (status == 0) { + if (!this.allTables.containsAll(paragraph.getBody().getTables())) + this.allTables.addAll(paragraph.getBody().getTables()); + return; + } + + String text = paragraph.getText(); + if (text == null || text.equals("") || !text.contains("{{")) continue; + else if (text.contains("{{v-")) logicLabelsInParagraph(paragraphs, i, params); + replaceLabelsInParagraph(paragraph, params); + } + } + + /** + * 清空标签被分割的其它文本 + * + * @param runs + */ + private void removeRun(List runs) { + //runs.remove(runs.size() - 1); + //for (XWPFRun run : runs) { + // run.setText("", 0); + //} + for (int i = 0; i < runs.size() - 1; i++) { + runs.get(i).setText("", 0); + } + } + + /** + * 逻辑语句处理 + */ + private void logicLabelsInParagraph(List paragraphs, Integer index, Map params) { + String nowText = ""; + int runCount = 0; + List labelRuns = new ArrayList<>(); + Boolean isShow = true; + + for (int i = index; i < paragraphs.size(); i++) { + XWPFParagraph paragraph = paragraphs.get(i); + List runs = paragraph.getRuns(); + + for (XWPFRun run : runs) { + //System.out.println(run.toString()); + if (run.getText(0) != null && (run.getText(0).contains("{{") || runCount > 0)) { + nowText += run.getText(0); + runCount++; + labelRuns.add(run); + + Matcher labels = NiceUtils.getMatchingLabels(nowText); + int labelFindCount = 0; + while (labels.find()) { + labelFindCount++; + String label = labels.group(); + //System.out.println(label); + + String[] key = label.split("#"); + + if (key.length == 2) { + Integer indexName = key[1].indexOf("=") + 1 + key[1].indexOf("&") + 1; + String keyName = indexName > 0 ? key[1].substring(0, indexName - 1) : key[1]; + if (params.containsKey(keyName)) { + String val = params.get(keyName) == null ? "" : params.get(keyName).toString(); + //条件判断语句 + if (key[0].equals("v-if")) { + if (key[1].contains("=")) { + isShow = val.equals(key[1].substring(indexName)); + } else if (key[1].contains("&")) { + Integer curVal = Integer.valueOf(key[1].substring(indexName)); + isShow = (Integer.valueOf(val) & curVal) == curVal; + } else { + isShow = val.equals("true"); + } + + if (isShow == false) { + if (nowText.indexOf("{{end-if}}") > nowText.indexOf(NiceUtils.labelFormat(label))) + nowText = nowText.replace(nowText.substring(nowText.indexOf(NiceUtils.labelFormat(label)), nowText.indexOf("{{end-if}}")), ""); + else + nowText = nowText.replace(nowText.substring(nowText.indexOf(NiceUtils.labelFormat(label))), ""); + } else nowText = nowText.replace(NiceUtils.labelFormat(label), ""); + + run.setText(nowText, 0); + removeRun(labelRuns); + } + } + } else if (label.equals("end-if")) { + run.setText(nowText.replace(NiceUtils.labelFormat(label), ""), 0); + removeRun(labelRuns); + isShow = true; + } + } + if (labelFindCount > 0) { + nowText = ""; + runCount = 0; + labelRuns = new ArrayList<>(); + } + } + + if (isShow != true) { + run.setText("", 0); + } + + } + } + } + + /** + * 段落填充标签 + * + * @param paragraph + * @param params + */ + private void replaceLabelsInParagraph(XWPFParagraph paragraph, Map params) { + //遍历文本对象,查找标识标签 + List runs = paragraph.getRuns(); + String nowText = ""; + int runCount = 0; + List labelRuns = new ArrayList<>(); + + //常规标签 + for (XWPFRun run : runs) { + //防止文本对象标签被分割 + if (run.getText(0) != null && (run.getText(0).contains("{{") || runCount > 0)) { + nowText += run.getText(0); + runCount++; + labelRuns.add(run); + + //System.out.println(nowText); + Matcher labels = NiceUtils.getMatchingLabels(nowText); + int labelFindCount = 0; + while (labels.find()) { + labelFindCount++; + String label = labels.group(); + + String[] key = label.split("#"); + Integer indexName = key[0].indexOf("=") + 1 + key[0].indexOf("&") + 1; + String keyName = indexName > 0 ? key[0].substring(0, indexName - 1) : key[0]; + //标签书签 + if (params.containsKey(keyName)) { + //普通文本标签 + Object val = params.get(keyName) == null ? "" : params.get(keyName); + if (key.length == 1) { + nowText = nowText.replace(NiceUtils.labelFormat(label), val.toString()); + run.setText(nowText, 0); + continue; + } + + if (key.length == 2) { + //日期类型填充 + if (key[1].startsWith("Date:")) { + String textVal = val.equals("") ? val.toString() : new SimpleDateFormat(key[1].replace("Date:", "")).format(val); + nowText = nowText.replace(NiceUtils.labelFormat(label), textVal); + run.setText(nowText, 0); + continue; + } + + //枚举数组标签 + if (key[1].startsWith("[") && key[1].endsWith("]")) { + String group = key[1].substring(1, key[1].length() - 1); + for (String keyVal : group.split(",")) { + if (keyVal.indexOf(val + ":") == 0) { + nowText = nowText.replace(NiceUtils.labelFormat(label), keyVal.replace(val + ":", "")); + run.setText(nowText, 0); + removeRun(labelRuns); + } + } + continue; + } + + //值判定类型标签 + String[] bool = key[1].split(":"); + String trueVal = bool[0]; + String falseVal = bool.length == 1 ? "" : bool[1]; + if (bool.length >= 1) { + String textVal = ""; + if (key[0].contains("=")) { + textVal = val.toString().equals(key[0].substring(indexName)) ? trueVal : falseVal; + } else if (key[0].contains("&")) { + Integer curVal = Integer.valueOf(key[0].substring(indexName)); + textVal = (Integer.valueOf(val.toString()) & curVal) == curVal ? trueVal : falseVal; + } else { + textVal = val.toString().equals("true") ? trueVal : falseVal; + } + nowText = nowText.replace(NiceUtils.labelFormat(label), textVal); + run.setText(nowText, 0); + removeRun(labelRuns); + continue; + } + + } + } else if (key[0].equals("v-image")) { + //图片标签处理 + + //获取图片相关信息 + String[] val = key[1].split(","); + int scale = 100; + String[] sizes = {}; + String picName = ""; + for (String valKey : val) { + if (valKey.startsWith("path:")) + picName = valKey.replace("path:", ""); + if (valKey.startsWith("scale:")) + scale = Integer.valueOf(valKey.replace("scale:", "")); + if (valKey.startsWith("size:")) + sizes = valKey.replace("size:", "").split("\\*"); + } + + if (params.containsKey(picName)) { + run.setText("", 0); + removeRun(labelRuns); + + String path = String.valueOf(params.get(picName)); + if (path != "" && path != null) { + try { + int width, height; + //计算高度宽度 + if (sizes.length == 2) { + width = Units.toEMU(Double.valueOf(sizes[0])); + height = Units.toEMU(Double.valueOf(sizes[1])); + } else { + File picFile = new File(path); + BufferedImage read = ImageIO.read(picFile); + width = Units.toEMU(read.getWidth() * scale / 100); + height = Units.toEMU(read.getHeight() * scale / 100); + } + + //插入图片 + InputStream stream = new FileInputStream(path); + run.addPicture(stream, XWPFDocumentPicType(path), picName, width, height); + stream.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + } + } + + if (labelFindCount > 0) { + nowText = ""; + runCount = 0; + labelRuns = new ArrayList<>(); + } + } + + } + } + + public int XWPFDocumentPicType(String path) { + if (path.endsWith(".emf")) { + return XWPFDocument.PICTURE_TYPE_EMF; + } else if (path.endsWith(".wmf")) { + return XWPFDocument.PICTURE_TYPE_WMF; + } else if (path.endsWith(".pict")) { + return XWPFDocument.PICTURE_TYPE_PICT; + } else if (path.endsWith(".jpeg") || path.endsWith(".jpg")) { + return XWPFDocument.PICTURE_TYPE_JPEG; + } else if (path.endsWith(".png")) { + return XWPFDocument.PICTURE_TYPE_PNG; + } else if (path.endsWith(".dib")) { + return XWPFDocument.PICTURE_TYPE_DIB; + } else if (path.endsWith(".gif")) { + return XWPFDocument.PICTURE_TYPE_GIF; + } else if (path.endsWith(".tiff")) { + return XWPFDocument.PICTURE_TYPE_TIFF; + } else if (path.endsWith(".eps")) { + return XWPFDocument.PICTURE_TYPE_EPS; + } else if (path.endsWith(".bmp")) { + return XWPFDocument.PICTURE_TYPE_BMP; + } else if (path.endsWith(".wpg")) { + return XWPFDocument.PICTURE_TYPE_WPG; + } + return 0; + } + + /** + * 清除条件语句产生的空段落 + */ + public void removeNullParagraphs() { + List paragraphs = docx.getParagraphs(); + List listBe = docx.getBodyElements(); + + for (int i = 0; i < listBe.size(); i++) { + if (listBe.get(i).getElementType() == BodyElementType.PARAGRAPH) { + if (paragraphs.get(docx.getParagraphPos(i)).getText().contains("R")) { + docx.removeBodyElement(i); + i--; + continue; + } + } + + } + } + + /** + * 段落条件标签处理 + * + * @param paragraph + * @param params + */ + private void syntaxLabelsInParagraph(XWPFParagraph paragraph, Map params) { + + } + + /** + * 设置word只读 + * + * @param pass + */ + public void setReadOnly(String pass) { + docx.enforceFillingFormsProtection(pass, HashAlgorithm.sha512); + } + + /** + * 保存word文件到目录下 + * + * @param path + * @param name + */ + public void save(String path, String name) { + try { + //removeNullParagraphs(); + FileOutputStream outStream = new FileOutputStream(path + name); + docx.write(outStream); + outStream.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 获取docx实体 + */ + public XWPFDocument getDocx() { + return this.docx; + } + +} diff --git a/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/module/docx/ttt/NiceExcel.java b/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/module/docx/ttt/NiceExcel.java new file mode 100644 index 000000000..646d177a0 --- /dev/null +++ b/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/module/docx/ttt/NiceExcel.java @@ -0,0 +1,325 @@ +package com.lizhw.tpl.module.docx.ttt; + +import org.apache.poi.hssf.usermodel.HSSFSheet; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.poifs.crypt.HashAlgorithm; +import org.apache.poi.ss.usermodel.ClientAnchor; +import org.apache.poi.ss.usermodel.CreationHelper; +import org.apache.poi.ss.usermodel.Drawing; +import org.apache.poi.util.StringUtil; +import org.apache.poi.util.Units; +import org.apache.poi.xssf.usermodel.XSSFCell; +import org.apache.poi.xssf.usermodel.XSSFRow; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.apache.poi.xwpf.usermodel.XWPFTable; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.text.SimpleDateFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; + +/** + * 基于模板快速生成word文档 + * 目前只支持xlsx文件 + *

+ * by miracleren@gmail.com + */ + +public class NiceExcel { + private XSSFWorkbook xlsx; + + /** + * 根据路径初始化word模板 + * + * @param path + */ + public NiceExcel(String path) { + if (!path.endsWith(".xlsx")) System.out.println("无效文档后缀,当前只支持xlsx格式Excel文档模板。"); + + FileInputStream in; + try { + in = new FileInputStream(path); + xlsx = new XSSFWorkbook(in); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (xlsx == null) xlsx = new XSSFWorkbook(); + } + } + + /** + * 往模板填充标签值 + * {{labelName}} + * + * @param labels 标签值 + */ + public void pushLabels(Map labels) { + //遍历excel所有sheet + for (int i = 0; i < xlsx.getNumberOfSheets(); i++) { + XSSFSheet sheet = xlsx.getSheetAt(i); + //表格遍历行 + for (int rowNum = 0; rowNum <= sheet.getLastRowNum(); rowNum++) { + XSSFRow row = sheet.getRow(rowNum); + if (row == null) { + continue; + } + for (int cellNum = 0; cellNum <= row.getLastCellNum(); cellNum++) { + XSSFCell cell = row.getCell(cellNum); + if (cell == null) { + continue; + } + replaceLabelsInCell(cell, labels); + } + } + } + } + + /** + * 段落填充标签 + * + * @param cell + * @param params + */ + private void replaceLabelsInCell(XSSFCell cell, Map params) { + String cellValue = cell.toString(); + if (cellValue.isEmpty() || cellValue.contains("col#")) + return; + + Matcher labels = NiceUtils.getMatchingLabels(cellValue); + while (labels.find()) { + String label = labels.group(); + + String[] key = label.split("#"); + Integer indexName = key[0].indexOf("=") + 1 + key[0].indexOf("&") + 1; + String keyName = indexName > 0 ? key[0].substring(0, indexName - 1) : key[0]; + //标签书签 + if (params.containsKey(keyName)) { + //普通文本标签 + Object val = params.get(keyName) == null ? "" : params.get(keyName); + if (key.length == 1) { + cellValue = cellValue.replace(NiceUtils.labelFormat(label), val.toString()); + cell.setCellValue(cellValue); + continue; + } + + if (key.length == 2) { + //日期类型填充 + if (key[1].startsWith("Date:")) { + String textVal = val.equals("") ? val.toString() : new SimpleDateFormat(key[1].replace("Date:", "")).format(val); + cellValue = cellValue.replace(NiceUtils.labelFormat(label), textVal); + cell.setCellValue(cellValue); + continue; + } + + //枚举数组标签 + if (key[1].startsWith("[") && key[1].endsWith("]")) { + String group = key[1].substring(1, key[1].length() - 1); + for (String keyVal : group.split(",")) { + if (keyVal.indexOf(val + ":") == 0) { + cellValue = cellValue.replace(NiceUtils.labelFormat(label), keyVal.replace(val + ":", "")); + cell.setCellValue(cellValue); + } + } + continue; + } + + //值判定类型标签 + String[] bool = key[1].split(":"); + String trueVal = bool[0]; + String falseVal = bool.length == 1 ? "" : bool[1]; + if (bool.length >= 1) { + String textVal = ""; + if (key[0].contains("=")) { + textVal = val.toString().equals(key[0].substring(indexName)) ? trueVal : falseVal; + } else if (key[0].contains("&")) { + Integer curVal = Integer.valueOf(key[0].substring(indexName)); + textVal = (Integer.valueOf(val.toString()) & curVal) == curVal ? trueVal : falseVal; + } else { + textVal = val.toString().equals("true") ? trueVal : falseVal; + } + cellValue = cellValue.replace(NiceUtils.labelFormat(label), textVal); + cell.setCellValue(cellValue); + } + + } + } else if (keyName.equals("v-if")) { + logicLabelsInParagraph(cell, params); + } + } + + } + + + /** + * 逻辑语句处理,同一cell内有效 + */ + private void logicLabelsInParagraph(XSSFCell cell, Map params) { + String cellValue = cell.toString(); + + Boolean isShow = true; + Matcher labels = NiceUtils.getMatchingLabels(cellValue); + while (labels.find()) { + String label = labels.group(); + String[] key = label.split("#"); + + if (key.length == 2) { + Integer indexName = key[1].indexOf("=") + 1 + key[1].indexOf("&") + 1; + String keyName = indexName > 0 ? key[1].substring(0, indexName - 1) : key[1]; + if (params.containsKey(keyName)) { + String val = params.get(keyName) == null ? "" : params.get(keyName).toString(); + //条件判断语句 + if (key[0].equals("v-if")) { + if (key[1].contains("=")) { + isShow = val.equals(key[1].substring(indexName)); + } else if (key[1].contains("&")) { + Integer curVal = Integer.valueOf(key[1].substring(indexName)); + isShow = (Integer.valueOf(val) & curVal) == curVal; + } else { + isShow = val.equals("true"); + } + + if (isShow == false) { + if (cellValue.indexOf("{{end-if}}") > cellValue.indexOf(NiceUtils.labelFormat(label))) + cellValue = cellValue.replace(cellValue.substring(cellValue.indexOf(NiceUtils.labelFormat(label)), cellValue.indexOf("{{end-if}}")), ""); + else + cellValue = cellValue.replace(cellValue.substring(cellValue.indexOf(NiceUtils.labelFormat(label))), ""); + } else cellValue = cellValue.replace(NiceUtils.labelFormat(label), ""); + + cell.setCellValue(cellValue); + } + } + } else if (label.equals("end-if")) { + cell.setCellValue(cellValue.replace(NiceUtils.labelFormat(label), "")); + } + } + } + + /** + * 填充表格内容到excel + * {{tableName:colName}} + * + * @param tableName + * @param list + */ + public void pushTable(String tableName, List> list) { + //遍历excel所有sheet + for (int i = 0; i < xlsx.getNumberOfSheets(); i++) { + XSSFSheet sheet = xlsx.getSheetAt(i); + //表格遍历行 + for (int rowNum = 0; rowNum <= sheet.getLastRowNum(); rowNum++) { + XSSFRow row = sheet.getRow(rowNum); + if (row == null) { + continue; + } + for (int cellNum = 0; cellNum <= row.getLastCellNum(); cellNum++) { + XSSFCell cell = row.getCell(cellNum); + if (cell != null && cell.toString().contains(tableName + "/col#")) { + //System.out.println("find the table by name :" + tableName); + + // 插入数据空白行,数据往后移 + sheet.shiftRows(rowNum + 1, sheet.getLastRowNum(), list.size() - 1); + + //插入表格数据 + int addNum = 0; + for (Map rowData : list) { + //拷贝当前行 + XSSFRow setRow = sheet.getRow(rowNum + addNum); + if (list.size() > addNum + 1) { + XSSFRow newRow = sheet.createRow(rowNum + addNum + 1); + copyRow(setRow, newRow); + } + + //填充当前行内容数据 + for (int setCellNum = 0; setCellNum <= row.getLastCellNum(); setCellNum++) { + XSSFCell setCell = setRow.getCell(setCellNum); + if (setCell != null) { + String text = setCell.toString(); + Matcher labels = NiceUtils.getMatchingLabels(text); + while (labels.find()) { + String label = labels.group(); + String[] key = label.split("#"); + if (rowData.containsKey(key[key.length - 1])) { + String val = text.replace(NiceUtils.labelFormat(label), rowData.get(key[key.length - 1]).toString()); + if (NiceUtils.isNumber(rowData.get(key[key.length - 1]))) + setCell.setCellValue(Double.parseDouble(val)); + else + setCell.setCellValue(val); + } + } + } + } + addNum++; + } + return; + } + } + } + } + } + + /** + * 拷贝行数据 + * + * @param currentRow + * @param newRow + */ + private static void copyRow(XSSFRow currentRow, XSSFRow newRow) { + newRow.setHeight(currentRow.getHeight()); + for (int i = 0; i < currentRow.getLastCellNum(); i++) { + XSSFCell oldCell = currentRow.getCell(i); + XSSFCell newCell = newRow.createCell(i); + if (oldCell != null) { + // 复制样式和值 + newCell.setCellStyle(oldCell.getCellStyle()); + switch (oldCell.getCellType()) { + case STRING: + newCell.setCellValue(oldCell.getStringCellValue()); + break; + case NUMERIC: + newCell.setCellValue(oldCell.getNumericCellValue()); + break; + case BOOLEAN: + newCell.setCellValue(oldCell.getBooleanCellValue()); + break; + // ...其他类型 + default: + newCell.setCellType(oldCell.getCellType()); + } + } + } + } + + /** + * 保存excel文件到目录下 + * + * @param path + * @param name + */ + public void save(String path, String name) { + try { + FileOutputStream outStream = new FileOutputStream(path + name); + xlsx.write(outStream); + outStream.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 设置excel只读 + * + * @param pass + */ + public void setReadOnly(String pass) { + xlsx.setWorkbookPassword(pass, HashAlgorithm.sha512); + } + +} diff --git a/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/module/docx/ttt/NiceUtils.java b/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/module/docx/ttt/NiceUtils.java new file mode 100644 index 000000000..2190a24b1 --- /dev/null +++ b/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/module/docx/ttt/NiceUtils.java @@ -0,0 +1,197 @@ +package com.lizhw.tpl.module.docx.ttt; + +import org.apache.poi.util.StringUtil; + +import java.lang.reflect.Field; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 通用方法 + *

+ * by miracleren@gmail.com + */ +public class NiceUtils { + + + /** + * {{par}} 参数查找正则 + * + * @param str 查找串 + * @return 返结果 + */ + public static Matcher getMatchingLabels(String str) { + Pattern pattern = Pattern.compile("(?<=\\{\\{)(.+?)(?=\\}\\})", Pattern.CASE_INSENSITIVE); + Matcher matcher = pattern.matcher(str); + return matcher; + } + + /** + * 补全label格式 + * + * @param label + * @return + */ + public static String labelFormat(String label) { + return "{{" + label + "}}"; + } + + /** + * 实体类转map + * + * @param entity + * @return + */ + public static Map entityToMap(Object entity) { + Map map = new HashMap<>(); + for (Field field : entity.getClass().getDeclaredFields()) { + try { + boolean flag = field.isAccessible(); + field.setAccessible(true); + Object o = field.get(entity); + map.put(field.getName(), o); + field.setAccessible(flag); + } catch (Exception e) { + e.printStackTrace(); + } + } + return map; + } + + /** + * 实体类列表转map列表 + * + * @param entityList + * @return + */ + public static List> listEntityToMap(List entityList) { + List> list = new ArrayList<>(); + for (Object entity : entityList) { + list.add(entityToMap(entity)); + } + return list; + } + + /** + * 转sting方法 + * + * @param object + * @return + */ + public static String toString(Object object) { + return object == null ? "" : object.toString(); + } + + + /** + * 判断对象是否是数值 + * + * @param object + * @return + */ + public static boolean isNumber(Object object) { + return object instanceof Number; + } + + + /** + * 遍历查找内容 + * + * @param map + * @param value + * @return + */ + public static Integer findInMapByValue(Map map, String value) { + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue().equals(value)) { + return entry.getKey(); // 返回找到值的键 + } + } + return null; // 如果未找到,返回null + } + + + /** + * 将字符串日期转换为Date类型,支持多种日期格式。 + * + * @param dateString 日期字符串 + * @param formats 可能的日期格式数组 + * @return 解析成功返回Date对象,解析失败抛出异常 + * @throws Exception 如果无法解析日期字符串 + */ + public static Date parseDate(String dateString, String[] formats) throws Exception { + for (String format : formats) { + SimpleDateFormat formatter = new SimpleDateFormat(format); + formatter.setTimeZone(TimeZone.getTimeZone("UTC")); // 设置时区,根据需要调整 + try { + return formatter.parse(dateString); + } catch (Exception e) { + // 当前格式不匹配,尝试下一个格式 + } + } + // 所有格式都不匹配,抛出异常 + throw new Exception("无法解析日期: " + dateString); + } + + /** + * 获取两个符号之间的内容 + * + * @param text 源字符串 + * @param start 开始符号 + * @param end 结束符号 + * @return 两个符号之间的内容 + */ + public static String getContentBetweenSymbols(String text, String start, String end) { + String regex = Pattern.quote(start) + "([\\s\\S]*?)" + Pattern.quote(end); + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(text); + + if (matcher.find()) { + return matcher.group(1); + } + return null; + } + + /** + * 是否数值 + * + * @param str + * @return + */ + public static boolean isNumeric(String str) { + try { + Integer.parseInt(str); + return true; + } catch (NumberFormatException e) { + try { + Double.parseDouble(str); + return true; + } catch (NumberFormatException e1) { + return false; + } + } + } + + /** + * 将Date对象转换为时间戳(毫秒数) + * + * @param date Date对象 + * @return 时间戳(毫秒数) + */ + public static long convertToTimeStamp(Date date) { + return date.getTime(); + } + + + /** + * 将时间戳对象转换为Date(毫秒数) + * + * @param date Date对象 + * @return 时间戳(毫秒数) + */ + public static Date convertTimeStampToDate(long date) { + return new Date(date); + } +} diff --git a/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/module/docx/ttt/TestTemplate.java b/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/module/docx/ttt/TestTemplate.java new file mode 100644 index 000000000..265828a82 --- /dev/null +++ b/ruoyi-site/ruoyi-tpl/src/main/java/com/lizhw/tpl/module/docx/ttt/TestTemplate.java @@ -0,0 +1,107 @@ +package com.lizhw.tpl.module.docx.ttt; + +import java.net.URLDecoder; +import java.util.*; + +/** + * @author: lee + * @email:miracleren@gmail.com + * @date:2023/6/5 + */ +public class TestTemplate { + static String path = Main.class.getClassLoader().getResource("static").getPath() + "/"; + + /** + * 测试示例模板生成word + */ + public static void buildTestDocx() { + //测试示例模板生成word + NiceDoc docx = new NiceDoc(path + "test.docx"); + + Map labels = new HashMap<>(); + //值标签 + labels.put("startTime", "1881年9月25日"); + labels.put("endTime", "1936年10月19日"); + labels.put("title", "精选作品目录"); + labels.put("press", "鲁迅同学出版社"); + + //枚举标签 + labels.put("likeBook", 2); + //布尔标签 + labels.put("isQ", true); + //等于 + labels.put("isNew", 2); + //多选二进制值 + labels.put("look", 3); + //if语句 + labels.put("showContent", 2); + //日期格式标签 + labels.put("printDate", new Date()); + + labels.put("fileReceiveBy", "陈先生"); + labels.put("fileRelation", 2); + labels.put("fileDate", new Date()); + + //添加头像 + labels.put("headImg", path + "head.png"); + + docx.pushLabels(labels); + + //表格 + List> books = new ArrayList<>(); + Map book1 = new HashMap<>(); + book1.put("name", "汉文学史纲要"); + book1.put("time", "1938年,鲁迅全集出版社"); + books.add(book1); + Map book2 = new HashMap<>(); + book2.put("name", "中国小说史略"); + book2.put("time", "1923年12月,上册;1924年6月,下册"); + books.add(book2); + docx.pushTable("books", books); + + + //生成文档 + docx.save(path, UUID.randomUUID() + ".docx"); + } + + /** + * 测试示例模板生成xlsx + */ + public static void buildTestXlsx() { + //测试示例模板生成word + NiceExcel excel = new NiceExcel(path + "test.xlsx"); + + Map labels = new HashMap<>(); + //值标签 + labels.put("date", "2023年1月1日"); + labels.put("title", "精选作品统计"); + //枚举标签 + labels.put("likeBook", 2); + //多选二进制值 + labels.put("lookType", 3); + //if语句 + labels.put("showBanner", 1); + //日期格式标签 + labels.put("printDate", new Date()); + + excel.pushLabels(labels); + + + //表格 + List> books = new ArrayList<>(); + for (int i = 0; i <= 10; i++) { + Map book = new HashMap<>(); + book.put("name", "汉文学史纲要" + i); + book.put("time", 1900 + i + "年"); + book.put("intro", "简明扼要的介绍,本书是一本好书,推荐" + i + "星"); + book.put("byName", "作者" + i + "号"); + book.put("pages", i * 100); + books.add(book); + } + excel.pushTable("books", books); + + + //生成文档 + excel.save(path, UUID.randomUUID() + ".xlsx"); + } +} diff --git a/ruoyi-site/ruoyi-tpl/src/main/resources/application-dev.yaml b/ruoyi-site/ruoyi-tpl/src/main/resources/application-dev.yaml new file mode 100644 index 000000000..1bc770999 --- /dev/null +++ b/ruoyi-site/ruoyi-tpl/src/main/resources/application-dev.yaml @@ -0,0 +1,40 @@ +spring: + datasource: + url: jdbc:mysql://127.0.0.1:3306/h5?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false + username: root + password: root + druid: + initial-size: 5 #连接池初始化大小 + min-idle: 10 #最小空闲连接数 + max-active: 20 #最大连接数 + web-stat-filter: + exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*" #不统计这些请求数据 + stat-view-servlet: #访问监控网页的登录用户名和密码 + login-username: druid + login-password: druid + data: + redis: + # Redis数据库索引(默认为0) + database: 0 + # Redis服务器地址 + host: 127.0.0.1 + # Redis服务器连接端口 + port: 6379 + # Redis服务器连接密码(默认为空) + password: difyai123456 + # 连接超时时间 + timeout: 10s + lettuce: + pool: + # 连接池最大连接数 + max-active: 200 + # 连接池最大阻塞等待时间(使用负值表示没有限制) + max-wait: -1ms + # 连接池中的最大空闲连接 + max-idle: 10 + # 连接池中的最小空闲连接 + min-idle: 0 + + jackson: + time-zone: GMT+8 + diff --git a/ruoyi-site/ruoyi-tpl/src/main/resources/application-local.yaml b/ruoyi-site/ruoyi-tpl/src/main/resources/application-local.yaml new file mode 100644 index 000000000..6ca3cbea6 --- /dev/null +++ b/ruoyi-site/ruoyi-tpl/src/main/resources/application-local.yaml @@ -0,0 +1,40 @@ +spring: + datasource: + url: jdbc:mysql://127.0.0.1:3306/mall-tams-core?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false + username: root + password: root + druid: + initial-size: 5 #连接池初始化大小 + min-idle: 10 #最小空闲连接数 + max-active: 20 #最大连接数 + web-stat-filter: + exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*" #不统计这些请求数据 + stat-view-servlet: #访问监控网页的登录用户名和密码 + login-username: druid + login-password: druid + data: + redis: + # Redis数据库索引(默认为0) + database: 0 + # Redis服务器地址 + host: 127.0.0.1 + # Redis服务器连接端口 + port: 6379 + # Redis服务器连接密码(默认为空) + password: difyai123456 + # 连接超时时间 + timeout: 10s + lettuce: + pool: + # 连接池最大连接数 + max-active: 200 + # 连接池最大阻塞等待时间(使用负值表示没有限制) + max-wait: -1ms + # 连接池中的最大空闲连接 + max-idle: 10 + # 连接池中的最小空闲连接 + min-idle: 0 + + jackson: + time-zone: GMT+8 + diff --git a/ruoyi-site/ruoyi-tpl/src/main/resources/application.yaml b/ruoyi-site/ruoyi-tpl/src/main/resources/application.yaml new file mode 100644 index 000000000..5fdb2d536 --- /dev/null +++ b/ruoyi-site/ruoyi-tpl/src/main/resources/application.yaml @@ -0,0 +1,78 @@ +spring: + profiles: + active: ${profiles.active} + application: + name: ruoyi-tpl + +# 开发环境配置 +server: + # 服务器的HTTP端口,默认为19100 + port: 19202 + +# MyBatisPlus配置 +mybatis-plus: + # 多包名使用 例如 org.dromara.**.mapper,org.xxx.**.mapper + mapperPackage: com.lizhw.tpl.module.**.dao + global-config: + dbConfig: + # 主键类型 + # AUTO 自增 NONE 空 INPUT 用户输入 ASSIGN_ID 雪花 ASSIGN_UUID 唯一 UUID + # 如需改为自增 需要将数据库表全部设置为自增 + idType: ASSIGN_ID + +# 多租户配置 +tenant: + # 是否开启 + enable: false + # 排除表 + excludes: + - sys_menu + + + +springdoc: + api-docs: + # 是否开启接口文档 + enabled: true + # swagger-ui: + # # 持久化认证数据 + # persistAuthorization: true + info: + # 标题 + title: '标题:${spring.application.name}多租户管理系统_接口文档' + # 描述 + description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...' + # 版本 + version: '版本号: 1.0.0' + # 作者信息 + contact: + name: Lion Li + email: crazylionli@163.com + url: https://gitee.com/dromara/RuoYi-Vue-Plus + components: + # 鉴权方式配置 + security-schemes: + apiKey: + type: APIKEY + in: HEADER + name: Authorization + #这里定义了两个分组,可定义多个,也可以不定义 + group-configs: + - group: 1.演示模块 + packages-to-scan: org.dromara.demo + - group: 2.通用模块 + packages-to-scan: org.dromara.web + - group: 3.系统模块 + packages-to-scan: org.dromara.system + - group: 4.代码生成模块 + packages-to-scan: org.dromara.generator + + +management: #开启SpringBoot Admin的监控 + endpoints: + web: + exposure: + include: '*' + endpoint: + health: + show-details: always diff --git a/ruoyi-site/ruoyi-tpl/src/main/resources/static/head.png b/ruoyi-site/ruoyi-tpl/src/main/resources/static/head.png new file mode 100644 index 000000000..09b349d04 Binary files /dev/null and b/ruoyi-site/ruoyi-tpl/src/main/resources/static/head.png differ diff --git a/ruoyi-site/ruoyi-tpl/src/main/resources/static/test.docx b/ruoyi-site/ruoyi-tpl/src/main/resources/static/test.docx new file mode 100644 index 000000000..1d1109673 Binary files /dev/null and b/ruoyi-site/ruoyi-tpl/src/main/resources/static/test.docx differ diff --git a/ruoyi-site/ruoyi-tpl/src/main/resources/static/test.xlsx b/ruoyi-site/ruoyi-tpl/src/main/resources/static/test.xlsx new file mode 100644 index 000000000..589aff000 Binary files /dev/null and b/ruoyi-site/ruoyi-tpl/src/main/resources/static/test.xlsx differ