# 事件

事件,是指每种控件在某种特定情况下而触发的动作,Meper通过在事件中定义不同的响应类型,而形成与最终用户的互动,从而赋予程序以灵魂。

当用户选中一个组件的时候,会自动列出该控件所支持的事件类型。如上图所示,按钮支持click事件。

目前系统支持以下几种事件类型:

  • Button Click 按钮的点击事件

  • Timer Click 定时器的点击事件

  • MenuBar Click 菜单栏的点击事件

  • Search Field Click 搜索框的点击事件

  • Item Click 表格等组件的点击事件

  • Item Double Click 表格等组件的双击事件

  • Item Select 表格数据的选中事件

  • Value Change Input类型组件的值发生变化时触发

  • Post Initialize 页面初始化完成事件,每个页面初始化完成时触发,且只触发一次

  • Tab Selected Change TabSheet组件的值发生变化时触发

  • Calendar Timeslot Click LumosResourceCalendar组件的时间槽被点击时触发

  • Calendar Entry Click LumosResourceCalendar组件中入口被点击时触发

  • Parameter Value Set 为页面传值所用,当ViewModel调用页面的setParmater方法时,会默认发布一个此事件

# Actions

在某种事件发生后,需要在事件中定义各种事件发生后的响应类型,来进行一系列的动作。

这些Action会随着平台的升级而逐渐丰富起来。如上图所示,当前所支持的响应类型主要为以下几种

# 弹出框

定义一个弹出框事件,比如点击添加按钮时,需要弹出一个窗体,就可以在添加按钮的actionflow流程下添加一个弹出框

  • 图 1:在Entry下双击“弹出框”,Entry与弹出框直接会自动加上连接线;或直接拖拽一个弹出框到actionflow下,再用连接线将Entry与弹出框连接起来。
  • 图 2:选中弹出框,点击下拉框选择弹出框页面,下拉框中展示的是系统所有弹出框类型的页面。
  • 图 3:弹出框页面选完之后,需要在弹出框后的连接线上选择弹出框定义的事件(这个事件在设计弹出框页面时,在弹出框GlobalEvent定义的事件),也就是点击按钮弹出对应页面后需要执行什么事件。

# 侧边弹出框

和弹出框类似,可以用于诸如点击表格某个数据,自动从侧边弹出详情信息等场景,和弹出框的区别是,弹出位置固定在页面的右边

# API

对于复杂的逻辑,不属于页面逻辑可以解决的部分,需要借助于调用后台的API进行处理。 一般用于调用后台API的场景主要有:

  • 核心逻辑在于后台处理,如产品在站位开始处理(startAtRouteStep)、产品在站位结束处理(completeAtRouteStep)等
  • 对于对象之间的关系绑定。如用户与角色的绑定、用于与权限的绑定

上图即为API Action的定义界面:

  • 系统服务 罗列出系统中的所有服务,选择系统服务后再选择服务中可以调用的方法,如调用UserService服务下的assignUserWithroles方法,绑定用户与角色。

  • 组件 列出当前页面所有组件(当前在设计页面中添加的各种组件),选择组件后再选择组件下可以调用的方法,如调用grid组件下的clear方法,清除grid下的数据。

  • ViewModel 列出当前页面所定义的Controller方法(调用后台ViewModel代码),选择ViewModel服务后再选择ViewModel下可以调用的方法,如生产定义页面调用 ProductConfigurationViewModel下的refreshTreeGrid方法,刷新生产定义树形表格。

  • 参数定义 一旦选中Method后,该方法所需要的参数将会全部现在下面的表格中,用户需要定义各个参数需要从哪里获取值,一共有三种取值方法

    • USER DEFINED 如果参数是一个常量,需要用户直接输入,可以使用该方法
    • COMPONENT 如果参数需要从页面元素中取值,一旦用户确定使用该方法取值,在 “值” 这一列将会列出当前页面中的所有的ID,对于取值组件,请参考Component
    • ACTION_CONTEXT 如果参数不能通过以上方法获取,最后一种策略可以从Action执行的上下文中获取。 往往一个事件中,会有多个Action的定义。对于一个按钮事件,既有LoadData这样的Action,又有API这样的Action。每一种Action的执行,系统都会 将Action中使用到的值放到Action执行的上下文中。这些值对于后续的Action可见。

ActionContext 取值示意

在各种API调用中,定义参数的时候,常常有ENUM作为参数的情况,此种情况,需要定义参数获取为USER_DEFINED。

ENUM本身作为常量,其值有可能固定不变,针对固定不变的情况,为了对这种情况提供支持,用户可以直接选择参数获取为User_Defined,然后直接输入ENUM的名称即可。

比如,User 对象中,
    public void setStatus(UserStatusType status) {
        getInternalObject().setStatus(status.getName());
    }
对于以上方法,用于可以直接输入:ACTIVE

那么在后台API调用的时候,对于ENUM类型的参数,需要直接从用户输入中解析为ENUM,进行API调用

ApiAction添加一个表单校验的选项

# 导航

该Action用于添加导航,将页面重定向到新地址,填写的URL可以是系统内部的网页也可以是系统外部的。

上图即为该事件的定义界面:

  • 系统页面 导航到系统设计页面,下拉框会展示系统内所有设计页面,不包含弹出框页面 。

  • URL 导航到一个指定URL地址。

  • 在新标签页中打开 是指在预览点击事件触发时,重开打开一个浏览器页面还是在当前预览页面中打开新页面。

# 确认框

该action用于触发某些事件时给予信息提醒,是否继续执行。如删除用户会弹出一个确认框确认是否删除,确定则用户删除,取消则取消删除用户操作。

# 提示框

用于向用户弹出操作成功提示、错误提示或警告信息。

# 删除对象

该action用于删除系统对象,一般用于删除grid数据。

# 提交保存数据

用于新增、修改弹出框页面提交数据保存到数据库表。

# SQL脚本

# Script

groovy是动态执行脚本,可以jvm中动态执行groovy代码,用于简单的逻辑处理。

# 关闭弹出框

用于弹出框在做提交数据的同时、或者点击弹出框关闭按钮,执行关闭弹出框事件。

# 关闭浏览器

用于关闭导航界面

# 刷新组件

对页面数据的一个刷新,如添加某个对象显示在表格下,对表格需要进行一个刷新操作。

# 获取组件值

获取当前页面某个组件的值,赋予变量名称,便于其他事件的调用。

# 设置组件值

给页面某个组件赋予值

# 启用/置灰组件

用于触发某个事件时,一些组件的启用或者置灰设定。如点击表格的数据,编辑、删除等按钮会启用,不选中数据时编辑、删除按钮置灰。

# 过滤器

用于使用特定条件过滤表格数据,如:用户在查询条件输入框中输入特定用户名,点击查询按钮后,根据输入的用户名过滤用户表格。 ❶ 从输入框获取输入的用户名,将获取到的值赋予变量名称userName ❷ 添加过滤器 ❸ 设置过滤目标、过滤属性、表达式 ❹ 在表达式框中输入上面获取到的userName

# 发布事件

主要有以下两个用途:

  • 简单的通知事件 当用户点击某个按钮时,发布一个事件,其他Action接收到这个事件后再执行后续操作。 比如:用户进行添加数据操作时,希望只有成功添加数据后,才会刷新Grid,可以在添加弹出框的确定按钮中添加OK事件,主页面弹出添加界面后,只有接收到OK事件,才会执行后续的刷新功能。

  • 通过给事件添加额外参数来实现跨页面传值。 比如:用户通过输入框+查询按钮的组合方式,来查找特定的物料编号。

具体设置过程:

# 简单的通知事件

弹出框❶ 定义事件❷ 添加发布事件Action❸ 选择要发布的事件

主界面❶ 弹出添加框❷ 在连接线上选择事件❸ 刷新组件

# 通过事件传值

如图所示为发布事件的一个简单应用,输入框+按钮查询物料

❶ 点击查询按钮❷ 弹出物料查询界面,选中一条物料后点击确定按钮❸ 物料编码被设置为选中物料的编码

弹出框 ❶ 定义事件,并点击事件后面的“+”号,为事件添加参数❷ 添加获取组件值Action,获取Grid中被选中值❸ 发布事件,将获取到的值赋值给事件参数

主界面 ❶ 弹出物料查询界面 ❷ 在连接线上选择事件 ❸ 添加设置组件值Action,在表达式一栏输入弹出框事件的附加参数,设置组件值

# 显示/隐藏组件

用于触发某个事件时,组件的显示和隐藏,true代表显示组件,false代表隐藏组件。

# 上传文件

需要组件库中的“上传”组件和ActionFlow库中的“提交上传文件”Action配合使用, 上传组件只是给用户选择本地文件用的,真正上传到数据库中的动作是由提交上传文件这个Action执行的,文件会上传到SYS_MEDIA表中。 提交上传文件还可以有返回值,需要给返回值起一个变量名,可以通过设置组件值Action将返回的变量赋值给文件预览Action,这样会弹出一个新窗口对上传的文件进行预览,也可以赋值给页面中的Image组件,直接在当前页面中预览,赋值给Image组件时只能赋值文件的Id,可通过getId()方法获取文件Id。

  • 上传组件可以配置后台流程,如果将提交上传文件Action配置在上传组件的流程中,程序在执行的时候,会在用户选择完文件的同时将文件上传到数据库中;
  • 也可以在其他按钮的流程中添加提交上传文件Action,比如:在弹出框的保存按钮中配置,当用户通过上传组件选择要上传的文件后,点击保存按钮之后,才会将所选文件上传到数据库中。

具体配置步骤: ❶ 在界面中添加上传组件; ❷ 在上传组件或其他按钮的流程中添加提交上传文件Action; ❸ 可以根据需要给返回值定义名称,赋值给预览文件或者Image组件;

Upload组件“上传结束”的事件配置三种用法:详见以下具体实现方法。

用法1:上传前预览。首先【API】获取文件流(此步骤不会生成Media对象,即不会保存进数据库),然后【设置组件值】将文件流赋值给Image组件。

用法2:上传后预览。首先【提交保存数据】(保存到数据库media表),然后【文件预览】。

用法3:上传后显示。首先【提交保存数据】(保存到数据库media表),然后【设置组件值】赋值给MediaFrame组件。

另外:是否要上传多个文件,在Upload组件的【基本属性】-【组件】-【File Max Files】中设置,1为单个文件,大于1为多个文件。

# 导出

导出当前页面数据。 ❶ 设计页面添加导出按钮,进入导出按钮事件页面 ❷ 事件页面添加导出控件,设置导出过滤条件(比如根据名称来查询的结果,导出对应名称过滤后的数据)

# 设置BPMN业务数据

bpmn工作流专用控件,用于表单提交事件,主要是把表单业务数据id传给工作流。

# 打印机打印

打印组件,用于生产过程中的一些常规打印需求。添加打印组件后,选中打印机组件进入属性设置(Action Properties)。 打印机ID可以不填,点击打印机模板下拉框展示系统中所有打印机模板文件,选择一个打印机模板后页面下方会直接带出打印机模板里的参数,输入对应的参数值。

ActionFlow页面一改以往下拉框形式,而是修改成了平铺展示(多个TAB),并且如果某个事件有内容的话,默认选中这个TAB,并且有内容的跟无内容的需要显示颜色标注。同时actionflow页面增加了一键format功能,当actionflow页面的控件排列比较杂乱时,可以通过一键format功能解决。

# Action 扩展

Action除了内置的实现, 平台也提供了扩展。 如需扩展,需要实现以下步骤:

  • 扩展Action:需继承ExtendAction类,并重写getActionFlowItemType()方法及getLabel()方法。
  • 扩展Action类型:需实现IWidgetActionFlowItemType接口
  • 扩展Action对应的属性面板:需实现IActionPropertiesPanel接口
  • 扩展Action对应的执行程序:需实现IActor接口

# 扩展Action

继承ExtendAction类

新扩展的Action类型,会在页面“Action Flow Library”面板看到,设计时可以选择扩展的类型。

ExtendAction定义

public class ExtendAction extends NodeData {
    private static final long serialVersionUID = 567285355342148548L;

    public ExtendAction() {
        super("extend-action");
    }

    @Override
    public String getLabel() {
        return "Extend";
    }

    @Override
    public void validate(IDisplay display) {

    }

    @Override
    public ExtendAction clone() {
        ExtendAction clone = (ExtendAction) super.clone();
        return clone;
    }
}

例.扩展的TextAction

public class TextAction extends ExtendAction {
    @Override
    public IWidgetActionFlowItemType getActionFlowItemType() {
        return MesWidgetActionFlowItemType.TEST;
    }

    @Override
    public String getLabel() {
        return "Text";
    }
}

# 扩展Action类型

创建一个enum并继承IWidgetActionFlowItemType接口。

IWidgetActionFlowItemType接口定义

public interface IWidgetActionFlowItemType {

    IWidgetActionFlowItemType getValue(String name);

    String getName();

    String getNameKey();

    String getDisplayName();

    String getIcon();

    Class<? extends IActionPropertiesPanel> getPanelClazz();

    Class<? extends CellData> getActionClazz();
}

例.MesWidgetActionFlowItemType

public enum MesWidgetActionFlowItemType implements IWidgetActionFlowItemType {
    /**
     *
     */
    TEST("Test", "lumos.design.event.test", "platform/icons/actionflow/Entry.png", TextAction.class, TestWidgetActionPanel.class);
    private String name;
    private String nameKey;
    private Class<? extends IActionPropertiesPanel> panelClass;
    private Class<? extends CellData> actionClass;
    private String iconPath;

    MesWidgetActionFlowItemType(String name, String nameKey, String iconPath, Class<? extends CellData> actionClass, Class<? extends IActionPropertiesPanel> panelClass) {
        this.name = name;
        this.nameKey = nameKey;
        this.iconPath = iconPath;
        this.actionClass = actionClass;
        this.panelClass = panelClass;
    }

    @Override
    public IWidgetActionFlowItemType getValue(String name) {
        if (name != null) {
            for (MesWidgetActionFlowItemType c : MesWidgetActionFlowItemType.values()) {
                if (c.getName().equals(name)) {
                    return c;
                }
            }
        }
        return null;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getNameKey() {
        return nameKey;
    }

    @Override
    public String getDisplayName() {
        return I18NUtility.getValue(getNameKey(), getName());
    }

    @Override
    public String getIcon() {
        return iconPath;
    }

    @Override
    public Class<? extends IActionPropertiesPanel> getPanelClazz() {
        return panelClass;
    }

    @Override
    public Class<? extends CellData> getActionClazz() {
        return actionClass;
    }
}

# 扩展Action属性面板

创建一个enum并继承 IActionPropertiesPanel 接口。

IActionPropertiesPanel接口定义

public interface IActionPropertiesPanel<T extends CellData> {
    IWidgetActionFlowItemType getActionType();

    void init(IDisplay display, T callData, WidgetActionFlowDiagram actionFlowDiagram, ActionFlowData actionFlowData);

    default void setDisplay(IDisplay display) {
    }

    default void setCellData(T callData) {
    }

    default void setActionFlowData(ActionFlowData actionFlowData) {
    }

    default void setDiagram(WidgetActionFlowDiagram actionFlowDiagram) {
    }

}

# 扩展Action执行

实现IActor接口。

IActor定义

public interface IActor {

    void execute(ActionFlowContext actionFlowContext);

}

TextActor

public class TextActor extends BaseActor<TextAction> {

    public TextActor(TextAction action) {
        super(action);
    }

    @Override
    protected boolean doExecute(ActionFlowContext actionFlowContext) {
        //自定义自己的执行逻辑
        return true;
    }

}

# Action一键检查功能

# Console展示错误信息

一键检查功能是指针对Action进行遍历检查,对可能出现的错误进行打印提示。

一键检查有两个入口,分别在页面头部位置和Action Flow画布的右上角位置

当点击一键check按钮时,系统会自动打开Console面板展示错误信息。

用户也可以按下 Ctrl+F12 键手动打开/关闭Console面板。

# validate扩展

重写 IAction 中的 validate 方法

以OpenDialogAction为例

public class OpenDialogAction extends CommonAction {
    @Override
    public void validate(IDisplay display) {
        if (type.equals(APIActionType.COMPONENT.getName())) {
            if (Strings.isNullOrEmpty(componentId) || display.getWidgetById(componentId) == null) {
                Designer.getConsole()
                    .error(this.getClass().getSimpleName() + " --- Component: '" + componentId + "' is not existed");
            }
        } else if (type.equals(APIActionType.SERVICE.getName())) {
            if (serviceClassName == null) {
                Designer.getConsole().error(this.getClass().getSimpleName() + " --- Service class name is null");
            }
            if (Strings.isNullOrEmpty(methodName)) {
                Designer.getConsole().error(this.getClass().getSimpleName() + " --- Method name is null");
            }
        }
    }
}

# 事务处理

对于众多的Action执行中,系统支持对于事务的基本处理。其原则为:

系统会在一次事件调用中,遍历所有的Action,找到第一个为 ITransactionalAction 类型的action,然后将后续的所有Action放入到事务中来处理。

public interface ITransactionalAction {}

目前实现以上的接口有以下几个Action:

  • ApiAction
  • SubmitDataAction
public class ApiAction extends BaseAction implements ITransactionalAction {

    private static final long serialVersionUID = -8906250438743395484L;

    private static Logger log = LoggerFactory.getLogger(LoadDataAction.class);

    private Class serviceName;
public class SubmitDataAction extends HasAction implements ITransactionalAction {
    private static final long serialVersionUID = -8030805740324876028L;

    private static final Logger log = LoggerFactory.getLogger(SubmitDataAction.class);
    public static final String SUMBMIT_DATA = "submitdata";