有小伙伴在星球上催了好幾次了,今天松哥就來和大家聊一聊流程中的表單。
1. 表單分類
整體上來說,我們可以將表單分為三種不同的類型:
動(dòng)態(tài)表單:這種表單定義方式我們可以配置表單中每一個(gè)字段的可讀性、可寫性、是否必填等信息,不過不能定義完整的表單頁面。
外置表單:外置表單我們只需要定義一下表單的 key,至于這個(gè) key 對(duì)應(yīng)的表單是什么樣子,則由開發(fā)者自己去維護(hù)。
內(nèi)置表單:這是內(nèi)置的表單定義以及渲染引擎,松哥在之前的一個(gè)不用寫代碼的案例,來看看Flowable到底給我們提供了哪些功能?一文中所使用的表單,就是這種。
另外小伙伴們需要注意,F(xiàn)lowable 中有很多不同類型的節(jié)點(diǎn),但是只有開始節(jié)點(diǎn)和任務(wù)節(jié)點(diǎn)是支持表單定義的,其他節(jié)點(diǎn)均不支持表單定義。
2. 動(dòng)態(tài)表單
今天我們就先來看看動(dòng)態(tài)表單的玩法。
假設(shè)我有如下一個(gè)請(qǐng)假流程:

在第一個(gè)任務(wù)節(jié)點(diǎn)中,需要填寫請(qǐng)假的基本信息,那么我們選中該節(jié)點(diǎn),然后點(diǎn)擊動(dòng)態(tài)表單屬性,如下圖:

然后就可以開啟動(dòng)態(tài)表單屬性的配置了:

我這里一共配置了四個(gè)屬性,這些屬性的含義應(yīng)該都好理解,我就不一一贅述了。
接下來我們來下載這個(gè)流程圖。
流程的 XML 文件下載下來之后,我們可以在看到在 UserTask 節(jié)點(diǎn)中多了 flowable:formProperty 標(biāo)簽,現(xiàn)在,如果我想將 UserTask 節(jié)點(diǎn)中的動(dòng)態(tài)表單屬性拷貝到啟動(dòng)節(jié)點(diǎn)中,直接拷貝即可,如下:
<process id="FormDemo01" name="FormDemo01" isExecutable="true">
<documentation>FormDemo01</documentation>
<startEvent id="startEvent1" flowable:formFieldValidation="true">
<extensionElements>
<flowable:formProperty id="startTime" name="請(qǐng)假開始時(shí)間" type="date" datePattern="yyyy-MM-dd HH:mm" required="true"></flowable:formProperty>
<flowable:formProperty id="endTime" name="請(qǐng)假結(jié)束時(shí)間" type="date" datePattern="yyyy-MM-dd HH:mm" required="true"></flowable:formProperty>
<flowable:formProperty id="reason" name="請(qǐng)假理由" type="string" required="true"></flowable:formProperty>
<flowable:formProperty id="days" name="請(qǐng)假天數(shù)" type="long" required="true"></flowable:formProperty>
</extensionElements>
</startEvent>
<userTask id="sid-F4DE03F1-D09F-4527-9267-0E5C276D08B8" name="提交請(qǐng)假申請(qǐng)" flowable:formFieldValidation="true">
<extensionElements>
<flowable:formProperty id="startTime" name="請(qǐng)假開始時(shí)間" type="date" datePattern="yyyy-MM-dd HH:mm" required="true"></flowable:formProperty>
<flowable:formProperty id="endTime" name="請(qǐng)假結(jié)束時(shí)間" type="date" datePattern="yyyy-MM-dd HH:mm" required="true"></flowable:formProperty>
<flowable:formProperty id="reason" name="請(qǐng)假理由" type="string" required="true"></flowable:formProperty>
<flowable:formProperty id="days" name="請(qǐng)假天數(shù)" 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="請(qǐng)假審批" 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? 和第一個(gè) userTask? 中都有 flowable:formProperty 標(biāo)簽。
接下來,按照我們之前所講的,我們來部署一下這個(gè)流程。部署完成之后,我們可以通過如下方式來查詢流程中的動(dòng)態(tài)表單信息:
@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);
}
}
小伙伴們可以看到,這個(gè)查詢是通過流程定義查詢的,所以這里查詢到的信息,其實(shí)也是和流程實(shí)例無關(guān)的。只是單純的查看一下啟動(dòng)節(jié)點(diǎn)上有哪些動(dòng)態(tài)表單需要輸入,以及這些動(dòng)態(tài)表單的類型。最終輸出日志如下:

3. 啟動(dòng)帶表單的實(shí)例
動(dòng)態(tài)表單,其實(shí)跟普通的變量有點(diǎn)像,啟動(dòng)的時(shí)候我們可以通過表單服務(wù)類來啟動(dòng),代碼如下:
@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 方法來啟動(dòng)流程實(shí)例,啟動(dòng)的時(shí)候,傳入了 vars 變量。
流程實(shí)例啟動(dòng)成功之后,我們?cè)?nbsp;ACT_RU_VARIABLE 表中就可以看到這些動(dòng)態(tài)表單的信息。

從這里可以看到我們剛剛存入的數(shù)據(jù)。
4. 查詢?nèi)蝿?wù)上的表單
現(xiàn)在我們的流程走到了 提交請(qǐng)假申請(qǐng)? 這一步了,我們?cè)诶L制流程圖的時(shí)候,提交請(qǐng)假申請(qǐng)? 這個(gè) UserTask 中也是有動(dòng)態(tài)表單的,前面啟動(dòng)流程時(shí)傳遞的動(dòng)態(tài)表單信息,現(xiàn)在已經(jīng)傳到 提交請(qǐng)假申請(qǐng) 這一步了,我們可以通過如下方式來進(jìn)行查詢:
@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);
}
}
小伙伴們看到,調(diào)用 formService.getTaskFormData 方法傳入 TaskId 即可進(jìn)行查詢。這個(gè)時(shí)候查詢出來的內(nèi)容就有值了:

可能有的小伙伴會(huì)說,這跟用變量有啥區(qū)別呀,用變量不也是這樣嗎?
變量是散的,而表單是整的。
在上面的代碼中,一個(gè)方法就可以提取出來所有的表單信息了,然后就遍歷就行了。
另外還需要注意,如果 提交請(qǐng)假申請(qǐng)? 中的動(dòng)態(tài)表單和啟動(dòng)節(jié)點(diǎn)的動(dòng)態(tài)表單不一致的話,提交請(qǐng)假申請(qǐng) 節(jié)點(diǎn)中有哪些動(dòng)態(tài)表單,就能拿到哪些數(shù)據(jù),其他的數(shù)據(jù)就不能通過表單拿到。
以上面的案例來說,startEvent 中有 startTime、endTime、reason 以及 days 四個(gè)動(dòng)態(tài)表單屬性,如果 提交請(qǐng)假申請(qǐng) 中只有 reason 和 days 兩個(gè)動(dòng)態(tài)表單屬性的話,那么就只能獲取這兩個(gè)動(dòng)態(tài)表單屬性,其他的動(dòng)態(tài)表單屬性則可以通過變量去獲取。
5. 保存與完成
對(duì)于 UserTask 上的表單,我們首先可以通過如下方式來提交表單數(shù)據(jù):
@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);
}
這個(gè)方法只是保存動(dòng)態(tài)表單變量,并不會(huì)完成當(dāng)前 Task。
如果想在提交表單變量的同時(shí)順便完成當(dāng)前 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);
}
該方法在提交表單變量的同時(shí),還會(huì)順便 complete 當(dāng)前 UserTask。
好啦,這就是關(guān)于動(dòng)態(tài)表單松哥和大家介紹的內(nèi)容啦~
動(dòng)態(tài)表單用法簡(jiǎn)單,很多小伙伴想不明白為什么要用表單,用變量不行嗎?技術(shù)上來說,變量當(dāng)然可以,但是變量是一個(gè)一個(gè)的,是零散的,而表單是整的,整存整取的。