用户审批意见回填到审批表单

需求

将审批同意时填写的审批意见以及图片回填到审批表单中。(填写节点同理)

用户审批意见回填到审批表单

实现方式一

通过审批后置函数操作流程参数的数据,并将流程参数数据回写到流程中。

流程设计

  1. 流程参数中自定义需要传递的字段。
    用户审批意见回填到审批表单

  2. 审批节点设计审批后置函数,后置函数由后端定义。
    用户审批意见回填到审批表单

  3. 添加更新数据节点,选择需要更新的业务表单字段,并在表达式中选择流程参数中自定义的字段。我这里更新审批意见和图片两个字段。
    用户审批意见回填到审批表单
    用户审批意见回填到审批表单

后置函数定义:

    /**
     * 审批后数据处理
     *
     * @param approvalNode 审批节点
     * @param context      上下文
     * @param dataJson     审批提交数据
     * @param result       审批结果
     */
    @Function(name = "approvalDataProcessFun", openLevel = API)
    @Function.Advanced(type = FunctionTypeEnum.QUERY, displayName = "审批后数据处理", category = FunctionCategoryEnum.CUSTOM_DESIGNER)
    public void approvalDataProcessFun(ApprovalNode approvalNode, WorkflowContext context, String dataJson, Boolean result) {
        List<WorkflowUserTask> workflowUserTasks = Models.origin().queryListByWrapper(Pops.<WorkflowUserTask>lambdaQuery()
                .from(WorkflowUserTask.MODEL_MODEL)
                .eq(WorkflowUserTask::getTaskId, context.getLastTaskInstanceId())
        );
        // 获取审批意见等放入流程参数
        for (WorkflowUserTask userTask : workflowUserTasks) {
            String remark = userTask.getRemark();
            List<String> pics = userTask.getPics();
            List<PamirsFile> attachments = userTask.getAttachments();

            Map<String, Object> paramMap = (Map<String, Object>) context.getContext().getOrDefault(ParamNode.PARAM_PREFIX, new HashMap<String, Object>());
            paramMap.put("remark", remark);
            paramMap.put("pics", pics);
        }

    }

实现方式二

通过审批动作(WorkflowUserTaskAction )扩展点实现

1、扩展点的定义

package pro.shushi.pamirs.work.core.extpoint;

import pro.shushi.pamirs.meta.annotation.Ext;
import pro.shushi.pamirs.meta.annotation.ExtPoint;
import pro.shushi.pamirs.workflow.app.api.model.WorkflowUserTask;

// @see:pro.shushi.pamirs.workflow.app.core.action.WorkflowUserTaskAction
/**
 *  Oinone所有的函数都提供了默认的前置扩展点、重载扩展点和后置扩展点,
 *  其技术名称的规则是所扩展函数的函数编码fun加上“Before”、“Override”和“After”后缀
  *  
  *  根据实际情况保留扩展点的接口和实现(可增加、可删减)
 */
@Ext(WorkflowUserTask.class)
public interface WorkflowUserTaskDealExtPoint {

    // 实际需要几个扩展点根据业务情况自行 增加 和 删除
    @ExtPoint(displayName = "审批同意后")
    WorkflowUserTask approveAgreeAfter(WorkflowUserTask workflowUserTask);

    @ExtPoint(displayName = "审批撤销后")
    WorkflowUserTask recallAfter(WorkflowUserTask workflowUserTask);

    @ExtPoint(displayName = "审批转审后")
    WorkflowUserTask approveTrangerAfter(WorkflowUserTask workflowUserTask);

    @ExtPoint(displayName = "审批转审前")
    WorkflowUserTask approveFallbackBefore(WorkflowUserTask workflowUserTask);

    @ExtPoint(displayName = "审批拒绝后")
    WorkflowUserTask approveRejustAfter(WorkflowUserTask workflowUserTask);

    @ExtPoint(displayName = "审批转审后")
    WorkflowUserTask approveFallbackAfter(WorkflowUserTask workflowUserTask);

    // …………
}

2、扩展点的实现

package pro.shushi.pamirs.work.core.extpoint;

import org.springframework.stereotype.Component;
import pro.shushi.pamirs.meta.annotation.Ext;
import pro.shushi.pamirs.meta.annotation.ExtPoint;
import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j;
import pro.shushi.pamirs.meta.util.JsonUtils;
import pro.shushi.pamirs.work.api.model.monthplan.WorkMonthlyPlan;
import pro.shushi.pamirs.work.api.model.monthplan.WorkPlanDelayApplication;
import pro.shushi.pamirs.workflow.app.api.model.WorkflowUserTask;

/**
 * 流程审操作扩展  @see:pro.shushi.pamirs.workflow.app.core.action.WorkflowUserTaskAction
 *  Oinone所有的函数都提供了默认的前置扩展点、重载扩展点和后置扩展点,
 *  其技术名称的规则是所扩展函数的函数编码fun加上“Before”、“Override”和“After”后缀
  *  
  *  根据实际情况保留扩展点的接口和实现 (可增加、可删减)
 */
@Ext(WorkflowUserTask.class)
@Component
@Slf4j
public class WorkflowUserTaskDealExtPointImpl implements WorkflowUserTaskDealExtPoint {

    // 根据实际情况是否保留
    @ExtPoint.Implement
    @Override
    public WorkflowUserTask approveRejustAfter(WorkflowUserTask userTask) {
        userTask.getNodeDataBizId();
        userTask.getModel();
         // 业务数据ID:userTask.getNodeDataBizId();
        log.info("=====================approveRejustAfter============================");
        return userTask;
    }

    @ExtPoint.Implement
    @Override
    public WorkflowUserTask approveAgreeAfter(WorkflowUserTask userTask) {
        log.info("=====================approveAgreeAfter============================");
        // WorkPlanDelayApplication 是具体的业务模型,根据事情情况修改。 其他的模型直接写else分支
        if (WorkPlanDelayApplication.MODEL_MODEL.equals(userTask.getModel())) {
            WorkPlanDelayApplication workPlanDelayApplication = JsonUtils.parseObject(userTask.getNodeContext(), WorkPlanDelayApplication.class);
            workPlanDelayApplication.setAuditRemark(userTask.getRemark());
            workPlanDelayApplication.setAuditAttachments(userTask.getAttachments());
            userTask.setPics(userTask.getPics());
            workPlanDelayApplication.updateById();
            userTask.setContent(JsonUtils.toJSONString(workPlanDelayApplication));
        } else if (WorkMonthlyPlan.MODEL_MODEL.equals(userTask.getModel())) {
            // WorkMonthlyPlan逻辑 ………… 其他模版类似
        }

        return userTask;
    }

    // 根据实际情况是否保留
    @ExtPoint.Implement
    @Override
    public WorkflowUserTask recallAfter(WorkflowUserTask userTask) {
        log.info("=====================recallAfter============================");
        return userTask;
    }

    // 根据实际情况是否保留
    @ExtPoint.Implement
    @Override
    public WorkflowUserTask approveTrangerAfter(WorkflowUserTask workflowUserTask) {
        return workflowUserTask;
    }

    // 根据实际情况是否保留
    @ExtPoint.Implement
    @Override
    public WorkflowUserTask approveFallbackBefore(WorkflowUserTask workflowUserTask) {
        return workflowUserTask;
    }

    // 根据实际情况是否保留
    @ExtPoint.Implement
    @Override
    public WorkflowUserTask approveFallbackAfter(WorkflowUserTask workflowUserTask) {
        return workflowUserTask;
    }

}

更多后置函数参考:自定义函数示例代码汇总

Oinone社区 作者:yexiu原创文章,如若转载,请注明出处:https://doc.oinone.top/other/25502.html

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

Like (0)
yexiu's avataryexiu数式员工
Previous 2026年1月7日 pm3:51
Next 2026年3月12日 pm7:53

相关推荐

  • 如何设计公用跳转动作(类似导入导出动作)

    背景 设计一个公共动作,在界面设计器可以拖到页面里,点击之后跳转指定页面。就像导入导出一样。 实现思路 元数据计算时,初始化跳转动作,为本模块以及依赖于本模块的所有模块生成该跳转动作。实现所有模型都有该跳转动作的元数据。 代码示例: package pro.shushi.pamirs.top.core.init; import com.google.common.collect.Lists; import org.apache.commons.collections4.MapUtils; import org.springframework.stereotype.Component; import pro.shushi.pamirs.boot.base.enmu.ActionTargetEnum; import pro.shushi.pamirs.boot.base.enmu.ActionTypeEnum; import pro.shushi.pamirs.boot.base.model.ViewAction; import pro.shushi.pamirs.boot.common.api.command.AppLifecycleCommand; import pro.shushi.pamirs.boot.common.extend.MetaDataEditor; import pro.shushi.pamirs.core.common.InitializationUtil; import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j; import pro.shushi.pamirs.meta.api.dto.meta.Meta; import pro.shushi.pamirs.meta.api.dto.meta.MetaData; import pro.shushi.pamirs.meta.domain.model.ModelDefinition; import pro.shushi.pamirs.meta.enmu.ActionContextTypeEnum; import pro.shushi.pamirs.meta.enmu.SystemSourceEnum; import pro.shushi.pamirs.meta.enmu.ViewTypeEnum; import pro.shushi.pamirs.top.api.TopModule; import pro.shushi.pamirs.top.api.model.Teacher; import java.util.*; import java.util.stream.Collectors; @Slf4j @Component public class DemoModuleAppInstall implements MetaDataEditor { @Override public void edit(AppLifecycleCommand command, Map<String, Meta> metaMap) { InitializationUtil util = InitializationUtil.get(metaMap, TopModule.MODULE_MODULE, TopModule.MODULE_NAME); if (util == null) { return; } if (MapUtils.isEmpty(metaMap)) { return; } Set<String> dependencyFileModels = metaMap.values().stream() .filter(v -> v.getData().containsKey(TopModule.MODULE_MODULE)) .map(Meta::getModule) .collect(Collectors.toSet()); for (String module : metaMap.keySet()) { if (!dependencyFileModels.contains(module)) { // 不依赖本模块,不生成跳转动作 continue; } Meta meta = metaMap.get(module); MetaData metaData = meta.getCurrentModuleData(); List<ModelDefinition> modelList = metaData.getDataList(ModelDefinition.MODEL_MODEL); for (ModelDefinition data : modelList) { makeDefaultModelViewAction(meta, data, dependencyFileModels); } } } // 跳转的xml模版 name public static final String DEFAULT_VIEW_NAME = "fixed_teacher_table"; private void makeDefaultModelViewAction(Meta meta, ModelDefinition data, Set<String> dependencyFileModels) { if (!dependencyFileModels.contains(data.getModule())) { // 当前模块使用了其他模块的模型,对方模块未依赖本模块,不生成跳转动作 return; } Map<String, Object> context = new HashMap<>(); context.put("model", "'" + data.getModel() + "'"); // 创建 跳转表格页面 viewAction,根据实际需求更改 makeDefaultViewAction(meta, data, "teacherListDialog", "教师表格", null, ActionContextTypeEnum.CONTEXT_FREE, ViewTypeEnum.TABLE, 99, Teacher.MODEL_MODEL, DEFAULT_VIEW_NAME,…

    2025年10月22日
    72600
  • 前端自定义组件之锚点分组

    本文将讲解如何通过自定义,实现锚点组件。这个锚点组件会根据界面设计器拖入的分组,动态解析出锚点。 实现路径 整体的实现思路是界面设计器拖个容器类的组件(这里以选项卡为例),自定义这个选项卡,往选项卡里拖拽的每个分组,每个锚点的名称是分组的标题。 1. 界面设计器拖出页面 我们界面设计器拖个选项卡组件,然后在选项页里拖拽任意多个分组。完成后点击右上角九宫格,选中选项卡,填入组件 api 名称,作用是把选项卡切换成我们自定义的锚点分组组件,这里的 api 名称和自定义组件的 widget 对应。最后发布页面,并绑定菜单。 2. 组件实现 widget 组件重写了选项卡,核心函数 renderGroups,通过 DslRender.render 方法渲染界面设计器拖拽的分组。 import { BasePackWidget, DslDefinition, DslRender, SPI, Widget } from '@oinone/kunlun-dependencies'; import TabsParseGroup from './TabsParseGroup.vue'; function fetchGroupChildren(widgets?: DslDefinition[], level = 1): DslDefinition[] { if (!widgets) { return []; } const children: DslDefinition[] = []; for (const widget of widgets) { if (widget.widget === 'group') { children.push(widget); } else if (level >= 1) { fetchGroupChildren(widget.widgets, level – 1).forEach((child) => children.push(child)); } } return children; } @SPI.ClassFactory( BasePackWidget.Token({ widget: 'TabsParseGroup' }) ) export class TabsParseGroupWidget extends BasePackWidget { public initialize(props) { super.initialize(props); this.setComponent(TabsParseGroup); return this; } // 获取分组的子元素 public get groupChildren(): DslDefinition[] { return fetchGroupChildren(this.template?.widgets); } @Widget.Reactive() public get groupTitles() { return this.groupChildren.map((group) => group.title); } // 根据容器子元素渲染左侧 @Widget.Method() public renderGroups() { if (this.groupChildren && this.groupChildren.length) { return this.groupChildren.map((group) => DslRender.render(group)); } } } vue组件核心内容是用component :is属性,渲染出配置的分组组件 <template> <div class="TabsParseGroup"> <a-anchor :affix="false"> <a-anchor-link v-for="(item, index) in groupTitles" :href="`#default-group-${index}`" :title="item" /> </a-anchor> <div v-for="(item, index) in groupComponents" :id="`default-group-${index}`"> <component :is="item" /> </div> </div> </template> <script lang="ts"> import { computed, defineComponent, PropType } from 'vue'; export default…

    2025年7月8日
    61200
  • 前端发布接入jenkins

    最原始的前端发布,会经过本地打包、压个 zip 包、通过工具手动上传、找到 leader 帮忙解压到对应的服务器上、同步文件服务器等等的步骤。每一个环节都是人工操作,发个版非常的繁琐。接入jenkins有助于我们简化CI/CD流程,实现前端发布自动化。 1. jenkins 安装部署(docker) 1-1 前置条件 安装 git、docker、配置 ssh git 安装 # enter 到底 yum install -y git # 查看git版本号 验证git安装成功 # git version 1.8.3.1 git –version docker 安装 # docker-ce Docker社区版 # docker-ce-cli Docker命令行界面(CLI) # containerd.io Docker插件,直接调用 Docker Compose # docker-compose-plugin Docker插件,直接调用 Docker Compose yum install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin 配置 ssh # Enter到底,最终会生成以下文件 # /root/.ssh/authorized_keys 允许无密码登录的公钥列表 # /root/.ssh/id_rsa 私钥文件 # /root/.ssh/id_rsa.pub 公钥文件 注意该文件里的内容是接下来要用的 ssh-keygen -t rsa -C "root" # 复制公钥文件的内容,添加到GitHub 的 SSH keys 或 任意其他远程仓库 vim /root/.ssh/id_rsa.pub 1-2 jenkins 安装 docker 拉取镜像 # 拉取nginx docker pull nginx # 拉取jenkins docker pull jenkins/jenkins:lts # 查看镜像是否安装成功 docker images # REPOSITORY TAG IMAGE ID CREATED SIZE # jenkins/jenkins lts 6a44d1dd2d60 3 weeks ago 468MB # nginx latest 53a18edff809 7 weeks ago 192MB 创建 docker 相关目录 # 创建docker的相关目录 mkdir -p ./docker/{compose,jenkins_home,nginx/conf,html/origin/{master,dev}} # 创建docker-compose.yml配置文件 cd ./docker/compose # 具体配置内容见下面 touch docker-compose.yml # 创建nginx.conf配置文件 cd ./docker/nginx/conf # 具体配置内容见下面 touch nginx.conf 最终目录结构如下 ./docker/ ├── compose/ │ └── docker-compose.yml # 空的 docker-compose 配置文件 └── html/ └── origin/ ├── master/ # 预留的 master 版本 HTML 目录(为空) └── dev/ # 预留的 dev 版本 HTML…

    2025年5月12日
    81100
  • 前端学习路径

    准备工作 环境准备-1h安装基础环境,需要Node和PNPM如果已经安装检查对应版本,体验阶段建议版本保持一致 克隆一个空工程-0.5h全新工程 注意事项-0.5h静态资源解压,如果还没有对应的后端服务。可以使用:https://demo.oinone.top(Oinone演示环境),账号密码:admin/admin 前端基础 1.1.1了解组件-3h– 组件如何开发– 组件如何复用– 组件如何嵌套 1.1.2 更近一步-5h– 构建更复杂的工作台– 如何发起后端请求– 构建一个通用的图表组件 1.1.3 深入了解概念-5h– 通用概念、名词解释– 页面渲染原理– 组件执行原理 1.1.4业务实战-实现通用的甘特图组件-8h– 怎么与第三方结合– 如何在开源组件上接入Oinone, 并复用 1.1.5 业务实战-实现通用的画廊组件-6h–如何更近一步复用–基础学习结束 前端进阶 2.1.1代码和无代码如何结合-4h– 了解无代码如何使用和基础概念 2.1.2使用无代码搭建增删改查-8h– 了解无代码如何使用和基础概念 最后一步 当您实现玩基础和进阶的所有的效果,Oinone的整体使用您已经大致掌握了,接下来您可以找一个当前业务的场景来实现它把!

    2025年9月1日
    1.2K00
  • 如何实现业务表格跳转页面设计器设计器页面

    后端实现 代理继承界面设计器视图模型 @Model.model(MyView.MODEL_MODEL) @Model(displayName = "视图代理") @Model.Advanced(type = ModelTypeEnum.PROXY) public class MyView extends UiDesignerViewProxy { public static final String MODEL_MODEL = "hr.simple.MyView"; @Field.Integer @Field(displayName = "页面布局ID") private Long uiDesignerViewLayoutId; } 重写查询接口,返回页面布局ID,重写创建接口,实现创建页面逻辑。 package pro.shushi.pamirs.top.core.action; import org.apache.commons.collections4.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import pro.shushi.pamirs.boot.base.constants.ViewConstants; import pro.shushi.pamirs.boot.base.enmu.ActionTargetEnum; import pro.shushi.pamirs.boot.base.ux.annotation.action.UxAction; import pro.shushi.pamirs.boot.base.ux.annotation.action.UxRoute; import pro.shushi.pamirs.boot.base.ux.annotation.button.UxRouteButton; import pro.shushi.pamirs.framework.connectors.data.sql.Pops; import pro.shushi.pamirs.framework.connectors.data.sql.query.LambdaQueryWrapper; import pro.shushi.pamirs.meta.annotation.Action; import pro.shushi.pamirs.meta.annotation.Function; import pro.shushi.pamirs.meta.annotation.Model; import pro.shushi.pamirs.meta.api.dto.condition.Pagination; import pro.shushi.pamirs.meta.api.dto.wrapper.IWrapper; import pro.shushi.pamirs.meta.api.session.PamirsSession; import pro.shushi.pamirs.meta.constant.FunctionConstants; import pro.shushi.pamirs.meta.enmu.*; import pro.shushi.pamirs.top.api.model.MyView; import pro.shushi.pamirs.ui.designer.api.designe.UiDesignerViewLayoutService; import pro.shushi.pamirs.ui.designer.model.UiDesignerViewLayout; import pro.shushi.pamirs.ui.designer.pmodel.UiDesignerViewLayoutProxy; import java.util.List; import java.util.Map; import java.util.stream.Collectors; /** * @author Yexiu at 20:39 on 2025/3/27 */ @Component @Model.model(MyView.MODEL_MODEL) public class MyViewAction { @Autowired private UiDesignerViewLayoutService uiDesignerViewLayoutService; @Action.Advanced(name = FunctionConstants.create, managed = true) @Action(displayName = "创建", summary = "添加", bindingType = ViewTypeEnum.FORM) @Function(name = FunctionConstants.create) @Function.fun(FunctionConstants.create) public MyView create(MyView data) { UiDesignerViewLayoutProxy uiDesignerViewLayoutProxy = new UiDesignerViewLayoutProxy(); uiDesignerViewLayoutProxy.setBizType(data.getBizType()); uiDesignerViewLayoutProxy.setDesignerActionBarType(data.getDesignerActionBarType()); uiDesignerViewLayoutProxy.setViewType(data.getType()); uiDesignerViewLayoutProxy.setModel(data.getModel()); uiDesignerViewLayoutProxy.setModule(PamirsSession.getServApp()); uiDesignerViewLayoutProxy.setViewTitle(data.getTitle()); uiDesignerViewLayoutProxy.setUsingDefaultView(data.getLoadLayout()); UiDesignerViewLayoutProxy saveUiDesigner = uiDesignerViewLayoutService.create(uiDesignerViewLayoutProxy); data.setDesignerViewId(saveUiDesigner.getId()); return data; } @Function.Advanced(type = FunctionTypeEnum.QUERY, displayName = "查询列表") @Function.fun(FunctionConstants.queryPage) @Function(openLevel = {FunctionOpenEnum.API, FunctionOpenEnum.LOCAL}) public Pagination<MyView> queryPage(Pagination<MyView> page, IWrapper<MyView> queryWrapper) { LambdaQueryWrapper<MyView> wrapper = Pops.<MyView>lambdaQuery().from(MyView.MODEL_MODEL) .eq(MyView::getSys, Boolean.FALSE) .eq(MyView::getSystemSource, SystemSourceEnum.UI); Pagination<MyView> myViewPagination = new MyView().queryPage(page, wrapper); List<MyView> content…

    2025年3月31日
    68800

Leave a Reply

Please Login to Comment