工作流动态表单最佳实践

需求背景

为了提高操作效率并简化流程设计过程,应对伙伴们反映的在流程设计器中,即使填写/审批流程相同,不同模型也需重新配置的问题,我们引入了“动态表单”功能。此功能旨在减少重复配置的需求,通过设置节点名称和绑定视图,便可实现审批流程相同而视图不同,从而使得相同的审批流程可以被高效重复利用。

为确保伙伴们能够更加方便的使用动态表单,我们将典型示例整合至“工作流”中。本节将结合这些示例详细阐述“动态表单”功能的应用方法。

使用主要分为两个步骤:流程设计和动态表单配置。

流程设计

触发节点配置

  • 应用:工作流
  • 选择模型:动态表单任务
  • 触发场景:更新数据时
  • 选择更新字段:触发次数

触发节点配置

填写/审批节点配置

  1. 动态表单按钮设置为启用状态。
  2. 数据来源设置为触发节点。
  3. 动态表单函数默认有一条内置函数,选中即可。
  4. 数据保存方式、填写人等信息,同从前使用方式。
  5. 在动态表单模式下,数据权限的管理将不再由流程设计器控制,而是由用户所填写的视图进行控制。
    • 例如,若希望用户在“一审”阶段不能修改某字段,则应在设计视图时将相关字段设为只读模式。同样,若某些信息不宜在“一审”阶段展示给客户,相应字段应在设计过程中被设置为隐藏状态。

填写节点配置

若需在“一审”节点接收“一填”阶段的结果,数据来源设置为“一填[动态表单]”。其他配置项保持不变,按照上述说明进行设置。

审批节点配置

若希望“二填”节点的数据由用户填写,并希望内容处于空白状态,可将数据来源设置为“触发”节点。

通过这种配置,“二填”节点的数据将不会受到“一填”和“一审”节点数据的影响。

二填节点配置

二审节点的配置同一审,此处省略。

“终审”节点希望接收到“一审”节点的数据,可以将数据来源设置为一审。

终审节点配置

动态表单配置

首先创建动态表单任务:工作流->动态表单任务->创建,给流程起个名字。

创建动态表单任务

节点任务配置

  1. 流程名:通过下拉菜单选择,数据为动态表单任务中,创建的流程信息。
  2. 节点名称:【强制】确保与流程设计中的节点名称完全一致,否则将无法正确匹配到对应节点的视图。
  3. 模型:选定用于绑定节点视图的模型。
  4. 视图:选择视图前须先确定模型,系统将自动列出所选模型下的通过UI创建的视图供选择。

创建任务节点配置

查看配置效果

在工作流模块->动态表单->动态表单任务,点击“详情”,可以看到该流程所配置的节点信息。

任务列表

动态表单任务详情页

动态表单任务详情

流程触发

点击“触发”按钮将使触发次数自动递增。在流程设计器中配置触发节点时,我们已设定“触发次数”字段的变化作为流程启动的条件。

点击触发按钮

触发节点配置筛选条件

通过完成上述操作,我们已成功配置动态表单。现在可以利用触发节点的筛选条件,来确定触发按钮被点击后,所启动的流程。

触发节点配置筛选条件

自定义动态表单函数

如果所提供的示例未能满足您的需求,您可以根据自身的需求,对触发条件和动态表单函数进行相应的定制与调整。

  • 动态表单函数定义规则如下:
    • namespace:强制为 WorkflowFunctionConstant.FUNCTION_NAMESPACE。
    • fun:强制以 WorkflowFunctionConstant.WORKFLOW_CUSTOM_VIEW_FUNCTION_PREFIX 为前缀。
  • 入参说明:
    • 参数1:节点数据,例如,配合instanceof可以判断当前是填写节点(WriteNode)还是审批节点(ApprovalNode)。
    • 参数2:触发节点的模型数据,如果您的触发节点不确定,可以通过Map接收参数。
    • 参数3:该节点所配置数据来源的数据。
  • 出参说明:视图,如果出参为null,流程终止运行,错误信息提示为“流程节点执行失败,动态表单函数获取视图为空”。
@Slf4j
@Component
@Fun(WorkflowFunctionConstant.FUNCTION_NAMESPACE)
public class DynamicFormCustom {

    /**
     * 根据动态表单任务获取视图
     *
     * @param node            节点数据
     * @param dynamicFormTask 触发节点数据
     * @param dataObj         源数据
     */
    @Function.fun(WorkflowFunctionConstant.WORKFLOW_CUSTOM_VIEW_FUNCTION_PREFIX + "fetchDynamicFormFunction")
    @Function.Advanced(displayName = "[内置]获取动态表单函数")
    @Function(name = "fetchDynamicFormFunction")
    public View fetchDynamicFormFunction(Node node, DynamicFormTask dynamicFormTask, Map<String, Object> dataObj) {
        DynamicFormTaskNode dynamicFormTaskNode = fetchDynamicFormTaskNode(node, dynamicFormTask);
        if (dynamicFormTaskNode == null) {
            return null;
        }
        dynamicFormTaskNode.fieldQuery(DynamicFormTaskNode::getView);
        return dynamicFormTaskNode.getView();
    }

    private DynamicFormTaskNode fetchDynamicFormTaskNode(Node node, DynamicFormTask dynamicFormTask) {
        List<DynamicFormTaskNode> dynamicFormTaskNodeList = Models.origin().queryListByWrapper(Pops.<DynamicFormTaskNode>lambdaQuery()
                .from(DynamicFormTaskNode.MODEL_MODEL)
                .eq(DynamicFormTaskNode::getNodeName, node.getNodeName())
                .eq(DynamicFormTaskNode::getTaskCode, dynamicFormTask.getCode())
        );
        if (CollectionUtils.isEmpty(dynamicFormTaskNodeList)) {
            return null;
        }
        if (dynamicFormTaskNodeList.size() > 1) {
            log.error("工作流动态获取表单函数视图匹配多个,{}", JsonUtils.toJSONString(dynamicFormTaskNodeList));
        }
        return dynamicFormTaskNodeList.get(0);
    }
}

Oinone社区 作者:夜神月原创文章,如若转载,请注明出处:https://doc.oinone.top/designer/workflowdesigner/16336.html

访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验

(0)
夜神月的头像夜神月数式员工
上一篇 2024年8月22日 am11:56
下一篇 2024年8月23日 am11:40

相关推荐

  • 流程设计流程结束通知SPI接口

    1.实现SPI接口 import pro.shushi.pamirs.meta.common.spi.SPI; import pro.shushi.pamirs.meta.common.spi.factory.SpringServiceLoaderFactory; import pro.shushi.pamirs.workflow.app.api.entity.WorkflowContext; import pro.shushi.pamirs.workflow.app.api.model.WorkflowInstance; @SPI(factory = SpringServiceLoaderFactory.class) public interface WorkflowEndNoticeApi { void execute(WorkflowContext context, WorkflowInstance instance); } 自定义通知逻辑 /** * 自定义扩展流程结束时扩展点 */ @Order(999) @Component @SPI.Service public class MyWorkflowEndNoticeApi implements WorkflowEndNoticeApi { @Override public void execute(WorkflowContext context, WorkflowInstance instance) { Long dataBizId = instance.getDataBizId(); //todo自定义逻辑 } }

    2023年12月26日
    1.1K00
  • 【工作流】如何在业务模型的列表中展示工作流的审批按钮

    介绍 当我们需要在业务模型的列表中展示审批的按钮时,我们可以通过自定义的方式完成扩展,这样就可以在业务数据中直接点击该动作按钮跳转到工作流待办详情页处理审批或填写任务 1.先在业务数据模型中新增userTaskList字段存放该条数据的工作流待办数据 @Model.model(DemoItem.MODEL_MODEL) @Model(displayName = "测试商品") public class DemoItem extends IdModel { @Field.one2many @Field(displayName = "工作流任务列表", store = NullableBoolEnum.FALSE, invisible = true) @Field.Relation(store = false) private List<WorkflowUserTask> userTaskList; } 2. 在业务数据的查询方法中查询当前登录用户在工作流任务中的数据 @Function.Advanced(type = FunctionTypeEnum.QUERY, displayName = "查询列表", timeout = 50000) @Function.fun(FunctionConstants.queryPage) @Function(openLevel = {FunctionOpenEnum.LOCAL, FunctionOpenEnum.REMOTE, FunctionOpenEnum.API}) public Pagination<DemoItem> queryPage(Pagination<DemoItem> page, QueryWrapper<DemoItem> queryWrapper) { new DemoItem().queryPage(page, queryWrapper); List<Long> bizIds = page.getContent().stream().map(DemoItem::getId).collect(Collectors.toList()); LambdaQueryWrapper<WorkflowUserTask> userTaskWrapper = new LambdaQueryWrapper<>(); userTaskWrapper.setModel(WorkflowUserTask.MODEL_MODEL); userTaskWrapper.select(WorkflowUserTask::getId, WorkflowUserTask::getNodeDataBizId); userTaskWrapper.eq(WorkflowUserTask::getUserId, PamirsSession.getUserId()) .eq(WorkflowUserTask::getModel, DemoItem.MODEL_MODEL) .eq(WorkflowUserTask::getStatus, WorkflowUserStatusEnum.ACTIVE) .in(WorkflowUserTask::getNodeDataBizId, bizIds); Pagination<WorkflowUserTask> userTaskPagination = new Pagination<>(); userTaskPagination.setCurrentPage(1); userTaskPagination.setSize(200L); userTaskPagination.setSort(new Sort().addOrder(SortDirectionEnum.DESC, WorkflowUserTask::getCreateDate)); List<WorkflowUserTask> userTasks = new WorkflowUserTask().queryListByWrapper(userTaskPagination, userTaskWrapper); Map<Long, WorkflowUserTask> map = userTasks.stream().collect(Collectors.toMap(WorkflowUserTask::getNodeDataBizId, v -> v, (a, b) -> a)); page.getContent().forEach((item) -> { WorkflowUserTask workflowUserTask = map.get(item.getId()); if (workflowUserTask != null) { item.setUserTaskList(Lists.newArrayList(workflowUserTask)); } }); return page; } 3. 在界面设计器拖一个服务端动作到操作列中 4. 将工作流任务列表字段userTaskList拖到表格列中并隐藏该字段 5. 前端自定义跳转到工作流待办详情页的动作组件 import { ActionType, ActionWidget, ClickResult, executeViewAction, RuntimeViewAction, ServerActionWidget, SPI, ViewActionTarget, ViewType, Widget } from '@kunlun/dependencies'; // name为界面设计器拖出的动作名称 @SPI.ClassFactory(ActionWidget.Token({ actionType: ActionType.Server, model: 'demo.DemoItem', name: 'uiServer0358d42817d64fe7908fe48dfce084d3' })) class WorkflowJumpActionWidget extends ServerActionWidget { @Widget.Reactive() public get invisible(): boolean { if (!this.activeRecords?.[0]?.userTaskList?.[0]?.id) { return true; } return super.invisible; } protected async clickAction(): Promise<ClickResult> { const…

    2024年5月8日
    1.5K00
  • 项目中工作流引入和流程触发

    目录 1. 使用工作流需要依赖的包和设置2. 触发方式2.1 自动触发方式2.2 触发方式 1.使用工作流需要依赖的包和设置 1.1 工作流需要依赖的模块 需在pom.xml中增加workflow、sql-record和trigger相关模块的依赖 workflow:工作流运行核心模块 sql-record:监听流程发布以后对应模型的增删改监听 trigger:异步任务调度模块 <dependency> <groupId>pro.shushi.pamirs.workflow</groupId> <artifactId>pamirs-workflow-api</artifactId> </dependency> <dependency> <groupId>pro.shushi.pamirs.workflow</groupId> <artifactId>pamirs-workflow-core</artifactId> </dependency> <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-sql-record-core</artifactId> </dependency> <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-trigger-core</artifactId> </dependency> <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-trigger-bridge-tbschedule</artifactId> </dependency> 在application.yml中增加对应模块的依赖以及sql-record路径以及其他相关设置 pamirs: … record: sql: #改成自己路径 store: /opt/pamirs/logs … boot: init: true sync: true modules: … – sql_record – trigger – workflow … sharding: define: data-sources: ds: pamirs models: "[trigger.PamirsSchedule]": tables: 0..13 event: enabled: true schedule: enabled: true # ownSign区分不同应用 ownSign: demo rocket-mq: # enabled 为 false情况不用配置 namesrv-addr: 192.168.6.2:19876 trigger: auto-trigger: true 2.触发方式 2.1自动触发方式 在流程设计器中设置触发方式,如果设置了代码触发方式则不会自动触发 2.2代码调用方式触发 2.2.1.再流程设计器中触发设置中,设置为是否人工触发设置为是 2.2.2.查询数据库获取该流程的编码 2.2.3.在代码中调用 /** * 触发⼯作流实例 */ private Boolean startWorkflow(WorkflowD workflowD, IdModel modelData) { WorkflowDefinition workflowDefinition = new WorkflowDefinition().queryOneByWrapper( Pops.<WorkflowDefinition>lambdaQuery() .from(WorkflowDefinition.MODEL_MODEL) .eq(WorkflowDefinition::getWorkflowCode, workflowD.getCode()) .eq(WorkflowDefinition::getActive, 1) ); if (null == workflowDefinition) { // 流程没有运⾏实例 return Boolean.FALSE; } String model = Models.api().getModel(modelData); //⼯作流上下⽂ WorkflowDataContext wdc = new WorkflowDataContext(); wdc.setDataType(WorkflowVariationTypeEnum.ADD); wdc.setModel(model); wdc.setWorkflowDefinitionDefinition(workflowDefinition.parseContent()); wdc.setWorkflowDefinition(workflowDefinition); wdc.setWorkflowDefinitionId(workflowDefinition.getId()); IdModel copyData = KryoUtils.get().copy(modelData); // ⼿动触发创建的动作流,将操作⼈设置为当前⽤户,作为流程的发起⼈ copyData.setCreateUid(PamirsSession.getUserId()); copyData.setWriteUid(PamirsSession.getUserId()); String jsonData = JsonUtils.toJSONString(copyData.get_d()); //触发⼯作流 新增时触发-onCreateManual 更新时触发-onUpdateManual Fun.run(WorkflowModelTriggerFunction.FUN_NAMESPACE, "onCreateManual", wdc, msgId, jsonData); return Boolean.TRUE; }

    2023年11月7日
    1.3K00
  • 【工作流】如何自定义支持在加签的同时审批同意

    1.添加工作流相关依赖 业务的api工程添加工作流相关依赖 package pro.shushi.pamirs.demo.api; import org.springframework.stereotype.Component; import pro.shushi.pamirs.core.common.CommonModule; import pro.shushi.pamirs.file.api.FileModule; import pro.shushi.pamirs.meta.annotation.Module; import pro.shushi.pamirs.meta.base.PamirsModule; import pro.shushi.pamirs.meta.common.constants.ModuleConstants; import pro.shushi.pamirs.user.api.UserModule; import pro.shushi.pamirs.workflow.app.api.WorkflowModule; @Component @Module( name = DemoModule.MODULE_NAME, displayName = "oinoneDemo工程", version = "1.0.0", priority = 1, dependencies = {ModuleConstants.MODULE_BASE, CommonModule.MODULE_MODULE, UserModule.MODULE_MODULE, // 此处新增工作流依赖 WorkflowModule.MODULE_MODULE, FileModule.MODULE_MODULE } ) @Module.module(DemoModule.MODULE_MODULE) @Module.Advanced(selfBuilt = true, application = true) public class DemoModule implements PamirsModule { public static final String MODULE_MODULE = "demo_core"; public static final String MODULE_NAME = "DemoCore"; @Override public String[] packagePrefix() { return new String[]{ "pro.shushi.pamirs.demo" }; } } 业务的core工程添加工作流相关依赖 <dependency> <groupId>pro.shushi.pamirs.workflow</groupId> <artifactId>pamirs-workflow-api</artifactId> </dependency> <dependency> <groupId>pro.shushi.pamirs.workflow</groupId> <artifactId>pamirs-workflow-core</artifactId> </dependency> 2.自定义action动作,在一个动作内分别执行工作流应用自带的加签和审核同意动作 package pro.shushi.pamirs.demo.core.action; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import pro.shushi.pamirs.meta.annotation.Action; import pro.shushi.pamirs.meta.annotation.Model; import pro.shushi.pamirs.meta.enmu.ActionContextTypeEnum; import pro.shushi.pamirs.meta.enmu.ViewTypeEnum; import pro.shushi.pamirs.workflow.app.api.model.WorkflowTaskInstance; import pro.shushi.pamirs.workflow.app.api.model.WorkflowUserTask; import pro.shushi.pamirs.workflow.app.core.action.WorkflowUserTaskAction; import pro.shushi.pamirs.framework.common.utils.ObjectUtils; @Component @Model.model(WorkflowUserTask.MODEL_MODEL) public class DemoWorkflowUserTaskAction { @Autowired private WorkflowUserTaskAction workflowUserTaskAction; @Transactional(rollbackFor = {Exception.class}) @Action(displayName = "加签并审核同意", bindingType = ViewTypeEnum.FORM, contextType = ActionContextTypeEnum.SINGLE) public WorkflowUserTask approveAddsignAgree(WorkflowUserTask workflowUserTask) throws Throwable { WorkflowUserTask addsignRes = workflowUserTaskAction.approveAddsign(ObjectUtils.clone(workflowUserTask)); WorkflowTaskInstance updateTaskInstance = new WorkflowTaskInstance(); updateTaskInstance.setId(addsignRes.getTaskId()); updateTaskInstance.setLocked(false); updateTaskInstance.updateById(); WorkflowUserTask res = workflowUserTaskAction.approveAgree(ObjectUtils.clone(workflowUserTask)); return res; } } 3.自定义加签的view视图文件workflow_signature_form.xml,放到src/resources目录下,从数据库复制出工作流加签页面原有的视图,将里面的加签动作替换为上面自定义的新动作approveAddsignAgree <view widget="form"> <template slot="actions"> <action name="internalGotoListTableRouter" priority="0" label="返回" actionType="CLIENT" fun="$$internal_GotoListTableRouter" type="default"/> <action…

    2024年6月6日
    1.2K00
  • 自定义流程设计器审批人公司列表

    打开弹窗时会默认选中返回的第一个公司。 依赖: <dependency> <groupId>pro.shushi.pamirs.workflow</groupId> <artifactId>pamirs-workflow-api</artifactId> </dependency> 只显示所有公司 ALL_COMPANY 表示所有公司。 @Order(10) @Component @SPI.Service public class CustomWorkflowCompanyQueryApi implements WorkflowCompanyQueryApi { @Override public Pagination<PamirsCompany> queryPage(Pagination<PamirsCompany> page, IWrapper<PamirsCompany> queryWrapper) { Pagination<PamirsCompany> pageResult = new Pagination<>(); pageResult.setContent(Lists.newArrayList(ALL_COMPANY)); pageResult.setTotalPages(1); return pageResult; } } 不显示所有公司 @Order(10) @Component @SPI.Service public class CustomWorkflowCompanyQueryApi implements WorkflowCompanyQueryApi { @Override public Pagination<PamirsCompany> queryPage(Pagination<PamirsCompany> page, IWrapper<PamirsCompany> queryWrapper) { return new PamirsCompany().queryPage(page, queryWrapper); } } 仅显示特定公司 @Order(10) @Component @SPI.Service public class CustomWorkflowCompanyQueryApi implements WorkflowCompanyQueryApi { private static final Set<String> COMPANY_CODES = Sets.newHashSet("CH0001", "CH0002"); @Override public Pagination<PamirsCompany> queryPage(Pagination<PamirsCompany> page, IWrapper<PamirsCompany> queryWrapper) { Set<String> companyCodes = Sets.newHashSet("CH0001", "CH0002"); LambdaQueryWrapper<PamirsCompany> query = ((QueryWrapper<PamirsCompany>) queryWrapper).lambda(); query.in(PamirsCompany::getCode, COMPANY_CODES); return new PamirsCompany().queryPage(page, queryWrapper); } }

    2025年9月4日
    82000

Leave a Reply

登录后才能评论