POI-TL插件开发-表格分组插件

POI-TL版本:1.12.2

改造于:LoopRowTableRenderPolicy
模板设计:

分组之前:

分组之后:

代码实现:

java 复制代码
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;
        }
    }

}

绑定标签:

java 复制代码
        Configure.builder().bind("商品列表", new LoopRowGroupTableRenderPolicy(new LoopRowGroupTableRenderPolicy.TableGroupPolicy("名称","分类",0,2)));
相关推荐
后端小张1 分钟前
【AI 学习】AI提示词工程:从入门到实战的全栈指南
java·人工智能·深度学习·学习·语言模型·prompt·知识图谱
Coder码匠1 小时前
Dockerfile 优化实践:从 400MB 到 80MB
java·spring boot
李慕婉学姐8 小时前
【开题答辩过程】以《基于JAVA的校园即时配送系统的设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
java·开发语言·数据库
奋进的芋圆10 小时前
Java 延时任务实现方案详解(适用于 Spring Boot 3)
java·spring boot·redis·rabbitmq
sxlishaobin10 小时前
设计模式之桥接模式
java·设计模式·桥接模式
model200510 小时前
alibaba linux3 系统盘网站迁移数据盘
java·服务器·前端
武藤一雄10 小时前
C# 关于多线程如何实现需要注意的问题(持续更新)
windows·后端·microsoft·c#·.net·.netcore·死锁
荒诞硬汉11 小时前
JavaBean相关补充
java·开发语言
提笔忘字的帝国11 小时前
【教程】macOS 如何完全卸载 Java 开发环境
java·开发语言·macos
2501_9418824811 小时前
从灰度发布到流量切分的互联网工程语法控制与多语言实现实践思路随笔分享
java·开发语言