Jmix 视图

任何 Jmix 视图都可以作为流程表单使用。与 输入对话框 相比能为用户提供更复杂的交互。

创建一个流程表单

一个 Jmix 视图的流程表单本质上就是常规的应用程序视图,只不过包含了一些带有流程相关信息的注解。

使用已有视图

如需将已有常规视图转换为流程表单:

应当避免转换已经在使用的视图。最好创建一个新视图作为流程表单。

使用 Jmix Studio

在 Jmix Studio 创建流程表单:

  1. 点击 New → View 打开 视图创建向导

  2. 选择 BPM Process form 模板。

    view wizard bpm process form

  3. 配置表单模板及其对应的元素:

    • 从以下两个选项中选择 Form Template(表单模板)

      • Process Form with Process Variables(带流程变量的表单):该表单将为所选择的一组流程变量创建页面组件。

      • Process Form for Entity Instance(实体实例表单):该表单将为特定实体实例的属性以及任何附加的流程变量创建页面组件。

    • 选择 Form Type(表单类型)

      • User task form:用于用户任务元素。

      • Start form:用于启动事件元素。

        jmix view wizard 1

  4. 如果选择 Process form for entity instance,需要指定实体类、变量名称,并配置 fetch plan。

  5. 指定流程变量。

  6. 如果选择 User task form,请指定表单的结果。默认为 submitreject。可以编辑这些或添加更多选项。

  7. 添加可本地化的消息。

  8. 点击 Create

与其他视图一样,表单会在应用程序中创建。

为元素设置流程表单

创建流程表单后,可以为元素配置使用该表单。请按照以下步骤操作:

  1. 打开 建模器

  2. 选择相应的元素(用户任务或启动事件)。

    user task

    form section

  3. Form type 设置为 Jmix view

  4. 设置 Open mode

    • Dialog:在对话框中显示表单。

    • Navigate:在独立的 URL 上以视图形式显示表单。

  5. 指定 View id。选项列表将包含应用程序内所有带 @ProcessForm 注解的视图。建模器会识别流程表单并根据其注解完成剩下的配置。

  6. 如果有任何 表单参数,需要相应地配置其值来源。

现在,流程表单已配置好,可在所选元素中使用。

@ProcessForm

在视图控制器添加 @ProcessForm 注解可以将视图转换为流程表单,并使其能被 建模器 识别。该注解可以包含以下属性:

  • 如需限制表单在特定的流程中使用,可以在 allowedProcessKeys 属性中指定流程的键值:

    @ProcessForm(allowedProcessKeys = {"process-1", "process-2"})
  • 如需定义表单结果,使用 outcomes 属性:

    @ProcessForm(
            outcomes = { (1)
                    @Outcome(id = "approve"),
                    @Outcome(id = "reject")
            }
    )
  • 如需定义输出变量,使用 outputVariables 属性:

    @ProcessForm(
            outputVariables = {
                    @OutputVariable(name = "order", type = Order.class),
                    @OutputVariable(name = "comment", type = String.class)
            }
    )
  • 如果只在某些特定的结果时才设置流程变量,可以按下面的示例将 outcomesoutputVariables 属性结合使用:

    @ProcessForm(
       outcomes = {
          @Outcome(
             id = "approve",
             outputVariables = {
                @OutputVariable(name = "nextActor", type = User.class) (1)
             }
          ),
          @Outcome(
            id = "reject",
               outputVariables = {
                  @OutputVariable(name = "rejectionReason", type = String.class) (2)
            }
          )
       },
       outputVariables = {
          @OutputVariable(name = "comment", type = String.class) (3)
       }
    )
    1 当任务以 approve 完成时,会设置 nextActor 变量。
    2 当任务以 reject 完成时,会设置 rejectionReason 变量。
    3 在所有情况下都会设置 comment 变量。
  • 如需声明 表单参数,请使用 params 属性。然后在类字段上设置 @ProcessFormParam 将字段与参数关联。

    @ProcessForm(
            params = {
                    @Param(name = "nextActor"),
                    @Param(name = "entityPickerCaption")
            }
    )
    
        //...
    
        @ProcessFormParam
        private String nextActor;
    
        @ProcessFormParam
        private String entityPickerCaption;
    @ProcessFormParam 还可以使用 name 属性指定参数名称。如果没有指定,则从字段名生成默认名称。

@ProcessVariable

在类字段和视图组件上使用 @ProcessVariable,可以在流程表单打开时设置流程变量的值。

该变量在 BeforeShowEvent 中可用。而在 InitEvent 中不可访问。
@ProcessVariable
private Date date;

@ViewComponent
@ProcessVariable(name = "order")
private EntityPicker<Order> orderEntityPicker;

如果没有显式指定 name,则默认的流程变量名称从字段名称派生。

加载动态属性

默认情况下,动态属性 的值在流程表单中不会被加载(因此也不会显示)。如果流程变量是一个具有动态属性的实体,可以通过在 params 数组中添加 @ProcessVariableParam 并将 ProcessVariableConstants.LOAD_DYN_ATTR_PARAM 键设置为 true 来加载并显示这些属性。例如:

@ProcessVariable(name="documentVar", params = {
        @ProcessVariableParam(key = ProcessVariableConstants.LOAD_DYN_ATTR_PARAM, value = "true")
})
private Document documentVar;

启用后,流程表单会自动为每个动态属性渲染可视化组件,而无需手动创建。

ProcessFormContext

ProcessFormContext 对象包含正在启动的流程的定义或需要完成的用户任务的信息。当从 启动流程我的任务 视图打开流程表单时,可以使用 ProcessFormContext。如需以编程方式打开带有注入的 ProcessFormContext 的流程表单,请使用 ProcessFormViews bean。

参考以下启动流程的示例:

@ProcessVariable
private Date date;

@ViewComponent
@ProcessVariable(name = "order")
private EntityPicker<Order> orderEntityPicker;

@Autowired
private ProcessFormContext processFormContext;

@Subscribe("startProcessBtn")
public void onStartProcessBtnClick(ClickEvent<JmixButton> event) {
    processFormContext.processStarting() (1)
            .withBusinessKey("order-1") (2)
            .addProcessVariable("date", date)
            .addProcessVariable("order", orderEntityPicker.getValue()) (3)
            .start(); (4)
    closeWithDefaultAction(); (5)
}
1 创建一个 ProcessStarting 实例。
2 为流程实例设置一个业务键值。
3 添加一个流程变量。
4 启动实际的流程。
5 关闭打开的窗口。

参考以下完成用户任务的示例:

@Autowired
private ProcessFormContext processFormContext;

@Subscribe("rejectBtn")
protected void onRejectBtnClick(ClickEvent<JmixButton> event) {
    processFormContext.taskCompletion() (1)
            .withOutcome("reject") (2)
            .saveInjectedProcessVariables() (3)
            .complete(); (4)
    closeWithDefaultAction(); (5)
}
1 创建一个 TaskCompletion 实例。
2 设置任务的结果。
3 带有 @ProcessVariables 注解的类字段值会被收集并保存为流程变量。
4 完成实际的任务。
5 关闭打开的窗口。

示例

启动流程表单

我们看一下将流程表单用作启动表单的示例。 表单展示两个字段:

  • 一个文本字段,用于输入 order number。

  • 一个 entityPicker,用于选择 manager,manager 可以是流程的下一个执行人。

视图 XML:

<view xmlns="http://jmix.io/schema/flowui/view"
      title="msg://com.company.bpmex1.view.forms/startProcessForm.title">
    <layout>
        <formLayout>
            <textField id="orderNumber"
                       label="msg://com.company.bpmex1.view.forms/orderNumber"
                       datatype="string"/>
            <entityPicker id="managerEntityPicker"
                          metaClass="User"
                          label="msg://managerEntityPicker.caption">
                <actions>
                    <action id="lookup" type="entity_lookup"/>
                    <action id="clear" type="entity_clear"/>
                </actions>
            </entityPicker>
        </formLayout>
        <hbox id="actionsPanel" spacing="true">
            <button id="startProcessBtn"
                    icon="CHECK"
                    text="msg://com.company.bpmex1.view.forms/startProcessBtn.text"/>
        </hbox>
    </layout>
</view>

视图控制器:

@ViewController("OrderApprovalStartForm")
@ViewDescriptor("order-approval-start-form.xml")
@ProcessForm (1)
public class OrderApprovalStartForm extends StandardView {

    @ViewComponent
    @ProcessVariable (2)
    private TypedTextField<String> orderNumber;

    @ViewComponent
    @ProcessVariable(name = "manager") (3)
    private EntityPicker<User> managerEntityPicker;

    @Autowired
    private ProcessFormContext processFormContext; (4)

    @Subscribe("startProcessBtn")
    public void onStartProcessBtnClick(ClickEvent<JmixButton> event) {
        processFormContext.processStarting()
                .withBusinessKey(orderNumber.getValue()) (5)
                .saveInjectedProcessVariables() (6)
                .start();
        closeWithDefaultAction();
    }
}
1 @ProcessForm 注解表示此视图是一个流程表单,可以在建模器使用。
2 声明注入的 orderNumber UI 组件是一个流程变量。由于我们开发的是流程启动表单, 这个变量还没有值,但是这个注解会在流程启动时使用。
3 与 ② 一样,但这里 manager 流程变量名与 managerEntityPicker 字段名不一致。
4 ProcessFormContext 对象启动流程。
5 当流程启动时,我们可以传入一个可选的流程实例业务键值。 这里我们用 orderNumber
6 saveInjectedProcessVariables() 方法在流程启动时将带有 @ProcessVariables 注解的字段值保存至流程变量。

除了使用 saveInjectedProcessVariables(),还可以手动添加流程变量:

@Subscribe("startProcessBtn")
public void onStartProcessBtnClick(ClickEvent<JmixButton> event) {
    processFormContext.processStarting()
            .withBusinessKey(orderNumber.getValue())
            .addProcessVariable("orderNumber", orderNumber.getValue())
            .addProcessVariable("manager",managerEntityPicker.getValue())
            .start();
    closeWithDefaultAction();
}

任务流程表单

我们看一下任务流程表单的示例,显式两个字段:

  • 第一个字段展示已有的流程变量值 - orderNumber

  • 第二个字段作为新增流程变量 - comment

ApproveReject 按钮分别使用对应的结果完成用户任务。

视图 XML:

<view xmlns="http://jmix.io/schema/flowui/view"
      title="msg://orderApprovalTaskForm.title">
    <layout>
        <formLayout>
            <textField id="orderNumber" readOnly="true"
                       label="msg://orderNumber"/>
            <textField id="commentField" label="msg://comment"/>
        </formLayout>
        <hbox id="actionsPanel" spacing="true">
            <button id="approveBtn" icon="CHECK" text="msg://approveBtn.text"/>
            <button id="rejectBtn" icon="BAN" text="msg://rejectBtn.text"/>
        </hbox>
    </layout>
</view>

视图控制器:

@ViewController("OrderApprovalTaskForm")
@ViewDescriptor("order-approval-task-form.xml")
@ProcessForm(
        outcomes = { (1)
                @Outcome(id = "approve"),
                @Outcome(id = "reject")
        }
)
public class OrderApprovalTaskForm extends StandardView {

    @ViewComponent
    @ProcessVariable (2)
    private TypedTextField<String> orderNumber;

    @ViewComponent
    @ProcessVariable(name = "comment") (3)
    private TypedTextField<String> commentField;

    @Autowired
    private ProcessFormContext processFormContext;

    @Subscribe("approveBtn")
    protected void onApproveBtnClick(ClickEvent<JmixButton> event) {
        processFormContext.taskCompletion()
                .withOutcome("approve")
                .saveInjectedProcessVariables() (4)
                .complete();
        closeWithDefaultAction();
    }

    @Subscribe("rejectBtn")
    protected void onRejectBtnClick(ClickEvent<JmixButton> event) {
        processFormContext.taskCompletion()
                .withOutcome("reject")
                .addProcessVariable("comment", commentField.getValue()) (5)
                .complete();
        closeWithDefaultAction();
    }
}
1 表单定义了两个可能的结果,建模器中可用与顺序流节点条件。 这个信息仅供建模器使用。
2 orderNumber 变量已经在流程启动时设置。 由于使用了 @ProcessVariable 注解, 流程变量 orderNumber 的值将在展示表单时设置给 orderNumber 文本控件。
3 comment 变量还未设置,但当我们在按钮点击监听器中完成任务时, 会处理 @ProcessVariable 注解中的变量。
4 任务完成时,所有带 @ProcessVariable 注解的字段都作为流程变量保存。
5 定义流程变量的另一种方式。除了使用 saveInjectedProcessVariables() 方法, 可以直接定义流程变量。

编程式打开流程表单

使用 ProcessFormViews 服务可以打开建模器中定义的流程启动表单和任务表单。

在下面的示例中,列表视图中的按钮可以打开流程启动表单。

@Autowired
private RepositoryService repositoryService;

@Autowired
protected ProcessFormViews processFormViews;

@Subscribe("startProcessBtn")
public void onStartProcessBtnClick(final ClickEvent<JmixButton> event) {
    ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery() (1)
            .processDefinitionKey("order-process")
            .latestVersion()
            .singleResult();

    processFormViews.openStartProcessForm(processDefinition, this); (2)
}
1 通过键值获取流程定义。
2 使用给定的流程定义显示流程启动表单。

使用 openTaskProcessForm() 打开任务表单:

@Autowired
private TaskService taskService;

@Autowired
private ProcessFormViews processFormViews;

@Subscribe("openTaskBtn")
public void onOpenTaskBtnClick(ClickEvent<JmixButton> event) {

    Task task = taskService.createTaskQuery()
            .processDefinitionKey("approve-order-process")
            .taskAssignee("admin")
            .active()
            .orderByTaskCreateTime()
            .list()
            .get(0);

    processFormViews.openTaskProcessForm(task, this);
}

带参数的流程表单

流程表单可以接收外部参数来调整其行为或外观,例如更改组件标题。与流程变量不同,这些表单参数的作用域更窄,仅在特定表单内可用(除非将它们的值保存到流程变量中)。

使用 @ProcessForm 注解及其 params 属性声明表单参数。通过 @ProcessFormParam 注解与类字段关联。

考虑这样一个场景:我们需要一个用于选择下一流程执行人的表单。该表单应包含一个 EntityPicker 组件来选择用户,然后将用户保存到流程变量中。此外,该表单还要在流程的不同步骤中重复使用,因此,可以通过参数设置实体选择器的标题以适应不同的步骤。

总共会有两个表单参数:

  • nextActor – 保存用户在 EntityPicker 中选择的值,以便在后续流程中使用。

  • entityPickerCaption – 用于根据流程步骤自定义 EntityPicker 组件上的标题。

视图 XML:

<view xmlns="http://jmix.io/schema/flowui/view"
      title="msg://com.company.bpmex1.view.forms/actorSelectionForm.title">
    <layout spacing="true">
        <formLayout width="20em">
            <entityPicker id="userEntityPicker"
                          metaClass="User"
                          property="username">
                <actions>
                    <action id="lookup" type="entity_lookup"/>
                    <action id="clear" type="entity_clear"/>
                </actions>
            </entityPicker>
        </formLayout>
        <hbox spacing="true">
            <button id="completeTaskBtn" icon="CHECK" text="msg://completeTask"/>
        </hbox>
    </layout>
</view>

视图控制器:

@ProcessForm(
        params = {
                @Param(name = "nextActor"),
                @Param(name = "entityPickerCaption")
        }
)
public class ActorSelectionForm extends StandardView {

    @Autowired
    private ProcessFormContext processFormContext;

    @ViewComponent
    private EntityPicker<String> userEntityPicker;


    //...

    @ProcessFormParam
    private String nextActor;

    @ProcessFormParam
    private String entityPickerCaption;

    @Subscribe
    private void onBeforeShow(BeforeShowEvent event) {
        userEntityPicker.setLabel(entityPickerCaption);
    }

    @Subscribe("completeTaskBtn")
    private void onCompleteTaskBtnClick(ClickEvent<JmixButton> event) {
        processFormContext.taskCompletion()
                .addProcessVariable(nextActor, userEntityPicker.getValue())
                .complete();
        closeWithDefaultAction();
    }
}

ProcessFormContext 可以获取流程表单所有参数的列表:

List<FormParam> formParams = processFormContext.getFormData().getFormParams();