简介:
上篇文章说到开发中我们需要通过在word中使用占位符来动态渲染一些数据,
原文链接SpringBoot poi-tl通过模板占位符生成word文件-CSDN博客,
本文讲解poi-tl实现word表格之合并表格。
1. word格式
2. 导出后的docx文件 样式
3. 依赖
上篇文章有说过
SpringBoot poi-tl通过模板占位符生成word文件-CSDN博客
4. 实现代码
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.config.ConfigureBuilder;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVMerge;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class EditWordUtil {
public static void main(String[] args) {
String filePath = "D:/biao4.docx";
try (FileInputStream resourceAsStream = new FileInputStream(new File(filePath));) {
Map<String, Object> map = new HashMap<>();
ArrayList<Map<String, Object>> dataList = new ArrayList<>();
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 2; j++) {
HashMap<String, Object> maps = new HashMap<>();
maps.put("name", "龙龙" + (i + 1));
maps.put("skill", "Java" + j);
maps.put("lv", (j + 1) + "级");
maps.put("remak", "无");
dataList.add(maps);
}
}
map.put("list", dataList);
// 调用生成 Word 文件方法,将结果保存到本地
EditWordUtil.mergeTablesFun("D:/out.docx", resourceAsStream, map, 0, 0);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 导出有合并表格的word
*
* @param outputPath
* @param templatePath
* @param data
* @param pos 表格在文档的第几个表中,获取表格 从0开始
* @param mergeColumnIndex 合并 相同的数据 第几列的单元格 从0开始
* @return
* @throws IOException
*/
public static boolean mergeTablesFun(String outputPath, InputStream templatePath, Map<String, Object> data,
int pos, int mergeColumnIndex
) throws IOException {
try (FileOutputStream out = new FileOutputStream(outputPath);
BufferedOutputStream bos = new BufferedOutputStream(out)) {
ConfigureBuilder builder = Configure.builder();
LoopRowTableRenderPolicy loopRowTableRenderPolicy = new LoopRowTableRenderPolicy();
handleEmptyKeys(data);
// 动态绑定
for (Map.Entry<String, Object> entry : data.entrySet()) {
if (entry.getValue() instanceof ArrayList) {
builder.bind(entry.getKey(), loopRowTableRenderPolicy);
// 如果是嵌套列表,递归绑定
bindNestedLists(entry.getKey(), entry.getValue(), builder);
}
}
Configure configure = builder.build();
// 读取模板并渲染数据
XWPFTemplate template = XWPFTemplate.compile(templatePath, configure).render(data);
// 获取文档对象
XWPFDocument document = template.getXWPFDocument();
// 表格在文档的第pos个表中,获取表格
XWPFTable table = document.getTableArray(pos);
// 合并 相同的数据 第mergeColumnIndex列的单元格
mergeCellsWithSameValue(table, mergeColumnIndex);
try {
template.write(bos);
template.close();
} catch (Exception e) {
e.printStackTrace();
return false;
}
out.flush();
bos.flush();
}
return true;
}
// 递归绑定嵌套列表
private static void bindNestedLists(String key, Object value, ConfigureBuilder builder) {
if (value instanceof ArrayList) {
for (Object item : (ArrayList<?>) value) {
if (item instanceof Map) {
for (Map.Entry<String, Object> nestedEntry : ((Map<String, Object>) item).entrySet()) {
if (nestedEntry.getValue() instanceof ArrayList) {
// 绑定嵌套列表
builder.bind(nestedEntry.getKey(), new LoopRowTableRenderPolicy());
bindNestedLists(nestedEntry.getKey(), nestedEntry.getValue(), builder);
}
}
}
}
}
}
// 递归检查map的key是否为空, 为空返回 ---
public static void handleEmptyKeys(Map<String, Object> map) {
// 遍历 Map 中的所有键值对
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
// 如果键为空或者键值为null
if (key == null || key.isEmpty()) {
map.put(key, "---");
}
// 如果值是 Map 类型,递归调用
if (value instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> nestedMap = (Map<String, Object>) value;
// 递归检查嵌套的 Map
handleEmptyKeys(nestedMap);
// 如果嵌套Map为空, 也设置为 "---"
if (nestedMap.isEmpty()) {
map.put(key, "---");
}
}
// 如果值为空
else if (value == null) {
map.put(key, "---");
}
}
}
// 合并相同值的单元格,colIndex 是要合并的列索引
private static void mergeCellsWithSameValue(XWPFTable table, int colIndex) {
String previousValue = null;
int startRow = -1;
int rowCount = table.getNumberOfRows();
for (int i = 0; i < rowCount; i++) {
XWPFTableRow row = table.getRow(i);
XWPFTableCell cell = row.getCell(colIndex);
String cellValue = cell.getText();
// 如果当前值与前一个值相同,继续合并
if (cellValue.equals(previousValue)) {
continue;
}
// 如果前一个值不为空,且有多行需要合并,执行合并
if (previousValue != null && i > startRow + 1) {
mergeCellsVertically(table, colIndex, startRow, i - 1);
}
// 更新为当前值,并记录新起始行
previousValue = cellValue;
startRow = i;
}
// 处理最后一组相同值的合并
if (startRow < rowCount - 1) {
mergeCellsVertically(table, colIndex, startRow, rowCount - 1);
}
}
// 合并表格垂直方向的单元格
private static void mergeCellsVertically(XWPFTable table, int col, int fromRow, int toRow) {
for (int i = fromRow; i <= toRow; i++) {
XWPFTableCell cell = table.getRow(i).getCell(col);
CTVMerge vMerge = CTVMerge.Factory.newInstance();
if (i == fromRow) {
// 第一个合并的单元格设置为 RESTART
vMerge.setVal(STMerge.RESTART);
} else {
// 其他单元格设置为 CONTINUE
vMerge.setVal(STMerge.CONTINUE);
}
cell.getCTTc().getTcPr().setVMerge(vMerge);
}
}
}
结语
标签:Map,word,SpringBoot,表格,int,new,poi,import,table From: https://blog.csdn.net/longshehui/article/details/143200182