POI-TL版本:1.12.2
改造于:LoopRowTableRenderPolicy
模板设计:
分组之前:
分组之后:
代码实现:
public class LoopRowGroupTableRenderPolicy implements RenderPolicy {
private String prefix;
private String suffix;
private boolean onSameLine;
private TableGroupPolicy tableGroupPolicy;
private final String MERGE_FLAG = "MERGE_FLAG";
public LoopRowGroupTableRenderPolicy(TableGroupPolicy tableGroupPolicy) {
this.prefix = "[";
this.suffix = "]";
this.onSameLine = false;
this.tableGroupPolicy = tableGroupPolicy;
}
@Override
public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
RunTemplate runTemplate = (RunTemplate) eleTemplate;
XWPFRun run = runTemplate.getRun();
try {
if (!TableTools.isInsideTable(run)) {
throw new IllegalStateException(
"The template tag " + runTemplate.getSource() + " must be inside a table");
}
XWPFTableCell tagCell = (XWPFTableCell) ((XWPFParagraph) run.getParent()).getBody();
XWPFTable table = tagCell.getTableRow().getTable();
run.setText("", 0);
int templateRowIndex = getTemplateRowIndex(tagCell);
List<MergeDTO> mergeList = new ArrayList<>();
if (data instanceof Iterable) {
// 对数据分组
List<Map<String, String>> group = groupData((List<Map<String, String>>) data);
Iterator<?> iterator = ((Iterable<?>) group).iterator();
XWPFTableRow templateRow = table.getRow(templateRowIndex);
int step = templateRow.getTableICells().size();
int insertPosition = templateRowIndex;
TemplateResolver resolver = new TemplateResolver(template.getConfig().copy(prefix, suffix));
boolean firstFlag = true;
int index = 0;
boolean hasNext = iterator.hasNext();
while (hasNext) {
HashMap root = (HashMap) iterator.next();
if (root.containsKey(MERGE_FLAG) && "Y".equals(root.get(MERGE_FLAG))) {
int start = tableGroupPolicy.getFromIndex() > 0 ? tableGroupPolicy.getFromIndex() : 0;
int end = tableGroupPolicy.getToIndex() >= step ? step - 1 : tableGroupPolicy.getToIndex();
mergeList.add(new MergeDTO(templateRowIndex, start, end));
}
hasNext = iterator.hasNext();
insertPosition = templateRowIndex++;
XWPFTableRow nextRow = table.insertNewTableRow(insertPosition);
setTableRow(table, templateRow, insertPosition);
// double set row
XmlCursor newCursor = templateRow.getCtRow().newCursor();
newCursor.toPrevSibling();
XmlObject object = newCursor.getObject();
nextRow = new XWPFTableRow((CTRow) object, table);
if (!firstFlag) {
// update VMerge cells for non-first row
List<XWPFTableCell> tableCells = nextRow.getTableCells();
for (XWPFTableCell cell : tableCells) {
CTTcPr tcPr = TableTools.getTcPr(cell);
CTVMerge vMerge = tcPr.getVMerge();
if (null == vMerge) continue;
if (STMerge.RESTART == vMerge.getVal()) {
vMerge.setVal(STMerge.CONTINUE);
}
}
} else {
firstFlag = false;
}
setTableRow(table, nextRow, insertPosition);
RenderDataCompute dataCompute = template.getConfig()
.getRenderDataComputeFactory()
.newCompute(EnvModel.of(root, EnvIterator.makeEnv(index++, hasNext)));
List<XWPFTableCell> cells = nextRow.getTableCells();
cells.forEach(cell -> {
List<MetaTemplate> templates = resolver.resolveBodyElements(cell.getBodyElements());
new DocumentProcessor(template, resolver, dataCompute).process(templates);
});
}
}
if (!CollectionUtils.isEmpty(mergeList)) {
mergeList.forEach(c -> mergeCellsHorizontal(table, c.getIndex(), c.getFromIndex(), c.getToIndex()));
}
table.removeRow(templateRowIndex);
} catch (Exception e) {
throw new RenderException("HackLoopTable for " + eleTemplate + " error: " + e.getMessage(), e);
}
}
private List<Map<String, String>> groupData(List<Map<String, String>> data) {
List<Map<String, String>> result = new ArrayList<>();
Map<String, List<Map<String, String>>> collect = data.stream().collect(Collectors.groupingBy(map -> {
if (map.containsKey(tableGroupPolicy.getGroupFieldName())) {
if (StringUtils.isNotEmpty(map.get(tableGroupPolicy.getGroupFieldName()))) {
return map.get(tableGroupPolicy.getGroupFieldName());
} else {
return "";
}
} else {
return "";
}
}));
collect.keySet().forEach(c -> {
Map<String, String> map = new HashMap<>();
map.put(MERGE_FLAG, "Y");
map.put(tableGroupPolicy.getFirstFieldName(), c);
result.add(map);
result.addAll(collect.get(c));
});
return result;
}
private int getTemplateRowIndex(XWPFTableCell tagCell) {
XWPFTableRow tagRow = tagCell.getTableRow();
return onSameLine ? getRowIndex(tagRow) : (getRowIndex(tagRow) + 1);
}
/**
* word跨列合并单元格
* table 表单对象
* row 合并行
* fromCell 起始列
* toCell 结束列
*/
private static void mergeCellsHorizontal(XWPFTable table, Integer row, Integer fromCell, Integer toCell) {
for (int cellIndex = fromCell; cellIndex <= toCell; cellIndex++) {
XWPFTableCell cell = table.getRow(row).getCell(cellIndex);
if (cellIndex == fromCell) {
// The first merged cell is set with RESTART merge value
cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
} else {
// Cells which join (merge) the first one, are set with CONTINUE
cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
}
}
}
@SuppressWarnings("unchecked")
private void setTableRow(XWPFTable table, XWPFTableRow templateRow, int pos) {
List<XWPFTableRow> rows = (List<XWPFTableRow>) ReflectionUtils.getValue("tableRows", table);
rows.set(pos, templateRow);
table.getCTTbl().setTrArray(pos, templateRow.getCtRow());
}
private int getRowIndex(XWPFTableRow row) {
List<XWPFTableRow> rows = row.getTable().getRows();
return rows.indexOf(row);
}
@Data
public static class TableGroupPolicy {
// 首字段名称
private String firstFieldName;
// 分组字段名称
private String groupFieldName;
// 合并开始下标
private Integer fromIndex;
// 合并结束下标
private Integer toIndex;
public TableGroupPolicy() {
}
public TableGroupPolicy(String firstFieldName, String groupFieldName, Integer fromIndex, Integer toIndex) {
this.firstFieldName = firstFieldName;
this.groupFieldName = groupFieldName;
this.fromIndex = fromIndex;
this.toIndex = toIndex;
}
}
@Data
static class MergeDTO {
// 当前下标
private Integer index;
// 合并开始下标
private Integer fromIndex;
// 合并结束下标
private Integer toIndex;
public MergeDTO() {
}
public MergeDTO(Integer index, Integer fromIndex, Integer toIndex) {
this.index = index;
this.fromIndex = fromIndex;
this.toIndex = toIndex;
}
}
}
绑定标签:
Configure.builder().bind("商品列表", new LoopRowGroupTableRenderPolicy(new LoopRowGroupTableRenderPolicy.TableGroupPolicy("名称","分类",0,2)));