mirror of
https://gitee.com/dromara/RuoYi-Vue-Plus.git
synced 2026-09-18 01:25:28 +08:00
ruoyi-tpl 文件模板打印后台
This commit is contained in:
parent
529c9e0ef2
commit
4031270b81
@ -12,6 +12,7 @@
|
||||
<modules>
|
||||
<module>ruoyi-sso-server</module>
|
||||
<module>ruoyi-h5</module>
|
||||
<module>ruoyi-tpl</module>
|
||||
<module>ruoyi-gateway</module>
|
||||
<module>ruoyi-tams</module>
|
||||
</modules>
|
||||
|
||||
20
ruoyi-site/ruoyi-tpl/Dockerfile
Normal file
20
ruoyi-site/ruoyi-tpl/Dockerfile
Normal file
@ -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
|
||||
|
||||
1
ruoyi-site/ruoyi-tpl/README.md
Normal file
1
ruoyi-site/ruoyi-tpl/README.md
Normal file
@ -0,0 +1 @@
|
||||
ruoyi-tpl 文件模板打印后台
|
||||
90
ruoyi-site/ruoyi-tpl/pom.xml
Normal file
90
ruoyi-site/ruoyi-tpl/pom.xml
Normal file
@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-site</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>ruoyi-tpl</artifactId>
|
||||
|
||||
<description>
|
||||
ruoyi-tpl 文件模板打印后台
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 租户模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-tenant</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- mybatis -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-mybatis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-doc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-log</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Mysql驱动包 -->
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-encrypt</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringBoot Web依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>4.1.2</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
0
ruoyi-site/ruoyi-tpl/sql/init_ddl.sql
Normal file
0
ruoyi-site/ruoyi-tpl/sql/init_ddl.sql
Normal file
0
ruoyi-site/ruoyi-tpl/sql/init_dml.sql
Normal file
0
ruoyi-site/ruoyi-tpl/sql/init_dml.sql
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<Class, JsonSerializer> SERIALIZER_MAP;
|
||||
public final static Map<Class, JsonDeserializer> 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);
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -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<String> excludes = tenantProperties.getExcludes();
|
||||
// 非业务表
|
||||
List<String> tables = ListUtil.toList(
|
||||
"gen_table",
|
||||
"gen_table_column"
|
||||
);
|
||||
tables.addAll(excludes);
|
||||
return tables.contains(tableName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -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秒内,不需要再发送预检验请求,可以缓存该结果
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
|
||||
}
|
||||
@ -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<ClassroomListVO> pageCourse(ClassroomPageQuery pageQuery);
|
||||
//
|
||||
// List<ClassroomListVO> refList();
|
||||
//
|
||||
// ClassroomListVO getCourseById(Long id);
|
||||
//
|
||||
// boolean saveCourse(ClassroomSaveDTO saveDTO);
|
||||
//
|
||||
// boolean updateCourseById(Long id, ClassroomSaveDTO saveDTO);
|
||||
//
|
||||
// boolean updateCourseEnableStateById(Long id, Integer enableState);
|
||||
//}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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文件
|
||||
* <p>
|
||||
* by miracleren@gmail.com
|
||||
*/
|
||||
|
||||
public class NiceDoc {
|
||||
//private HWPFDocument doc;
|
||||
private XWPFDocument docx;
|
||||
private int status = 0;
|
||||
private final List<XWPFTable> 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<String, Object> labels) {
|
||||
//遍历普通段落内容对像,填充标签值
|
||||
List<XWPFParagraph> paragraphs = docx.getParagraphs();
|
||||
replaceLabelsInParagraphs(paragraphs, labels);
|
||||
|
||||
//遍历表格内容,并填充标签值
|
||||
List<XWPFTable> tables = status == 0 ? docx.getTables() : this.allTables;
|
||||
for (XWPFTable table : tables) {
|
||||
//表格行
|
||||
List<XWPFTableRow> rows = table.getRows();
|
||||
for (XWPFTableRow row : rows) {
|
||||
//表格单元格
|
||||
List<XWPFTableCell> cells = row.getTableCells();
|
||||
for (XWPFTableCell cell : cells) {
|
||||
//表格段落
|
||||
List<XWPFParagraph> cellParagraphs = cell.getParagraphs();
|
||||
replaceLabelsInParagraphs(cellParagraphs, labels);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//页眉标签值填充
|
||||
List<XWPFHeader> headers = docx.getHeaderList();
|
||||
for (XWPFHeader header : headers) {
|
||||
List<XWPFParagraph> headerParagraphs = header.getListParagraph();
|
||||
replaceLabelsInParagraphs(headerParagraphs, labels);
|
||||
}
|
||||
|
||||
//页脚填充
|
||||
List<XWPFFooter> footers = docx.getFooterList();
|
||||
for (XWPFFooter footer : footers) {
|
||||
List<XWPFParagraph> 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<Map<String, Object>> list) {
|
||||
List<XWPFTable> tables = this.allTables;
|
||||
for (XWPFTable table : tables) {
|
||||
boolean isFind = false;
|
||||
XWPFTableRow baseRow = null;
|
||||
|
||||
List<XWPFTableRow> rows = table.getRows();
|
||||
int rowCount = rows.size();
|
||||
for (int i = 0; i < rowCount; i++) {
|
||||
List<XWPFTableCell> cells = rows.get(i).getTableCells();
|
||||
for (XWPFTableCell cell : cells) {
|
||||
List<XWPFParagraph> 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<String, Object> 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<String, Object> 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<XWPFParagraph> paragraphs, Map<String, Object> 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<XWPFRun> 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<XWPFParagraph> paragraphs, Integer index, Map<String, Object> params) {
|
||||
String nowText = "";
|
||||
int runCount = 0;
|
||||
List<XWPFRun> labelRuns = new ArrayList<>();
|
||||
Boolean isShow = true;
|
||||
|
||||
for (int i = index; i < paragraphs.size(); i++) {
|
||||
XWPFParagraph paragraph = paragraphs.get(i);
|
||||
List<XWPFRun> 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<String, Object> params) {
|
||||
//遍历文本对象,查找标识标签
|
||||
List<XWPFRun> runs = paragraph.getRuns();
|
||||
String nowText = "";
|
||||
int runCount = 0;
|
||||
List<XWPFRun> 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<XWPFParagraph> paragraphs = docx.getParagraphs();
|
||||
List<IBodyElement> 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<String, Object> 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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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文件
|
||||
* <p>
|
||||
* 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<String, Object> 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<String, Object> 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<String, Object> 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<Map<String, Object>> 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<String, Object> 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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
|
||||
/**
|
||||
* 通用方法
|
||||
* <p>
|
||||
* 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<String, Object> entityToMap(Object entity) {
|
||||
Map<String, Object> 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<Map<String, Object>> listEntityToMap(List<Object> entityList) {
|
||||
List<Map<String, Object>> 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<Integer, String> map, String value) {
|
||||
for (Map.Entry<Integer, String> 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);
|
||||
}
|
||||
}
|
||||
@ -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<String, Object> 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<Map<String, Object>> books = new ArrayList<>();
|
||||
Map<String, Object> book1 = new HashMap<>();
|
||||
book1.put("name", "汉文学史纲要");
|
||||
book1.put("time", "1938年,鲁迅全集出版社");
|
||||
books.add(book1);
|
||||
Map<String, Object> 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<String, Object> 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<Map<String, Object>> books = new ArrayList<>();
|
||||
for (int i = 0; i <= 10; i++) {
|
||||
Map<String, Object> 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");
|
||||
}
|
||||
}
|
||||
40
ruoyi-site/ruoyi-tpl/src/main/resources/application-dev.yaml
Normal file
40
ruoyi-site/ruoyi-tpl/src/main/resources/application-dev.yaml
Normal file
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
78
ruoyi-site/ruoyi-tpl/src/main/resources/application.yaml
Normal file
78
ruoyi-site/ruoyi-tpl/src/main/resources/application.yaml
Normal file
@ -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
|
||||
BIN
ruoyi-site/ruoyi-tpl/src/main/resources/static/head.png
Normal file
BIN
ruoyi-site/ruoyi-tpl/src/main/resources/static/head.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 168 KiB |
BIN
ruoyi-site/ruoyi-tpl/src/main/resources/static/test.docx
Normal file
BIN
ruoyi-site/ruoyi-tpl/src/main/resources/static/test.docx
Normal file
Binary file not shown.
BIN
ruoyi-site/ruoyi-tpl/src/main/resources/static/test.xlsx
Normal file
BIN
ruoyi-site/ruoyi-tpl/src/main/resources/static/test.xlsx
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user