当前位置:   article > 正文

使用poi-tl操作word模板记录_poi-tl 分页符

poi-tl 分页符

需求:生成一份领料单,其中包括文本、表格、图片二维码,为了表格内容好看,表格内容每页最多30行。每页都需要基本信息表头。

效果如下:

maven依赖:

  1. <!-- excel工具 -->
  2. <dependency>
  3. <groupId>org.apache.poi</groupId>
  4. <artifactId>poi-ooxml</artifactId>
  5. <version>${poi.version}</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.apache.poi</groupId>
  9. <artifactId>poi-ooxml-schemas</artifactId>
  10. <version>${poi-ooxml-schemas.version}</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>org.apache.poi</groupId>
  14. <artifactId>poi</artifactId>
  15. <version>${poi.version}</version>
  16. </dependency>
  17. <dependency>
  18. <groupId>com.deepoove</groupId>
  19. <artifactId>poi-tl</artifactId>
  20. <version>${poi-tl.version}</version>
  21. </dependency>

注意点:poi-tl 1.12.x 要求 POI 版本在 5.2.2+.

模板制作:

说明:

1、为什么要用区块?由于需求是每页30行,每页都要有基本信息表头,所以用区块标签,将表头和表格作为一个区块。区块可循环N次,一个区块就是一页。

2、为什么要分页标记?通过分页标记来插入分页符。

3、为什么底部签名要用整个标签?表格内容动态,会将底部内容挤到下一页,所以通过代码在最后一行插入签名文本。

4、为什么不用excel模板?excel单元格插入图片展示不美观,且不好分页打印。其实也可以用excel操作,参考这篇文章:使用poi操作excel模板记录

完整代码,仅供参考:

  1. public SysFile materialUseGenWord(MachineMaterialUseResult useResult) {
  2. try {
  3. // 从resources下获取模板
  4. ClassPathResource resource = new ClassPathResource("template/lldmb.docx");
  5. InputStream inputStream = resource.getInputStream();
  6. // 添加二维码图片
  7. BufferedImage qrCodeImage = QrCodeUtil.generate(useResult.getSerialNo(), QrConfig.create().setMargin(1));
  8. // 计算页数:每页30行,新页都要有表头
  9. double page = 1;
  10. if (useResult.getParts().size() > 30) {
  11. page = Math.ceil((double) useResult.getParts().size() / 30);
  12. }
  13. List<Map<String, Object>> foreachList = new ArrayList<>();
  14. // 区块标签
  15. Map<String, Object> resMap = new HashMap<>();
  16. resMap.put("list", foreachList);
  17. for (int i = 0; i < page; i++) {
  18. log.info("第{}页表格数据", i + 1);
  19. List<MachineMaterialUseDetail> parts = new ArrayList<>();
  20. for (int j = 0; j < 30; j++) {
  21. if (i * 30 + j < useResult.getParts().size()) {
  22. parts.add(useResult.getParts().get(i * 30 + j));
  23. } else {
  24. break;
  25. }
  26. }
  27. Map<String, Object> materialMap = new HashMap<>();
  28. // 表格行数据循环标签:置于循环行的上一行
  29. materialMap.put("tables", parts);
  30. // 其他标签
  31. materialMap.put("serialNo", useResult.getSerialNo());
  32. materialMap.put("useDate", useResult.getUseTime());
  33. materialMap.put("deptName", "测试部门");
  34. materialMap.put("warehouse", "DJ0001");
  35. materialMap.put("projectCode", "XM123121");
  36. materialMap.put("projectName", "项目0001");
  37. materialMap.put("startTime", "2024-05-06");
  38. // 插入分页标记,最后一页不用分页
  39. if (i != page - 1) {
  40. materialMap.put("isPageBreak", "分页标记");
  41. }
  42. // 底部文字(签名)
  43. String bottomWord = "\n制单人:" +
  44. useResult.getSponsor() +
  45. " 发料人:" +
  46. useResult.getOperator() +
  47. " 领料人:领料人";
  48. materialMap.put("bottomWord", Texts.of(bottomWord).create());
  49. materialMap.put("qrCode", Pictures.ofBufferedImage(qrCodeImage, PictureType.PNG).size(120, 120).create());
  50. foreachList.add(materialMap);
  51. }
  52. //渲染表格:将插件应用到表格标签
  53. LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
  54. Configure config = Configure.builder().bind("tables", policy).build();
  55. XWPFTemplate template = XWPFTemplate.compile(inputStream, config).render(resMap);
  56. // 替换分页标记为分页符
  57. List<XWPFParagraph> paragraphs = template.getXWPFDocument().getParagraphs();
  58. for (XWPFParagraph p : paragraphs) {
  59. List<XWPFRun> runs = p.getRuns();
  60. if (runs != null) {
  61. for (XWPFRun r : runs) {
  62. String text = r.getText(0);
  63. if (text != null && text.contains("分页标记")) {
  64. text = text.replace("分页标记", "");
  65. r.setText(text, 0);
  66. r.addBreak(BreakType.PAGE);
  67. }
  68. }
  69. }
  70. }
  71. // 1将文档写入到输出流
  72. // FileOutputStream outStream = new FileOutputStream("D:\\opt\\mes\\领料单.docx");
  73. // template.write(outStream);
  74. // 2将文档作为响应返回
  75. // response.setHeader("Content-Disposition", "attachment; filename=exported-word.docx");
  76. // response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
  77. // OutputStream outStream = new BufferedOutputStream(response.getOutputStream());
  78. // template.write(outStream);
  79. // 将word转为输出流
  80. ByteArrayOutputStream outStream = new ByteArrayOutputStream();
  81. template.write(outStream);
  82. template.close();
  83. // 3将输出流转为 multipartFile 并上传
  84. MockMultipartFile multipartFile = new MockMultipartFile("file", useResult.getSerialNo() + "领料单" + System.currentTimeMillis() + ".docx", null, outStream.toByteArray());
  85. outStream.close();
  86. R<SysFile> upload = fileService.upload(multipartFile, 1);
  87. return upload.getData();
  88. } catch (IOException e) {
  89. throw new RuntimeException(e);
  90. }
  91. }

遇到的问题:

将模板放入resource目录下,无法读取,maven打包时,报错MalformedInputException。

解决方法:解决 `MalformedInputException: Input length = 1` 错误

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/709523
推荐阅读
相关标签
  

闽ICP备14008679号