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

需求背景

通常我们会碰到一些带表格的报表需求,一般是包含一些表格和一些循环列表的需求,每个表格的样式可以不一样。
有些公司会使用一些专业的报表软件,例如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

    
        org.springframework.boot
        spring-boot-starter-parent
        1.5.2.RELEASE
        
    
    
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.boot
            spring-boot-starter-thymeleaf
        
        
            org.projectlombok
            lombok
            1.16.10
        
    

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文件

主要利用两点,

  • 一个是th:attr

例如,根模板里面:

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

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

这里有两点特别说明:
1、

这种方式行不通,通过th:attr无法增加th:attr标签
2、
这种方式也不行,只能解析后面那个th:text,解析结果如下:
${userName}

那如果又想加th:text标签又想设置text内容怎么办呢?答案是增加一层div:

  • 一个是th:utext

例如,根模板里面:

如果td.value是以下字符串,则通过根模板生成后的子模板html代码是:

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

样式和合并单元格问题

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


效果演示

子模板template2


image.png

子模板2填充数据后:


image.png

子模板template3
image.png

子模板3填充数据后:


image.png

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

常用语法

注释

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

 
  
you can see me only before Thymeleaf processes me!

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

hello!

goodbye!

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


    
... ...
...

删除代码段

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

  
    Mild Cinnamon
    1.99
    yes
    
      3 comment/s
      view
    
  

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

  • all: 移除标签和所有子元素
  • body: 移除所有子元素,保留标签
  • tag: 移除标签,保留子元素
  • all-but-first: 保留第一个子元素,移除所有其他
  • none : 什么也不做。这个值在动态求值的时候会有作用

特别注意点

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

你可能感兴趣的:(利用thymeleaf双层模板技术,制作个性化报表)