Skip to content

Approval archive — generate this PDF with Java (Template)

JQuick-PdfScenario Demos

Template2 页 A4 · 44 KB

Multi-page approval archive: paginated flow records with repeated table headers and the approval number in the page header.

Download the PDFView raw template fileBack to all demos

Approval archive — Template
Rendered output

Output specs

PDF pages
2
Chart type
Template
Template lines
184
Source lines
106

Java source · JQuickBiz76ApprovalArchive.java

This is the complete test class that produced the PDF above — copy it and run as-is; see the quick-start section for the Maven dependency.

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Copyright (c) [2025-2099] Martin ([email protected])
 */
package com.github.paohaijiao.biz.scenario;

import com.github.paohaijiao.config.JPdfConfig;
import com.github.paohaijiao.executor.JQuickPdfFactory;

import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class JQuickBiz76ApprovalArchive {

    private static final String PDF_DIR = "D:/test/pdf/";
    private static final String OUT_FILE = PDF_DIR + "76_approval_archive.pdf";
    private static final String TEMPLATE = "report/biz/76_approval_archive.txt";

    public static void main(String[] args) throws Exception {
        new File(PDF_DIR).mkdirs();

        // ---------- 1. 组织数据(普通 Map,无实体类)----------
        Map<String, Object> data = new LinkedHashMap<String, Object>();
        data.put("approvalNo", "OA-2026-09-0247");
        data.put("applicant", "刘洋(数据平台部)");
        data.put("applyTime", "2026年09月02日 09:12");
        data.put("nodeCount", "12 个");

        // 流程记录:逐条 bind 为 recN_node / recN_approver / recN_result / recN_opinion / recN_time
        List<String[]> records = new ArrayList<String[]>();
        records.add(new String[]{"发起申请", "刘洋", "提交", "申请采购 PDF 报表引擎企业授权 5 套", "2026-09-02 09:12"});
        records.add(new String[]{"部门经理审批", "王敏", "通过", "业务量增长确实需要扩容,同意采购", "2026-09-02 10:35"});
        records.add(new String[]{"财务初审", "赵磊", "驳回", "预算科目填写有误,请调整为「软件购置费-信息化」", "2026-09-02 14:20"});
        records.add(new String[]{"申请人修改", "刘洋", "提交", "已按财务意见调整预算科目并补充报价单", "2026-09-03 08:50"});
        records.add(new String[]{"财务初审", "赵磊", "通过", "科目已更正,金额与报价单一致", "2026-09-03 09:40"});
        records.add(new String[]{"法务审核", "陈静", "通过", "软件许可协议无排他条款,允许签署", "2026-09-03 11:05"});
        records.add(new String[]{"IT 架构评审", "孙浩", "通过", "与现有报表平台兼容,无需额外中间件", "2026-09-03 15:30"});
        records.add(new String[]{"分管副总审批", "周建国", "转交", "由总经理审批(金额超 20 万)", "2026-09-04 09:00"});
        records.add(new String[]{"总经理审批", "李国华", "通过", "同意采购,要求 30 日内完成部署", "2026-09-04 16:45"});
        records.add(new String[]{"合同签署", "王敏", "完成", "合同 HT-2026-0918 已用印", "2026-09-08 10:00"});
        records.add(new String[]{"付款执行", "财务部", "完成", "首付款 30% 已支付", "2026-09-12 14:00"});
        records.add(new String[]{"到货验收", "刘洋", "完成", "5 套授权已激活,验收单 YS-2026-0921", "2026-09-21 11:00"});
        String[] keys = {"node", "approver", "result", "opinion", "time"};
        for (int i = 0; i < records.size(); i++) {
            for (int j = 0; j < keys.length; j++) {
                data.put("rec" + (i + 1) + "_" + keys[j], records.get(i)[j]);
            }
        }

        // 申请内容:三段
        data.put("applyPara1", "因集团报表业务量增长,现有 3 套 PDF 报表引擎授权已无法满足日均 12 万份报表的生成需求,高峰期排队时间超过 20 分钟,影响业务部门早会数据获取。");
        data.put("applyPara2", "本次申请采购企业级授权 5 套,供应商为 ××数字科技有限公司,单价 42,000 元,总价 210,000 元,含一年技术支持与两次现场培训。采购方式为单一来源采购,理由为现有系统基于该引擎开发,更换供应商需重构 300 余个报表模板,成本远高于本次采购。");
        data.put("applyPara3", "预计扩容后日均处理能力提升至 30 万份,高峰排队时间可控制在 2 分钟以内。");

        // 附件清单
        List<String> attachments = Arrays.asList(
                "《PDF 报表引擎采购申请单》",
                "供应商正式报价单(BJ-2026-0912-0031)",
                "单一来源采购论证意见",
                "IT 架构评审记录",
                "合同 HT-2026-0918 扫描件(已用印)",
                "验收单 YS-2026-0921");
        for (int i = 0; i < attachments.size(); i++) {
            data.put("att" + (i + 1), attachments.get(i));
        }

        // ---------- 2. 渲染 ----------
        JPdfConfig config = new JPdfConfig();
        config.getLayoutConfig().setFlexLayout(true);
        // 顶部留出 34pt 给页眉
        config.getDoc().setMargins(Arrays.asList(58, 50, 55, 50));
        config.getHeaderConfig().setEnabled(true);
        config.getHeaderConfig().setText("审批单号:" + data.get("approvalNo") + " | OA 审批归档报告");
        config.getHeaderConfig().setFontSize(8);
        config.getHeaderConfig().setHeight(34);
        config.getFooterConfig().setEnabled(true);
        config.getFooterConfig().setFontSize(8);
        config.getFooterConfig().setPageNumberFormat("第 %d 页");

        JQuickPdfFactory factory = new JQuickPdfFactory(config);
        factory.bindAll(data);
        byte[] pdf = factory.executeResource(TEMPLATE);

        FileOutputStream out = new FileOutputStream(OUT_FILE);
        out.write(pdf);
        out.close();
        System.out.println("已生成:" + OUT_FILE);
    }
}

Minimal invocation (3 lines)

Skip the full test class — the core is these three lines: bind the chart SVG, execute the template, get the PDF bytes. Copy them straight into your project.

// config 可省略;svg 来自 JChartRendererFactory.renderChart(...),见下方完整源码
byte[] pdf = new JQuickPdfFactory(config).executeResource("report/biz/76_approval_archive.txt");

Template file · 76_approval_archive.txt

An HTML-like template with inline CSS that renders to PDF once bound to data. The full template is below — copy it or download the raw file.

<pdf>
<body>

  <h1 style="fontSize:18pt; bold:true; color:#1F4E79; textAlignment:center; characterSpacing:4pt">'审 批 归 档 报 告'</h1>
  <p style="fontSize:8.5pt; color:#5A6B7C; textAlignment:center; marginBottom:8px">'本报告由 OA 系统自动生成,归档后不可修改'</p>

  // ---------- 审批概况 ----------
  <table style="width:100%; fontSize:9pt">
    <tr>
      <td style="width:78px; padding:5px; backgroundColor:#E8EDF2; bold:true; border:1px solid #C3CDD8">'审批单号'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${approvalNo}</td>
      <td style="width:78px; padding:5px; backgroundColor:#E8EDF2; bold:true; border:1px solid #C3CDD8">'申请人'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${applicant}</td>
    </tr>
    <tr>
      <td style="width:78px; padding:5px; backgroundColor:#E8EDF2; bold:true; border:1px solid #C3CDD8">'申请时间'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${applyTime}</td>
      <td style="width:78px; padding:5px; backgroundColor:#E8EDF2; bold:true; border:1px solid #C3CDD8">'流程节点数'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${nodeCount}</td>
    </tr>
  </table>

  // ---------- 一、审批流程记录 ----------
  <h2 style="fontSize:11pt; bold:true; color:#1F4E79; borderLeft:3px solid #1F4E79; paddingLeft:6px; marginTop:12px; marginBottom:5px">'一、审批流程记录'</h2>
  <table style="width:100%; fontSize:8.5pt">
    <tr>
      <th style="width:30px; padding:4px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79">'序号'</th>
      <th style="width:100px; padding:4px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79">'审批节点'</th>
      <th style="width:70px; padding:4px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79">'审批人'</th>
      <th style="width:56px; padding:4px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79">'审批结果'</th>
      <th style="width:175px; padding:4px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79">'审批意见'</th>
      <th style="padding:4px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79">'处理时间'</th>
    </tr>
    <tr>
      <td style="padding:4px; textAlign:center; border:1px solid #C3CDD8">'1'</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec1_node}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec1_approver}</td>
      <td style="padding:4px; textAlign:center; bold:true; border:1px solid #C3CDD8">${rec1_result}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec1_opinion}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec1_time}</td>
    </tr>
    <tr>
      <td style="padding:4px; textAlign:center; border:1px solid #C3CDD8">'2'</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec2_node}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec2_approver}</td>
      <td style="padding:4px; textAlign:center; bold:true; border:1px solid #C3CDD8">${rec2_result}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec2_opinion}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec2_time}</td>
    </tr>
    <tr>
      <td style="padding:4px; textAlign:center; border:1px solid #C3CDD8">'3'</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec3_node}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec3_approver}</td>
      <td style="padding:4px; textAlign:center; bold:true; color:#B71C1C; border:1px solid #C3CDD8">${rec3_result}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec3_opinion}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec3_time}</td>
    </tr>
    <tr>
      <td style="padding:4px; textAlign:center; border:1px solid #C3CDD8">'4'</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec4_node}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec4_approver}</td>
      <td style="padding:4px; textAlign:center; bold:true; border:1px solid #C3CDD8">${rec4_result}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec4_opinion}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec4_time}</td>
    </tr>
    <tr>
      <td style="padding:4px; textAlign:center; border:1px solid #C3CDD8">'5'</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec5_node}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec5_approver}</td>
      <td style="padding:4px; textAlign:center; bold:true; border:1px solid #C3CDD8">${rec5_result}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec5_opinion}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec5_time}</td>
    </tr>
    <tr>
      <td style="padding:4px; textAlign:center; border:1px solid #C3CDD8">'6'</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec6_node}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec6_approver}</td>
      <td style="padding:4px; textAlign:center; bold:true; border:1px solid #C3CDD8">${rec6_result}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec6_opinion}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec6_time}</td>
    </tr>
    <tr>
      <td style="padding:4px; textAlign:center; border:1px solid #C3CDD8">'7'</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec7_node}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec7_approver}</td>
      <td style="padding:4px; textAlign:center; bold:true; border:1px solid #C3CDD8">${rec7_result}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec7_opinion}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec7_time}</td>
    </tr>
    <tr>
      <td style="padding:4px; textAlign:center; border:1px solid #C3CDD8">'8'</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec8_node}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec8_approver}</td>
      <td style="padding:4px; textAlign:center; bold:true; color:#5A6B7C; border:1px solid #C3CDD8">${rec8_result}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec8_opinion}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec8_time}</td>
    </tr>
    <tr>
      <td style="padding:4px; textAlign:center; border:1px solid #C3CDD8">'9'</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec9_node}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec9_approver}</td>
      <td style="padding:4px; textAlign:center; bold:true; border:1px solid #C3CDD8">${rec9_result}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec9_opinion}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec9_time}</td>
    </tr>
    <tr>
      <td style="padding:4px; textAlign:center; border:1px solid #C3CDD8">'10'</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec10_node}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec10_approver}</td>
      <td style="padding:4px; textAlign:center; bold:true; border:1px solid #C3CDD8">${rec10_result}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec10_opinion}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec10_time}</td>
    </tr>
    <tr>
      <td style="padding:4px; textAlign:center; border:1px solid #C3CDD8">'11'</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec11_node}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec11_approver}</td>
      <td style="padding:4px; textAlign:center; bold:true; border:1px solid #C3CDD8">${rec11_result}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec11_opinion}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec11_time}</td>
    </tr>
    <tr>
      <td style="padding:4px; textAlign:center; border:1px solid #C3CDD8">'12'</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec12_node}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec12_approver}</td>
      <td style="padding:4px; textAlign:center; bold:true; border:1px solid #C3CDD8">${rec12_result}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec12_opinion}</td>
      <td style="padding:4px; border:1px solid #C3CDD8">${rec12_time}</td>
    </tr>
  </table>

  // ---------- 二、申请内容 ----------
  <h2 style="fontSize:11pt; bold:true; color:#1F4E79; borderLeft:3px solid #1F4E79; paddingLeft:6px; marginTop:12px; marginBottom:5px">'二、申请内容'</h2>
  <p style="fontSize:9.5pt; color:#333333; textIndent:19pt; marginBottom:4px">${applyPara1}</p>
  <p style="fontSize:9.5pt; color:#333333; textIndent:19pt; marginBottom:4px">${applyPara2}</p>
  <p style="fontSize:9.5pt; color:#333333; textIndent:19pt; marginBottom:4px">${applyPara3}</p>

  // ---------- 三、附件说明 ----------
  <h2 style="fontSize:11pt; bold:true; color:#1F4E79; borderLeft:3px solid #1F4E79; paddingLeft:6px; marginTop:12px; marginBottom:5px">'三、附件说明'</h2>
  <table style="width:100%; fontSize:9pt">
    <tr>
      <th style="width:36px; padding:5px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79">'序号'</th>
      <th style="width:300px; padding:5px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79">'附件名称'</th>
      <th style="padding:5px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79">'归档状态'</th>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'1'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${att1}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'已归档'</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'2'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${att2}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'已归档'</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'3'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${att3}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'已归档'</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'4'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${att4}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'已归档'</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'5'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${att5}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'已归档'</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'6'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${att6}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'已归档'</td>
    </tr>
  </table>

  <div style="marginTop:14px">
    <p style="fontSize:8.5pt; color:#8a939b">'归档信息:本报告与流程审批记录一并存入电子档案系统,保存期限 10 年,调阅需经档案管理员授权。'</p>
  </div>

</body>
</pdf>

More demos in this group

The PDF, preview image, template and source code on this page were all produced by a real JQuick-Pdf run and are free to copy into your own project.