像素级精确报表把数据定义和版式定义分开管理:Report Query 负责提供报表需要的数据,Report Layout 负责用 Word 或 Excel 模板控制最终文档的外观。APEX 会把布局文件和 JSON 数据发送给远程打印服务,由打印服务合成并返回 PDF、Word 或 Excel 等输出文件。
21 生成像素级精确报表#
本章的核心流程是:先准备可用的远程打印服务,再创建报表模板和数据查询,最后从页面或报表区域触发下载。一个 Report Query 可以包含多个数据源;每个数据源可对应模板中的一个循环名称。只要模板能够表达需要的字体、列宽、分页、页眉页脚和图文排版,就可以用同一套机制生成业务所需的固定格式文档。
学习时要同时关注三件事:模板中的标签名称、APEX 中的数据循环名称、实际 JSON 中的属性名称。这三者不一致时,文档仍可能生成,但对应位置会为空或重复结构不正确。
官方来源:Producing Pixel-Perfect Reports
21.1 设置应用的远程打印服务器#
应用可以使用实例级打印服务器配置来生成报表。官方示例使用 Oracle Cloud Infrastructure Document Generator Function。它使用一组标签在 Word 或 Excel 模板中引用报表数据,并支持以 Microsoft Word 或 Excel 文件作为布局模板,输出 PDF、Word 或 Excel 文件。
对应用开发者来说,最简单的设置是在应用属性中把 Print Server Type 设为 Use Instance Settings。这样应用会继承实例管理员已经配置好的远程打印服务。
实例管理员需要先在实例设置中完成打印服务器配置。以 Oracle Document Generator 为例,管理员会配置数据库云凭据、对象存储桶访问权限、函数相关 OCID 和访问 URL。完成后,各工作区中的应用开发者可以直接使用该打印能力,而不需要在每个应用中处理这些 OCI 细节。
- 前提:实例管理员已配置可用的远程打印服务器。
- 操作位置:应用定义中的打印相关属性,以及管理员侧的 Instance Settings。
- 验证点:应用的 Print Server Type 使用实例设置;后续 Test Report 或页面下载可以成功返回文件。
- 预期结果:应用开发者不需要在业务页面中直接维护打印服务器凭据和 OCI 访问参数。
官方来源:Setting Your App's Remote Print Server
21.2 使用 Excel 模板打印报表#
本节用 Woods Clinic 的员工任期报表说明如何把应用数据合并到 Excel 模板中并生成 PDF。业务需求是让 HR 代表按部门生成员工任期报表。实现路径包括:设计 Excel 模板标签、创建 Report Layout、创建 Report Query、设置数据循环名称、检查 JSON、再从页面触发打印下载。
官方来源:Printing Report with an Excel Template
21.2.1 用标签在 Excel 中格式化数据#
模板可以用 Microsoft Excel 创建。设计模板前,先列出报表需要的数据来源、字段和每个来源的短名称。官方示例中的 Tenure Report 使用三组数据:
- 部门信息,短名称为
dep,字段包括dname、deptno、loc。 - 按任期排序的员工信息,短名称为
emp_t,字段包括ename、empno、tenure、job。 - 按姓名排序的员工信息,短名称为
emp_n,字段包括ename、hiredate、job。
在模板中,用 {#shortname} 和 {/shortname} 包围循环区域,用 {fieldname} 引用字段值。字段名称应与报表源列名的小写形式匹配。
部门信息可以写成:
{#dep}Department {dname} ({deptno}) in {loc}{/dep}
Employees by Tenure 区域使用横向重复行。竖线表示 Excel 单元格边界,Document Generator 会对 {#emp_t} 和 {/emp_t} 之间的单元格行按数据行重复。
{#emp_t}{ename}({empno}) | {tenure} | {job}{/emp_t}
Employees by Name 区域使用纵向重复列。Document Generator 会对 {:emp_n} 和 {/emp_n} 之间的单元格列按数据行重复。
{:emp_n}{ename}
-
{hiredate}
-
{job}{/emp_n}
- 前提:已明确报表需要的字段、排序方式和分组方式。
- 操作位置:Microsoft Excel 模板文件。
- 验证点:短名称和字段标签与后续 Report Query 的 Data Loop Name、列名一致。
- 预期结果:模板能描述部门标题、按任期的员工列表,以及按姓名排列的员工信息区域。
官方来源:Formatting Data in Excel with Tags
21.2.2 为文档模板创建报表布局#
Excel 模板文件需要作为共享组件中的 Report Layout 保存。示例中布局名称为 Tenure Report,并设置 Static ID。这个静态 ID 可在业务逻辑中调用 APEX_PRINT.GENERATE_DOCUMENT 时引用,用来生成 PDF。
上传模板时,可以把 woods_clinic_tenure_report.xlsx 拖放到 File 区域,也可以点击该区域从系统文件对话框中选择文件。
- 前提:Excel 模板文件已保存并包含正确的 Document Generator 标签。
- 操作位置:共享组件中的 Report Layouts。
- 验证点:布局名称、静态 ID、模板文件类型和上传文件都正确。
- 预期结果:APEX 中出现一个可被 Report Query 或打印流程引用的布局定义。
官方来源:Creating Layout for Document Template
21.2.3 定义报表查询来源#
Report Query 用来提供报表数据。示例中的 Department Employee Tenure 定义包含三个报表源,并引用 Tenure Report 布局。报表源可以来自本地 SQL、远程查询、表、视图、REST Data Source 等;示例的三个来源都使用本地数据库 SQL 查询。
部门来源使用应用项 G_DEPTNO_FOR_REPORT 作为绑定变量:
select deptno, dname, loc
from dept
where deptno = :G_DEPTNO_FOR_REPORT
Set Bind Values 按钮可以为查询中的绑定变量设置示例值。点击 Download 时,APEX 会用这些示例值生成代表报表数据的 JSON 文件;点击 Test Report 时,同一份 JSON 会与布局一起发送到远程打印服务器,用于验证输出结果。
- 前提:布局已创建,查询需要的表和绑定变量可用。
- 操作位置:共享组件中的 Report Queries。
- 验证点:每个报表源都能单独返回数据;绑定变量示例值可以生成样例 JSON。
- 预期结果:报表查询可向模板提供部门信息、按任期排序的员工信息和按姓名排序的员工信息。
官方来源:Defining Report Query Sources
21.2.4 为来源指定数据循环名称#
每个报表源的 Data Loop Name 必须与模板标签中的短名称匹配。它就是模板中用来“遍历”该来源每一行数据的名称。示例中,部门来源使用 dep,因此 Excel 模板可以引用:
{#dep}Department {dname} ({deptno}) in {loc}{/dep}
- 前提:模板中已经定义了短名称,例如
dep、emp_t、emp_n。 - 操作位置:Report Query 中每个 Report Source 的属性。
- 验证点:下载 JSON 后,顶层属性名称与模板循环名称一致。
- 预期结果:模板中的循环标签能正确找到对应数据数组。
官方来源:Assigning Sources a Data Loop Name
21.2.5 在报表来源中使用 SQL 能力#
报表源查询可以使用 SQL 的常规能力,也可以调用自定义函数来把数据塑造成模板需要的形式。示例中的 Employees in Department (by Tenure) 来源使用 emp_t 作为数据循环名称,并调用 EBA_DEMO_WOODSHR_REPORT.TENURE_YEARS_MONTHS 把入职日期格式化成类似 1 year, 5 months 的任期文本。
select empno,
ename,
job,
eba_demo_woodshr_report.tenure_years_months(hiredate) as tenure
from emp
where deptno = :G_DEPTNO_FOR_REPORT
order by hiredate
辅助函数示例:
-- In package eba_demo_woodshr_report
function tenure_years_months (p_date in date)
return varchar2
is
l_months number;
l_years number;
l_rem_months number;
begin
if p_date is null then
return null;
end if;
-- Whole months completed between the dates
l_months := floor(months_between(trunc(sysdate), trunc(p_date)));
-- If p_date is in the future, clamp to 0
if l_months < 0 then
l_months := 0;
end if;
l_years := trunc(l_months / 12);
l_rem_months := mod(l_months, 12);
return l_years || ' ' ||
case when l_years = 1 then 'year' else 'years' end || ', ' ||
l_rem_months || ' ' ||
case when l_rem_months = 1 then 'month' else 'months' end;
end;
- 前提:报表源查询可访问需要的包函数、表和绑定变量。
- 操作位置:Report Query 的 SQL 来源定义。
- 验证点:查询列名与模板字段标签一致,格式化后的列值已经适合直接显示。
- 预期结果:模板不需要承担业务计算,只负责呈现已经整理好的数据。
官方来源:Using SQL Features in Report Sources
21.2.6 检查报表查询 JSON#
Report Query 定义页上的 Download 按钮可以下载报表使用的 JSON。每个 Data Loop Name 会成为顶层 JSON 属性,其值是该来源行对象组成的数组。每个行对象中的小写属性名对应报表源列名。
{
"dep": [
{
"deptno": 20,
"dname": "RESEARCH",
"loc": "DALLAS"
}
],
"emp_t": [
{
"empno": 7566,
"ename": "JONES",
"job": "MANAGER",
"tenure": "44 years, 6 months"
},
{
"empno": 7788,
"ename": "SCOTT",
"job": "ANALYST",
"tenure": "1 year, 2 months"
}
],
"emp_n": [
{
"empno": 7876,
"ename": "ADAMS",
"job": "CLERK",
"hiredate": "12-JAN-1983"
},
{
"empno": 7788,
"ename": "SCOTT",
"job": "ANALYST",
"hiredate": "14-JUL-2024"
}
]
}
- 前提:每个报表源已经设置正确的绑定值和数据循环名称。
- 操作位置:Report Query 定义页。
- 验证点:JSON 顶层名称、字段名称、数组结构与 Excel 模板标签一致。
- 预期结果:在测试 PDF 前,可以先用 JSON 明确判断数据契约是否正确。
官方来源:Inspecting the Report Query JSON
21.2.7 从页面打印报表#
页面可以通过按钮、应用项和 Print Report 页面进程触发报表生成与下载。示例中,PRINT 按钮与 P35_DEPTNO 选择列表放在同一行,HR 代表先选择部门,再点击 Print and Download Report。
用户提交页面后,After Submit 计算会把应用项 G_DEPTNO_FOR_REPORT 设置为 P35_DEPTNO 的值。这样报表源查询用绑定变量引用该应用项时,就能拿到用户选择的部门编号。
应用项赋值后,Print Report 页面进程使用 Department Employee Tenure 报表查询生成并下载 PDF。文件名可以使用替换字符串,例如把 &P35_DEPTNO. 作为下载文件名的一部分。APEX 也提供原生 Print Report 动态动作,功能相同,只是由页面事件触发,而不是页面提交触发。
- 前提:页面上已有部门选择项,报表查询使用的应用项可接收该部门编号。
- 操作位置:页面设计器中的按钮、计算、页面进程或动态动作。
- 验证点:提交后应用项值正确,报表查询使用预期部门,文件名替换值生效。
- 预期结果:用户点击按钮后直接下载指定部门的像素级 PDF。
官方来源:Printing Report from a Page
21.2.8 以用户身份体验报表生成#
从最终用户视角看,流程很短:HR 代表 Susan 打开只对 HR Representatives 可见的 Tenure Report 页面,在列表中选择 RESEARCH 部门,然后点击 Print and Download Report。
按钮触发后,APEX 会执行报表查询定义中的所有来源查询,把结果格式化为 JSON,把 JSON 和关联布局发送给远程打印服务器,然后下载返回的 PDF 文件。
Susan 打开 PDF 后,可以看到 Woods Clinic Tenure Report 的成品。该 PDF 包含 Employees by Tenure 区域,员工按行列出;也包含 Employees by Name 区域,员工信息按列横向排布。
- 前提:页面授权、按钮、计算、报表查询、布局和远程打印服务都已配置完成。
- 操作位置:运行时应用页面。
- 验证点:所选部门影响 JSON 和 PDF 内容,下载文件正常打开。
- 预期结果:最终用户无需理解模板和报表源,只需选择条件并下载成品 PDF。
官方来源:Experiencing Report Generation as a User
21.3 使用 Word 模板定制报表#
Report Layout 也可以用来定制交互式报表或经典报表区域的 PDF 下载格式。Excel 示例强调从页面触发自定义文档;Word 示例则强调覆盖报表区域默认的 PDF 输出布局。
官方来源:Customizing Reports with Word Template
21.3.1 用标签在 Word 中格式化数据#
本例用 Word 文档定制 Employee Directory 页上交互式报表的 PDF 下载格式。与 Excel 模板一样,Word 模板使用 Oracle Document Generator 标签,把报表区域的数据映射到文档版式中。
Employee Directory 布局需要一组数据:员工目录信息,短名称为 dir,字段包括 ename、job、mgr、deptno。
模板中同样使用 {#shortname} 和 {/shortname} 包围数据来源,用 {fieldname} 引用字段值。字段标签名是与布局关联的报表区域列名的小写形式。
Word 文档中的表格可以只保留一行,四个字段分别放在四个单元格中。竖线表示单元格边界:
{#dir}{ename} | {job} | {mgr} | {deptno}{/dir}
- 前提:交互式报表区域已有稳定列集合,且 PDF 布局依赖的列不会随意删除。
- 操作位置:Microsoft Word 模板文件。
- 验证点:短名称
dir和字段标签都与后续布局或报表区域数据一致。 - 预期结果:下载 PDF 时,报表行会按 Word 表格样式重复输出。
官方来源:Formatting Data in Word with Tags
21.3.2 设置布局的数据循环名称#
当布局与报表区域配对时,短名称设置在布局定义自身上,而不是在多个 Report Query 来源中逐一设置。示例中的 Employee Directory 布局把 Data Loop Name 设为 dir,以匹配 Word 模板中的标签,并上传 emp_directory.docx 作为模板文件。
- 前提:Word 模板已经使用
{#dir}和{/dir}包围重复区域。 - 操作位置:共享组件中的 Report Layout 定义。
- 验证点:布局的 Data Loop Name 为
dir,模板文件为预期的 Word 文档。 - 预期结果:报表区域导出的 JSON 可以被 Word 模板正确遍历。
官方来源:Setting Layout Data Loop Name
21.3.3 覆盖报表区域的 PDF 布局#
在 Employee Directory 页面中,选择交互式报表区域,然后在属性编辑器的 Printing 选项卡中把 Layout 设为 Employee Directory,即可覆盖默认 PDF 格式。
- 前提:自定义 Word 布局已经上传,数据循环名称与模板一致。
- 操作位置:页面设计器中报表区域的 Printing 属性。
- 验证点:区域 PDF 下载使用自定义布局,而不是默认报表 PDF 样式。
- 预期结果:用户从报表区域下载 PDF 时得到企业统一格式的文档。
官方来源:Overriding PDF Layout of Report Regions
21.3.4 下载定制后的报表 PDF#
配置完成后,用户在交互式报表的 Actions 菜单中选择 Download,再选择 PDF 格式,就会得到像素级精确的 Employee Directory。用户下载前对 Job 列进行筛选、对 Deptno 列进行排序,这些筛选和排序也会影响 PDF 中出现的行以及排序方式。
用户打开 PDF 后,会看到经过筛选、排序的 Employee Directory 内容,并且这些内容已经套用了自定义的像素级布局。
- 前提:报表区域启用了下载,PDF 格式使用自定义布局。
- 操作位置:运行时交互式报表的 Actions 菜单。
- 验证点:当前筛选、排序会反映到下载的 PDF;模板依赖列未被隐藏或移除。
- 预期结果:用户得到与当前报表视图一致、且采用企业自定义版式的 PDF。