问题
如果我们的word要导出这样一个动态表格。数据的场景是会变化的,我这个月可能有五个场景,下个月可能就只有4个场景,而且场景的顺序也是可以改变的。如果利用poi-tl模版引擎利用模版来导出表格,我们会遇到两个问题。
1、模版写死了我们没办法动态的改变场景数据的顺序。
2、我们不知道要写多少个模版,因为场景的数据是会发生改变的。
实现逻辑
那这样的功能要怎么实现呢?
于是我就想,既然要改变顺序那么我就不把模版写死,每个表格的模版我们就用一个编号,然后在代码中循给每个模版按排序好的数据有序的遍历赋值。
这样可以解决顺序的问题,那要写多少个模版呢?其实我们最多有多少个场景我们就写多少个模版,然后把多余的模版给清除掉?
实现代码
自定义插件
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;
}
}