Spring BootSpring-BootSpring cloud

利用thymeleaf双层模板技术,制作个性化报表

2018-03-22  本文已影响148人  staconfree

需求背景

通常我们会碰到一些带表格的报表需求,一般是包含一些表格和一些循环列表的需求,每个表格的样式可以不一样。
有些公司会使用一些专业的报表软件,例如jasperReport,如意报表等,虽然这种软件功能很强大,但是需要一定的学习门槛,而且使用中调整样式并没有想象中方便,最重要的是此类模板设计、模板导出、模板上传都是跟业务系统分开的,不能嵌入系统中使用,不能做到在线编辑,在线预览。
为此,我专门研究了thymeleaf来完成此类需求的办法,希望做到报表上的所有表格都可以通过配置配出来,支持纯文本和动态变量,支持图片,所有表格的样式可以通过css来随意配置,支持循环列表,支持横向和纵向合并单元格
为什么选择thymeleaf而不是选择freemark等其他模板技术,主要基于两个原因:
1、spring官方推荐用thymeleaf,并且springboot有官方适配
2、thymeleaf编写的模板能直接打开预览,不会像freemark破坏了html原本的格式。

实现思路

核心思路是:设计一个根模板(root-template.html),它可以通过设计好的数据库配置生成新的具体模板(biz-template.html),并且biz-template.html里面包含具体的业务数据的变量,这样就可以结合业务数据展示出报表。这就是我称之为双层模板的原因。
技术细节包含以下几点:
1、根模板(root-template.html)如何设计
2、需要用thymeleaf解析html并生成html,就必须使用API的方式去实现,且html的路径可配置,否则线上环境无法生成html到jar包里
3、生成后的html也需要包含thymeleaf的标签,那就需要研究thymeleaf如何生成带标签的模板
4、样式和合并单元格如何控制

根模板设计

根页面定好10个table区域,每个区域都是一样的逻辑,判断table配置的数据集是否为空,为空则整个table不显示,不为空,则先循环table配置的tr信息,tr里面再循环配置的td信息。
根模板如下:


image.png

springboot整合thymeleaf,并支持api的方式生成html

1、pom.xml

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.RELEASE</version>
        <relativePath/>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.10</version>
        </dependency>
    </dependencies>

2、application.properties

server.port=8080

spring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.check-template-location=true
spring.thymeleaf.suffix=.html
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.content-type=text/html
spring.thymeleaf.mode=HTML5

report.templates.path=

3、ThymeleafConfig配置类

@Configuration
@EnableWebMvc
public class ThymeleafConfig extends WebMvcAutoConfiguration {
    @Autowired
    ApplicationContext applicationContext;

    /**
     * eg: E:/data/resources/templates/
     */
    @Value("${report.templates.path}")
    private String reportTemplatesPath;

    @Bean
    public ViewResolver viewResolver(SpringTemplateEngine templateEngine) {
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setTemplateEngine(templateEngine);
        viewResolver.setCache(false);
        return viewResolver;
    }

    @Bean
    public SpringTemplateEngine templateEngine() {
        SpringTemplateEngine engine = new SpringTemplateEngine();
//        engine.setEnableSpringELCompiler(true);
        engine.setTemplateResolver(templateResolver());
        return engine;
    }

    @Bean
    public ITemplateResolver templateResolver() {
        SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
        resolver.setApplicationContext(applicationContext);
        URL resource = this.getClass().getClassLoader().getResource("templates/");       //这里把系统获取到的Class的path替换为源码对应的Path,这样修改的时候就可以动态刷新
        String devResource = resource.getFile().toString().replaceAll("target/classes", "src/main/resources");
        if (reportTemplatesPath!=null&&!"".equals(reportTemplatesPath.trim())){
            devResource=reportTemplatesPath.trim();
        }
        resolver.setPrefix("file://"+devResource);
        resolver.setCacheable(false);//不允许缓存
        resolver.setSuffix(".html");
        return resolver;
    }
}

这样就可以使用以下代码来生成html文件了

    @Autowired
    private TemplateEngine templateEngine;

        Context context = new Context();
        context.setVariable("table1", templateData.getTable1());
        FileWriter write = new FileWriter(devResource+"/biz-template.html");
        templateEngine.process("root-template", context, write);

利用thymeleaf生成带thymeleaf标签的html文件

主要利用两点,

例如,根模板里面:

<div th:attr="'th:text'=${td.value}"></div>

如果td.value=${userName},则通过根模板生成后的子模板html代码是:

<div th:text="${userName}"></div>

看到没,子模板就也可以再用thymeleaf进行解析

这里有两点特别说明:
1、<div th:attr="'th:attr'=${td.value}"></div> 这种方式行不通,通过th:attr无法增加th:attr标签
2、<div th:attr="'th:text'=${td.value}" th:text="${td.value}"></div> 这种方式也不行,只能解析后面那个th:text,解析结果如下:
<div>${userName}</div>
那如果又想加th:text标签又想设置text内容怎么办呢?答案是增加一层div:
<div th:attr="'th:text'=${td.value}" ><div th:text="${td.value}"></div></div>

例如,根模板里面:

<div th:utext="${td.value}"></div>

如果td.value是以下字符串<img th:src="${imgUrl}">,则通过根模板生成后的子模板html代码是:

<div><img th:src="${imgUrl}"></div>

不错吧,这样也可以再用thymeleaf解析

样式和合并单元格问题

使用th:style,th:class,th:rowspan,th:clospan来控制
根模板中要设置全局自定义样式变量可以使用如下配置:

<style th:inline="text">
    [[${style}]]
</style>

效果演示

子模板template2


image.png

子模板2填充数据后:


image.png
子模板template3
image.png

子模板3填充数据后:


image.png

最终的html如果需要转成pdf,请参考最好的html转pdf工具(wkhtmltopdf)

常用语法

注释

1、静态文件打开可以看到,但是thymeleaf生成后看不到

<!--/*--> 
  <div>
     you can see me only before Thymeleaf processes me!
  </div>
<!--*/-->

2、静态文件打开看不到,但是thymeleaf生成后看得到

<span>hello!</span>
<!--/*/
  <div th:text="${...}">
    ...
  </div>
/*/-->
<span>goodbye!</span>

3、块代码段,th:block,可以配合thymeleaf的其他标签去定义代码块,例如th:each如果循环单行<tr>的时候,th:each可以放在<tr>标签里,但是如果要循环多行<tr>的话,则可以配合th:block来使用,如下:

<table>
  <th:block th:each="user : ${users}">
    <tr>
        <td th:text="${user.login}">...</td>
        <td th:text="${user.name}">...</td>
    </tr>
    <tr>
        <td colspan="2" th:text="${user.address}">...</td>
    </tr>
  </th:block>
</table>

删除代码段

以下是Thymeleaf的一个例子。我们可以使用th:remove来删除指定的部分,这在原型设计和调试的时候很有用。

  <tr th:remove="all">
    <td>Mild Cinnamon</td>
    <td>1.99</td>
    <td>yes</td>
    <td>
      <span>3</span> comment/s
      <a href="comments.html">view</a>
    </td>
  </tr>

th:remove可接受的值有5个:

特别注意点

Thymeleaf中无法利用map.key表达式获取到map的键值,特别是使用 th:if="map.key =null" 的时候,如果map里面不包含key,则立马报错,所以如果要用从map里面取某个key,则map里面必须要有这个key值(value为空也必须设置为null),否则会报错

参考

Springboot 之 引入Thymeleaf
thymeleaf 基本语法
spring boot(四):thymeleaf使用详解
Spring Web MVC框架(十二) 使用Thymeleaf
使用Thymeleaf API渲染模板生成静态页面
Spring Boot中Thymeleaf引擎动态刷新
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html

上一篇 下一篇

猜你喜欢

热点阅读