pom.xml文件
<dependency>
<groupId>org.freemarkergroupId>
<artifactId>freemarkerartifactId>
<version>2.3.30version>
dependency>
FreemarkerUtil.java
package com.jeesite.modules.jss.Utils;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Map;
public class FreemarkerUtil {
public Template getTemplate(String name) {
try {
这里是对应的你使用jar包的版本号:2.3.30
Configuration configuration = new Configuration(Configuration.VERSION_2_3_30);
// 空值时不显示
configuration.setClassicCompatible(true);
//第二个参数 为你对应存放.ftl文件的包名 (template在resource文件夹下)
configuration.setClassForTemplateLoading(this.getClass(), "/template");
Template template = configuration.getTemplate(name);
return template;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
在excel/word文件里画好模板,将excel/word文件另存为.xml文件。在template文件夹下新建模板文件,后缀为.ftl,然后将.xml文件的内容复制到模板文件中。
循环数据集行的部分,在ftl文件里使用<#list>实现:
excel循环行:
word循环行:
<#list priceInfo.jssProductList as product>
<w:tr w:rsidR="003F3BC7" w:rsidRPr="00F12F15" w14:paraId="3C7E34E4" w14:textId="77777777" w:rsidTr="00E74DC5">
<w:tc>
<w:tcPr>
<w:tcW w:w="1134" w:type="dxa"/>
w:tcPr>
<w:p w14:paraId="0E684322" w14:textId="6CB43827" w:rsidR="003F3BC7" w:rsidRPr="00F12F15" w:rsidRDefault="00FA3CA0" w:rsidP="003F3BC7">
<w:pPr>
<w:jc w:val="left"/>
<w:rPr>
<w:sz w:val="18"/>
<w:lang w:eastAsia="zh-CN"/>
w:rPr>
w:pPr>
<w:r>
<w:rPr>
<w:rFonts w:ascii="等线" w:eastAsia="等线" w:hAnsi="等线" w:hint="eastAsia"/>
<w:sz w:val="18"/>
<w:lang w:eastAsia="zh-CN"/>
w:rPr>
<w:t>${product.itemDesc}w:t>
w:r>
w:p>
w:tc>
w:tr>
#list>
金额格式化: ${(product.price?string('0.00'))!'0.00'}
日期格式化: ${contract.wantedDeliveryDate?string('yyyy-MM-dd')}
值为NULL时格式转化会出错,加上 “!", !"后的内容表示数据为null时显示的值
<#list jssOrders.jssProductList as product>
<#list product.jssDeviceList as device>
<#if (device_index == 0)>
<Row ss:AutoFitHeight="0">
<Cell ss:MergeDown="${product.jssDeviceList?size-1}" ss:StyleID="m3012774613960"><Data ss:Type="String">${product.itemDesc}Data>Cell>
<Cell ss:MergeDown="${product.jssDeviceList?size-1}" ss:StyleID="m3012774613980"><Data ss:Type="String">${product.model}Data>Cell>
<Cell ss:MergeDown="${product.jssDeviceList?size-1}" ss:StyleID="m3012774614020"><Data ss:Type="String">${product.qty}Data>Cell>
<Cell ss:StyleID="s75"><Data ss:Type="String">${device.deviceNo}Data>Cell>
<Cell ss:StyleID="s76"><Data ss:Type="String">${device.remark2}Data>Cell>
Row>
#if>
<#if (device_index > 0)>
<Row ss:AutoFitHeight="0">
<Cell ss:Index="4" ss:StyleID="s75"><Data ss:Type="String">${device.deviceNo}Data>Cell>
<Cell ss:StyleID="s76"><Data ss:Type="String">${device.remark2}Data>Cell>
Row>
#if>
#list>
#list>
语法:
<#list itemList as item>
集合长度:${itemList?size}
循环时的下标:item_index
合并单元格:ss:MergeDown="${product.jssDeviceList?size-1}"
连续发送多个文件下载请求时,先发起的请求会被自动取消掉,只能成功下载最后一个请求的文件。所以创建多个临时iframe进行请求。
// 打印按钮按下事件
$("#btnPrint").click(function(){
$("#dataGrid input:checkbox:checked").each(function(){
var id = $(this).attr("id").substr(13);
var iframe = document.createElement("iframe");
iframe.style.display = "none";
iframe.style.height = 0;
iframe.src = "${ctx}/jss/jssContract/exportData?id=" + id;
document.body.appendChild(iframe);
// 5分钟之后删除
setTimeout(()=>{
iframe.remove();
}, 5 * 60 * 1000);
});
});
/**
* 导出数据到Excel/Word文件
* @param request
* @param response
* @return
* @throws IOException
*/
@RequestMapping(value = "exportData")
public void exportData(String id , HttpServletRequest request, HttpServletResponse response) throws IOException, TemplateException
{
//获取数据
JssContract jssContract = jssContractService.getExportData(id);
Map<String,Object> root = new HashMap<String,Object>();
root.put("contract", jssContract);
// Excel
response.setHeader("content-Type", "application/msexcel");
// word
// response.setHeader("content-Type", "application/msword");
// 下载文件的默认名称 (word时后缀名改为.doc)
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("合同_" + jssContract.getContractNo() + ".xls", "UTF-8"));
FreemarkerUtil freemarkerUtil = new FreemarkerUtil();
// 配置的template文件夹下
Template template = freemarkerUtil.getTemplate("jssContractTemplate.ftl");
template.process(root,new OutputStreamWriter(response.getOutputStream()));
}
默认情况下,freemarker的变量必须有值,如果没有被赋值的变量在页面上使用就会抛出异常,出错的信息都会显示在页面上。
解决办法:
方法一、我们可以在页面上使用freemarker变量时 以 ${xxx?if_exists} 来处理空值的情况,或采用默认值的方法避免此类问题。但每个freemarker变量都这样处理确实比较让人心烦,请看以下方法。
方法二、在类路径下 加入 freemarker.properties 文件,里面配置 classic_compatible=true。
方法三、通过freemarker.template.Configuration的 config.setClassicCompatible(true);
(通过源码我们看到,其实)方法二、方法三是思想是一致的,只是实现方法不同而已。如果应用中已经存在了 freemarker.properties 并配置了其他的属性,可以在这里配置,否则推荐使用方法三.)
方法四、在ftl文件内引入 。
总结:方法四是需要在每个需要这样处理的页面都要引入的,比较麻烦,、还是选择使用 方法二、方法三好了。但是方法二、方法三也不是万能的。例如我在action中定义一个MyBean类的对象为 myBean,MyBean中有Comp属性。在页面上就要 用如下语句使用: m y B e a n . c o m p , 这 里 m y B e a n 可 能 为 n u l l , c o m p 也 可 能 为 n u l l 。 这 时 候 就 要 使 用 方 法 一 了 , {myBean.comp},这里myBean可能为null,comp也可能为null。这时候 就要使用方法一了, myBean.comp,这里myBean可能为null,comp也可能为null。这时候就要使用方法一了,{(myBean.comp)!} 或 ${(myBean.comp)?if_exists}。
${book.name?if_exists } //用于判断如果存在,就输出这个值
${book.name?default(‘xxx’)}//默认值xxx
${book.name!"xxx"}//默认值xxx
${book.date?string('yyyy-MM-dd')} //日期格式
${book?string.number} 20 //三种不同的数字格式
${book?string.currency}--<#-- $20.00 -->
${book?string.percent}—<#-- 20% -->
<#assign foo=ture /> //声明变量,插入布尔值进行显示
${foo?string("yes","no")} <#-- yes -->
大小比较符号使用需要注意:(xml的原因),可以用于比较数字和日期
使用lt、lte、gt和gte来替代<、<=、>和>= 也可以使用括号<#if (x>y)>
<#if condition>...
<#elseif condition2>...
<#elseif condition3>......
<#else>...
空值判断:<#if book.name?? >
<#switch value>
<#case refValue1>
...
<#break>
<#case refValue2>
...
<#break>
<#default>
...
#switch>
<#list student as stu>
${stu}<br/>
#list>
item_index:当前变量的索引值
item_has_next:是否存在下一个对象 其中item名称为as后的变量名,如stu
集合长度判断
<#if student?size != 0> 判断=的时候,注意只要一个=符号,而不是==
5.ftl文本中的空格会被忽略掉,且使用 不能解析,可使用阿斯克码表示空格