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;}@Overridepublic 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 rowXmlCursor newCursor = templateRow.getCtRow().newCursor();newCursor.toPrevSibling();XmlObject object = newCursor.getObject();nextRow = new XWPFTableRow((CTRow) object, table);if (!firstFlag) {// update VMerge cells for non-first rowList<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 valuecell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);} else {// Cells which join (merge) the first one, are set with CONTINUEcell.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);}@Datapublic 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;}}@Datastatic 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)));