poi-tl自定义表格渲染插件可实现子循环

文档地址

http://deepoove.com/poi-tl/

poi-tl的maven依赖

<dependency>
  <groupId>com.deepoove</groupId>
  <artifactId>poi-tl</artifactId>
  <version>1.12.1</version>
</dependency>

插件代码


import com.alibaba.fastjson.JSON;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.exception.RenderException;
import com.deepoove.poi.policy.RenderPolicy;
import com.deepoove.poi.render.compute.EnvModel;
import com.deepoove.poi.render.compute.RenderDataCompute;
import com.deepoove.poi.render.processor.DocumentProcessor;
import com.deepoove.poi.render.processor.EnvIterator;
import com.deepoove.poi.resolver.TemplateResolver;
import com.deepoove.poi.template.ElementTemplate;
import com.deepoove.poi.template.MetaTemplate;
import com.deepoove.poi.template.run.RunTemplate;
import com.deepoove.poi.util.ReflectionUtils;
import com.deepoove.poi.util.TableTools;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xwpf.usermodel.*;

import java.util.*;

@Slf4j
public class MultilevelLoopRowTableRenderPolicy implements RenderPolicy {
    private String prefix;
    private String suffix;
    private boolean onSameLine;
    private String titleName;
    private String contextName;

    public MultilevelLoopRowTableRenderPolicy(String titleName, String contextName) {
        this(titleName, contextName, false);
    }

    public MultilevelLoopRowTableRenderPolicy(String titleName, String contextName, boolean onSameLine) {
        this("{[", "]}", titleName, contextName, onSameLine);
    }

    public MultilevelLoopRowTableRenderPolicy(String prefix, String suffix, String titleName, String contextName) {
        this(prefix, suffix, titleName, contextName, false);
    }

    public MultilevelLoopRowTableRenderPolicy(String prefix, String suffix, String titleName, String contextName, boolean onSameLine) {
        this.prefix = prefix;
        this.suffix = suffix;
        this.onSameLine = onSameLine;
        this.titleName = titleName;
        this.contextName = contextName;
    }

    @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);
            if (null != data && data instanceof Iterable) {
                TemplateResolver resolver = new TemplateResolver(template.getConfig().copy(prefix, suffix));

                //获取多行模板
                List<XWPFTableRow> templateRows = getAllTemplateRow(table, templateRowIndex);
                //保存子标题内容,以便后续操作中获取光标
                final XWPFTableRow firstTitleTemplateRow = templateRows.get(0);
                //保存子列表内容,以便后续中获取光标操作
                final XWPFTableRow firstSonRowTemplateRow = templateRows.get(1);
                int index = 0;
                //title row num
                int titleRowPosition = getRowIndex(firstTitleTemplateRow);
                //content row num
                int sonRowPosition = getRowIndex(firstSonRowTemplateRow);
                //foreach data
                Iterator<?> iterator = ((Iterable<?>) data).iterator();
                boolean hasNext = iterator.hasNext();
                while (hasNext) {
                    Object root = iterator.next();
                    hasNext = iterator.hasNext();
                    //parse data
                    Map<String, Object> jsonObject = JSON.parseObject(JSON.toJSONString(root));
                    String dqmc = (String) jsonObject.get(titleName);
                    //copy title row start
                    if (!copy(table, firstTitleTemplateRow, titleRowPosition)) {
                        throw new RenderException("create new table fail...");
                    }
                    XWPFTableRow dqnewRow = table.getRow(titleRowPosition);
                    setTableRow(table, dqnewRow, titleRowPosition);
                    //copy title row end

                    //render title
                    Map<String, Object> map = new HashMap<>();
                    map.put(titleName, dqmc);
                    List<XWPFTableCell> dqcells = dqnewRow.getTableCells();
                    RenderDataCompute dqDataCompute = template.getConfig()
                            .getRenderDataComputeFactory()
                            .newCompute(EnvModel.of(map, EnvIterator.makeEnv(++index, hasNext)));
                    dqcells.forEach(tableCell -> {
                        List<MetaTemplate> metaTemplates = resolver
                                .resolveBodyElements(tableCell.getBodyElements());
                        new DocumentProcessor(template, resolver, dqDataCompute)
                                .process(metaTemplates);
                    });
                    //table add to row . so ++
                    titleRowPosition++;
                    //get son list
                    List sonList = (List) jsonObject.get(contextName);
                    //plus son list size
                    titleRowPosition = titleRowPosition + ((Collection<?>) sonList).size();

                    //foreach son list
                    Iterator<?> sonDataIterator = ((Iterable<?>) sonList).iterator();
                    boolean sonDataIteratorHasNext = sonDataIterator.hasNext();
                    while (sonDataIteratorHasNext) {
                        Object sonData = sonDataIterator.next();
                        sonDataIteratorHasNext = sonDataIterator.hasNext();

                        //copy son row start
                        if (!copy(table, firstSonRowTemplateRow, sonRowPosition)) {
                            throw new RenderException("create new table fail...");
                        }
                        XWPFTableRow newRow = table.getRow(sonRowPosition);
                        setTableRow(table, newRow, sonRowPosition);
                        //copy son row end
                        //render title
                        List<XWPFTableCell> cells = newRow.getTableCells();
                        RenderDataCompute dataCompute = template.getConfig()
                                .getRenderDataComputeFactory()
                                .newCompute(EnvModel.of(sonData, EnvIterator.makeEnv(++index, sonDataIteratorHasNext)));
                        cells.forEach(tableCell -> {
                            List<MetaTemplate> metaTemplates = resolver
                                    .resolveBodyElements(tableCell.getBodyElements());
                            new DocumentProcessor(template, resolver, dataCompute)
                                    .process(metaTemplates);
                        });
                        //add son row. so ++
                        sonRowPosition++;
                    }
                    //add title row. so ++
                    sonRowPosition = sonRowPosition + 1;
                }

                //remove last template row
                removeTableRow(table, table.getNumberOfRows() - getMultipleTemplateRowNum(), templateRows.size());
            }
        } catch (Exception e) {
            throw new RenderException("HackLoopTable for " + eleTemplate + "error: " + e.getMessage(), e);
        }
    }

    private List<XWPFTableRow> getAllTemplateRow(XWPFTable table, int startIndex) {
        int tempRowNum = getMultipleTemplateRowNum();
        List<XWPFTableRow> rows = table.getRows();
        return new Vector<>(rows.subList(startIndex, startIndex + tempRowNum));
    }

    private int getMultipleTemplateRowNum() {
        return 2;
    }

    private int getTemplateRowIndex(XWPFTableCell tagCell) {
        XWPFTableRow tagRow = tagCell.getTableRow();
        return onSameLine ? getRowIndex(tagRow) : (getRowIndex(tagRow) + 1);
    }


    private void removeTableRow(XWPFTable table, int startIndex, int size) {
        for (int i = 0; i < size; ++i) {
            table.removeRow(startIndex);
        }
    }


    @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);
    }

    /**
     * description 深拷贝一行数据
     *
     * @param table
     * @param sourceRow
     * @param rowIndex
     * @return
     * @date 2023/6/15 16:23
     * @author Eugene
     */
    public boolean copy(XWPFTable table, XWPFTableRow sourceRow, int rowIndex) {
        //在表格指定位置新增一行
        XWPFTableRow targetRow = table.insertNewTableRow(rowIndex);
        //复制行属性
        targetRow.getCtRow().setTrPr(sourceRow.getCtRow().getTrPr());
        List<XWPFTableCell> cellList = sourceRow.getTableCells();
        if (null == cellList) {
            return false;
        }
        //复制列及其属性和内容
        XWPFTableCell targetCell = null;
        for (XWPFTableCell sourceCell : cellList) {
            targetCell = targetRow.addNewTableCell();
            //列属性
            targetCell.getCTTc().setTcPr(sourceCell.getCTTc().getTcPr());
            //段落属性
            if (sourceCell.getParagraphs() != null && sourceCell.getParagraphs().size() > 0) {
                targetCell.getParagraphs().get(0).getCTP().setPPr(sourceCell.getParagraphs().get(0).getCTP().getPPr());
                if (sourceCell.getParagraphs().get(0).getRuns() != null && sourceCell.getParagraphs().get(0).getRuns().size() > 0) {
                    XWPFRun cellR = targetCell.getParagraphs().get(0).createRun();
                    cellR.setText(sourceCell.getText());
                    cellR.setBold(sourceCell.getParagraphs().get(0).getRuns().get(0).isBold());
                } else {
                    targetCell.setText(sourceCell.getText());
                }
            } else {
                targetCell.setText(sourceCell.getText());
            }
        }
        return true;
    }
}

数据格式

@Data
public class ExportBo {

    private String titleName;

    private List<Map<String, Object>> sonList;
}

使用方法

        MultilevelLoopRowTableRenderPolicy policy = new MultilevelLoopRowTableRenderPolicy("titleName", "sonList");
        Configure config = Configure.builder().bind("list", policy).build();
        InputStream templateIs = getClass().getClassLoader().getResourceAsStream("template/template.docx");
        XWPFTemplate template = XWPFTemplate.compile(templateIs, config).render(
                new HashMap<String, Object>() {{
                    put("list", exportBoList);
                }}
        );
        ExportUtil.downloadDocument("XXX.docx", template, request, response);

word模板

测试
序号名字专业
{{list}}{[titleName]}
{[num]}{[name]}{[major]}

效果展示

测试
序号名字专业
1班
1张三计算机
2张四计算机
2班
3李四软件工程
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值