将一整个页面类似大屏的数据模块,全部输出成word,涉及到雷达图,折线图,表单,饼图等多种数据结构转word
poi-tl是一个特别好用的工具,你想要绘画什么图形,只要你按他的对应结构封装返回,poi会自动帮你绘画图表,填充数据,
前面都是很基础的,不想看的可直接往下拉,下面有图形
/**
* ReportToWordUtil
*生成报告Word工具
* @author Zhang Zhi Hao
* 2022/9/07
*/
@Slf4j
@Service
public class ReportToWordUtil {
@Autowired
private IxxxService xxxService;//你的service业务类
private String profile;//你项目保存的路径 每个项目不一样,小伙伴自己悟
/**
* 获取路径 入参根据你们实际项目来,ruoyi框架的话可以套用SysConfig
* @param sysConfig
*/
@Autowired
public void setProfile(SysConfig sysConfig){
profile = SysConfig.getProfile()+relatePath;
}
private String relatePath = "file/";//业务需求加入的前缀 可不加
/**
* 根据前端传的条件,生成不一样的页面数据
* @param req
* @return
*/
public AjaxResult report(xxxxReq req,HttpServletResponse response){
//模板放在resouce/template目录下,这要不会就自己悟
ClassPathResource classPathResource = new ClassPathResource("./template/reportTimeType.docx");//从项目中获取你的模板
//获取数据
Map <String, Object> datas = getData(req);
//3.创建XWPFTemplate对象,并设置读取模板路径和要渲染的数据
XWPFTemplate template = XWPFTemplate.compile(classPathResource.getStream()).render(datas);
//获取路径
HashMap <String, String> pathMap = makePath();
//输出
FileOutputStream absolutePathFileOutputStream = null;
try {
absolutePathFileOutputStream = new FileOutputStream(pathMap.get("absolutePath"));
template.writeAndClose(absolutePathFileOutputStream);
absolutePathFileOutputStream.close();
FileUtils.writeBytes(pathMap.get("absolutePath"), response.getOutputStream());
} catch (Exception e) {
log.info("获取绝对路径错误");
e.printStackTrace();
return AjaxResult.error("生成失败");
}
return AjaxResult.success();
}
private HashMap <String, String> makePath() {
// 存储的目录 拼接你想要的目录名字,
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("report/")
.append("xxxx").append(".docx");
// 文件相对路径
String pathFile = stringBuilder.toString();
// 组合成文件路径
File file = new File(profile + pathFile);
// 获取文件的所在目录路径
File parent = new File(file.getParent());
// 没有该目录就创建
if (!parent.exists()) {
parent.mkdirs();
}
HashMap<String, String> stringStringHashMap = new HashMap<>();
// 绝对路径
stringStringHashMap.put("absolutePath", file.toString());
// 相对路径
stringStringHashMap.put("relativePath", pathFile);
return stringStringHashMap;
}
}
上面是一些基础,都有对应的注释可看,根据实际需求去做即可,下面是对应图行的方法,先上我word模板,然后再贴每个图形的代码和注释。
在word中注入数据,如{{xxxx}}这是直接注入数据,不转任何结构,{{#xxx}}即转换为表格形式
时间格式记得转换一下再填充 xxxx年xx月xx日,我这没做转换。接下来贴代码更清晰
/**
* 构建word所需要的数据模型
* @return
*/
private Map<String, Object> getData(xxxxReq req){
Map <String, Object> datas = new HashMap <>();
String date = String.valueOf(new Date());
datas.put("reportTimeType",date);
//获取准备填充的数据
List <xxxVo> records= xxxService.xxxListWithOutPage(req);
//全局异常处理
if (records.size()<=0) {
throw new GlobalException("当前页面没有可以生成的数据");
}
//表格数据准备 表结构都是用RowRenderData结构
RowRenderData[] rowRenderData = new RowRenderData[records.size() + 1];
//第0行使我们的表头,可以用字符串写死,也可以用你数据中的字段名称转中文,看需求
rowRenderData[0] = Rows.of("xxx", "xxx", "xxx", "xxx","xxx","xxx","xxx","xxx").center().bgColor("8DB4E3").create();
//赋值
for (int i = 0; i <records.size(); i++) {
rowRenderData[i+1] = Rows.create(records.get(i).getPlanNo(),records.get(i).getWorkLiablerName(),
String.valueOf(records.get(i).getPlanBeginTime()),String.valueOf(records.get(i).getPlanFinishTime()),
records.get(i).getConOperRiskValue(),records.get(i).getBenchmarkRiskValue(),
records.get(i).getConstructionUnitName(),records.get(i).getWorkTeamName()
);
}
//创建结构
TableRenderData tableRenderData = Tables.create(rowRenderData);
//放入data中,"table",对应word中的{{#table}}一一对应,放入data后进行绘画
datas.put("table",tableRenderData);
接下来是复杂图形
1.雷达图
在你的模板中添加雷达图,点设置, 文本选项文本框,可选文字,填写你代码中对应的key{{xxx}}
不是表单,直接填入数据,poi会帮你转换,上代码结构
xxxVo xxx =xxxService.getxxxByReq(req);
//对象判空方法
if (Optional.ofNullable(xxx ).isPresent()) {
//ChartMultiSeriesRenderData 雷达图,折线图,多树状图都可以用这个结构,ofMultiSeries第一个参数对应你的标题,和你图中下面一排的数据
//addSeries,在对应你不同的系列数据,要一一对应上,在折现图中会有一个lineSeries方法,即划线数据
ChartMultiSeriesRenderData chartMultis = Charts.
ofMultiSeries("xxxxxx",
new String[]{"xxx", "xxx", "xxx", "xxx", "xxx"})
.addSeries("违章指数", new Integer[]{
xxx.getXxx(),
xxx.getXxxx(),
xxx.getXxxx(),
xxx.getXxx(),
xxx.getXxxxxx()})
.create();
datas.put("violationIndex",chartMultis);
}
效果图,折现图一样,模板画上折线图,贴参数,就不上了,直接上代码转结构把
//转字符串数组 自己写方法调你需要填充的数据,这里直接给结构,折线图
//addLineSeries 第一个参数为你折线数据的key,可以理解为标签,系列
ChartMultiSeriesRenderData comb = Charts.ofComboSeries("终端数量",stringList.toArray(new String[stringList.size()]))
.addBarSeries("xxx",integers.toArray(new Integer[integers.size()]))
.addLineSeries("xxx",doubles.toArray(new Double[doubles.size()]))
.create();
datas.put("numberOfTerminals",comb);
按poi的结构来,封装对应的数据进去,大同小异,各位大佬,新人刚刚入坑,有不好的地方手下留情,欢迎建议和留言,后续可能会考虑封装一下,针对每个图形单独抽取,还有个饼图的没发出来,大同小异,觉得有需要的同学可以留言