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

背景

设计一个公共动作,在界面设计器可以拖到页面里,点击之后跳转指定页面。就像导入导出一样。

实现思路

元数据计算时,初始化跳转动作,为本模块以及依赖于本模块的所有模块生成该跳转动作。实现所有模型都有该跳转动作的元数据。

代码示例:

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, ActionTargetEnum.DIALOG,
                context);
    }

    private void makeDefaultViewAction(Meta meta, ModelDefinition data,
                                             String viewActionName,
                                             String displayName,
                                             String title,
                                             ActionContextTypeEnum contextType,
                                             ViewTypeEnum viewType,
                                             int priority,
                                             String resModel,
                                             String resViewName,
                                             ActionTargetEnum target,
                                             Map<String, Object> context) {
        String sign = ViewAction.sign(data.getModel(), viewActionName);
        ViewAction defaultViewAction = meta.getData().get(data.getModule())
                .getDataItem(ViewAction.MODEL_MODEL, sign);
        boolean newAction = false;
        if (null == defaultViewAction) {
            defaultViewAction = new ViewAction();
            newAction = true;
        }
        if (newAction || SystemSourceEnum.MANUAL.equals(defaultViewAction.getSystemSource())) {
            defaultViewAction.setDisplayName(displayName)
                    .setLabel(defaultViewAction.getDisplayName())
                    .setName(viewActionName)
                    .setModel(data.getModel());
            defaultViewAction.setTitle(title)
                    .setViewType(viewType)
                    .setTarget(Optional.ofNullable(target).orElse(ActionTargetEnum.ROUTER))
                    .setResModel(resModel)
                    .setResViewName(resViewName)
                    .setResModule(null)
                    .setResModuleName(null)
                    .setContextType(contextType)
                    .setActionType(ActionTypeEnum.VIEW)
                    .setBindingType(Lists.newArrayList(ViewTypeEnum.TABLE))
                    .setContext(context)
                    .setPriority(priority)
                    .setSystemSource(SystemSourceEnum.MANUAL);
            defaultViewAction.setSign(sign);
            if (newAction) {
                defaultViewAction.construct();
                meta.getData().get(data.getModule()).addData(defaultViewAction);
            } else {
                defaultViewAction.disableMetaCompleted();
            }
        }
    }
}

xml模版示例,默认模版文件放到/pamirs/views/模块编码/template下面

可以利用界面设计器生成需要的跳转页面。在base_view表里面根据model以及title查询,template字段就是xml模版。拷贝出来之后,在view标签里指定一个name(即上文的DEFAULT_VIEW_NAME),然后保存为xml文件放到resource/pamirs/views/模块编码/template路径下面

<view model="top.Teacher" name = "fixed_teacher_table"  type="table">
    <template colSpan="FULL" slot="search">
        <field colSpan="QUARTER" data="enumType" widget="Select"/>
        <field allowClear="true" autoFillOptions="true" colSpan="QUARTER" data="professionalId" label="科目id" showThousandth="false" statistics="false" widget="Integer"/>
    </template>
    <template colSpan="FULL" cols="24" slot="tableGroup"/>
    <template slot="actionBar">
        <action label="导入" name="internalGotoListImportDialog"/>
        <action label="导出" name="internalGotoListExportDialog" sync="true"/>
        <action label="打印" name="internalGotoPrintDialog"/>
    </template>
    <template checkbox="true" colSpan="FULL" defaultPageSize="OPTION_2" enableSequence="false" filter="" inlineActiveCount="THREE" slot="table" sortable="false">
        <field allowClear="true" autoFillOptions="true" colSpan="HALF" data="teacherName" label="教师名字" patternType="NONE" showCount="false" type="TEXT" widget="Input"/>
        <field allowClear="true" autoFillOptions="true" colSpan="HALF" data="field00001" invisible="true" label="嵌入网页" patternType="NONE" showCount="false" type="TEXT" widget="Input"/>
        <field allowClear="true" autoFillOptions="true" colSpan="HALF" data="readStatus" label="读取状态" optionColorStyle="COLORFUL" widget="Select">
            <options>
                <option displayName="未读" label="未读" name="NO_READ"/>
                <option displayName="结束" label="结束" name="READ"/>
            </options>
        </field>
        <field data="createDate" widget="DateTimePicker"/>
        <field data="writeDate" widget="DateTimePicker"/>
        <template slot="rowActions" colSpan="FULL">
        </template>
        <field data="id" invisible="true"/>
    </template>
</view>

效果:

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

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

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

Like (0)
yexiu's avataryexiu数式员工
Previous 2025年10月13日 pm3:50
Next 2025年11月10日 am10:37

相关推荐

  • 如何通过业务数据拿到工作流用户任务待办

    在模型里面建一个非存储字段,用来传输工作流用户任务待办ID。。 界面设计器把这个字段拖到列表页里,并在跳转动作上配置上下文参数,把任务待办id传到表单页里。 重写教师模型的queryPage,通过业务数据id查询出每条业务数据的工作流用户任务待办id返回给前端。 @Function.Advanced(displayName = "查询教师列表", type = FunctionTypeEnum.QUERY, category = FunctionCategoryEnum.QUERY_PAGE, managed = true) @Function(openLevel = {FunctionOpenEnum.LOCAL, FunctionOpenEnum.REMOTE, FunctionOpenEnum.API}) public Pagination<Teacher> queryPage(Pagination<Teacher> page, IWrapper<Teacher> queryWrapper) { Pagination<Teacher> teacherPagination = new Teacher().queryPage(page, queryWrapper); List<Teacher> content = teacherPagination.getContent(); if (CollectionUtils.isEmpty(content)) { return teacherPagination; } List<Long> teacherIds = content.stream().map(Teacher::getId).collect(Collectors.toList()); List<WorkflowUserTask> workflowUserTasks = Models.data().queryListByWrapper(Pops.<WorkflowUserTask>lambdaQuery() .from(WorkflowUserTask.MODEL_MODEL) .in(WorkflowUserTask::getNodeDataBizId, teacherIds) .orderByDesc(WorkflowUserTask::getCreateDate) ); if (CollectionUtils.isEmpty(workflowUserTasks)) { return teacherPagination; } Map<Long/*业务id*/, WorkflowUserTask> userTaskMap = workflowUserTasks.stream().collect(Collectors.toMap( WorkflowUserTask::getNodeDataBizId, a -> a, (old, n) -> old) ); for (Teacher teacher : content) { if (userTaskMap.containsKey(teacher.getId())) { teacher.setWorkflowUserTaskId(userTaskMap.get(teacher.getId()).getId()); } } return teacherPagination; } 查看效果,任务待办id成功传到表单里面。

    2025年1月10日
    2.5K00
  • 前端发布接入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日
    74300
  • 用户审批意见回填到审批表单

    需求 将审批同意时填写的审批意见以及图片回填到审批表单中。(填写节点同理) 实现方式一 通过审批后置函数操作流程参数的数据,并将流程参数数据回写到流程中。 流程设计 流程参数中自定义需要传递的字段。 审批节点设计审批后置函数,后置函数由后端定义。 添加更新数据节点,选择需要更新的业务表单字段,并在表达式中选择流程参数中自定义的字段。我这里更新审批意见和图片两个字段。 后置函数定义: /** * 审批后数据处理 * * @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…

    2026年2月10日
    54200
  • 自定义流程设计器审批人公司列表

    打开弹窗时会默认选中返回的第一个公司。 依赖: <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日
    1.1K00
  • 前端自定义组件之锚点分组

    本文将讲解如何通过自定义,实现锚点组件。这个锚点组件会根据界面设计器拖入的分组,动态解析出锚点。 实现路径 整体的实现思路是界面设计器拖个容器类的组件(这里以选项卡为例),自定义这个选项卡,往选项卡里拖拽的每个分组,每个锚点的名称是分组的标题。 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日
    53800

Leave a Reply

Please Login to Comment