JAVA 使用IText7 + Freemarker 动态数据生成PDF实现案例

发布时间 2023-05-30 17:28:32作者: ghostmen

技术方案:IText7 + Freemarker

技术文档

  1. Itext 官网:https://itextpdf.com/
  2. itext API文档:https://api.itextpdf.com/iText7/java/7.1.14/
  3. FreeMarker API文档:https://freemarker.apache.org/docs/index.html
  4. CSS 文档:https://www.runoob.com/css/css-tutorial.html
  5. HTML文档:https://www.runoob.com/html/html-tutorial.html

使用maven导入相关依赖

<properties>
        <itext.version>7.1.15</itext.version>
</properties>
<dependencies>
    	<!-- itext7 -->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>kernel</artifactId>
            <version>${itext.version}</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>io</artifactId>
            <version>${itext.version}</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>layout</artifactId>
            <version>${itext.version}</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>forms</artifactId>
            <version>${itext.version}</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>pdfa</artifactId>
            <version>${itext.version}</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>pdftest</artifactId>
            <version>${itext.version}</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>font-asian</artifactId>
            <version>${itext.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.18</version>
        </dependency>

        <!--itext7 html转pdf用到的包-->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>html2pdf</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.31</version>
        </dependency>
</dependencies>

实现方案

  1. 配置Freemarker引擎

首先,我们需要配置Freemarker引擎,指定模板文件所在的路径并设置默认编码。我们可以使用以下代码来完成配置:

Configuration config = new Configuration(Configuration.getVersion()); 
config.setTemplateLoader(new ClassTemplateLoader(PdfGenerator.class, templatesPath)); config.setDefaultEncoding("UTF-8");

这里的templatesPath变量表示我们存放模板文件的相对路径。由于我们是在Java中运行程序,所以要使用ClassTemplateLoader类来加载模板文件。

  1. 加载模板文件并填充数据

一旦我们配置好了Freemarker引擎,就可以加载模板文件并将要填充的数据传递给它。可以使用以下代码来完成这一步骤:

Template template = config.getTemplate(templatesName); 
StringWriter out = new StringWriter(); 
template.process(data, out); out.flush();

这里的templatesName变量表示我们要加载的模板文件的名称。数据通过process方法传递给模板引擎,填充模板并生成HTML代码。

  1. 将HTML代码转换为PDF文件

一旦我们有了HTML代码,就可以使用IText7将其转换为PDF文件。使用以下代码将HTML代码转换为PDF文件:

ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 
PdfWriter writer = new PdfWriter(outputStream); 
PdfDocument pdf = new PdfDocument(writer); 
Document document = new Document(pdf, PageSize.A4); 
document.setMargins(2, 4, 2, 2); 
FontSet fontSet = new FontSet(); 
fontSet.addFont(fontsPath); 
FontProvider fontProvider = new FontProvider(fontSet); 
ConverterProperties converterProps = new ConverterProperties(); converterProps.setFontProvider(fontProvider); 
HtmlConverter.convertToPdf(htmlContent, pdf, converterProps); 
pdf.close(); 
byte[] bytes = outputStream.toByteArray();

这里的fontsPath变量表示我们使用的中文字体的路径。我们使用了IText7提供的HtmlConverter类来将HTML代码转换为PDF文件,并将字体设置为中文字体。

在完成上述步骤后,我们便能够成功实现使用IText7和Freemarker引擎生成PDF文件的功能。

附工具类完成代码

import com.alibaba.fastjson.JSONObject;
import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.font.FontProvider;
import com.itextpdf.layout.font.FontSet;
import freemarker.cache.ClassTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;

import java.io.*;
import java.util.HashMap;
import java.util.Map;

/**
 * @date: 2023年5月23日, 0023 下午 08:21
 * @version: 1.0.0
 * @Description:PDF工具类
 * @Time: 2023-05-23 20:21
 */
public class PdfGenerator {

    private static final String templatesPath = "/templates/";
    private static final String fontsPath = "/fonts/simhei.ttf";
    private static final String templatesName = "template.html";

    /**
     * 使用Freemarker引擎加载HTML模板文件并填充变量值,并将HTML字符串转换为PDF文件
     *
     * @param data             模板要填充的数据
     * @throws Exception
     */
    public static byte[] generatePDF(Map<String, Object> data) throws Exception {
        Configuration config = new Configuration(Configuration.getVersion());
        // 设置Freemarker引擎的模板路径
        config.setTemplateLoader(new ClassTemplateLoader(PdfGenerator.class, templatesPath));
        config.setDefaultEncoding("UTF-8");
        Template template = config.getTemplate(templatesName);
        StringWriter out = new StringWriter();
        template.process(data, out);
        out.flush();
        // 使用Freemarker引擎加载HTML模板文件并填充变量值
        String htmlContent = out.toString();
        byte[] bytes = convertHtmlToPdf(htmlContent);
        return bytes;
    }

    /**
     * 使用iText 7将HTML字符串转换为PDF文件,并返回PDF文件的二进制数据
     *
     * @param htmlString    待转换的HTML字符串
     * @return              返回生成的PDF文件内容
     * @throws IOException
     */
    private static byte[] convertHtmlToPdf(String htmlString) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        PdfWriter writer = new PdfWriter(outputStream);
        PdfDocument pdf = new PdfDocument(writer);
        Document document = new Document(pdf, PageSize.A4);
        // 设置左、右、上、下四个边距的值,以点(pt)为单位
        document.setMargins(2, 4, 2, 2);
        // 设置中文字体
        FontSet fontSet = new FontSet();
        fontSet.addFont(fontsPath);
        FontProvider fontProvider = new FontProvider(fontSet);
        ConverterProperties converterProps = new ConverterProperties();
        converterProps.setFontProvider(fontProvider);
        // 调用HtmlConverter类的convertToPdf函数,将HTML字符串转换为PDF文件
        HtmlConverter.convertToPdf(htmlString, pdf, converterProps);
        pdf.close();
        // 将PDF文件转换为字节数组并返回
        return outputStream.toByteArray();
    }
}

注:templatesPath,fontsPath,templatesName在src/main/resources下

附HTML模板文件代码

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
    <style type="text/css">
        @page {
            size: A4;
            margin: 5mm 10mm; /* 上下和左右两个方向的边距分别为 10mm 和 20mm */
        }
        body {
            width: 180mm;
            height: 297mm;
        }

        .item_content {
            width: 95%;
            height: 100%;
        }

        .item_content .title {
            overflow: hidden; /* 清除浮动 */
            box-sizing: border-box; /* 盒模型以边框为界计算宽度 */
            display: flex;
            height: 20pt;
            width: 100%;
            align-items: center;
            justify-content: center;
        }

        .item_content .title .circle {
            float: left;
            width: 3%;
            padding-right: 0.5px; /* 右侧间距 */
            align-self: center;
            justify-self: center;
            margin-right: -1pt;
            margin-top: 2pt;
        }

        .item_content .title .title_text {
            float: left;
            width: 12%;
            color: #409EFF;
            text-align: center;
            position: relative;
            font-size: 15pt;
            padding-right: 0.5px; /* 右侧间距 */
            align-self: center;
            justify-self: center;
        }

        .item_content .title .line {
            float: right;
            width: 85%;
            border-top: 0.75pt solid black;
            margin-top: 10pt;
        }

        .item_content .section {
            overflow: hidden; /* 清除浮动 */
            box-sizing: border-box; /* 盒模型以边框为界计算宽度 */
            display: flex;
            height: 100%; /* 高度设为100% */
            margin: auto;
        }

        .item_content .section .section_photo {
            float: left;
            width: 14.5%;
            text-align: center;
        }

        .item_content .section .section_info {
            float: right;
            width: 84.5%;
            align-self: center;
            justify-self: center;
            text-align: left;
        }

        .item_content .section .section_info .name {
            color: #409EFF;
            font-size: 21pt;
        }

        .item_content .section .section_info .address {
            width: 100%;
            height: 40px;
            line-height: 40px;
            background: #5195db;
            color: white;
            font-size: 15pt;
            padding: 0 15pt;
            margin: 10px 0 0;
            -webkit-print-color-adjust: exact;
        }

        .item_content .section .section_info .divider {
            width: 100%;
            margin: 15pt 0;
            border-top: 0.75pt solid #ccc;
        }

        .item_content .section .section_info .info-container {
            overflow: hidden;/* 清除浮动后的高度问题 */
            margin-top: 1%;
            margin-bottom: 1%;
            font-size: 11.25pt;
            gap: 4%;
        }

        .item_content .section .section_info .info-container .left-container{
            float: left;
            width: 48%;
            mmargin-top: 1%;
            margin-bottom: 1%;
        }

        .item_content .section .section_info .info-container .right-container{
            float: right;
            width: 48%;
            margin-top: 1%;
            margin-bottom: 1%;
        }

        .item_content .section .section_info .info-container .left-container .id-card-photo-container{
            overflow: hidden;/* 清除浮动后的高度问题 */
        }

        .item_content .section .section_info .info-container .left-container .id-card-photo-container .left-id-card-photo{
            float: left;
            width: 40%;/* 左半部分的宽度为 40% */
        }

        .item_content .section .section_info .info-container .left-container .id-card-photo-container .right-id-card-photo{
            float: right;
            width: 60%;/* 右半部分的宽度为 60% */
        }

        .item_content .section .section_info .record-container {
            overflow: hidden;/* 清除浮动后的高度问题 */
            margin-top: 1%;
            margin-bottom: 1%;
            font-size: 11.25pt;
            gap: 4%;
        }

        .item_content .section .section_info .record-container .left-record {
            float: left;
            width: 48%;/* 左半部分的宽度为 50% */
            mmargin-top: 1%;
            margin-bottom: 1%;
            text-align: left;
        }

        .item_content .section .section_info .record-container .right-record {
            float: right;
            width: 48%;/* 右半部分的宽度为 50% */
            margin-top: 1%;
            margin-bottom: 1%;
            text-align: left;
        }

        .item_content .experience {
            width: 90%;
            margin: 15px auto 20px;
            background: #f5f5f5;
            -webkit-print-color-adjust: exact;
            padding: 10px 20px;
        }

        .item_content .experience .experience-info{
            overflow: hidden;/* 清除浮动后的高度问题 */
            font-size: 11.25pt;
        }

        .item_content .experience .experience-info .left-experience{
            float: left; /* 左浮动 */
            width: 33.33%; /* 固定宽度 */
            box-sizing: border-box; /* 盒模型以边框为界计算宽度 */
            padding-right: 7.5pt; /* 右侧间距 */
            text-align: left;
        }

        .item_content .experience .experience-info .middle-experience{
            float: left; /* 左浮动 */
            width: 33.33%; /* 固定宽度 */
            box-sizing: border-box; /* 盒模型以边框为界计算宽度 */
            padding-right: 7.5px; /* 右侧间距 */
            text-align: left;
        }

        .item_content .experience .experience-info .right-experience{
            float: left; /* 左浮动 */
            width: 33.33%; /* 固定宽度 */
            box-sizing: border-box; /* 盒模型以边框为界计算宽度 */
            text-align: left;
        }

        .item_content .table_style {
            border-collapse: collapse;
            width: 100%;
        }

        .item_content .table_style td {
            text-align: center;
            height: 11.25pt;
            padding: 3.75pt 7.5pt;
            min-width: 97.5pt;
            max-width: none;
        }

        .item_content .table_style thead td {
            font-weight: 600;
            background: #f3f3f3;
            font-size: 11.25pt;
        }

        .item_content .table_style thead th {
            font-weight: 600;
            background: #f3f3f3;
            font-size: 13pt;
        }
    </style>
</head>
<body style="text-align: center">
<div>
    <div>
        <div class="scroll-show">
            <div style="margin-right:3%;margin-left:5%;">
                <div class="item_content">
                    <div class="title">
                        <div class="circle"><img src="https://egongban.oss-cn-shenzhen.aliyuncs.com/2023/05/29/b506e8108f4d9ee742370f4301e170a.png" style="width: 14pt; height: 14pt;"></div>
                        <div class="title_text">个人信息</div>
                        <div class="line"></div>
                    </div>
                    <div class="section">
                        <div class="section_photo">
                            <#if projectWorker.avatarUrl?has_content>
                                <#assign imgUrl = projectWorker.avatarUrl>
                                <#assign imgAlt = "Image description">
                                <img src="${imgUrl}" alt="${imgAlt}"
                                     style="width: 75pt; height: 75pt;margin-top: 110pt;"></img>
                            </#if>
                        </div>
                        <div class="section_info">
                            <div class="name">${projectWorker.name}</div>
                            <div class="address">所属省市:${projectWorker.address}</div>
                            <div class="info-container">
                                <div class="left-container">
                                    <div class="id-card-photo-container">
                                        <div class="left-id-card-photo">身份证照片:</div>
                                        <div class="right-id-card-photo">
                                            <#if projectWorker.idCardFrontUrl?has_content>
                                                <#assign imgUrl = projectWorker.idCardFrontUrl>
                                                <#assign imgAlt = "Image description">
                                                <img src="${imgUrl}" alt="${imgAlt}"
                                                     style="width: 37.5pt; height: 37.5pt;margin-right: 3.75pt;">
                                            </#if>
                                            <#if projectWorker.idCardBackUrl?has_content>
                                                <#assign imgUrl = projectWorker.idCardBackUrl>
                                                <#assign imgAlt = "Image description">
                                                <img src="${imgUrl}" alt="${imgAlt}"
                                                     style="width: 37.5pt; height: 37.5pt;">
                                            </#if>
                                        </div>
                                    </div>
                                    <div>姓名:${projectWorker.name}</div>
                                    <div>性别:<#if projectWorker.gender?has_content>${projectWorker.gender}</#if></div>
                                    <div>民族:<#if projectWorker.nationality?has_content>${projectWorker.nationality}</#if></div>
                                    <div>身份证有效日期:<#if projectWorker.dateIssue?has_content && projectWorker.expiryDate?has_content>${projectWorker.dateIssue?substring(0, 10)} — ${projectWorker.expiryDate?substring(0, 10)}</#if></div>
                                </div>
                                <div class="right-container">
                                    <div style="height: 37.5pt">身份证:<#if projectWorker.idCard?has_content>${projectWorker.idCard}</#if></div>
                                    <div>年龄:<#if projectWorker.age?has_content>${projectWorker.age}</#if></div>
                                    <div>住址:<#if projectWorker.address?has_content>${projectWorker.address}</#if></div>
                                    <div>发证机关:<#if projectWorker.issuingAuthority?has_content>${projectWorker.issuingAuthority}</#if></div>
                                </div>
                            </div>
                            <div class="divider"></div>
                            <div class="info-container">
                                <div class="left-container">
                                    <div>联系电话:<#if projectWorker.phone?has_content>${projectWorker.phone}</#if></div>
                                    <div>家庭电话:<#if projectWorker.familyPhone?has_content>${projectWorker.familyPhone}</#if></div>
                                    <div>微信号码:<#if projectWorker.wechat?has_content>${projectWorker.wechat}</#if></div>
                                    <div>联系地址:<#if projectWorker.contactAddress?has_content>${projectWorker.contactAddress}</#if></div>
                                </div>
                                <div class="right-container">
                                    <div>工人工种:<#if projectWorker.workTypeName?has_content>${projectWorker.workTypeName}</#if></div>
                                    <div>紧急联系人:<#if projectWorker.emergencyContactPerson?has_content>${projectWorker.emergencyContactPerson}</#if></div>
                                    <div>紧急联系人关系:<#if projectWorker.emergencyContactRelation?has_content>${projectWorker.emergencyContactRelation}</#if></div>
                                    <div>紧急联系人电话:<#if projectWorker.emergencyContactPhone?has_content>${projectWorker.emergencyContactPhone}</#if></div>
                                </div>
                            </div>
                            <div class="divider"></div>
                            <div class="record-container">
                                <#if (projectWorker.projectWorkerBankCardList?has_content && projectWorker.projectWorkerBankCardList?size > 0)>
                                    <#list projectWorker.projectWorkerBankCardList as record>
                                        <#if record?index % 2 == 0>
                                            <div class="left-record">
                                                <div>工资银行卡:${record.bankName}</div>
                                                <div>工资卡开户支行:${record.branchBankName}</div>
                                                <div>工资卡卡号:${record.bankCard}</div>
                                            </div>
                                        <#else>
                                            <div class="right-record">
                                                <div>工资银行卡:${record.bankName}</div>
                                                <div>工资卡开户支行:${record.branchBankName}</div>
                                                <div>工资卡卡号:${record.bankCard}</div>
                                            </div>
                                        </#if>
                                    </#list>
                                </#if>
                            </div>
                            <div class="divider"></div>
                            <div class="info-container">
                                <div class="left-container">
                                    <div>政治面貌:<#if projectWorker.politicalAffiliationName?has_content>${projectWorker.politicalAffiliationName}</#if></div>
                                    <div>文化水平:<#if projectWorker.educationalLevelName?has_content>${projectWorker.educationalLevelName}</#if></div>
                                    <div>是否服兵役:<#if projectWorker.militaryService?has_content>${projectWorker.militaryService?string('是', '否')}</#if></div>
                                    <div>服役时间:<#if projectWorker.enlistmentBeginTime?has_content && projectWorker.enlistmentEndTime?has_content>${projectWorker.enlistmentBeginTime?substring(0, 10)} — ${projectWorker.enlistmentEndTime?substring(0, 10)}</#if></div>
                                    <div>婚姻状况:<#if projectWorker.maritalStatus?has_content>${projectWorker.maritalStatus?string('已婚', '未婚')}</#if></div>
                                    <div>家庭成员:<#if projectWorker.familyMember?has_content>${projectWorker.familyMember}</#if></div>
                                    <div>从业时间:<#if projectWorker.employmentBeginTime?has_content && projectWorker.employmentEndTime?has_content>${projectWorker.employmentBeginTime?substring(0, 10)} — ${projectWorker.employmentEndTime?substring(0, 10)}</#if></div>
                                </div>
                                <div class="right-container">
                                    <div>专业技能:<#if projectWorker.professionalSkill?has_content>${projectWorker.professionalSkill}</#if></div>
                                    <div>专业证书:
                                        <#if projectWorker.emergencyContactPerson?has_content>
                                            ${projectWorker.emergencyContactPerson}
                                        </#if>
                                    </div>
                                    <div>有无困难:<#if projectWorker.difficulty?has_content>${projectWorker.difficulty}</#if></div>
                                    <div>对企业的希望:<#if projectWorker.enterpriseExpectation?has_content>${projectWorker.enterpriseExpectation}</#if></div>
                                    <div>对自身的职业规划:<#if projectWorker.oneselfOccupationalPlan?has_content>${projectWorker.oneselfOccupationalPlan}</#if></div>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="title">
                        <div class="circle"><img src="https://egongban.oss-cn-shenzhen.aliyuncs.com/2023/05/29/b506e8108f4d9ee742370f4301e170a.png" style="width: 14pt; height: 14pt;"></div>
                        <div class="title_text" id="experience">从业经历</div>
                        <div class="line"></div>
                    </div>
                    <div class="experience">
                        <#if (workingExperienceList?has_content && workingExperienceList?size> 0)>
                            <#list workingExperienceList as record>
                                <#if record?has_content>
                                    <div class="experience-info">
                                        <div class="left-experience">
                                            <div>项目名称:<#if record.projectName?has_content>${record.projectName}</#if></div>
                                            <div>合同名称:<#if record.contractName?has_content>${record.contractName}</#if></div>
                                            <div>合伙项名称:<#if record.contractItemName?has_content>${record.contractItemName}</#if></div>
                                            <div>项目工种:<#if record.workTypeName?has_content>${record.workTypeName}</#if></div>
                                            <div>评分:<#if record.score?has_content>${record.score}</#if></div>
                                            <#if (record.userAEvalRecordsVo?has_content)>
                                                <#list record.userAEvalRecordsVo as vo>
                                                    <#if vo?has_content && (vo?index == 0 || vo?index % 3 == 0)>
                                                        <div>${vo.indicatorName}: ${vo.avgScore}</div>
                                                    </#if>
                                                </#list>
                                            </#if>
                                        </div>
                                        <div class="middle-experience">
                                            <div>所属公司:<#if record.companyName?has_content>${record.companyName}</#if></div>
                                            <div>所属小组:<#if record.teamName?has_content>${record.teamName}</#if></div>
                                            <div>记工方式:<#if record.payTypeName?has_content>${record.payTypeName}</#if></div>
                                            <div>进场日期:<#if record.approachTime?has_content>${record.approachTime}</#if></div>
                                            <br>
                                            <#if (record.userAEvalRecordsVo?has_content)>
                                                <#list record.userAEvalRecordsVo as vo>
                                                    <#if vo?has_content && (vo?index == 1 || vo?index % 3 == 1)>
                                                        <div>${vo.indicatorName}: ${vo.avgScore}</div>
                                                    </#if>
                                                </#list>
                                            </#if>
                                        </div>
                                        <div class="right-experience">
                                            <div>退场日期:<#if record.exitTime?has_content>${record.exitTime}</#if></div>
                                            <div>角色:<#if record.workerTypeName?has_content>${record.workerTypeName}</#if><#if record.partnerTypeName?has_content>${record.partnerTypeName}</#if></div>
                                            <br>
                                            <br>
                                            <br>
                                            <#if (record.userAEvalRecordsVo?has_content)>
                                                <#list record.userAEvalRecordsVo as vo>
                                                    <#if vo?has_content && (vo?index == 2 || vo?index % 3 == 2)>
                                                        <div>${vo.indicatorName}: ${vo.avgScore}</div>
                                                    </#if>
                                                </#list>
                                            </#if>
                                        </div>
                                    </div>
                                </#if>
                            </#list>
                        </#if>
                    </div>
                    <div class="title">
                        <div class="circle"><img src="https://egongban.oss-cn-shenzhen.aliyuncs.com/2023/05/29/b506e8108f4d9ee742370f4301e170a.png" style="width: 14pt; height: 14pt;"></div>
                        <div class="title_text" id="training">安全培训</div>
                        <div class="line"></div>
                    </div>
                    <div style="width: 90%;margin: 15px auto;">
                        <table class="table_style">
                            <thead>
                            <tr>
                                <th style="width: 33%;">培训视频</th>
                                <th style="width: 33%;">状态</th>
                                <th style="width: 33%;">观看日期</th>
                            </tr>
                            </thead>
                            <tbody>
                                <#if (workerTrainingRecordList?has_content)>
                                    <#list workerTrainingRecordList as record>
                                        <#if record?has_content>
                                            <tr>
                                                <td>
                                                    <#if record.title?has_content>${record.title}</#if>
                                                </td>
                                                <td>
                                                    <#if record.statusName?has_content>${record.statusName}</#if>
                                                </td>
                                                <td>
                                                    <#if record.date?has_content>${record.date}</#if>
                                                </td>
                                            </tr>
                                        </#if>
                                    </#list>
                                </#if>
                            </tbody>
                        </table>
                    </div>
                    <div class="title">
                        <div class="circle"><img src="https://egongban.oss-cn-shenzhen.aliyuncs.com/2023/05/29/b506e8108f4d9ee742370f4301e170a.png" style="width: 14pt; height: 14pt;"></div>
                        <div class="title_text" id="record">奖惩记录</div>
                        <div class="line"></div>
                    </div>
                    <div style="width: 90%;margin: 15px auto;">
                        <table class="table_style">
                            <thead>
                            <tr>
                                <th style="width: 25%;font-size: 16px;">项目名称</th>
                                <th style="width: 25%;">类型</th>
                                <th style="width: 25%;">金额</th>
                                <th style="width: 25%;">备注</th>
                            </tr>
                            </thead>
                            <tbody>
                                <#if (workerRewardPunishRecordList?has_content)>
                                    <#list workerRewardPunishRecordList as record>
                                        <#if record?has_content>
                                            <tr>
                                                <td>
                                                    <#if record.projectName??>${record.projectName}</#if>
                                                </td>
                                                <td>
                                                    <#if record.typeName??>${record.typeName}</#if>
                                                </td>
                                                <td>
                                                    <#if record.amount??>${record.amount}</#if>
                                                </td>
                                                <td>
                                                    <#if record.remark??>${record.remark}</#if>
                                                </td>
                                            </tr>
                                        </#if>
                                    </#list>
                                </#if>
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>