一.简介
整体上来说,我们可以将Flowable 的表单分为三种不同的类型:
- 动态表单
这种表单定义方式我们可以配置表单中每一个字段的可读性、可写性、是否必填等信息,不过不能定义完整的表单页面。 - 外置表单
外置表单我们只需要定义一下表单的 key,至于这个 key 对应的表单是什么样子,则由开发者自己去维护。 - 内置表单
这是内置的表单定义以及渲染引擎,
另外需要注意的,Flowable 中有很多不同类型的节点,但是只有开始节点和任务节点是支持表单定义的,其他节点均不支持表单定义。
二.动态表单
假设有一个请假流程,流程图如下:
在第一个任务节点中,需要填写请假的基本信息,那么选中该节点,然后点击动态表单属性,截图如下:
然后就可以开启动态表单属性的配置了,截图如下:
@Test
void test11() {
HistoricProcessInstance pi = historyService.createHistoricProcessInstanceQuery().singleResult();
List<HistoricIdentityLink> links = historyService.getHistoricIdentityLinksForProcessInstance(pi.getId());
for (HistoricIdentityLink link : links) {
logger.info("userId:{}",link.getUserId());
}
}
这个是查询流程对应的处理人,对应的 SQL语句如下:
select * from ACT_HI_IDENTITYLINK where PROC_INST_ID_ = ?
如果想查询任务的处理人,代码如下:
这里一共配置了四个属性,这些属性的含义应该都好理解,就不一一赘述了。
接下来下载这个流程图。流程的 XML 文件下载下来之后,可以在看到在 UserTask 节点中多了 flowable:formProperty 标签,如果想将 UserTask 节点中的动态表单属性拷贝到启动节点中,直接拷贝即可,代码如下:
<process id="FormDemo01" name="FormDemo01" isExecutable="true">
<documentation>FormDemo01</documentation>
<startEvent id="startEvent1" flowable:formFieldValidation="true">
<extensionElements>
<flowable:formProperty id="startTime" name="请假开始时间" type="date" datePattern="yyyy-MM-dd HH:mm" required="true"></flowable:formProperty>
<flowable:formProperty id="endTime" name="请假结束时间" type="date" datePattern="yyyy-MM-dd HH:mm" required="true"></flowable:formProperty>
<flowable:formProperty id="reason" name="请假理由" type="string" required="true"></flowable:formProperty>
<flowable:formProperty id="days" name="请假天数" type="long" required="true"></flowable:formProperty>
</extensionElements>
</startEvent>
<userTask id="sid-F4DE03F1-D09F-4527-9267-0E5C276D08B8" name="提交请假申请" flowable:formFieldValidation="true">
<extensionElements>
<flowable:formProperty id="startTime" name="请假开始时间" type="date" datePattern="yyyy-MM-dd HH:mm" required="true"></flowable:formProperty>
<flowable:formProperty id="endTime" name="请假结束时间" type="date" datePattern="yyyy-MM-dd HH:mm" required="true"></flowable:formProperty>
<flowable:formProperty id="reason" name="请假理由" type="string" required="true"></flowable:formProperty>
<flowable:formProperty id="days" name="请假天数" type="long" required="true"></flowable:formProperty>
</extensionElements>
</userTask>
<sequenceFlow id="sid-2A8D19F2-927C-4FCE-AF31-534425B1CA18" sourceRef="startEvent1" targetRef="sid-F4DE03F1-D09F-4527-9267-0E5C276D08B8"></sequenceFlow>
<userTask id="sid-9136F312-F00B-467E-A61B-F2932BA9068A" name="请假审批" flowable:formFieldValidation="true"></userTask>
<sequenceFlow id="sid-877A95AB-B8A4-47FE-BC9F-0998FEAEC52C" sourceRef="sid-F4DE03F1-D09F-4527-9267-0E5C276D08B8" targetRef="sid-9136F312-F00B-467E-A61B-F2932BA9068A"></sequenceFlow>
<endEvent id="sid-E26593D4-C67B-4784-98EE-772B9659F805"></endEvent>
<sequenceFlow id="sid-1A5C4E8C-6705-4148-A0E3-E7769631BFD9" sourceRef="sid-9136F312-F00B-467E-A61B-F2932BA9068A" targetRef="sid-E26593D4-C67B-4784-98EE-772B9659F805"></sequenceFlow>
</process>
可以看到,在 startEvent 和第一个 userTask 中都有 flowable:formProperty 标签。
接下来,按照之前所讲的,来部署一下这个流程。部署完成之后,可以通过如下方式来查询流程中的动态表单信息,代码如下:
@Autowired
FormService formService;
@Test
void test01() {
ProcessDefinition pd = repositoryService.createProcessDefinitionQuery().processDefinitionKey("FormDemo01").latestVersion().singleResult();
StartFormData startFormData = formService.getStartFormData(pd.getId());
System.out.println("startFormData.getDeploymentId() = " + startFormData.getDeploymentId());
System.out.println("startFormData.getFormKey() = " + startFormData.getFormKey());
List<FormProperty> formProperties = startFormData.getFormProperties();
for (FormProperty fp : formProperties) {
String value = fp.getValue();
String id = fp.getId();
boolean readable = fp.isReadable();
boolean writable = fp.isWritable();
boolean required = fp.isRequired();
String name = fp.getName();
FormType type = fp.getType();
String key = "";
if (type instanceof EnumFormType) {
key = "values";
} else if (type instanceof DateFormType) {
key = "datePattern";
}
Object information = type.getInformation(key);
logger.info("value:{},id:{},readable:{},writeable:{},required:{},name:{},info:{}", value, id, readable, writable, required, name, information);
}
}
这个查询是通过流程定义查询的,所以这里查询到的信息,其实也是和流程实例无关的。只是单纯的查看一下启动节点上有哪些动态表单需要输入,以及这些动态表单的类型。最终输出日志,截图如下:
三.启动带表单的实例
动态表单,其实跟普通的变量有点像,启动的时候可以通过表单服务类来启动,代码如下:
@Test
void test02() {
ProcessDefinition pd = repositoryService.createProcessDefinitionQuery().processDefinitionKey("FormDemo01").latestVersion().singleResult();
Map<String, String> vars = new HashMap<>();
vars.put("startTime", "2022-10-10 10:10");
vars.put("endTime", "2022-10-12 10:10");
vars.put("reason", "玩两天");
vars.put("days", "3");
ProcessInstance pi = formService.submitStartFormData(pd.getId(), vars);
}
这里通过 formService.submitStartFormData 方法来启动流程实例,启动的时候,传入了 vars 变量。
流程实例启动成功之后,在 ACT_RU_VARIABLE 表中就可以看到这些动态表单的信息。
从这里可以看到刚刚存入的数据。
四.查询任务上的表单
现在流程走到了 提交请假申请 这一步了,在绘制流程图的时候,提交请假申请 这个 UserTask 中也是有动态表单的,前面启动流程时传递的动态表单信息,现在已经传到 提交请假申请 这一步了,可以通过如下方式来进行查询,代码如下:
@Test
void test03() {
Task task = taskService.createTaskQuery().singleResult();
TaskFormData taskFormData = formService.getTaskFormData(task.getId());
List<FormProperty> formProperties = taskFormData.getFormProperties();
for (FormProperty fp : formProperties) {
String value = fp.getValue();
String id = fp.getId();
boolean readable = fp.isReadable();
boolean writable = fp.isWritable();
boolean required = fp.isRequired();
String name = fp.getName();
FormType type = fp.getType();
String key = "";
if (type instanceof EnumFormType) {
key = "values";
} else if (type instanceof DateFormType) {
key = "datePattern";
}
Object information = type.getInformation(key);
logger.info("value:{},id:{},readable:{},writeable:{},required:{},name:{},info:{}", value, id, readable, writable, required, name, information);
}
}
调用 formService.getTaskFormData 方法传入 TaskId 即可进行查询。这个时候查询出来的内容就有值了,截图如下:
这跟用变量有啥区别呀,用变量不也是这样吗?
变量是散的,而表单是整的。
在上面的代码中,一个方法就可以提取出来所有的表单信息了,然后就遍历就行了。
另外还需要注意,如果 提交请假申请 中的动态表单和启动节点的动态表单不一致的话,提交请假申请 节点中有哪些动态表单,就能拿到哪些数据,其他的数据就不能通过表单拿到。
以上面的案例来说,startEvent 中有 startTime、endTime、reason 以及 days 四个动态表单属性,如果 提交请假申请 中只有 reason 和 days 两个动态表单属性的话,那么就只能获取这两个动态表单属性,其他的动态表单属性则可以通过变量去获取。
五.保存与完成
对于 UserTask 上的表单,首先可以通过如下方式来提交表单数据,代码如下:
@Test
void test04() {
Task task = taskService.createTaskQuery().singleResult();
Map<String, String> vars = new HashMap<>();
vars.put(“startTime”, “2022-10-11 11:11”);
vars.put(“endTime”, “2022-10-19 11:11”);
formService.saveFormData(task.getId(), vars);
}
这个方法只是保存动态表单变量,并不会完成当前 Task。
如果想在提交表单变量的同时顺便完成当前 UserTask,代码如下:
@Test
void test04() {
Task task = taskService.createTaskQuery().singleResult();
Map<String, String> vars = new HashMap<>();
vars.put("startTime", "2022-10-11 11:11");
vars.put("endTime", "2022-10-19 11:11");
formService.submitTaskFormData(task.getId(), vars);
该方法在提交表单变量的同时,还会顺便 complete 当前 UserTask。