Activiti6--[6]使用动态表单属性_2

前提

已经实现了动态表单。对申请人提交的申请表单进行审批。此时要显示申请人在申请表单填写的相关信息,然后进行审批是否同意该申请。
那么接上一步继续研发。

api说明

// 获取任务表单属性
formService.getTaskFormData(taskId).getFormProperties()
// 提交表单完成当前任务
formService.submitTaskFormData(taskId, formValues);

流程图设计

总流程

总流程设计

开始节点

username->请假人->string 勾选必输、可读、可写
leave_date_start->请假开始日期->date 勾选必输、可读、可写
leave_date_end->请假结束日期->date 勾选必输、可读、可写
days->请假天数->long 勾选必输、可读、可写
开始节点

审核节点

username->请假人->string 勾选可读
leave_date_start->请假开始日期->date 勾选可读
leave_date_end->请假结束日期->date 勾选可读
days->请假天数->long 勾选可读
choose->是否同意 (枚举类型)勾选必输、可读、可写
审核节点

步骤

在点击办理任务的时候,调用获取当前任务的表单元素信息,前端通过返回的表单数据以及上一步骤填写的数据回显。之后完成表单审核提交完成任务。

服务层

接口层

import java.util.List;
import java.util.Map;

/**
 * 动态表单服务层
 */
public interface IXKFlowFormService {

	 // 上面保持不变   ......

    /**
     * 根据任务id获取该任务节点的动态表单设计
     *
     * @param taskId 任务id
     */
    XKFlowFormDto getTaskFormPropertiesByTaskId(String taskId);

    /**
     * 提交表单并且完成任务
     *
     * @param taskId     任务id
     * @param formValues 提交的表单数据
     */
    void submitTaskFormData(String taskId, Map<String, String> formValues);
}

实现层

import org.activiti.engine.FormService;
import org.activiti.engine.form.FormProperty;
import org.activiti.engine.form.FormType;
import org.activiti.engine.form.StartFormData;
import org.activiti.engine.form.TaskFormData;
import org.activiti.engine.impl.form.EnumFormType;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
public class XKFlowFormServiceImpl implements IXKFlowFormService {
    
    //... 其余保持不变
    /**
     * 根据流程定义id获取动态表单设计
     *
     * @param processDefinitionId 流程定义id
     */
    @Override
    @Transactional(readOnly = true)
    public XKFlowFormDto getStartFormPropertiesByProcessDefinitionId(String processDefinitionId) {
        StartFormData formData = formService.getStartFormData(processDefinitionId);
        XKFlowFormDto xkFormDto = new XKFlowFormDto();
        xkFormDto.setProcessDefinitionName(formData.getProcessDefinition().getName());
        if (formData == null) return xkFormDto;
        //获取表单字段值
        List<FormProperty> formProperties = formData.getFormProperties();
        if (formProperties == null) {
            return xkFormDto;
        }
        List<XKFormPropertyDto> xkFormPropertyDtos = BeanMapperUtils.mapList(formProperties, FormProperty.class, XKFormPropertyDto.class);
        for (int i = 0; i < formProperties.size(); i++) {
            FormType type = formProperties.get(i).getType();
            // 处理枚举类型
            if (type instanceof EnumFormType) {
                xkFormPropertyDtos.get(i).getType().setValues(type.getInformation("values"));
            }
            // 1204: 补充处理日期类型
            if (type instanceof DateFormType) {
                xkFormPropertyDtos.get(i).getType().setDatePattern((String) type.getInformation("datePattern"));
            }
        }
        xkFormDto.setFormProperties(xkFormPropertyDtos);
        return xkFormDto;
    }

     @Override
    @Transactional(readOnly = true)
    public XKFlowFormDto getTaskFormPropertiesByTaskId(String taskId) {
        TaskFormData taskFormData = formService.getTaskFormData(taskId);
        XKFlowFormDto xkFlowFormDto = new XKFlowFormDto();
        // 设置任务的名称
      xkFlowFormDto.setName(taskFormData.getTask().getName());
        if (taskFormData == null) {
            return xkFlowFormDto;
        }

        if (taskFormData.getFormProperties() == null) {
            return xkFlowFormDto;
        }
        List<FormProperty> properties = taskFormData.getFormProperties();

        List<XKFormPropertyDto> xkFormPropertyDtos = BeanMapperUtils.mapList(properties, FormProperty.class, XKFormPropertyDto.class);

        for (int index = 0; index < properties.size(); index++) {
            FormType type = properties.get(index).getType();
            // 处理枚举类型
            if (type instanceof EnumFormType) {
                xkFormPropertyDtos.get(index).getType().setValues(type.getInformation("values"));
            }
            // 处理日期,将日期转换类型接收
            if (type instanceof DateFormType) {
                xkFormPropertyDtos.get(index).getType().setDatePattern((String) type.getInformation("datePattern"));
            }
        }
        xkFlowFormDto.setFormProperties(xkFormPropertyDtos);
        return xkFlowFormDto;
    }


    @Override
    @Transactional(readOnly = false)
    public void submitTaskFormData(String taskId, Map<String, String> formValues) {
        formService.submitTaskFormData(taskId, formValues);
    }
}

控制层

因加入了发起人,接口变动,需要接收前端传递的发起人id。填写申请表单并且发起流程的接口需要变动,主要改动如下:

//... 其余保持不变
public class XKActivitiFormController {

    @Autowired
    private IXKFlowFormService formService;

    @GetMapping("/getStartFormProperties/{processDefinitionId}")
    @ApiOperation(value = "根据流程定义id获取表单属性", httpMethod = "GET", notes = "根据流程定义id获取表单属性")
    @ApiImplicitParams({
            @ApiImplicitParam(value = "流程定义id", name = "processDefinitionId", required = true, paramType = "path"),
    })
    public APIResponse<XKFlowFormDto> getFormPropertiesByProcessDefinitionId(@PathVariable("processDefinitionId") String processDefinitionId) {
        if (StringUtils.isEmpty(processDefinitionId)) {
            return new APIResponse<>(-1, "参数不正确");
        }
        return new APIResponse<>(formService.getStartFormPropertiesByProcessDefinitionId(processDefinitionId));
    }

    /**
     * 提交表单并且启动流程
     *
     * @param processDefinitionId
     * @param userId
     * @param formValues
     * @return
     */
    @ApiOperation(value = "提交表单并且启动流程", httpMethod = "POST", notes = "提交表单并且启动流程")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "processDefinitionKey", dataType = "String", paramType = "path", value = "流程定义Key"),
            @ApiImplicitParam(name = "userId", dataType = "String", paramType = "path", value = "发起人id"),
            @ApiImplicitParam(name = "formValues", dataType = "map", paramType = "query", value = "表单数据"),
    })
    @PostMapping("/start/{processDefinitionId}/{userId}/")
    public APIResponse<String> submitStartForm(@PathVariable("processDefinitionId") String processDefinitionId, @PathVariable("userId") String userId, @RequestBody Map<String, String> formValues) {
        if (StringUtils.isEmpty(processDefinitionId)) {
            return new APIResponse<>(-1, "参数不正确");
        }
        // 根据流程定义id和表单数据启动流程
        formService.submitStartFormData(processDefinitionId, userId, null,formValues);
        return new APIResponse<>(0, "流程启动成功");
    }


    /**
     * 提交表单并且启动流程
     *
     * @param processDefinitionId
     * @param businessKey
     * @param formValues
     * @return
     */
    @ApiOperation(value = "根据流程定义id并启动业务流程绑定业务key", httpMethod = "POST", notes = "根据流程定义id并启动业务流程绑定业务key")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "processDefinitionKey", dataType = "String", paramType = "path", value = "流程定义Key"),
            @ApiImplicitParam(name = "userId", dataType = "String", paramType = "path", value = "发起人id"),
            @ApiImplicitParam(name = "businessKey", dataType = "String", paramType = "path", value = "业务key"),
            @ApiImplicitParam(name = "formValues", dataType = "map", paramType = "query", value = "表单数据"),
    })
    @PostMapping("/start/{processDefinitionId}/{userId}/{businessKey}/")
    public APIResponse<String> submitStartForm(@PathVariable("processDefinitionId") String processDefinitionId, @PathVariable("userId") String userId, @PathVariable("businessKey") String businessKey, @RequestBody Map<String, String> formValues) {
        if (StringUtils.isEmpty(processDefinitionId)) {
            return new APIResponse<>(-1, "参数不正确");
        }
        // 根据流程定义id和表单数据启动流程
        formService.submitStartFormData(processDefinitionId, userId,businessKey, formValues);
        return new APIResponse<>(0, "流程启动成功");
    }


    @GetMapping("/getTaskFormProperties/{taskId}")
    @ApiOperation(value = "根据流程定义id获取动态表单key", httpMethod = "GET", notes = "根据流程定义id获取动态表单key")
    @ApiImplicitParams({
            @ApiImplicitParam(value = "任务id", name = "taskId", required = true),
    })
    public APIResponse<XKFlowFormDto> getTaskFormPropertiesByTaskId(@PathVariable("taskId") String taskId) {
        if (StringUtils.isEmpty(taskId)) {
            return new APIResponse<>(-1, "参数不正确");
        }
        return new APIResponse<>(formService.getTaskFormPropertiesByTaskId(taskId));
    }

    /**
     * 提交表单并且完成任务
     *
     * @param taskId     任务id
     * @param formValues 表单数据
     * @return
     */
    @ApiOperation(value = "根据流程定义Key启动流程", httpMethod = "POST", notes = "根据流程定义Key启动流程")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "processDefinitionKey", dataType = "String", paramType = "path", value = "流程定义Key"),
            @ApiImplicitParam(name = "formValues", dataType = "map", paramType = "query", value = "表单数据"),
    })
    @PostMapping("/completeTask/{taskId}/")
    public APIResponse<String> submitTaskFormData(@PathVariable("taskId") String taskId, @RequestBody Map<String, String> formValues) {
        if (StringUtils.isEmpty(taskId)) {
            return new APIResponse<>(-1, "参数不正确");
        }
        //启动流程,提交表单
        formService.submitTaskFormData(taskId, formValues);
        return new APIResponse<>(0, "任务完成");
    }


}

页面

代码

页面进行了优化,完成任务和完成申请表单的渲染采用一个页面进行操作且加入发起人传递给后端。具体代码如下

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
 
<body>
<form id="dynamicForm" name="dynamicForm" method="post">
    <div class="mini-toolbar" style="text-align:left">
        <a class="mini-button" iconCls="icon-add" style="margin-right:10px" id="saveBtn"
           onclick="submitFormData('dynamicForm')">提交</a>
        <a class="mini-button" iconCls="icon-remove" style="margin-right:10px" onclick="onCancel">关闭</a>
    </div>
    <div class="title">
        <span class="title-text" id="title">发起</span>
    </div>
    <table class="formtable" cellspacing="0" cellpadding="0" id="formTable">
    </table>
</form>
<script th:inline="javascript">
    /*<![CDATA[*/
    // 获取流程定义id
    var id = [[${id}]];
    var action = [[${action}]];
    // 判断是任务,还是开始节点。从上一个页面点击然后进行该页面
    var getFormPropertiesUrl = action==="TASK"?"/api/base/activiti/form/getTaskFormProperties/":"/api/base/activiti/form/getStartFormProperties/";


    // 获取当前用户的登录名
    var userId = [[${session.XK_USER.userId}]];


    function submitFormData(formId, url,isClose) {
        //提交表单数据
        var form = new mini.Form(formId);
        if (!form.validate()) {
            return;
        }
        mini.get("saveBtn").setVisible(false);
        var data = form.getData(true);      //获取表单多个控件的数据
        var json = mini.encode(data);   //序列化成JSON
        if(action==="TASK"){ // 代表当前提交的是任务表单
            url="/api/base/activiti/form/completeTask/"+id;
        }else{
            // 提交的开始表单节点
            url="/api/base/activiti/form/start/"+id+"/"+userId;
        }

        $.ajax({
            url: url+"/",
            type: "post",
            contentType: "application/json",
            data: json,
            success: function (text) {
                if (null == text) {
                    mini.alert("提交失败,操作超时,请重试!");
                } else {
                    var data = mini.decode(text);
                    if (data.msgCode != 0) {
                        mini.alert("提交失败<br/><b>错误码为:[" + data.msgCode + "],错误信息为:" + data.msg + "</b>");
                        mini.get("saveBtn").setVisible(true);
                    } else {
                        if (null == data.msg) {
                            showTips("提交成功");
                        } else {
                            showTips(data.msg);
                        }
                        if (null != isClose && "N" == isClose) {

                        } else {
                            setTimeout(function () {
                                window.CloseOwnerWindow();
                            }, 2000);

                        }
                    }
                }
            },
            error: function (jqXHR, textStatus, errorThrown) {
                mini.get("saveBtn").setVisible(true);
                var data = mini.decode(jqXHR.responseText);
                //console.log("jqXHR",jqXHR);
                mini.alert("提交失败<br/><b>错误码为:[" + data.code + "],错误信息为:" + data.message + "</b>");
                //window.CloseOwnerWindow();

            }
        });
    }

    // 异步请求表单
    $.ajax({
        url: getFormPropertiesUrl + id,
        type: "GET",
        contentType: "application/json",
        success: function (text) {
            if (null == text) {
                mini.alert("请求失败,请重试!");
            } else {
                var result = mini.decode(text);
                if (result.msgCode != 0) {
                    mini.alert("请求失败<br/><b>错误码为:[" + data.msgCode
                        + "],错误信息为:" + data.message + "</b>");
                } else {
                    $("#title").html(result.object.name);
                    var html = "";
                    if (result.object.formProperties == null) {
                        return;
                    }
                    result.object.formProperties.forEach(function (e, index) {
                        //  判断是否可写,如果可写的,readOnly即可
                        var readOnly = !e.writable ? ' readOnly="readOnly " ' : "";
                        // 判断是否为可写并且是必须的,则显示碧血填写
                        var required =  e.writable && e.required ? ' required="required" ' : "";
                        // 判断是否为可写,可写的话组件显示name属性
                        var comPonentName = e.writable?  ' name="'+e.id+'" ':"";
                        var className = 'mini-textbox';

                        if (index % 2 == 0) {
                            html += '<tr>\n';
                        }
                        html +=
                            ' <td class="formtable-cell formtable-label" > <font color="red">' + (required != "" ? "*" : "") + '</font>' + e.name + ':</td>\n' ;
                        // 后面的类型如果多了以后,这里可以补充
                        var otherHtml= '';
                        var defaultValue = e.value==null || e.value ==undefined ? "":e.value;
                        switch (e.type.name) {
                            case "string":
                                className = 'mini-textbox';
                                otherHtml = '<td class="formtable-cell"><input  '+comPonentName+'  value="'+defaultValue+'"  class="' + className + '" style="width:80%"  ' + readOnly + required + '   /></td>\n';
                                break;
                            case "long":
                                otherHtml = '<td class="formtable-cell"><input  '+comPonentName+'  value="'+defaultValue+'" class="' + className  + '"  vtype="int" style="width:80%"  ' + readOnly + required + '   /></td>\n';
                                break;
                            case "date":
                                className = 'mini-datepicker';
                                if (defaultValue!=null &&defaultValue != "") {
                                    var v = new Date(defaultValue);
                                    defaultValue = mini.formatDate(v,e.type.datePattern);
                                }
                                otherHtml = '<td class="formtable-cell"><input  '+comPonentName+'   format="'+e.type.datePattern+'" value="'+defaultValue+'" class="' + className  + '" style="width:80%"  ' + readOnly + required + '   /></td>\n';
                                break;
                            case 'double':
                                otherHtml = '<td class="formtable-cell"><input value="'+defaultValue+'" class="' + className  +  '" vtype="float" style="width:80%"  ' + readOnly + required + '   /></td>\n';
                                break;
                            case 'enum':
                                className='mini-combobox';
                                var data = [];
                                console.log(comPonentName)
                                for(var key  in  e.type.values){
                                    data.push({name:e.type.values[key],value:key});
                                }
                                otherHtml = '<td class="formtable-cell"><input '+comPonentName+'   value="'+defaultValue+'" data=\''+JSON.stringify(data)+'\'  valueField="value" textField="name" class="' + className + '"  style="width:80%"  ' + readOnly + required + '   /></td>\n';
                                break;
                            default:
                                otherHtml = '<td class="formtable-cell"><input value="'+defaultValue+'" class="' + className  +  '" style="width:80%"  ' + readOnly + required + '   /></td>\n';
                                break;

                        }

                        html +=  otherHtml;
                        if (index % 2 == 1) {
                            html += '</tr>';
                        }
                    });

                    if (result.object.formProperties.length == 1) {
                        html += '</tr>';
                    }

                    $("#formTable").append(html)
                    mini.parse()
                }
            }
        },
        error: function () {
            mini.alert("请求失败,请检查网络状态!");
        }
    });

    /*]]>*/
</script>
</body>
</html>

效果

申请表单填写

申请表单填写

任务列表页面

任务列表页面

完成任务

完成任务

源码解析

为什么只需要设置动态表单组件,第二个审核节点不用做什么配置就可以显示结果?唯一的特殊点是,审核节点中凡是上一个申请表单中的属性名的英文名都保持了一致。难道是因为这个原因?通过debug发现:
在这里插入图片描述
也就是说在调用获取任务节点表单信息的时候获取了上一个节点的信息。具体代码:
在这里插入图片描述
会根据我传递进来的属性名进行匹配。所以只需要保持表单name一致,则会自动查找上一个提交的表单数据。

出现的问题

发起人没有值

点击发起,填写完表单发起流程。发现调用方法启动流程没有发起人的相关信息。
解决方案:前端传递当前发起人的id,然后通过以下代码设置发起人,之后再提交表单发起流程。

 Authentication.setAuthenticatedUserId(userId);
 // 在设置了发起人id之后再发起流程。
formService.submitStartFormData(processDefinitionId, map);

具体原因是因为工作流在内部是从Authentication中获取发起人id,所以在发起流程之前设置发起人的id。
在这里插入图片描述

动态表单日期组件

注意:在设计动态表单时,日期组件的格式化一定要写正确。否则返回的value会一直是空。原因是因为在提交表单时候,日期没有解析正确,导致没有存储到变量中。 比如这里的是yyyy-MM-dd
在这里插入图片描述

审核表单的特殊处理

主要为了解决审核提交表单时,提交了不可写的属性数据到后端。因为上一个节点填写数据流转到下一个节点一般都是不允许编辑的。所以设置关于上一个表单的字段只有可读,其余均不勾选。
也就是用以下代码实现:

var readOnly = !e.writable ? ' readOnly="readOnly " ' : "";
// 判断是否为可写并且是必须的,则显示碧血填写
var required =  e.writable && e.required ? ' required="required" ' : "";
// 判断是否为可写,可写的话组件显示name属性
var comPonentName = e.writable?  ' name="'+e.id+'" ':"";

待解决的问题

  • 变量以及表达式的意义
  • 自定义表单
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页