当 APEX 原生功能不能完全满足应用需求时,可以通过插件扩展平台能力。插件是共享组件,可以为应用增加新的区域、页面项、动态操作、页面处理、AI 工具、工作流活动、数据源、认证方案或授权方案。模板组件适合具备基础 HTML 能力的开发者以声明式方式创建可复用 UI;其他类型的插件通常需要编写代码。

7 使用插件增加功能#

插件创建完成后,团队成员可以像使用内置功能一样,在 APEX Builder 中以声明式方式使用它。开始自己开发插件之前,应先在 APEX 社区的“应用商店”站点 apex.world 搜索,也许所需扩展能力已经有现成插件可以下载。

应用引入插件后,可以在 Shared Components > Plug-ins 列表页的 Utilization 选项卡查看每个插件被哪些页面或组件使用。这一步对升级评估、依赖审查和移除废弃插件都很重要。

7.1 使用 HTML 技能构建可复用组件#

模板组件(Template Component)是一种不需要编写服务端代码即可创建的可复用用户界面元素。熟悉基础 HTML、#NAME# 占位符替换和 HTML 模板指令的开发者,可以创建复杂但易复用的 UI 控件。其他团队成员在 Page Designer 中使用这些组件时,只需要像配置原生区域一样,在 Property Editor 中填写属性。

本节围绕模板组件的几个核心能力展开:自定义属性、插槽、模板指令、可用形态、报表模板、分组、分页、行选择、属性作用域、行级动作以及组件文件。

7.1.1 模板组件概览#

模板组件用 HTML 标签、#NAME# 形式的占位符和模板指令组合出可复用 UI。自定义属性让使用组件的开发者在 Page Designer 中指定要显示的数据和影响输出的设置;插槽则允许组件接收其他区域、页面项或按钮。

开发者可以用 {with/}...{apply/} 指令按名称应用模板组件,也可以在 Page Designer 中把它作为区域来使用。组件可被配置为:能容纳其他组件的自定义区域、格式化单行数据的 Single (Partial),或渲染多行数据的 Multiple (Report)。作为多行报表使用时,模板组件还可以自动支持分组、行选择和分页。如果组件需要 CSS 或 JavaScript,也可以把这些文件纳入组件定义。

<strong>#NAME#</strong> → #VALUE#

这个简单模板可把 NAMEVALUE 属性显示成一组名称和值。官方示例在 Cards 区域正文、经典报表列和单行 Partial 三个位置复用同一个模板组件,用于显示员工姓名、工资、职位等信息。

图 7-1 三种方式复用模板组件
  • 前提:具备一个可编辑的 APEX 应用,并能访问 Shared Components 中的 Template Components。
  • 操作区域:Shared Components > Template Components,以及 Page Designer 中使用该组件的区域或列。
  • 验证:同一组件在不同页面位置输出一致的名称/值格式,属性值随当前行或当前配置变化。

7.1.2 自定义属性提供数据和设置#

自定义属性是由组件作者定义、由组件使用者在 Property Editor 中配置的命名占位符。模板可以用 #ATTRNAME# 插入属性值,也可以在模板指令中引用属性名称来进行条件输出。

每个属性除了静态 ID 和名称,还包含用于改善 Page Designer 配置体验的信息。例如,Session State Value 类型会显示数据源列的选择列表;Yes/No 类型会显示开关;Select List 类型让开发者从预定义选项中选择。其他属性类型还可提供图标选择器、颜色选择器、HTML 代码编辑器、链接构建器等。

为属性编写帮助文本和示例很重要。使用组件的开发者可以在 Page Designer 的 Help 选项卡中理解每个设置如何影响输出。默认情况下,自定义属性会按你设置的显示顺序出现在 Property Editor 的 Attributes 选项卡、Settings 分组中。如果多个属性共同服务于一个目的,应创建属性组并把相关属性放在同一组里,让配置界面更清楚。

图 7-2 Settings 区域中的自定义属性
  • 前提:已有一个模板组件,且至少需要一个由使用者配置的数据列或显示设置。
  • 操作区域:模板组件编辑页的 Attributes 或 Custom Attributes 区域。
  • 验证:在 Page Designer 选中组件实例后,相关属性按分组显示在 Property Editor 中,并带有可理解的帮助说明。

7.1.3 插槽支持组件组合#

插槽(Slots)让模板组件能够组合其他区域、页面项和按钮。组件作者只需要定义命名插槽,并在模板中用 #SLOTNAME# 放置插槽内容。Page Designer 会根据插槽配置引导开发者把允许的组件放进去,并执行约束。例如你声明某个插槽只能包含按钮,Page Designer 就会限制团队成员只能把按钮添加到该插槽。

官方示例组件名为 Content with Action Buttons,包含两个插槽:MAIN_CONTENTACTION_BUTTONS。Partial 模板用条件指令判断按钮插槽和高度属性是否存在:

{if ACTION_BUTTONS/}
  <div class="action-buttons">
    #ACTION_BUTTONS#
  </div>
{endif/}
{if HEIGHT/}
  <div style="height:#HEIGHT#">
    #MAIN_CONTENT#
  </div>
{else/}
  #MAIN_CONTENT#
{endif/}

定义插槽时,可以规定 ACTION_BUTTONS 只允许按钮,MAIN_CONTENT 只允许区域,并支持 12 列网格布局。还可以把 Main Content 设为子区域默认插槽,把 Action Buttons 设为按钮默认插槽,这会减少使用者在 Page Designer 中手工选择插槽的步骤。

图 7-3 配置模板组件的插槽行为

开发者在页面上使用该组件时,Page Designer 会显示 Action Buttons 与 Main Content 插槽,并确保按钮和区域分别进入正确位置。

图 7-4 在 Page Designer 中使用模板组件插槽

运行页面后,组件会把 ACTION_BUTTONS 插槽中的按钮套用为动作按钮样式,并把 MAIN_CONTENT 插槽中的两个区域按 12 列网格的 6 + 6 布局显示。

图 7-5 插槽组合按钮和区域后的运行效果

同一个组件也可以在另一个页面中组合不同按钮和区域,例如在 Action Buttons 插槽放入 Adjust Budget 和 Initiate Audit 按钮,在 Main Content 插槽放入 Department Info 与 Budget History 标签页。

图 7-6 Content with Action Buttons 模板组件的另一种使用方式
  • 前提:模板组件需要接收页面开发者提供的按钮、区域或页面项。
  • 操作区域:模板组件的 Slots 配置,以及 Page Designer 的 Rendering 树。
  • 验证:不符合插槽约束的组件不能被放入;运行页面时,插槽内容出现在模板定义的位置。

7.1.4 使用指令应用模板#

只要某处允许使用模板指令,开发者就可以按模板组件的内部名称应用它。语法是先用 {with/} 提供属性值,再用 {apply TEMPLATE_NAME/} 应用模板:

{with/}
   ATTR1:=VALUE1
   ATTR2:=VALUE2
    ⋮
{apply TEMPLATE_NAME/}
  • 前提:知道模板组件的 Internal Name,并知道该模板需要哪些属性。
  • 操作区域:支持模板指令的 HTML、模板或组件配置字段。
  • 验证:运行页面时,模板按传入属性输出内容;缺失必要属性时,应能从输出或调试信息中定位问题。

7.1.5 控制组件可用形态#

即使开发者始终可以用 {with/}...{apply/} 指令按名称应用模板组件,组件作者仍然可以通过 Available as 选项决定它是否以及如何出现在 Page Designer 中。只要选择了某个可用形态,Page Designer 就会在区域面板和区域类型列表中显示该组件。

Region-only 组件只显示自定义属性和插槽,不提供额外内置设置。选择 Single (Partial)Multiple (Report) 后,Property Editor 会显示 Source 区域,让开发者配置数据源;数据源列随后可被自定义属性引用。

Single (Partial) 的数据源最多只能返回一行,否则运行时会报错。由于这个约束,它只渲染 Partial 模板,不需要分页控件。Multiple (Report) 会显示分页和分组相关设置;如果组件定义时启用了 Has Row Selection Support,还会显示行选择相关设置。

  • 前提:明确组件是静态布局容器、单行格式化器,还是多行报表。
  • 操作区域:模板组件编辑页的 Available as / Standard Attributes 设置。
  • 验证:在 Page Designer 新建区域时,组件只以预期形态出现,并显示与该形态匹配的配置项。

7.1.6 组成组件的模板#

每个模板组件都有一个主模板,名为 Partial。它表示运行时网页中的一部分,也是决定组件属性如何呈现在页面上的主要标记。

如果组件可作为 Multiple (Report) 区域使用,还可以定义以下附加模板:

  • Report Row:包裹每一行数据,并用 #APEX$PARTIAL# 引入 Partial。
  • Report Group:包裹每个不同的分组,并用 #APEX$ROWS# 引入该组中的行。
  • Report Body:包裹当前页的行,并用 #APEX$ROWS# 引入这些行。
  • Report Container:包裹报表主体,并用 #APEX$REPORT_BODY# 引入 Report Body。

官方示例 Grouped Name/Value Pairs 定义了报表属性 TITLE、组件属性 NAMEVALUE、分组属性 GROUPNAMEGROUPID,以及一个 BUTTONS 插槽。使用者配置标题、把 DNAMEDEPTNO 映射为分组名称和分组 ID、把 ENAMESAL 映射为名称和值,并在 Buttons 插槽中加入 Process Selected Employees 按钮。

图 7-7 用于格式化 Multiple (Report) 组件的模板
  • 前提:组件需要渲染多行数据,并可能支持分组、分页或行选择。
  • 操作区域:模板组件编辑页中的 Partial、Report Row、Report Group、Report Body 和 Report Container 模板。
  • 验证:开启或关闭分组时,输出仍由同一套模板组合生成;按钮插槽在预期位置出现。

7.1.7 呈现分组数据#

当模板组件可作为 Multiple (Report) 区域使用时,可以按数据源中的共同值对行进行分组。组件作者在 Report Group 模板中定义每个分组的 HTML 标记,并用 #APEX$ROWS# 放入该组的行。

使用组件时,开发者需要配置数据源,并在一个或多个列上启用 Group 开关。所有启用 Group 的列都必须出现在区域数据源的 ORDER BY 子句中。组件会为分组列值的每一种不同组合渲染一个新分组。只要至少有一列启用了 Group,Property Editor 就会显示额外的 Report Group 属性。

图 7-8 Property Editor 中的 Report Group 属性

同一个组件既能显示普通结果,也能显示分组结果。官方示例左侧未配置分组列,右侧启用了 DNAMEDEPTNO 的 Group 设置,并把它们映射到组件的 Group Name 与 Group ID 属性。

图 7-9 模板组件显示未分组与已分组数据
  • 前提:数据源中有稳定的分组列,例如部门编号、课程名称或状态类别。
  • 操作区域:Page Designer 中组件区域的数据源列属性。
  • 验证:启用 Group 后,输出按指定列分组;SQL 中对应列包含在 ORDER BY 中。

7.1.8 配置分页支持#

对于可作为 Multiple (Report) 区域使用的模板组件,可以配置用户如何查看首屏之外的更多结果:滚动加载或分页浏览。这个能力同时适用于普通结果和分组结果。

选择分页时,用户每次看到一页数据,页大小由开发者配置。官方示例使用 EMP 表,每页显示 5 行;右侧示例还按部门对结果进行了分组。

图 7-10 普通数据与分组数据的分页
  • 前提:组件以 Multiple (Report) 方式使用,且数据量超过首屏可读范围。
  • 操作区域:Page Designer 中区域的分页相关属性。
  • 验证:普通结果和分组结果都能按配置页大小翻页或滚动加载,分组标题与行内容保持一致。

7.1.9 启用行选择#

如果模板组件可作为 Multiple (Report) 区域使用,可以在组件编辑页的 Standard Attributes 区域勾选 Has Row Selection Support。启用后,使用该组件的开发者会在 Property Editor 中看到 Row Selection 配置区域。

开发者可以启用单选、多选或仅聚焦选择,并可提供一个页面项名来保存所选行的主键。对于多行选择,Current Selection Page Item 中保存的值用冒号(:)分隔多个主键。

图 7-11 Multiple (Report) 模板组件的行选择设置

官方示例中,Grouped Name/Value Pairs 组件出现两次:两个实例都显示 ENAMESAL,并配置为每页 5 行;右侧实例还用 DNAMEDEPTNO 作为分组名称和分组 ID。最终用户在左侧选择单行,在右侧选择多个员工。组件的 Entity Title 设置使用单数 employee 和复数 employees,所以选择消息能显示为更自然的 “1 employee selected” 或 “2 employees selected”。

图 7-12 单选与多选显示实体标题

页面包含一个 Handle Selected Emps 页面处理,用 PL/SQL 拆分 P26_SELECTED_EMPLOYEES 页面项中以冒号分隔的员工主键。如果没有选择员工,处理会用文本消息键 SELECT_AT_LEAST_ONE_EMP 添加可翻译错误消息;如果处理了一个或多个员工,则用 APEX_LANG.GET_MESSAGE 取得可翻译成功消息,并赋给隐藏页面项 P26_SUCCESS_MESSAGE。页面处理的 Success Message 属性引用 &P26_SUCCESS_MESSAGE.

declare
    l_processed apex_t_varchar2;
begin
    for cur_emp in
       (select ename, empno
          from emp
         where empno in
            (select column_value
               from apex_string.split_numbers(
                  :P26_SELECTED_EMPLOYEES,':')))
    loop
        apex_string.push(l_processed,cur_emp.ename);
        -- Process cur_emp.empno employee here
    end loop;
    -- SELECT_AT_LEAST_ONE_EMP => "Please select at least one employee"
    if l_processed is null then
        apex_error.add_error(p_error_code       => 'SELECT_AT_LEAST_ONE_EMP',
                             p_display_location => apex_error.c_on_error_page,
                             p_page_item_name   => null);
    else
        -- PROCESSED_ONE  => "Processed employee %0 successfully"
        -- PROCESSED_MANY => "Successfully processed employees %0"
        :P26_SUCCESS_MESSAGE := apex_lang.get_message(
                                   'PROCESSED_'||
                                   case l_processed.count
                                   when 1 then 'ONE' else 'MANY' end,
                                    apex_t_varchar2('0',
                                      apex_string.join(l_processed,', ')));
    end if;
end;

点击 Process Selected Employees 按钮后,最终用户可能看到三种结果:未选择员工时显示错误;选择单个员工时显示单数成功消息;选择多个员工时显示包含员工列表的复数成功消息。

图 7-13 处理零个、一个和多个所选行
  • 前提:组件是 Multiple (Report),且数据源能提供稳定主键。
  • 操作区域:模板组件 Standard Attributes、Page Designer 的 Row Selection 设置、页面处理 Processes。
  • 验证:所选主键写入指定页面项;未选择时显示错误;选择一行和多行时分别显示正确成功消息。

7.1.10 属性作用域与模板可见性#

每个自定义属性都有一个作用域:ComponentReportReport Group。作用域决定该属性的值能在哪些模板中被引用。

作用域 可用模板
Component Partial
Report Partial、Report Row、Report Body、Report Group、Report Container
Report Group Report Group

官方示例 Grouped Name/Value Pairs 组件中,NameValue 是 Component 作用域,Title 是 Report 作用域,Group NameGroup ID 是 Report Group 作用域。

图 7-14 Grouped Name/Value Pairs 组件中的不同作用域属性
  • 前提:组件需要在 Partial、报表行、报表容器或分组模板中分别访问不同属性。
  • 操作区域:模板组件的自定义属性定义。
  • 验证:属性只在对应作用域允许的模板中使用;分组属性不会被误放到行模板中。

7.1.11 动作位置与动作模板#

如果模板组件显示单行或多行数据,也可以定义动作位置(Action Positions)。动作位置是行级插槽,使用组件的团队成员可以在其中定义按钮或菜单,对特定行执行操作。

定义动作位置名称后,在 Partial 或 Report Row 模板中用 #ACTIONPOSITIONNAME# 引用它。每个动作位置会关联一个动作模板,由动作模板定义按钮或菜单项的 HTML 标记。

图 7-15 在动作位置中定义菜单和子菜单选项

运行时,最终用户会在每一行看到对应动作。官方示例中,Content Row 区域显示两张发票,用户在第一张发票的动作菜单中选择 Invoice Complete

图 7-16 最终用户选择模板组件行级动作
  • 前提:组件每一行都可能需要查看、编辑、完成、删除或打开菜单等动作。
  • 操作区域:模板组件的 Action Positions / Action Templates,以及使用组件页面中的动作配置。
  • 验证:每行动作只作用于当前行;菜单项、按钮样式和动态操作触发都符合预期。

7.1.12 定义要加载的文件和文件 URL#

如果组件使用 CSS 或 JavaScript,应把这些代码放进独立组件文件中,以便维护。官方示例在 component.css 中定义被选中行的样式:

li.is-selected {
    background-color: #e0f0ff;
    color: #003366;
}

在模板组件编辑页的 Files 选项卡中创建文件。APEX 保存或修改 CSS、JavaScript 文件时,会自动创建较小的压缩版本。文件列表中的 Reference 列提供了在 File URLs to Load 选项卡中引用文件的名称。需要加载哪些组件文件,就按 Reference 字符串一行一个列出。

图 7-17 模板组件的文件列表
  • 前提:组件的样式或交互逻辑不适合内联在模板中。
  • 操作区域:模板组件编辑页的 Files 与 File URLs to Load 选项卡。
  • 验证:运行页面时 CSS/JS 已加载;浏览器 Network 面板能看到对应组件文件;压缩文件由 APEX 自动生成。

7.2 使用 apex.world 插件#

通过 apex.world 这类社区“应用商店”使用现有插件,可以快速把其他开发者或团队开放给 APEX 社区的功能加入应用。apex.world 站点地址是 https://apex.world

使用第三方插件前,建议先检查支持的 APEX 版本、最近更新时间、许可证、源代码可见性、依赖库、安全性、可访问性和维护者活跃度。导入和测试应在非生产环境中完成,并记录插件版本、下载来源和回退方案。

7.2.1 跟踪员工培训状态#

假设应用用户需要跟踪员工培训状态,状态从 EnrolledIn Progress,再到 CompletedCertified。官方数据模型使用熟悉的 EMP 表,并增加 EMP_TRAINING 表记录员工参加的培训,以及 EMP_COURSE 表记录课程。

图 7-18 跟踪培训进度的 EMP 相关表

可以先用 Interactive Report 区域显示员工培训状态,并通过 Actions > Format > Control Break 按课程做视觉分组。官方截图显示 Vector Search 课程下有 5 名员工处于不同完成阶段。

图 7-19 使用 Interactive Report 显示员工培训状态

不过,对于持续推进的任务状态,看板(Kanban Board)更直观。理想界面包含四个纵向状态列:Enrolled、In Progress、Completed、Certified;还包含两个横向泳道,代表 Vector Search 与 AI Features 两门课程。每个员工卡片位于课程与状态交叉的单元格中。

图 7-20 展示员工培训状态的 Kanban 看板草图
  • 前提:已有员工、课程和培训状态数据,且状态值能映射到固定阶段。
  • 操作区域:应用数据模型、Interactive Report 区域、后续 Kanban 插件区域。
  • 验证:同一数据集既能用报表分组查看,也能自然映射为看板列和课程泳道。

7.2.2 下载并导入插件#

在 apex.world 的 Plug-ins 选项卡中搜索 kanban,可以找到官方示例使用的 Material Kanban Board 插件。点击下载链接并解压后,可得到 region_type_plugin_material_kanban.sql 插件导出文件。

图 7-21 apex.world 上的 Kanban Board 插件

导入插件时,进入应用的 Shared Components > Plug-ins 列表页,点击 Import 按钮,选择下载得到的 region_type_plugin_material_kanban.sql 文件,然后点击 Next 完成导入。导入对话框中,File Type 应选择 Plug-in

图 7-22 将插件导入应用
  • 前提:在非生产应用中操作,并已下载可信来源的插件 SQL 文件。
  • 操作区域:Shared Components > Plug-ins > Import。
  • 验证:导入完成后,Plug-ins 列表中出现 Material Kanban Board;Utilization 初始为空或仅显示后续创建的使用点。

7.2.3 配置 Kanban 插件#

插件安装后,会出现在 Page Designer 可创建的区域类型列表中。官方示例新建 Material Kanban Board 区域,并按照插件文档配置 ConfigJSON。四个静态列分别对应应用需要的 Enrolled、In Progress、Completed 和 Certified 状态;由于示例还要使用分组能力,所以把 groupExtension 设为 true

{
    "staticColumns": [
       {"COLUMN_ID": "1",
        "COLUMN_TITLE": "Enrolled",
        "COLUMN_ICON": "fa-calendar"},
       {"COLUMN_ID": "2",
        "COLUMN_TITLE": "In Progress",
        "COLUMN_ICON": "fa-head-microchip"},
       {"COLUMN_ID": "3",
        "COLUMN_TITLE": "Completed",
        "COLUMN_ICON": "fa-graduation-cap"},
       {"COLUMN_ID": "4",
        "COLUMN_TITLE": "Certified",
        "COLUMN_ICON": "fa-certificate"}],
    "groupExtension": true,
    "groupColWidth": 10
}
图 7-23 定义 Kanban Board 状态列和图标

随后编写区域的 SQL Query,定义看板要显示的信息。查询连接 EMP_TRAININGEMP_COURSEEMP 表,取出课程和参加培训员工的关键信息。这个插件通过 SQL 查询中的列别名识别每列在看板中的作用:TITLE 是卡片标题,GROUP_TITLE 是泳道标题,COLUMN_ID 对应前面 JSON 中定义的状态列编号。

图 7-24 配置 Kanban 区域 SQL Query
SELECT
    t.id AS ID,
    e.ename AS TITLE,
    case t.status
        when 'ENROLLED'    then 1
        when 'IN_PROGRESS' then 2
        when 'COMPLETED'   then 3
        when 'CERTIFIED'   then 4
    end AS COLUMN_ID,
    c.id   AS GROUP_ID,
    c.name AS GROUP_TITLE
FROM
   emp_training t
   join emp e using (empno)
   join emp_course c on t.course_id = c.id
order by c.name

完成 ConfigJSON 和 SQL Query 后,运行页面即可用看板快速查看员工培训状态。再增加少量 PL/SQL 代码后,还可以允许最终用户通过拖放改变员工培训状态。官方最终图按课程拆分泳道,并把员工放入不同培训阶段。

图 7-25 Kanban Board 区域插件展示员工培训状态
  • 前提:Material Kanban Board 插件已导入应用,数据表中已有培训记录和状态值。
  • 操作区域:Page Designer 中的区域类型、插件属性 ConfigJSON、区域 Source / SQL Query。
  • 验证:页面运行后显示四个状态列和按课程区分的泳道;员工卡片出现在与其状态匹配的列中。