使用Freemarker的宏和模板生成html打印

一、背景

最近公司有业务,需要传递数据将其生成静态HTML,然后HTML可转PDF,也可打印。网上查阅了一下freemarker,颇感兴趣,
学习使用后,记录过程,方便回顾,同时也希望能帮到各位猿友。

 

二、涉及技术

freemarker、springboot、lodop、itext

 

三、业务步骤

1.freemarker的maven依赖

<dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

2.freemarker的配置(我用的是yml文件)

# 配置数据源
spring:
    freemarker:
        allow-request-override: false
        cache: false
        check-template-location: true
        charset: UTF-8
        content-type: text/html
        expose-request-attributes: false
        expose-session-attributes: false
        expose-spring-macro-helpers: false
        suffix: .html
        settings:
            default_encoding: UTF-8
        enabled: true
        request-context-attribute: rc
        template-path : D:/yxhData/staticData/res/template/html

其中template-path为模版的路径

3.freemarker的宏

import java.io.IOException;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.chongdong.common.exception.APIException;
import com.chongdong.common.util.JacksonUtils;
import com.chongdong.common.util.freemarker.Freemarker;
import com.chongdong.data.entity.OrdBase;
import com.chongdong.data.entity.OrdExtend;
import com.chongdong.service.order.biz.OrdBaseBiz;

import freemarker.core.Environment;
import freemarker.template.TemplateDirectiveBody;
import freemarker.template.TemplateDirectiveModel;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
import freemarker.template.WrappingTemplateModel;
/**
 * 订单宏
 * @author yanxh
 *
 */
@Repository
public class OrderDetail implements TemplateDirectiveModel {
	
	@Autowired
	private OrdBaseBiz ordBaseBiz;
	
	/**
	* @param env 系统环境变量,通常用它来输出相关内容,如Writer out = env.getOut();
	* @param map 自定义标签传过来的对象,其key=自定义标签的参数名,value值是TemplateModel类型,而TemplateModel是一个接口类型,通常我们都使用TemplateScalarModel接口来替代它获取一个String 值,如TemplateScalarModel.getAsString();当然还有其它常用的替代接口,如TemplateNumberModel获取number,TemplateHashModel等
	* @param loopVars 循环替代变量
	* @param body 用于处理自定义标签中的内容;当标签是<@myDirective />格式时,body=null
	*/
	@Override
	public void execute(Environment env, Map map, TemplateModel[] loopVars,
			TemplateDirectiveBody body) throws TemplateException, IOException {
		
		/**
		 * 订单ID字符串
		 */
		Long orderId = Freemarker.getLong(map, "orderId");
		
		
		/**
		 * 获取订单信息
		 */
		OrdBase ordBase = null;
		try {
			ordBase = ordBaseBiz.getDetail(orderId);
		} catch (APIException e) {
			e.printStackTrace();
		}
		
		/**
		 * 获取dataJson
		 */
		Map<String, Object> jsonMap = null;
		try {
			OrdExtend ordExtend = ordBase.getOrdExtend();
			if(ordExtend != null) {
				String dataJson = ordExtend.getDataJson();
				if(!StringUtils.isBlank(dataJson)) {
					jsonMap = JacksonUtils.json2map(dataJson);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		
		loopVars[0]=WrappingTemplateModel.getDefaultObjectWrapper().wrap(ordBase);
		loopVars[1]=WrappingTemplateModel.getDefaultObjectWrapper().wrap(jsonMap);
		body.render(env.getOut()); 
	}

}

这是一个订单详情的宏定义,参数为订单的ID,返回值为订单详细信息和订单扩展信息

4.freemarker的配置类



import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import com.chongdong.service.order.biz.macro.OrdBatchDetail;
import com.chongdong.service.order.biz.macro.OrdBatchDetailWithField;
import com.chongdong.service.order.biz.macro.OrderDetail;

import freemarker.template.Configuration;
import freemarker.template.TemplateModelException;

@org.springframework.context.annotation.Configuration
public class FreemarkerConfig {

	@Autowired
	private Configuration configuration;
	
	@Autowired
	private OrderDetail orderDetail;
	
	@Autowired
	private OrdBatchDetail ordBatchDetail;
	
	@Autowired
	private OrdBatchDetailWithField ordBatchDetailWithField;

	@Value("${spring.freemarker.template-path}")
    private String templateLoaderPath; // 模板物理路径
	
	@PostConstruct
	public void setSharedVariable() throws TemplateModelException {
		configuration.setSharedVaribles(getSharedVaribles());
		configuration.setClassicCompatible(true); // 处理页面空值
		// 使用物理地址作为模版路径
		try {
			configuration.setDirectoryForTemplateLoading(new File(templateLoaderPath));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public Map<String, Object> getSharedVaribles() {
		Map<String, Object> sharedVaribles = new HashMap<String, Object>();
		sharedVaribles.put("_order", orderDetail);
		sharedVaribles.put("_ordBatch", ordBatchDetail);
		sharedVaribles.put("_ordBatchWithField", ordBatchDetailWithField);
		return sharedVaribles;
	}

}

配置类设置freemarker的一些参数,同时将宏加入到配置中

5.模版

<!DOCTYPE HTML>
<html>
<head>
	<meta charset="utf-8" />
	<link rel="stylesheet" href="../../res/css/reset.css" type="text/css" media="screen" />
	<script type="text/javascript" src="../../res/js/jquery-1.9.1.min.js"></script>
</head>
<body>
	<div class="printList_box">
	   <#list orderIds?split(",") as orderId>
		   <@_order orderId="${orderId}"; ordBase, jsonMap>
			   <div class="pb_logo"><img src="../../res/images/Buyers/pb_logo.png" /></div>
			   <h2 class="plb_h2">${ordBase.orgName}电子签收单</h2>
			   <table class="plb_table">
				   <tr>
					   <td class="plb_td_w10">订单编号</td>
					   <td>${ordBase.id?c}</td>
					   <td class="plb_td_w10">订单金额</td>
					   <td class="plb_td_w10" colspan="2">¥${ordBase.amount?c}</td>
				   </tr>
				   <tr>
					   <td class="plb_td_w10">报销批次</td>
					   <td>${batchNo}</td>
					   <td class="plb_td_w10">采购时间</td>
					   <td colspan="2">${ordBase.createTime?string('yyyy-MM-dd HH:mm:ss')}</td>
				   </tr>
				   <tr>
					   <td class="plb_td_w10">采购人(学号)</td>
					   <td>${ordBase.buyerName}(${jsonMap.buyerNo})</td>
					   <td class="plb_td_w10">所属实验室</td>
					   <td colspan="2">${jsonMap.byraccountDep}</td>
				   </tr>
				   <tr>
					   <td class="plb_td_w10">实验室负责人(工号)</td>
					   <td>${jsonMap.byraccountDirect2UerName}(${jsonMap.byraccountDirect2UerNo})</td>
					   <td class="plb_td_w10"></td>
					   <td colspan="2"></td>
				   </tr>
				   <tr class="y_tr_h50">
					   <td class="plb_td_w10">收货地址</td>
					   <td colspan="4">${ordBase.ordAddress.provinceName}${ordBase.ordAddress.cityName}${ordBase.ordAddress.address}</td>
				   </tr>
				   <tr>
					   <td class="plb_td_w10">供应商名称</td>
					   <td>${ordBase.prvName}</td>
					   <td class="plb_td_w10">经费出处</td>
					   <td colspan="2">${jsonMap.byraccountName}</td>
				   </tr>
				   <tr>
					   <td class="plb_td_w10">商品编号</td>
					   <td class="plb_td_w10">商品名称</td>
					   <td class="plb_td_w10">数量</td>
					   <td class="plb_td_w10">单价</td>
					   <td class="plb_td_w10">小计</td>
				   </tr>
				   <#list ordBase.ordProducts as prd>
					   <tr>
						   <td>${prd.code}</td>
						   <td>${prd.name}&nbsp;&nbsp;&nbsp;${prd.nameEnglish}</td>
						   <td>${prd.prodCount?c}</td>
						   <td>¥${prd.tradePrice?c}</td>
						   <td>¥${(prd.prodCount * prd.tradePrice)?c}</td>
					   </tr>
				   </#list>
					<tr class="td_b_none">
						<#list jsonMap.approvalPoints as node>
							<td>${node.approvalOrg}:${node.approvalerName}<br />${node.approvalTime!''}</td>
						</#list>
						<td>签收人:${jsonMap.signerName}<br />${jsonMap.signTime!''}</td>
					</tr>
			   </table>
			   <br/>
			   <br/>
			</@_order>
	   </#list>
	</div>
</body>
</html>
<style>
	body{
		font-family:SimSun
	}
	.printList_box{
		width: 90%;
		margin: 0 auto;
		height: auto;
		position: relative;
		padding-bottom: 50px;
	}
	.plb_h2{
		text-align: center;
		line-height: 40px;
		font-size: 20px;
		padding-top: 20px;
		font-weight: bold;
	}
	.plb_table{
		width: 100%;
		border-collapse:collapse;
	}
	.plb_table tr{}
	.plb_table tr td{
		text-align: center;
		border: 1px solid #ccc;
		font-size: 12px;
		line-height: 16px;
		padding: 5px;
		word-break: break-all;
		word-wrap:break-word;
	}
	.plb_table tr.td_b_none td{
		border: none;
		text-align: left;
	}
	.plb_table tr.plb_tr01{
		height: 28px;
	}
	.plb_table tr.plb_tr01 td{
		font-size: 14px;
		font-weight: bold;
		text-align: left;
		border: none;
	}
	.plb_table tr.plb_tr02{
		height: 34px;
	}
	.plb_table tr td.b_r_none{
		border-right: none;
	}
	.plb_table tr td.b_l_none{
		border-left: none;
	}
	.table_Remarks{
		width: 100%;
		padding: 50px 0;
		font-size: 14px;
		line-height: 24px;
	}
	.plb_td_w70{
		width: 70px;
	}
	.plb_td_w100{
		width: 100px;
	}
	.plb_td_w10{
		width: 120px;
	}
	.plb_purpose{
		width: 90%;
		height: 100%;
		padding-left: 5%;
		text-align: left;
	}
	.plb_box{
		padding: 10px;
	}
	.plb_text_p01{
		height: 100px;
		text-align: left;
	}
	.plb_text_p02{
		text-align: right;
	}
	.plb_text_p02 span{
		display: inline-block;
		padding: 0 50px 0 100px;
	}
	.Total_box{
		width: 100%;
		height: auto;
	}
	.Total_box p{
		line-height: 40px;
		text-align: right;
	}
	.Total_box div{}
	.Total_box div span{
		float: left;
		display: inline-block;
		width: 18%;
		font-weight: bold;
		font-size: 14px;
	}
	.Total_box div em{
		float: right;
		font-weight: bold;
		font-size: 14px;
	}
	.pb_left{
		position: absolute;
		left: -20px;
		top: 88px;
		width: 10px;
	}
	.pb_right{
		position: absolute;
		right: -20px;
		top: 88px;
		width: 10px;
	}
	.pb_logo{
		position: absolute;
		right: 0;
		top: 20px;
		width: 170px;
		opacity: 0.7;
	}
	.pb_logo img{
		display: block;
		width: 100%;
	}
	.pb_right span{
		padding-bottom: 20px;
	}
	.plb_com_text{
		width: 200px;
	}
	.plb_table tr td.pb_ta_left{
		text-align: left;
		font-weight: bold;
	}
	.pb_data_box{
		width: 150px;
	}
	.pb_data_box span{
		display: inline-block;
		width: 50px;
		text-align: left;
		font-weight: bold;
	}
	.ys_box01{
		text-align: left;
		padding: 20px 0 20px 20px;
		overflow: hidden;
	}
	.ys_box01 span{
		float: left;
		padding: 0 5px;
		margin-right: 20px;
	}
	.ys_box01 input{
		float: left;
		margin-top: 3px;
	}
	.ys_box02{
		text-align: left;
		padding-bottom: 50px;
		padding-left: 20px;
	}
	.ys_box02 p{
		padding: 10px 0;
	}
	.ys_box02 span{
		float: left;
		padding-right: 20px;
	}
	.ys_box02 input{
		float: left;
		margin-top: 3px;
		margin-right: 5px;
	}
	.ys_box02 font{
		display: inline-block;
		padding: 0 200px 100px 0;
	}
	.y_tr_h50{
		height: 50px;
	}
</style>

6.生成html

	/**
	 * 生成html
	 * @param freemarkerCfg freemarker的默认配置
	 * @param data 需要传递至模版页面的参数
	 * @param templatePath 模版页面的名称
	 * @param htmlPath 生成HTML的存放路径
	 * @throws TemplateNotFoundException
	 * @throws MalformedTemplateNameException
	 * @throws ParseException
	 * @throws IOException
	 * @throws TemplateException
	 */
    public static void createHTML(Configuration freemarkerCfg,Map<String,Object> data,String templatePath,String htmlPath) throws TemplateNotFoundException,
    MalformedTemplateNameException, ParseException, IOException, TemplateException{
        Template template = freemarkerCfg.getTemplate(templatePath,"UTF-8");
        File htmlFile = new File(htmlPath);
        if(!htmlFile.getParentFile().exists()) {
        	htmlFile.getParentFile().mkdirs();
        }
        Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(htmlFile), "UTF-8"));
        template.process(data, out);
        out.flush();
        out.close();
    }

 

四、过程讲解

       调用接口调试工具(我用的是自己搭建的swagger,具体步骤可参考5分钟学会swagger配置-中文界面配置,也可使用postman)访问接口调用第6步的方法,模版接到请求后开始向指定路径输出静态页面,期间调用宏查询相关数据。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值