社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
前端vue做的各种维度的报表,原来是通过前端整体截屏导出成PDF,但部分报表在遇到跨页时会被截断,客户体验极差。然后又考虑客户可能需要修改报表中的一些内容,因此需要导出成word文档解决跨页截断和满足修改报表内容的问题。前期解决方案预研时试过jacob、poi方案,但jacob只能用于windows平台(要引用一个dll文件),并且jacob和poi都存在样式方面的难题。后来通过其他渠道了解了freemarker,于是通过freemarker的把前端请求的报表数据填充到模板文件,生成word文档(导出功能由后端java实现)
首先上一张效果图,由于数据保密性,故前端页面的报表原样就不展示,导出的word文档的效果图和页面报表几乎一样
#三、功能点
#六、实现步骤
##1. 设计模板
按照前端报表展示样式,设计模板,并将模板中需要动态被参数填充的部分使用占位符代替,如标题使用${title},图表标题使用${title_1}、${title_2}、${title_3},图表总结词用${summary_1},${summary_2},${summary_3},以此类推.下图为使用占位符替换之后的word模板
##2. 另存模板为xml
上一步设计好模板并替换关键内容为占位符后,需要保存成xml模板文件,然后将xml模板文件中的图片base64编码替换成占位符,例如下面模板片段
<pkg:part pkg:name="/word/media/image16.png" pkg:contentType="image/png" pkg:compression="store">
<pkg:binaryData>${base64_11}</pkg:binaryData>
</pkg:part>
<pkg:part pkg:name="/word/media/image11.png" pkg:contentType="image/png" pkg:compression="store">
<pkg:binaryData>${base64_9_1}</pkg:binaryData>
</pkg:part>
<pkg:part pkg:name="/word/media/image9.png" pkg:contentType="image/png" pkg:compression="store">
<pkg:binaryData>${base64_8_2}</pkg:binaryData>
</pkg:part>
<pkg:part pkg:name="/word/media/image10.png" pkg:contentType="image/png" pkg:compression="store">
<pkg:binaryData>${base64_8_3}</pkg:binaryData>
</pkg:part>
<pkg:part pkg:name="/word/media/image8.png" pkg:contentType="image/png" pkg:compression="store">
<pkg:binaryData>${base64_8_1}</pkg:binaryData>
</pkg:part>
##3. 新建maven工程
本人使用的开发工具是Idea 2018.1版本,创建maven项目并创建包名,结构如下:
export-doc
└─src
│ ├─main
│ │ ├─java
│ │ │ └─com
│ │ │ └─zhuxl
│ │ │ └─exportdoc
│ │ │ │
│ │ │ ├─component
│ │ │ │ └─handler
│ │ │ │
│ │ │ ├─configuration
│ │ │ │
│ │ │ ├─controller
│ │ │ │
│ │ │ ├─entity
│ │ │ │
│ │ │ ├─service
│ │ │ │ │
│ │ │ │ └─impl
│ │ │ │
│ │ │ └─util
│ │ │
│ │ └─resources
│ │
│ └─test
│ └─java
└─pom.xml
##4. 添加相关依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.5.13.RELEASE</version>
<optional>true</optional>
</dependency>
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
(exclude = {DataSourceAutoConfiguration.class})
参数表示不自动加载参数连接数据库,因为本demo无数据库连接,仅演示service里调用工具类方法导出word,不需要操作数据库,因此需要添加这个参数,否则启动会报连接数据库异常。
<dependency>
<groupId>com.didispace</groupId>
<artifactId>spring-boot-starter-swagger</artifactId>
<version>1.4.1.RELEASE</version>
</dependency>
<!-- LOMBOK begin -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
</dependency>
<!-- LOMBOK end -->
<!-- FASTJSON begin -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.31</version>
</dependency>
<!-- FASTJSON end -->
<!-- FREEMARKER begin -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
<!-- FREEMARKER end -->
##5. 创建接口请求参数对象类
使用java类来接收请求的json数据
@Data
@ApiModel(value = "贫困人群报表导出请求对象")
public class ReportExportWordRequest {
@ApiModelProperty(value = "区域级别", name = "unitLevel")
private Integer unitLevel;
@ApiModelProperty(value = "区域编码", name = "unitCode")
private String unitCode;
@ApiModelProperty(value = "报表类型", name = "type", notes = "poverty:贫困人群报告;disable:残疾人群报告;poverty_disable:贫困且残疾人群报告")
private String type;
@ApiModelProperty(value = "报表标题", name = "title")
private String title;
@ApiModelProperty(value = "报告水印", name = "watermark")
private String watermark;
@ApiModelProperty(value = "报表生成日期", name = "date")
private String date;
@ApiModelProperty(value = "该区域报表描述第一段", name = "description1")
private String description1;
@ApiModelProperty(value = "该区域报表描述第二段", name = "description2")
private String description2;
@ApiModelProperty(value = "报表中每个图表的内容列表", name = "reports")
private List<ReportContentRequest> reports;
}
@Data
@ApiModel("单个图表请求对象")
public class ReportContentRequest {
@ApiModelProperty(value = "报表中排列序号", name = "serial")
private Integer serial;
@ApiModelProperty(value = "单个图表标题", name = "title")
private String title;
@ApiModelProperty(value = "单个图表base64编码值", name = "base64")
private String base64;
@ApiModelProperty(value = "单个图表内容总结", name = "summary")
private String summary;
@ApiModelProperty(value = "该标题下存在多个报表", name = "children")
private List<ReportContentRequest> children;
}
该工具类是实现导出word功能的核心类,读取模板文件,格式化请求参数,填充模板生成word文档的功能都在此工具类完成
public class WordGeneratorUtils {
private static Configuration configuration = null;
private static Map<String, Template> allTemplates = null;
private static class FreemarkerTemplate {
public static final String POVERTY = "poverty";
}
static {
configuration = new Configuration(Configuration.VERSION_2_3_28);
configuration.setDefaultEncoding("utf-8");
configuration.setClassForTemplateLoading(WordGeneratorUtils.class, "/freemarker/template");
allTemplates = new HashMap();
try {
allTemplates.put(FreemarkerTemplate.POVERTY, configuration.getTemplate(FreemarkerTemplate.POVERTY + ".ftl"));
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
private WordGeneratorUtils() {
throw new AssertionError();
}
public static File createDoc(Map<String, String> dataMap) {
try {
String name = dataMap.get("title") + dataMap.get("date") + ".doc";
File f = new File(name);
Template t = allTemplates.get(dataMap.get("template"));
// 这个地方不能使用FileWriter因为需要指定编码类型否则生成的Word文档会因为有无法识别的编码而无法打开
Writer w = new OutputStreamWriter(new FileOutputStream(f), "utf-8");
t.process(dataMap, w);
w.close();
return f;
} catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException("生成word文档失败");
}
}
public static Map<String, String> parseToMap(ReportExportWordRequest request) {
Map<String, String> datas = new HashMap(32);
//主标题
datas.put("title", request.getTitle());
datas.put("date", request.getDate());
datas.put("watermark", request.getWatermark());
datas.put("description1", request.getDescription1());
datas.put("description2", request.getDescription2());
//遍历设置报表
List<ReportContentRequest> contents = request.getReports();
datas.put("template", request.getType());
for (ReportContentRequest c : contents) {
if (c.getChildren() == null || c.getChildren().size() == 0) {
//无子报表
datas.put("title_" + c.getSerial(), c.getTitle());
datas.put("base64_" + c.
版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/musuny/article/details/80783891
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
-
发表于 2020-03-08 13:23:19
- 阅读 ( 646 )
- 分类:前端
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!