poi-tl导出复杂的动态表格

问题

如果我们的word要导出这样一个动态表格。数据的场景是会变化的,我这个月可能有五个场景,下个月可能就只有4个场景,而且场景的顺序也是可以改变的。如果利用poi-tl模版引擎利用模版来导出表格,我们会遇到两个问题。

1、模版写死了我们没办法动态的改变场景数据的顺序。

2、我们不知道要写多少个模版,因为场景的数据是会发生改变的。

image-20240220191410072

实现逻辑

那这样的功能要怎么实现呢?

于是我就想,既然要改变顺序那么我就不把模版写死,每个表格的模版我们就用一个编号,然后在代码中循给每个模版按排序好的数据有序的遍历赋值。

这样可以解决顺序的问题,那要写多少个模版呢?其实我们最多有多少个场景我们就写多少个模版,然后把多余的模版给清除掉?

实现代码

自定义插件

public class DssReportIndexTableDataPolicy extends DynamicTableRenderPolicy {
    int startRow = 1;
    int abnormalStartRow = 2;
    @Override
    public void render(XWPFTable table, Object data) throws Exception {
        if (null == data) return;
        RowRenderDataDTO convert = (RowRenderDataDTO) data;
        List<RowRenderData> abnormalDae = convert.getAbnormalDae();
        List<RowRenderData> rowRenderData = convert.getTableDate();
        // 如果数据为空则把模版表格删除
        if (CollectionUtils.isEmpty(abnormalDae) && CollectionUtils.isEmpty(rowRenderData)){
            table.getCTTbl().newCursor().removeXml();
        }else {
            // 指标异常的院系
            if (CollectionUtils.isNotEmpty(abnormalDae)){
                table.removeRow(abnormalStartRow);
                for (int i=abnormalDae.size()-1;i>=0;i--) {
                    XWPFTableRow insertNewTableRow = table.insertNewTableRow(abnormalStartRow);
                    for (int j = 0; j < 6; j++) {
                        insertNewTableRow.createCell();
                        if (i==0){
                            // 去除底部的边框
                            setBottomCellBorders(insertNewTableRow.getCell(j), STBorder.NONE);
                        }else if (i<abnormalDae.size()-1){
                            // 去除底部和上面的边框
                            setBottomAndTopCellBorders(insertNewTableRow.getCell(j),STBorder.NONE,STBorder.NONE);
                        }else if (i==abnormalDae.size()-1){
                            // 最后一行去除上部的边框
                            setTopCellBorders(insertNewTableRow.getCell(j),STBorder.NONE);
                        }
                    }

                    // 合并单元格
                    TableTools.mergeCellsHorizonal(table, abnormalStartRow, 0, 5);
                    // 单行渲染
                    TableRenderPolicy.Helper.renderRow(table.getRow(abnormalStartRow), abnormalDae.get(i));
                }
            }else {
                table.removeRow(abnormalStartRow);
            }

            if (CollectionUtils.isNotEmpty(rowRenderData)) {
                table.removeRow(startRow);
                // 循环插入行,必须采用这种遍历,不然数据会反过来
                for (int i = rowRenderData.size() - 1; i >= 0; i--) {
                    //根据数据长度创建对应行数
                    XWPFTableRow insertNewTableRow = table.insertNewTableRow(startRow);

                    for (int j = 0; j < 6; j++) {
                        //根据列的数量创建对应单元格
                        insertNewTableRow.createCell();
                        if (j==0){
                            setCellBorders(insertNewTableRow.getCell(j),STBorder.NONE,STBorder.SINGLE,STBorder.NONE,STBorder.NONE);
                        }else if (j==5){
                            setCellBorders(insertNewTableRow.getCell(j),STBorder.NONE,STBorder.NONE,STBorder.NONE,STBorder.SINGLE);
                        } else {
                            setCellBorders(insertNewTableRow.getCell(j),STBorder.NONE,STBorder.NONE,STBorder.NONE,STBorder.NONE);
                        }
                        // 如果异常数据为空则要保留边框
                        if (i == rowRenderData.size() - 1){
                            setBottomCellBorders(insertNewTableRow.getCell(j),STBorder.SINGLE);
                        }
                    }
                    // 单行渲染
                    TableRenderPolicy.Helper.renderRow(table.getRow(startRow), rowRenderData.get(i));
                }

            }else {
                // 没有数据时,删除该行
                table.removeRow(startRow);
            }
        }
    }
    // 设置单元格边框
    private static void setCellBorders(XWPFTableCell cell, STBorder.Enum top, STBorder.Enum left, STBorder.Enum  bottom, STBorder.Enum  right) {
        CTTcBorders cellBorders = cell.getCTTc().addNewTcPr().addNewTcBorders();
        cellBorders.addNewTop().setVal(top);
        cellBorders.addNewLeft().setVal(left);
        cellBorders.addNewBottom().setVal(bottom);
        cellBorders.addNewRight().setVal(right);
    }

    private static void setBottomAndTopCellBorders(XWPFTableCell cell, STBorder.Enum bottom,STBorder.Enum top) {
        CTTcBorders cellBorders = cell.getCTTc().addNewTcPr().addNewTcBorders();
        cellBorders.addNewTop().setVal(top);
        cellBorders.addNewBottom().setVal(bottom);
    }
    private static void setTopCellBorders(XWPFTableCell cell, STBorder.Enum top) {
        CTTcBorders cellBorders = cell.getCTTc().addNewTcPr().addNewTcBorders();
        cellBorders.addNewTop().setVal(top);
    }

    private static void setBottomCellBorders(XWPFTableCell cell, STBorder.Enum bottom) {
        CTTcBorders cellBorders = cell.getCTTc().addNewTcPr().addNewTcBorders();
        cellBorders.addNewBottom().setVal(bottom);
    }

}

测试类

@SpringBootTest
class PoiApplicationTests {
    private static final Map<Integer, String> numberWordsMap = new HashMap<>();

    static {
        numberWordsMap.put(1, "one");
        numberWordsMap.put(2, "two");
        numberWordsMap.put(3, "three");
        numberWordsMap.put(4, "four");
        numberWordsMap.put(5, "five");
        numberWordsMap.put(6, "six");
        numberWordsMap.put(7, "seven");
        numberWordsMap.put(8, "eight");
        numberWordsMap.put(9, "nine");
        // 添加更多数字与单词的映射
    }
    @Test
    void tableExportTest() throws IOException {
        HashMap<String, Object> renderMap = new HashMap<>();
        int index=1;
        // 获取数据
        List<SceneTableData> dataList = getDataList();
        for (SceneTableData sceneTableData : dataList) {
            renderMap.put(numberWordsMap.get(index)+"_name", index+"、"+sceneTableData.getSceneName());
               renderMap.put(numberWordsMap.get(index)+"_score","指标得分"+sceneTableData.getSceneScore());
            List<RowRenderData> sceneDetailDataRenderData = getSceneDetailDataRenderData(sceneTableData.getDssReportIndexDTOs());
            List<RowRenderData> abnormalDataRenderData = getAbnormalDataRenderData(sceneTableData.getIndexAbnormalList());
            RowRenderDataDTO rowRenderDataDTO = new RowRenderDataDTO();
            rowRenderDataDTO.setTableDate(sceneDetailDataRenderData);
            rowRenderDataDTO.setAbnormalDae(abnormalDataRenderData);
            renderMap.put(numberWordsMap.get(index),rowRenderDataDTO);
            index++;
        }
        int size =5;
        // 其他场景不考虑所以要减去
        int remainingIndices = size - index;
        for (int i = index; i < index + remainingIndices; i++) {
            // 传空数据为了删除多余的模版
            renderMap.put(numberWordsMap.get(i), new RowRenderDataDTO());
        }
        // 给在表格的标签绑定插件
        DssReportIndexTableDataPolicy dssReportIndexTableDataPolicy = new DssReportIndexTableDataPolicy();
        Configure config = Configure.builder()
                .bind("one", dssReportIndexTableDataPolicy)
                .bind("two", dssReportIndexTableDataPolicy)
                .bind("three", dssReportIndexTableDataPolicy)
                .bind("four", dssReportIndexTableDataPolicy)
                .bind("five", dssReportIndexTableDataPolicy)
                .build();

        XWPFTemplate template = XWPFTemplate.compile("D:\\learning\\test\\test\\poi\\src\\main\\resources\\template.docx",config).render(renderMap);
        template.write(new FileOutputStream("D:\\learning\\test\\test\\poi\\src\\main\\resources\\output.docx"));
    }

    /**
     * 获取上半个表的数据
     * @param SceneDetailData 上半部分数据
     * @return List<RowRenderData>
     */
    public List<RowRenderData> getSceneDetailDataRenderData(List<SceneDetailData> SceneDetailData){
        List<RowRenderData> rowRenderDataList=new ArrayList<>();
        for (SceneDetailData sceneDetailData : SceneDetailData) {
            RowRenderData rowRenderData = Rows.of("",sceneDetailData.getBehavior(),
                    sceneDetailData.getIndex(),
                    sceneDetailData.getResult(),
                    sceneDetailData.getReferenceValue(), "").horizontalCenter().verticalCenter().textFontFamily("仿宋").textFontSize(14).create();
            rowRenderDataList.add(rowRenderData);
        }
        return rowRenderDataList;
    }

    /**
     * 获取表下半部分的数据
     * @param abnormalData 下半部分数据
     * @return List<RowRenderData>
     */
    public List<RowRenderData> getAbnormalDataRenderData(List<String> abnormalData){
        List<RowRenderData> renderDataList=new ArrayList<>();
        for (String s : abnormalData) {
            RowRenderData rowRenderData = Rows.of(s).verticalCenter().textFontFamily("仿宋").textFontSize(14).create();
            renderDataList.add(rowRenderData);
        }
        return renderDataList;
    }
    public List<SceneTableData> getDataList(){
        List<SceneTableData> reslutList=new ArrayList<>();
        List<SceneDetailData> sceneDetailDataList=new ArrayList<>();
        // 学业场景
        sceneDetailDataList.add(SceneDetailData.builder().behavior("不及格").index("不及格率").result("-1.44%").referenceValue("0~13%").build());
        sceneDetailDataList.add(SceneDetailData.builder().behavior("").index("异常增幅").result("-1.86%").referenceValue("0~13%").build());
        sceneDetailDataList.add(SceneDetailData.builder().behavior("到课").index("到课率").result("0%").referenceValue("0~13%").build());
        reslutList.add(SceneTableData.builder().sceneName("学业场景").sceneScore("100").dssReportIndexDTOs(sceneDetailDataList).indexAbnormalList(Collections.emptyList()).build());
        // 上网场景
        sceneDetailDataList.add(SceneDetailData.builder().behavior("沉迷网络").index("时长异常率").result("24.93% ").referenceValue("0~13%").build());
        sceneDetailDataList.add(SceneDetailData.builder().behavior("").index("流量异常率").result(".93%").referenceValue("0~13%").build());
        List<String> abnomalList= new ArrayList<>();
        abnomalList.add("指标项异常的院系");
        abnomalList.add("沉迷网络-时长异常率:国际动画学院、华为ICT学院、创意艺术学院、音乐与影视艺术、智能科学与技术、数字经济与管理、软件动漫学院、虚拟现实学院");
        reslutList.add(SceneTableData.builder().sceneName("上网场景").sceneScore("55").dssReportIndexDTOs(sceneDetailDataList).indexAbnormalList(abnomalList).build());
        return reslutList;
    }


}

模版

image-20240220193119996

导出效果

image-20240220193501723

  • 13
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力发光的程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值