项目场景:
项目中需要在word中生成自定义的表格,框架自带的方法和工具类无法满足,需要自定义生成表格。
生成如下样式的表格:
解决方案:
自定义一个word表格绘制工具,自定义一个tableRenderData
,然后使用Poi-tl的 TableRenderPolicy
解析并绘制表格。
依赖引入:
<!-- poi-tl -->
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.10.0</version>
</dependency>
<!-- spring-el表达式 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
</dependency>
1、创建word表格自定义绘制工具:
public class CustomTableUtil {
private static final Logger LOG = LoggerFactory.getLogger(CustomTableUtil.class);
/**
* 空气质量日历表格
* @param data 数据集
* @param calendars 监测数据
* @param tableTag 模板占位符
* @param title 首行标题
*/
public static void renderAirQualityCalendar(Map<String,Object> data, List<AirQualityCalendar> calendars, String tableTag,String title) {
if (calendars == null || calendars.isEmpty()){
data.put(tableTag,null);
return;
}
TableRenderData tableRenderData;
//标题行合并规则
MergeCellRule rule = MergeCellRule.builder().map(MergeCellRule.Grid.of(0, 0),
MergeCellRule.Grid.of(0, 32)).build();
//设置列宽
double[] colWidthsCm = new double[33];
for (int i = 0; i < 33; i++) {
if (i == 0) {
colWidthsCm[i] = 1.1;
}else if(i == 1) {
colWidthsCm[i] = 1.4;
}else {
colWidthsCm[i] = ((27.2-2.5)/31);
}
}
//创建表格
tableRenderData = Tables.ofWidth(27.2,colWidthsCm)
.mergeRule(rule).border(BorderStyle.DEFAULT).center().create();
//创建首行标题行
RowRenderData titleRow = Rows.of( title,title).textColor("000000")
.bgColor("FFFFFF").textFontFamily("宋体").textBold().textFontSize(8).center().create();
tableRenderData.addRow(titleRow);
//表头
RowRenderData row0 = Rows.of( "名称","年月").textColor("000000")
.bgColor("FFFFFF").textFontFamily("宋体").textBold().textFontSize(8).center().create();
tableRenderData.addRow(row0);
//为一二行添加列 此处也可满足动态表头的需求
for (int i = 1; i < 32; i++) {
CellRenderData firstCell;
CellRenderData titleCell;
if (i<10){
firstCell = Cells.of(title).create();
titleCell = Cells.of("0"+i).create();
}else {
firstCell = Cells.of(title).create();
titleCell = Cells.of(i+"").create();
}
titleRow.addCell(firstCell);
row0.addCell(titleCell);
}
//通过反射获取属性名称批量添加每天的aqi数据行 具体逻辑根据自己的实际需求来
for (AirQualityCalendar calendar : calendars) {
RowRenderData row = Rows.of(calendar.getPointName(), calendar.getDate())
.bgColor("FFFFFF").center().create();
Class<? extends AirQualityCalendar> calendarClass = calendar.getClass();
Field[] fields = calendarClass.getDeclaredFields();
for (Field field : fields) {
if (field.getName().startsWith("day")) {
CellRenderData titleCell = null;
try {
field.setAccessible(true);
String aqi = (String) field.get(calendar);
if (StringUtils.isEmpty(aqi)) {
titleCell = Cells.of("").create();
row.addCell(titleCell);
continue;
}
//通过aqi数值设置背景色
if ("--".equals(aqi)){
titleCell = Cells.of(aqi).create();
}else {
int aqiInt = Integer.parseInt(aqi);
if(aqiInt < 100){
titleCell = Cells.of(aqi).bgColor("00e400").create();
}
if(aqiInt >= 50 && aqiInt < 100){
titleCell = Cells.of(aqi).bgColor("FFFF00").create();
}
if(aqiInt >= 100 && aqiInt < 150){
titleCell = Cells.of(aqi).bgColor("ff7e00").create();
}
if(aqiInt >= 150 && aqiInt < 200){
titleCell = Cells.of(aqi).bgColor("99004c").create();
}
if(aqiInt >= 200){
titleCell = Cells.of(aqi).bgColor("7e0023").create();
}
}
row.addCell(titleCell);
} catch (IllegalAccessException e) {
LOG.error("绘制空气质量日历表格失败:数据类型转换错误");
}
}
}
setStyle(row,"宋体","Times New Roman",7.5);
tableRenderData.addRow(row);
}
data.put(tableTag,tableRenderData);
}
/**
* 设置段落字体
* @param row 表格行
* @param fontFamily 本地字体
* @param westernFontFamily 西文字体
* @param fontSize 字号
*/
private static void setStyle(RowRenderData row,String fontFamily,String westernFontFamily,double fontSize){
for (CellRenderData cell : row.getCells()) {
for (RenderData content : cell.getParagraphs().get(0).getContents()) {
TextRenderData renderData = (TextRenderData) content;
Style style = new Style();
style.setColor("000000");
style.setFontSize(fontSize);
style.setFontFamily(fontFamily);
style.setWesternFontFamily(westernFontFamily);
renderData.setStyle(style);
}
}
}
2、创建word模板,设置占位符:
创建一个word模板,放到资源目录下(也可配置到数据或存放到文件服务器)。
3、创建一个文件流导出的处理类:
public class XWPFTemplateUtil {
private static final Logger LOG = LoggerFactory.getLogger(XWPFTemplateUtil.class);
/**
* 通过模板下载文件
* @param templateName word模板文件名称
* @param fileName 导出的文件名称
* @param data 需要填充的数据
*/
public static void renderAndSaveDocxFile(String templateName, String fileName, Map<String,Object> data,
ConfigureBuilder builder, HttpServletResponse response) throws IOException {
//获取word模板文件
Resource resource = new ClassPathResource("static/" + templateName);
// 浏览器端下载
response.setCharacterEncoding("utf-8");
response.setContentType("application/msword;charset=utf-8");
response.setHeader("Content-Disposition", "attachment;filename*=UTF-8''"
.concat(URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20")));
response.flushBuffer();
try (InputStream inputStream = resource.getInputStream();
OutputStream os = response.getOutputStream()) {
if (builder == null) {
builder = Configure.builder();
}
// 使用 springEl 表达式
builder.useSpringEL();
Configure config = builder.build();
XWPFTemplate.compile(inputStream, config).render(data).writeAndClose(os);
} catch (Exception e) {
e.printStackTrace();
LOG.error("导出文件失败:"+e.getMessage());
}
}
}
4、设置数据,绑定插件,导出结果:
//获取数据
List<AirQualityCalendar> airQualityCalendars1 = queryAirQualityCalendar(dataParam);
CustomTableUtil.renderAirQualityCalendar(data,airQualityCalendars1,"AirQualityCalendar1","首行标题");
List<AirQualityCalendar> airQualityCalendars2 = queryAirQualityCalendar(lastParam);
CustomTableUtil.renderAirQualityCalendar(data,airQualityCalendars2,"AirQualityCalendar2","首行标题");
ConfigureBuilder builder = Configure.builder();
//绑定插件
builder.bind("AirQualityCalendar1", new TableRenderPolicy());
builder.bind("AirQualityCalendar2", new TableRenderPolicy());
try {
XWPFTemplateUtil.renderAndSaveDocxFile("空气质量日历模板.docx","空气质量日历.docx",data,builder,response);
} catch (Exception e) {
log.error("生成空气质量日历失败:"+e.getMessage());
}