3.3.4 模型的继承

在我们的很多项目中,客户都是有个性化需求的,就像我们不能找到两件一模一样的东西,何况是企业的经营与管理思路,多少都会有差异。常规的方式只能去修改标准产品的逻辑来适配客户的需求。导致后续标品维护非常困难。而在介绍完这节以后是不是让你更加清晰认知到我们2.4.2【oinone独特性之每一个需求都可以是一个模块】一文中所表达的特性带来的好处呢?

一、继承方式

继承方式可以分为五种:

  • 抽象基类ABSTRACT,只保存不希望为每个子模型重复键入的信息的模型,抽象基类模型不生成数据表存储数据,只供其他模型继承模型可继承域使用,抽象基类可以继承抽象基类。

  • 扩展继承EXTENDS,子模型与父模型的数据表相同,子模型继承父模型的字段与函数。存储模型之间的继承默认为扩展继承。

  • 多表继承MULTI_TABLE,父模型不变,子模型获得父模型的可继承域生成新的模型;父子模型不同表,子模型会建立与父模型的一对一关联关系字段(而不是交叉表),使用主键关联,同时子模型会通过一对一关联关系引用父模型的所有字段。多表继承父模型需要使用@Model.MultiTable来标识,子模型需要使用@Model.MultiTableInherited来标识。

  • 代理继承PROXY,为原始模型创建代理,可以增删改查代理模型的实体数据,就像使用原始(非代理)模型一样。不同之处在于代理继承并不关注更改字段,可以更改代理中的元信息、函数和动作,而无需更改原始内容。一个代理模型必须仅能继承一个非抽象模型类。一个代理模型可以继承任意数量的没有定义任何模型字段的抽象模型类。一个代理模型也可以继承任意数量继承相同父类的代理模型。

  • 临时继承TRANSIENT,将父模型作为传输模型使用,并可以添加传输字段。

二、继承约束

通用约束

对于扩展继承,查询的时候,父模型只能查询到父模型字段的数据,子模型可以查询出父模型及子模型的字段数据(因为派生关系所以子模型复刻了一份父模型的字段到子模型中)。

系统不会为抽象基类创建实际的数据库表,它们也没有默认的数据管理器,不能被实例化也无法直接保存,它们就是用来被继承的。抽象基类完全就是用来保存子模型们共有的内容部分,达到重用的目的。当它们被继承时,它们的字段会全部复制到子模型中。

系统不支持非jar包依赖模型的继承。

多表继承具有阻断效应,子模型无法继承多表继承父模型的存储父模型的字段,需要使用@Model.Advanced注解的inherited属性显示声明继承父模型的父模型。但是可以继承多表继承父模型的抽象父模型的字段。

可以使用@Model.Advanced的unInheritedFields和unInheritedFunctions属性设置不从父类继承的字段和函数。

跨模块继承约束

如果模型间的继承是跨模块继承,应该与模型所属模块建立依赖关系;如果模块间有互斥关系,则不允许建立模块依赖关系,同理模型间也不允许存在继承关系。

跨模块代理继承,对代理模型的非inJvm函数调用将使用远程调用方式;跨模块扩展(同表)继承将使用本地调用方式,如果是数据管理器函数,将直连数据源。

模型类型与继承约束

  • 抽象模型可继承:抽象模型(Abstract)

  • 临时模型可继承:抽象模型(Abstract)、传输模型(Transient)

  • 存储模型可继承:抽象模型(Abstract)、存储模型(Store)、存储模型(多表,Multi-table Store),不可继承多个Store或Multi-table Store

  • 多表存储模型(父)可继承:同扩展继承

  • 多表存储模型(子)在继承单个Multi-table Store后可继承:抽象模型(Abstract)、存储模型(Store),不可继承多个Store

  • 代理模型可继承:

    • 抽象模型(Abstract),须搭配继承Store、Multi-table Store或Proxy

    • 存储模型(Store),不可继承多个Store或Multi-table Store

    • 存储模型(多表,Multi-table Store),不可继承多个Store或Multi-table Store

    • 代理模型(Proxy),可继承多个Proxy,但多个父Proxy须继承自同一个Store或Multi-table Store,且不能再继承其他Store或Multi-table Store

同名字段以模型自身字段为有效配置,若模型自身不存在该字段,继承字段以第一个加载的字段为有效配置,所以在多重继承的情况下,未避免继承同名父模型字段的不确定性,在自身模型配置同名字段来确定生效配置。

三、继承的使用场景

模型的继承可以继承父模型的元信息、字段、数据管理器和函数

  • 抽象基类 解决公用字段问题

  • 扩展继承 解决开放封闭原则、跨模块扩展等问题

  • 多表继承 解决多型派生类字段差异问题和前端多存储模型组合外观问题

  • 代理继承 解决同一模型在不同场景下的多态问题(一表多态)

  • 临时继承 解决使用现有模型进行数据传输问题

举例,前端多存储模型组合外观问题可通过多表继承的子模型,并一对一关联到关联模型,同时使用排除继承字段去掉不需要继承的字段。子模型通过默认模型管理器提供查询功能给前端,默认查询会查询子模型数据列表并在列表行内根据一对一关系查出关联模型数据合并,关联模型数据展现形态在行内是平铺还是折叠,在详情是分组还是选项卡可以自定义view进行配置

扩展继承 父子同表,模型在所有场景都有一致化的表现,意味着原模型被扩展成了新模型,父子模型的表名一致,模型编码不同,可覆盖父模型的模型管理器、数据排序规则、函数

多表继承 父子多表,父子间有隐式一对一关系,即父子模型都增加了一对一关联关系字段,同时父模型的字段被引用到子模型,且引用字段为只读字段,意味着子模型不可以直接更改父模型的字段值,子模型不继承父模型的模型管理器、数据排序规则、函数,子模型拥有自己的默认模型管理器、数据排序规则、函数。多表继承具有阻断效应,子模型无法自动多表继承父模型的存储父模型,需要显式声明多表继承父模型的存储父模型。

代理继承 代理模型继承并可覆盖父模型的模型管理器、数据排序规则、函数,同时可以使用排除继承字段和函数来达到不同场景不同视觉交互的效果。

3.3.4 模型的继承

图3-3-4-1 继承的使用场景

四、抽象基类(举例)

参考前文中3.3.2【模型的类型】一文中关于抽象模型的介绍

五、多表继承(举例)

场景设计如下

3.3.4 模型的继承

图3-3-4-2 多表继承设计场景

Step1 新建宠物品种、宠狗品种和萌猫品种模型

  1. 新建宠物品种模型,用@Model.MultiTable(typeField = "kind"),申明为可多表继承父类,typeField指定为kind字段
package pro.shushi.pamirs.demo.api.model;

import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;
import pro.shushi.pamirs.meta.base.IdModel;

@Model.MultiTable(typeField = "kind")
@Model.model(PetType.MODEL_MODEL)
@Model(displayName="品种",labelFields = {"name"})
public class PetType extends IdModel {

    public static final String MODEL_MODEL="demo.PetType";

    @Field(displayName = "品种名")
    private String name;

    @Field(displayName = "宠物分类")
    private String kind;
}

图3-3-4-3 多表继承示例代码

  1. 新建宠狗品种模型,用@Model.MultiTableInherited(type = PetDogType.KIND_DOG),申明以多表继承模式继承PetType,覆盖kind字段(用defaultValue设置默认值,用invisible = true设置为前端不展示),更多模块元数据以及模型字段元数据配置详见4.1.6【模型之元数据详解】一文
package pro.shushi.pamirs.demo.api.model;

import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;

@Model.MultiTableInherited(type = PetDogType.KIND_DOG)
@Model.model(PetDogType.MODEL_MODEL)
@Model(displayName="宠狗品种",labelFields = {"name"})
public class PetDogType extends PetType {

    public static final String MODEL_MODEL="demo.PetDogType";
    public static final String KIND_DOG="DOG";

    @Field(displayName = "宠物分类",defaultValue = PetDogType.KIND_DOG,invisible = true)
    private String kind;
}

图3-3-4-4 多表继承示例代码

  1. 新建萌猫品种模型,用@Model.MultiTableInherited(type = PetCatType.KIND_CAT),申明以多表继承模式继承PetType,覆盖kind字段(用defaultValue设置默认值,用invisible = true设置为前端不展示),并新增一个CatShapeEnum枚举类型的字段shape
package pro.shushi.pamirs.demo.api.enumeration;

import pro.shushi.pamirs.meta.annotation.Dict;
import pro.shushi.pamirs.meta.common.enmu.BaseEnum;

@Dict(dictionary = CatShapeEnum.DICTIONARY,displayName = "萌猫体型")
public class CatShapeEnum extends BaseEnum<CatShapeEnum,Integer> {

    public static final String DICTIONARY ="demo.CatShapeEnum";

    public final static CatShapeEnum BIG =create("BIG",1,"大","大");
    public final static CatShapeEnum SMALL =create("SMALL",2,"小","小");
}

图3-3-4-5 多表继承示例代码

package pro.shushi.pamirs.demo.api.model;

import pro.shushi.pamirs.demo.api.enumeration.CatShapeEnum;
import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;

@Model.MultiTableInherited(type = PetCatType.KIND_CAT)
@Model.model(PetCatType.MODEL_MODEL)
@Model(displayName="萌猫品种",labelFields = {"name"})
public class PetCatType extends PetType {

    public static final String MODEL_MODEL="demo.PetCatType";
    public static final String KIND_CAT="CAT";

    @Field(displayName = "宠物分类",defaultValue = PetCatType.KIND_CAT,invisible = true)
    private String kind;

    @Field.Enum
    @Field(displayName = "宠物体型")
    private CatShapeEnum shape;
}

图3-3-4-6 多表继承示例代码

Step2 配置菜单后,重启查看效果

  1. 为了更好帮助大家更好地理解,这里先把对应的菜单配置上,从页面来看看效果。下面以注解方式进行菜单配置,更多详见3.5.1【构建第一个Menu】一文。
package pro.shushi.pamirs.demo.core.init;

import pro.shushi.pamirs.boot.base.constants.ViewActionConstants;
import pro.shushi.pamirs.boot.base.ux.annotation.action.UxRoute;
import pro.shushi.pamirs.boot.base.ux.annotation.navigator.UxMenu;
import pro.shushi.pamirs.boot.base.ux.annotation.navigator.UxMenus;
import pro.shushi.pamirs.demo.api.model.*;

@UxMenus
public class DemoMenus implements ViewActionConstants {
    @UxMenu("宠物品种")@UxRoute(PetType.MODEL_MODEL) class PetTypeMenu{}
    @UxMenu("萌猫品种")@UxRoute(PetCatType.MODEL_MODEL) class CatTypeMenu{}
    @UxMenu("宠狗品种")@UxRoute(PetDogType.MODEL_MODEL) class DogTypeMenu{}
}

图3-3-4-7 对应菜单配置

  1. 在宠狗品种中新增、修改、列表都隐藏了品种类型字段,并附上了默认值DOG

3.3.4 模型的继承

图3-3-4-8 宠狗品种新增页

3.3.4 模型的继承

图3-3-4-9 宠狗品种列表页

3.3.4 模型的继承

图3-3-4-10 宠狗品种数据表

  1. 在萌猫品种中新增、修改、列表都增加宠物体型枚举字段,并且隐藏了品种类型字段,并附上了默认值CAT

3.3.4 模型的继承

图3-3-4-11 萌猫品种新增页

3.3.4 模型的继承

图3-3-4-12 萌猫品种列表页

3.3.4 模型的继承

图3-3-4-13 萌猫品种数据表

Step3 为宠狗和萌猫品种模型实现默认读写扩展点,保证父子表数据同步

多表继承默认父子表是不同步的,不过oinone为每一个模型的默认数据管理器都提供的默认读写扩展点,我们可以主动利用这个特性来进行手动同步,更多扩展点知识详见3.4.3【函数相关特性】部分中的扩展点一文,会介绍除了利用系统默认提供的扩展点,还有如何自定义扩展。

  1. PetCatType和PetDogType都需要实现扩展点来完成与父模型的同步,所以这里把相同逻辑做了提取AbstractPetTypeExtPoint
package pro.shushi.pamirs.demo.core.extpoint;

import pro.shushi.pamirs.core.common.CopyHelper;
import pro.shushi.pamirs.demo.api.model.PetType;
import pro.shushi.pamirs.framework.connectors.data.sql.Pops;
import pro.shushi.pamirs.meta.api.Models;
import pro.shushi.pamirs.meta.base.extpoint.DefaultReadWriteExtPoint;

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

public abstract class AbstractPetTypeExtPoint  extends DefaultReadWriteExtPoint {

    public T createBefore(T data) {
        data.construct();
        return data;
    }

    public T updateBefore(T data) {
        return data;
    }

    public T createAfter(T data) {
        CopyHelper.simpleReplace(data, new PetType()).create();
        return data;
    }

    public T updateAfter(T data) {
        CopyHelper.simpleReplace(data, new PetType()).updateById();
        return data;
    }

    public List deleteBefore(List data) {
        List petTypeIdList = new ArrayList();
        for(T item:data){
            petTypeIdList.add(item.getId());
        }
        Models.data().deleteByWrapper(Pops.lambdaQuery().from(PetType.MODEL_MODEL).in(PetType::getId,petTypeIdList));
        return data;
    }
}

图3-3-4-14 相同逻辑提取AbstractPetTypeExtPoint

  1. PetCatTypeExtPoint继承AbstractPetTypeExtPoint类,并通过@Ext(PetCatType.class)申明扩展点所扩展函数所在类为PetCatType,并@ExtPoint.Implement申明对应扩展点的实现方法实例
package pro.shushi.pamirs.demo.core.extpoint;

import pro.shushi.pamirs.demo.api.model.PetCatType;
import pro.shushi.pamirs.meta.annotation.Ext;
import pro.shushi.pamirs.meta.annotation.ExtPoint;

import java.util.List;

@Ext(PetCatType.class)
public class PetCatTypeExtPoint extends AbstractPetTypeExtPoint{

    @Override
    @ExtPoint.Implement
    public PetCatType createBefore(PetCatType data) {
        return super.createBefore(data);
    }
    @Override
    @ExtPoint.Implement
    public PetCatType updateBefore(PetCatType data) {
        return super.updateBefore(data);
    }
    @Override
    @ExtPoint.Implement
    public PetCatType createAfter(PetCatType data) {
        return super.createAfter(data);
    }
    @Override
    @ExtPoint.Implement
    public PetCatType updateAfter(PetCatType data) {
        return super.updateAfter(data);
    }
    @Override
    @ExtPoint.Implement
    public List deleteBefore(List data) {
        return super.deleteBefore(data);
    }
}

图3-3-4-15 PetCatTypeExtPoint继承AbstractPetTypeExtPoint类

  1. PetDogTypeExtPoint继承AbstractPetTypeExtPoint类,并通过@Ext(PetDogType.class)申明扩展点所扩展函数所在类为PetDogType,并@ExtPoint.Implement申明对应扩展点的实现方法实例
package pro.shushi.pamirs.demo.core.extpoint;

import pro.shushi.pamirs.demo.api.model.PetDogType;
import pro.shushi.pamirs.meta.annotation.Ext;
import pro.shushi.pamirs.meta.annotation.ExtPoint;

import java.util.List;

@Ext(PetDogType.class)
public class PetDogTypeExtPoint extends AbstractPetTypeExtPoint{

    @Override
    @ExtPoint.Implement
    public PetDogType createBefore(PetDogType data) {
        return super.createBefore(data);
    }
    @Override
    @ExtPoint.Implement
    public PetDogType updateBefore(PetDogType data) {
        return super.updateBefore(data);
    }
    @Override
    @ExtPoint.Implement
    public PetDogType createAfter(PetDogType data) {
        return super.createAfter(data);
    }
    @Override
    @ExtPoint.Implement
    public PetDogType updateAfter(PetDogType data) {
        return super.updateAfter(data);
    }
    @Override
    @ExtPoint.Implement
    public List deleteBefore(List data) {
        return super.deleteBefore(data);
    }
}

图3-3-4-16 PetDogTypeExtPoint继承AbstractPetTypeExtPoint类

Step4 重启查看效果:在萌猫品种和宠狗品种下新增、修改、删除,则会在宠物品种下看到对应记录变化

3.3.4 模型的继承

图3-3-4-17 父模型宠物品种列表页

3.3.4 模型的继承

图3-3-4-18 父模型宠物品种数据表

六、扩展继承(举例)

场景设计(如下图3-3-55所示)

3.3.4 模型的继承

图3-3-4-19 扩展继承场景设计

Step1 新建宠物商品、宠狗商品和萌猫商品模型,并配置菜单

  1. 新建宠物商品模型为普通存储模型,用扩展继承父类设置公共字段如:店铺,品种等,注意type字段类型为PetType
package pro.shushi.pamirs.demo.api.model;

import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;

import java.math.BigDecimal;

@Model.model(PetItem.MODEL_MODEL)
@Model(displayName = "宠物商品",summary="宠物商品")
public class PetItem  extends AbstractDemoCodeModel{

    public static final String MODEL_MODEL="demo.PetItem";
    @Field(displayName = "商品名称",required = true)
    private String itemName;

    @Field(displayName = "商品价格",required = true)
    private BigDecimal price;

    @Field(displayName = "店铺",required = true)
    private PetShop shop;

    @Field(displayName = "品种")
    @Field.many2one
    private PetType type;
}

图3-3-4-20 新建宠物商品模型为普通存储模型

  1. 新建宠狗商品模型继承宠物商品模型,注意这里把type字段类型覆盖为PetType的子类PetDogType,并新增原产地字段provenance
package pro.shushi.pamirs.demo.api.model;

import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;

@Model.model(PetDogItem.MODEL_MODEL)
@Model(displayName = "宠狗商品",summary="宠狗商品")
public class PetDogItem extends PetItem{

    public static final String MODEL_MODEL="demo.PetDogItem";

    @Field(displayName = "品种")
    @Field.many2one
    private PetDogType type;

    @Field(displayName = "原产地")
    private String provenance;
}

图3-3-4-21 新建宠狗商品模型继承宠物商品模型

  1. 新建萌猫商品模型继承宠物商品模型,注意这里把type字段类型覆盖为PetType的子类PetCatType。
package pro.shushi.pamirs.demo.api.model;

import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;

@Model.model(PetCatItem.MODEL_MODEL)
@Model(displayName = "萌猫商品",summary="萌猫商品")
public class PetCatItem extends PetItem{
    public static final String MODEL_MODEL="demo.PetCatItem";

    @Field(displayName = "品种")
    @Field.many2one
    private PetCatType type;
}

图3-3-4-22 新建萌猫商品模型继承宠物商品模型

  1. 为了更好帮助大家更好地理解,这里先把对应的菜单配置上,从页面来看看效果。下面以注解方式进行菜单配置,更多详见3.5.1【构建第一个Menu】一文。
package pro.shushi.pamirs.demo.core.init;

import pro.shushi.pamirs.boot.base.constants.ViewActionConstants;
import pro.shushi.pamirs.boot.base.ux.annotation.action.UxRoute;
import pro.shushi.pamirs.boot.base.ux.annotation.navigator.UxMenu;
import pro.shushi.pamirs.boot.base.ux.annotation.navigator.UxMenus;
import pro.shushi.pamirs.demo.api.model.*;

@UxMenus
public class DemoMenus implements ViewActionConstants {
    @UxMenu("商品管理")@UxRoute(PetItem.MODEL_MODEL) class ItemMenu{}
    @UxMenu("宠狗商品")@UxRoute(PetDogItem.MODEL_MODEL) class DogItemMenu{}
    @UxMenu("萌猫商品")@UxRoute(PetCatItem.MODEL_MODEL) class CatItemMenu{}
    @UxMenu("宠物品种")@UxRoute(PetType.MODEL_MODEL) class PetTypeMenu{}
    @UxMenu("萌猫品种")@UxRoute(PetCatType.MODEL_MODEL) class CatTypeMenu{}
    @UxMenu("宠狗品种")@UxRoute(PetDogType.MODEL_MODEL) class DogTypeMenu{}
}

图3-3-4-23 以注解方式进行菜单配置

至此模型就建完了,还是很简单的吧

Step2 分别新建三条宠物商品、宠狗商品和萌猫商品记录,查看效果

  1. 父模型宠物商品的商品管理可以看到品类中可选柴犬、加菲猫,数据来源是品种的父模型PetType,店铺下拉框展示为"-",因为店铺模型没有配置label字段,可以在店铺模型的注解上增加“@Model(displayName = "宠物店铺",summary="宠物店铺",labelFields = {"shopName"})”,即可解决。这里先选择第一个往下执行

3.3.4 模型的继承

图3-3-4-24 宠物商品管理

  1. 父模型宠狗商品的商品管理可以看到品类中可选柴犬,数据来源是品种的子模型PetDogType,而且新增源产地字段;

3.3.4 模型的继承

图3-3-4-25 父模型宠狗商品管理

  1. 父模型萌猫商品的商品管理可以看到品类中可选加菲猫,数据来源是品种的子模型PetCatType;

3.3.4 模型的继承

图3-3-4-26 父模型萌猫商品管理

Step3 分别观察宠物商品、宠狗商品和萌猫商品的列表页,我们会发现数据记录都是三条,但是展示字段会随着模型不同而有差异

  1. 父页面在品种一列中可以显示所有品种名称

3.3.4 模型的继承

图3-3-4-27 父页面在品种一列中可以显示所有品种名称

  1. 宠狗页面在品种一列中只能显示PetDogType的品种名称、但多了原产地字段

3.3.4 模型的继承

图3-3-4-28 宠狗页面品种列展示

  1. 萌猫页面在品种一列中只能显示PetCatType的品种名称

3.3.4 模型的继承

图3-3-4-29 萌猫页面品种列中只展示PetCatType的品种名称

  1. 对应的数据库表:数据都在一张记录表中,新增了原产地字段

3.3.4 模型的继承

图3-3-4-30 对应数据库表

Step4 顺带优化下宠物店铺的展示Labels

  1. 给PetShop增加@Model(labelFields ={"shopName"} )注解,labelFields是为模型的“数据标题, 用于前端展示”,其默认值为name,但PetShop没有name字段,所以我们列表上展示不出来。更多元数据见4.1.6【模型之元数据详解】一文;
package pro.shushi.pamirs.demo.api.model;

import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;

import java.sql.Time;

@Model.model(PetShop.MODEL_MODEL)
@Model(displayName = "宠物店铺",summary="宠物店铺",labelFields ={"shopName"} )
public class PetShop extends AbstractDemoIdModel {
    public static final String MODEL_MODEL="demo.PetShop";

    @Field(displayName = "店铺名称",required = true)
    private String shopName;

    @Field(displayName = "开店时间",required = true)
    private Time openTime;

    @Field(displayName = "闭店时间",required = true)
    private Time closeTime;
}

图3-3-4-31 给PetShop增加@Model(labelFields ={shopName} )注解

  1. 重启查看效果,店铺字段可以展示出来

3.3.4 模型的继承

图3-3-4-32 店铺字段可展示

Step5 思考在扩展继承模式下数据隔离问题

多表继承需要自己搞定父、子模型的数据同步问题,同表继承则需要自己搞定数据隔离问题。我们可以重写queryPage这个Function,如我们在3.3.2【模型类型】一文中介绍“代理模型类型”的部分时,涉及的PetShopProxyAction就覆盖了queryPage。这里举例PetCatItem的queryPage覆盖,PetDogItem留给大家自行练习

  1. 给PetItem显示增加一个typeId字段,方便在PetCatItemAction中用于过滤条件使用。为什么说显示增加呢?因为type字段是一个many2one的字段在没有配置@Field.Relation的情况下,oinone会为PetItem模型自动推断出一个typeId字段去关联PetType模型的Id。@Field.Relation的relationFields为本模型的关联字段,referenceFields为目标模型关联字段。如符合推断规范可以不配置@Field.Relation,就如这个场景配不配效果是一样的。还是那句话“更多元数据详见4.1.6【模型之元数据详解】一文”;
package pro.shushi.pamirs.demo.api.model;

import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;

import java.math.BigDecimal;

@Model.model(PetItem.MODEL_MODEL)
@Model(displayName = "宠物商品",summary="宠物商品")
public class PetItem  extends AbstractDemoCodeModel{
    public static final String MODEL_MODEL="demo.PetItem";

    @Field(displayName = "商品名称",required = true)
    private String itemName;

    @Field(displayName = "商品价格",required = true)
    private BigDecimal price;

    @Field(displayName = "店铺",required = true)
    private PetShop shop;

    @Field(displayName = "品种")
    @Field.many2one
    @Field.Relation(relationFields = {"typeId"},referenceFields = {"id"})
    private PetType type;

    @Field(displayName = "品种类型",invisible = true)
    private Long typeId;
}

图3-3-4-33 给PetItem显示增加一个typeId字段

  1. 覆盖PetCatItem名为“queryFilters”的函数,增加typeId的过滤条件为PetCatType表对应的Id列表,则针对PetCatItem的所有查询都会自动加上“queryFilters”的函数返回的过滤条件
package pro.shushi.pamirs.demo.core.action;

import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import pro.shushi.pamirs.core.common.behavior.impl.DataStatusBehavior;
import pro.shushi.pamirs.demo.api.model.PetCatItem;
import pro.shushi.pamirs.demo.api.model.PetCatType;
import pro.shushi.pamirs.framework.connectors.data.sql.Pops;
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.constant.FunctionConstants;
import pro.shushi.pamirs.meta.enmu.FunctionOpenEnum;
import pro.shushi.pamirs.meta.enmu.FunctionTypeEnum;

import java.util.List;
import java.util.stream.Collectors;

@Model.model(PetCatItem.MODEL_MODEL)
@Component
public class PetCatItemAction extends DataStatusBehavior {

    @Override
    protected PetCatItem fetchData(PetCatItem data) {
        return data.queryById();
    }
    @Action(displayName = "启用")
    public PetCatItem dataStatusEnable(PetCatItem data){
        data = super.dataStatusEnable(data);
        data.updateById();
        return data;
    }

    @Function.Advanced(displayName = "查询模型数据的默认过滤条件", type = FunctionTypeEnum.QUERY, managed = true)
    @Function(openLevel = {LOCAL})
    public String queryFilters() {
        StringBuilder sqlWhereCondition = new StringBuilder();
        List typeList = new PetCatType().queryList();
        if(!CollectionUtils.isEmpty(typeList)){
//          sqlWhereCondition.append("type_id");
            sqlWhereCondition.append(PStringUtils.fieldName2Column(LambdaUtil.fetchFieldName(PetCatItem::getTypeId)));
            sqlWhereCondition.append(StringUtils.SPACE).append(SqlConstants.IN).append(CharacterConstants.LEFT_BRACKET);
            for(PetCatType petCatType: typeList){
                sqlWhereCondition.append(petCatType.getId()).append(CharacterConstants.SEPARATOR_COMMA);
            }
            sqlWhereCondition.deleteCharAt(sqlWhereCondition.lastIndexOf(CharacterConstants.SEPARATOR_COMMA));
            sqlWhereCondition.append(StringUtils.SPACE).append(CharacterConstants.RIGHT_BRACKET);
        }
        return sqlWhereCondition.toString();
    }
}

图3-3-4-34 覆盖函数的代码示例

  1. 重启查看效果,萌猫商品只能看到PetCatType对应的数据记录了,赶紧自己动手试试PetDogItem的改造吧

3.3.4 模型的继承

图3-3-4-35 重启查看效果

七、代理继承(举例)

代理模型在3.3.2【模型的类型】一文中有介绍过,代理模型看名字就知道其本身是通过继承方式代理另一个存储模型,这里不过多介绍。我们来尝试一下它继承的特殊性“一个代理模型也可以继承任意数量继承相同父类的代理模型”

场景设计(如下图3-3-72所示)

3.3.4 模型的继承

图3-3-4-36 代理继承场景设计

Step1 新建宠物店铺代理模型A和宠物店铺代理模型B,同时修改PetItem

  1. 新建宠物店铺代理模型A申明为代理模型,新增一个one2many字段items,用@Field.Relation申明关联字段
package pro.shushi.pamirs.demo.api.proxy;

import pro.shushi.pamirs.demo.api.model.PetItem;
import pro.shushi.pamirs.demo.api.model.PetShop;
import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;
import pro.shushi.pamirs.meta.enmu.ModelTypeEnum;
import pro.shushi.pamirs.user.api.model.PamirsUser;

import java.util.List;

@Model.model(PetShopProxyA.MODEL_MODEL)
@Model.Advanced(type = ModelTypeEnum.PROXY)
@Model(displayName = "宠物店铺代理模型A",summary="宠物店铺代理模型A")
public class PetShopProxyA extends PetShop {
    public static final String MODEL_MODEL="demo.PetShopProxyA";

    @Field.one2many
    @Field(displayName = "商品列表")
    @Field.Relation(relationFields = {"id"},referenceFields = {"shopId"})
    private List items;
}

图3-3-4-37 新建宠物店铺代理模型A申明为代理模型

  1. 新建宠物店铺代理模型B申明为代理模型,用 @Model.Advanced(inherited ={PetShopProxy.MODEL_MODEL,PetShopProxyA.MODEL_MODEL})声明继承多个同源代理模型,并且新增一个one2many字段catItems用@Field.Relation申明关联字段
package pro.shushi.pamirs.demo.api.proxy;

import pro.shushi.pamirs.demo.api.model.PetCatItem;
import pro.shushi.pamirs.demo.api.model.PetShop;
import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;
import pro.shushi.pamirs.meta.enmu.ModelTypeEnum;

import java.util.List;

@Model.model(PetShopProxyB.MODEL_MODEL)
@Model.Advanced(type = ModelTypeEnum.PROXY,inherited ={PetShopProxy.MODEL_MODEL,PetShopProxyA.MODEL_MODEL} )
@Model(displayName = "宠物店铺代理模型B",summary="宠物店铺代理模型B")
public class PetShopProxyB extends PetShop {

    public static final String MODEL_MODEL="demo.PetShopProxyB";

    @Field.one2many
    @Field(displayName = "萌猫商品列表")
    @Field.Relation(relationFields = {"id"},referenceFields = {"shopId"})
    private List catItems;
}

图3-3-4-38 新建宠物店铺代理模型B申明为代理模型

  1. 修改PetItem增加一个labelFields={"itemName"}注解,给PetItem显示增加一个shopId字段方便,在Service中用于过滤条件使用。
package pro.shushi.pamirs.demo.api.model;

import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;

import java.math.BigDecimal;

@Model.model(PetItem.MODEL_MODEL)
@Model(displayName = "宠物商品",summary="宠物商品",labelFields={"itemName"})
public class PetItem  extends AbstractDemoCodeModel{

    public static final String MODEL_MODEL="demo.PetItem";
    @Field(displayName = "商品名称",required = true)
    private String itemName;

    @Field(displayName = "商品价格",required = true)
    private BigDecimal price;

    @Field(displayName = "店铺",required = true)
    private PetShop shop;

    @Field(displayName="店铺id",invisible = true)
    private Long shopId;

    @Field(displayName = "品种")
    @Field.many2one
    @Field.Relation(relationFields = {"typeId"},referenceFields = {"id"})
    private PetType type;

    @Field(displayName = "品种类型",invisible = true)
    private Long typeId;
}

图3-3-4-39 PetItem代码修改示例

Step2 覆盖PetShopProxyB的queryPage和queryOne函数

PetShopProxyB的queryPage和queryOne都查catItems\creater\items三个字段

package pro.shushi.pamirs.demo.core.action;

import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import pro.shushi.pamirs.core.common.behavior.impl.DataStatusBehavior;
import pro.shushi.pamirs.demo.api.model.PetShop;
import pro.shushi.pamirs.demo.api.proxy.PetShopProxy;
import pro.shushi.pamirs.demo.api.proxy.PetShopProxyB;
import pro.shushi.pamirs.framework.faas.utils.ArgUtils;
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.constant.FunctionConstants;
import pro.shushi.pamirs.meta.enmu.FunctionOpenEnum;
import pro.shushi.pamirs.meta.enmu.FunctionTypeEnum;

@Model.model(PetShopProxyB.MODEL_MODEL)
@Component
public class PetShopProxyBAction  {

    @Function.Advanced(type= FunctionTypeEnum.QUERY)
    @Function.fun(FunctionConstants.queryPage)
    @Function(openLevel = {FunctionOpenEnum.API})
    public Pagination queryPage(Pagination page, IWrapper queryWrapper){
        Pagination result = new PetShopProxy().queryPage(page,queryWrapper);
        if(!CollectionUtils.isEmpty(result.getContent())) {
            //自身继承【宠物店铺】后扩展的字段catItems
            new PetShopProxyB().listFieldQuery(result.getContent(),PetShopProxyB::getCatItems);
            //继承【宠物店铺代理模型】而来的字段creater,java语言限制【宠物店铺代理模型B】没有getCreate()方法
            new PetShopProxyB().listFieldQuery(result.getContent(),"creater");
            //继承【宠物店铺代理模型A】而来的字段items,java语言限制【宠物店铺代理模型B】没有getItems()方法
            new PetShopProxyB().listFieldQuery(result.getContent(),"items");
        }
        return result;
    }
    @Function.Advanced(type= FunctionTypeEnum.QUERY)
    @Function.fun(FunctionConstants.queryByEntity)
    @Function(openLevel = {FunctionOpenEnum.API})
    public PetShopProxyB queryOne(PetShopProxyB query){
        query = query.queryById();
        //自身继承【宠物店铺】后扩展的字段catItems
        query.fieldQuery(PetShopProxyB::getCatItems);
        //继承【宠物店铺代理模型】而来的字段creater,java语言限制【宠物店铺代理模型B】没有getCreate()方法
        query.fieldQuery("creater");
        //继承【宠物店铺代理模型A】而来的字段items,java语言限制【宠物店铺代理模型B】没有getItems()方法
        query.fieldQuery("items");
        return query;
    }
}

图3-3-4-40 函数覆盖代码示例

Step3 配置菜单,并重启看效果

  1. 配置菜单:请参考本节前面介绍自行增加三个菜单商店管理、商店管理A、商店管理B分别对应宠物店铺代理模型、宠物店铺代理模型A、宠物店铺代理模型B的管理入口

  2. 宠物店铺代理模型覆盖了queryPage方法但没有覆盖queryOne方法,所以列表页有显示新增字段创建者,但详情页没有。具体详见3.3.2【模型的类型】一文中的“代理模型”介绍部分;

3.3.4 模型的继承

图3-3-4-41 宠物店铺代理模型的列表页

3.3.4 模型的继承

图3-3-4-42 宠物店铺代理模型的详情页

  1. 宠物店铺代理模型A只增加了一个商品列表字段,没有覆盖对应query相关方法,所以列表页有这个字段但没有值;

3.3.4 模型的继承

图3-3-4-43 宠物店铺代理模型A的列表页

  1. 宠物店铺代理模型B因为同源多表继承了宠物店铺代理模型和宠物店铺代理模型A所以它拥有商品列表、萌猫商品列表、创建者三个字段,并且同时覆盖queryPage和queryOne函数,所以列表和详情都有值。

3.3.4 模型的继承

图3-3-4-44 宠物店铺代理模型B的列表页

3.3.4 模型的继承

图3-3-4-45 宠物店铺代理模型B的详情页

八、临时继承(举例)

参考前文3.3.2【模型的类型】一文中对传输模型的介绍,差别在于一个继承抽象基类,一个继承其他模型类型,不管什么类型都只是复用父类的字段,自己还是传输模型

九、总结

在我们的很多项目中,客户都是有个性化需求的,就像我们不能找到两件一模一样的东西,何况是企业的经营与管理思路,多少都会有差异。常规的方式只能去修改标准产品的逻辑来适配客户的需求。导致后续标品维护非常困难。而在介绍完这节以后是不是让你更加清晰认知到我们2.4.2【oinone独特性之每一个需求都可以是一个模块】一文中所表达的特性带来的好处呢?

Oinone社区 作者:史, 昂原创文章,如若转载,请注明出处:https://doc.oinone.top/oio4/9235.html

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

Like (1)
史, 昂's avatar史, 昂数式管理员
Previous 2024年5月23日 am9:42
Next 2024年5月23日 am9:49

相关推荐

  • 3.2.3 应用中心

    在App Finder 中点击应用中心可以进入oinone的应用中心,可以看到oinone平台所有应用列表、应用大屏、以及技术可视化。 一、应用列表 标准版本不支持在线安装,只能通过boot工程的yml文件来配置安装模块。在 www.oinone.top 官方SaaS平台客户可以在线管理应用生命周期如:安装、升级、卸载。同时针对已安装应用可以进行无代码设计(前提安装了设计器),针对应用类的模块则可进行收藏,收藏后会在App Finder中的我收藏的应用中出现。在应用列表中可以看到我们已经安装的应用以及模块,我们oinoneDemo工程也在其中。 图3-2-35 Oinone的应用列表 图3-2-36 应用收藏后会在App Finder的【我收藏的应用】中出现 二、应用大屏 但我们的测试应用没有设置应用类目,则无法在应用大屏中呈现。 图3-2-37 未设置应用类目则无法在应用大屏中呈现 三、技术可视化 在技术可视化页面,出展示已经安装模块的元数据,并进行分类呈现 图3-2-38 云数据分类呈现

    2024年5月23日
    2.8K00
  • 3.5.2.2 构建View的Template

    我们在很多时候需要自定义模型的管理页面,而不是直接使用默认页面,比如字段的展示与隐藏,Action是否在这个页面上出现,搜索条件自定义等等,那么本章节带您一起学习如何自定义View的Template。 自定义View的Template 在使用默认layout的情况下,我们来做几个自定义视图Template,并把文件放到指定目录下。 图3-5-2-14 自定义View的Template 第一个Tabel Step1 自定义PetTalent的列表 我们先通过数据库查看默认页面定义,找到base_view表,过滤条件设置为model =\’demo.PetTalent\’,我们就看到该模型下对应的所有view,这些是系统根据该模型的ViewAction对应生成的默认视图,找到类型为【表格(type = TABLE)】的记录,查看template字段。 图3-5-2-15 base_view表查看template字段 <view name="tableView" cols="1" type="TABLE" enableSequence="false"> <template slot="actions" autoFill="true"/> <template slot="rowActions" autoFill="true"/> <template slot="fields"> <field invisible="true" data="id" label="ID" readonly="true"/> <field data="name" label="达人"/> <field data="dataStatus" label="数据状态"> <options> <option name="DRAFT" displayName="草稿" value="DRAFT" state="ACTIVE"/> <option name="NOT_ENABLED" displayName="未启用" value="NOT_ENABLED" state="ACTIVE"/> <option name="ENABLED" displayName="已启用" value="ENABLED" state="ACTIVE"/> <option name="DISABLED" displayName="已禁用" value="DISABLED" state="ACTIVE"/> </options> </field> <field data="createDate" label="创建时间" readonly="true"/> <field data="writeDate" label="更新时间" readonly="true"/> <field data="createUid" label="创建人id"/> <field data="writeUid" label="更新人id"/> </template> <template slot="search" autoFill="true" cols="4"/> </view> 图3-5-2-16 base_view表查看template字段 对比view的template定义与页面差异,从页面上看跟view的定义少了,创建人id和更新人id。因为这两个字段元数据定义里invisible属性。 a. 当XML里面没有配置,则用元数据覆盖了。 b. 当XML里面配置了,则不会用元数据覆盖了。 在下一步中我们只要view的DSL中给这两个字段加上invisible="false"就可以展示出来了 图3-5-2-17 查看列表展示 图3-5-2-18 invisible属性 新建pet_talent_table.xml文件放到对应的pamirs/views/demo_core/template目录下,内容如下 a. 对比默认视图,在自定义视图时需要额外增加属性model="demo.PetTalent" b. name设置为"tableView",系统重启后会替换掉base_view表中model为"demo.PetTalent",name为"tableView",type为"TABLE"的数据记录。 ⅰ. name不同的但type相同,且viewAction没有指定时,根据优先级priority进行选择。小伙伴可以尝试修改name="tableView1",并设置priority为1,默认生成的优先级为10,越小越优先。 ccreateUid和writeUid字段,增加invisible="false"的属性定义 <view name="tableView" model="demo.PetTalent" cols="1" type="TABLE" enableSequence="false"> <template slot="actions" autoFill="true"/> <template slot="rowActions" autoFill="true"/> <template slot="fields"> <field invisible="true" data="id" label="ID" readonly="true"/> <field data="name" label="达人"/> <field data="dataStatus" label="数据状态"> <options> <option name="DRAFT" displayName="草稿" value="DRAFT" state="ACTIVE"/> <option name="NOT_ENABLED" displayName="未启用" value="NOT_ENABLED" state="ACTIVE"/> <option name="ENABLED" displayName="已启用" value="ENABLED" state="ACTIVE"/> <option name="DISABLED" displayName="已禁用" value="DISABLED" state="ACTIVE"/> </options> </field> <field data="createDate" label="创建时间" readonly="true"/> <field data="writeDate" label="更新时间" readonly="true"/> <field data="createUid" label="创建人id" invisible="false"/> <field data="writeUid" label="更新人id" invisible="false"/> </template> <template slot="search" autoFill="true" cols="4"/> </view> 图3-5-2-19 增加invisible=”false”的属性定义 Step2 重启应用看效果 图3-5-2-20 示例效果 第一个Form Step1 自定义PetTalent的编辑页…

    2024年5月23日
    1.7K00
  • 报表

    1. 业务场景 报表不局限于表格的样式,还能以各式各样的图表形式展现各项汇总数据 这有利于管理者更为直观地了解公司的经营情况,便于后续进行分析,提高对公司的管理水平。 2. 操作流程 1)进入数据可视化,进入报表tab,维护分组信息; 2)在二级分组名称后点击“+”【添加报表】,对报表进行编辑设计; 3)创建完成后可以【编辑】报表标题、备注; 4)需要通过【选择图表、创建图表】完善报表内容; 5)完善后可以点击【发布】报表,则报表此时可以被引用; 6)如果报表有更新,则可以点击【更新发布】,使业务系统引用对报表变为最新的报表信息; 7)如果报表数据不再可以公开使用,则需要通过【隐藏】功能将报表的引用权限收起,此时数据大屏、前端业务系统均不可再引用该报表,但不影响已被引用的报表; 8)隐藏后可以【取消隐藏】,报表恢复隐藏前的状态和功能,可以被引用。 3. 操作流程图解 3.1 创建分组 1)操作流程:创建分组 2)操作路径:数据可视化-报表-创建分组 3)点击搜索框后的「+」创建一级分组,输入一级分组名称后,点击一级分组后的「+」创建二级分组,输入二级分组名称后,此时分组创建完成,可以在二级分组下创建报表 3.2 编辑分组名称 1)操作流程:选择分组-编辑分组名称 2)操作路径:数据可视化-报表-编辑分组名称 3)鼠标移动至需要修改的分组上,点击出现的「编辑图标」,可以修改分组名称,修改后分组名称实时更新 3.3 删除分组 1)操作流程:选择分组-删除分组 2)操作路径:数据可视化-报表-删除分组 3)鼠标移动至需要删除的分组上,当分组下无报表时出现「删除图标」,可以点击图标后删除分组,删除一级分组时对应所有的二级分组也会被删除,删除后消失,只要分组下没有报表的分组才能直接删除成功 3.4 创建报表 1)操作流程:选择二级分组-创建报表 2)操作路径:数据可视化-报表-二级分组-创建报表 3)鼠标移动至需要创建报表的二级分组上,出现「+」,点击图标后=需要填写报表标题; a. 报表标题:最大支持20个字,支持汉字、数字、大小写字母、-;同个一级分组下不允许重复; 4)创建后可以选择报表需要展示的图表 3.5 删除报表 1)操作流程:选择报表-删除报表 2)操作路径:数据可视化-报表-二级分组-报表名称-删除图表 3)未发布或者已发布但没有被隐藏的报表,并且没被前端或者数据大屏引用,才展示报表菜单名称后的删除图标 4)删除报表后报表消失 3.6 选择图表 1)操作流程:选择报表-为报表选择图表 2)操作路径:数据可视化-报表-二级分组-报表名称-选择图表 3)选择单个未发布或者已发布但没有被隐藏的报表,点击【选择图表】,弹出“选择图表”弹窗,对该报表需要展示的图表进行选择 a. 需要选择图表的一级分组后才能选择图表; b. 可以多选图表,选择的图表只能是已选一级分组下的未隐藏的未被选择的图表;选择一个二级分组时,默认该二级分组下的图表会全部被选中,图表会按照选中的顺序展示在报表列表; 4)选择图表后,报表信息保持展示图表的最新效果;如果图表更新了,但是报表没有发布最新,则报表在前端展示的仍为最近发布的版本; 5)如果图表中存在超过一行的图内筛选项,则在报表处原始的图表尺寸只能查看一行图内筛选项,需要根据图表在报表处的等比拖动效果展示更多的图内筛选项 3.7 创建图表 1)操作流程:选择报表-创建图表 2)操作路径:数据可视化-报表-二级分组-创建图表 3)选择单个未发布或者已发布但没有被隐藏的报表,点击【创建图表】,弹出“创建图表”弹窗,需要填写图表标题、模型、方法; a. 图表标题:最大支持20个字,支持汉字、数字、大小写字母、-;同个一级分组下不允许重复; b. 模型:需要选择来源数据对应的模型; c. 方法:选择模型后需要选择方法,方法是用来提取模型数据的逻辑; 4)选择成功后进入图表设计页面,创建图表保存后返回,返回到当前报表页面,新创建的图表展示在报表的第一个位置 3.8 拖拽图表 1)操作流程:选择报表-拖拽图表 2) 操作路径:数据可视化-报表-二级分组-报表名称-拖拽图表 3)所有的报表均可拖拽图表,拖拽时需要选择图表,可以上下左右等比拖动,图表的内容也会根据拖动的比例进行缩放,展示全部的被遮挡图表内容 4)拖拽后实时生效,报表信息保持展示最新效果 3.9 移除图表 1)操作流程:选择报表-移除图表 2)操作路径:数据可视化-报表-二级分组-报表名称-移除图表 3)未发布或者已发布但没有被隐藏的报表,并且没被前端或者数据大屏引用,此时可以针对不需要的图表进行移除 4)选择移除后不展示在报表中,不影响原图表 5)报表移除图表后实时更新,更新发布后,前端可以展示最新的报表信息,如果未发布,则仅数据大屏可展示最新的报表信息,前端仍为最近发布的报表 3.10 设置报表筛选项 1)操作流程:选择报表-设置报表筛选项 2)操作路径:数据可视化-报表-二级分组-报表名称-更多-报表筛选项-添加 3)添加时选择筛选项字段的类型,关联每个图表对应的字段,一个图表只能关联一个 4)关联后可以按关联字段查询图表数据 3.11 发布 1)操作流程:选择报表-发布报表 2)操作路径:数据可视化-图表-二级分组-报表-发布 3)选择单个未发布且没有被隐藏的报表,点击【发布】按钮,报表发布后可以被前端引用,报表状态变为已发布,展示最近发布时间; 4)如果报表发布后有更新内容,会展示的更新类型:更新报表信息/更新图表内容/选择图表/移除图表 3.12 查看最近一次发布的版本 1)操作流程:选择报表-查看最近一次发布的版本 2)操作路径:数据可视化-报表-二级分组-报表名称-查看最近一次发布的版本 3)当报表发布后有更新,在最近发布时间左侧展示【查看】,在最近发布时间下展示更新的类型,点击查看可以查看最近发布的版 3.13 更新发布 1)操作流程:选择报表-更新发布报表 2)操作路径:数据可视化-报表-二级分组-报表名称-更新发布报表 3)选择单个已发布且没有被隐藏的报表,并且该报表在上次发布后有所更新,可以点击【更新发布】按钮,将最新的报表内容发布至业务系统,业务系统引用的报表为最新内容; 4)如果更新了内容,但未点击更新发布,则前端业务系统查看的报表仍为最近发布的报表 3.14 隐藏 1)操作流程:选择报表-隐藏报表 2)操作路径:数据可视化-报表-二级分组-报表名称-隐藏报表 3)报表默认不隐藏,可以切换是否隐藏=是 a. 未发布的报表,较隐藏前,不可以操作【发布】,可以【取消隐藏】; b. 已发布的图表,较隐藏前,只能操作【导出图片、导出excel、取消隐藏】; c. 隐藏后的报表不可以被引用,但不影响已经被引用的数据 3.15 取消隐藏 1) 操作流程:选择报表-取消隐藏报表 2) 操作路径:数据可视化-报表-二级分组-报表名称-取消隐藏报表 3) 隐藏后的报表可以取消隐藏,切换是否隐藏=否,取消隐藏后,报表恢复隐藏前的状态和功能,可以被引用 3.16 查看引用 1)流程:选择报表-查看被哪些页面引用 2)操作路径:数据可视化-报表-二级分组-报表-更多-查看引用 3)选择具体的报表,查看当前报表被引用的所有信息 3.17 不允许别人编辑 1)流程:选择报表-不允许别人编辑 2)操作路径:数据可视化-报表-二级分组-报表-更多-不允许别人编辑 3)选择自己创建的报表,对报表是否允许其他人编辑进行设置;如果设置为不允许,则其他人无法编辑报表 3.18 不允许别人引用 1)流程:选择图表-更多-不允许别人引用 2)操作路径:数据可视化-报表-二级分组-报表-更多-不允许别人引用 3)选择自己创建的报表,对报表是否允许他人引用进行设置;如果设置为不允许,则其他人无法引用到该报表 3.19 导出图片 1) 操作流程:选择报表-导出图片 2) 操作路径:数据可视化-报表-二级分组-报表名称-导出图片 3) 选择报表后,点击【导出图片】按钮可以将当前报表导出为图片 3.20 导出EXCEL 1)操作流程:选择报表-导出EXCEL 2)操作路径:数据可视化-报表-二级分组-报表名称-报表导出EXCEL 3)选择报表后,点击【导出EXCEL】按钮可以将当前报表导出为EXCEL

    2024年6月20日
    2.3K00
  • 3.5.7.8 自定义菜单栏

    在业务中,可能会遇到需要对菜单栏的交互或UI做全新设计的需求,这个时候可以自定义菜单栏组件支持。 首先继承平台的CustomMenuWidget 组件,将自己vue文件设置到component处 import { NavMenu, SPI, ViewWidget } from '@kunlun/dependencies'; import Component from './CustomMenu.vue'; @SPI.ClassFactory( ViewWidget.Token({ // 这里的widget跟平台的组件一样,这样才能覆盖平台的组件 widget: 'nav-menu' }) ) export class CustomMenuWidget extends NavMenu { public initialize(props) { super.initialize(props); this.setComponent(Component); return this; } } vue文件中继承平台的props,编写自定义页面代码 export const NavMenuProps = { /** * 当前模块 */ module: { type: Object as PropType<IModule | null> }, /** * 树结构的菜单 */ menus: { type: Array as PropType<IResolvedMenu[]>, default: () => [] }, /** * 菜单类型,现在支持垂直、水平、和内嵌模式三种 */ mode: { type: String as PropType<'vertical' | 'horizontal' | 'inline'>, default: 'inline' }, /** * 菜单栏是否折叠收起 */ collapsed: { type: Boolean, default: false }, /** * 当前展开的 SubMenu 菜单项 key 数组 */ openKeys: { type: Array as PropType<string[]>, default: () => [] }, /** * 当前选中的菜单项 key 数组 */ selectKeys: { type: Array as PropType<string[]>, default: () => [] }, /** * 菜单搜索下拉选中菜单项 */ selectMenuBySearch: { type: Function as PropType<(menuName: String) => void> }, /** * 选中菜单项 */ selectMenu: { type: Function as PropType<(menuName: String) => Promise<void>> }, /** * SubMenu 展开/关闭的回调 */ openChange: { type: Function as PropType<(openKeys: string[]) => void> }, /**…

    2024年5月23日
    2.1K00
  • 4.1.23 框架之信息传递

    在4.1.13【Action之校验】、3.4.1【构建第一个Function】等文章中,都用到PamirsSession.getMessageHub()来设置返回消息,基本上都是在传递后端逻辑判断的异常信息,而且在系统报错时也会通过它来返回错误信息,前端接收到错误信息则会以提示框的方式进行错误提示。其实后端除了可以返回错误信息以外,还可以返回调试、告警、成功、信息等级别的信息给前端。但是默认情况下前端只提示错误信息,可以通过前端的统一配置放开提示级别,有点类似后端的日志级别。 一、不同信息类型的举例 Step1 新建PetTypeAction 借用PetType模型的表格页做为信息传递的测试入口,为PetType模型新增一个ServerAction,在代码中对信息的所有类型进行模拟 package pro.shushi.pamirs.demo.core.action; import org.springframework.stereotype.Component; import pro.shushi.pamirs.demo.api.model.PetCatItem; import pro.shushi.pamirs.demo.api.model.PetType; import pro.shushi.pamirs.meta.annotation.Action; import pro.shushi.pamirs.meta.annotation.Model; import pro.shushi.pamirs.meta.api.dto.common.Message; import pro.shushi.pamirs.meta.api.session.PamirsSession; import pro.shushi.pamirs.meta.enmu.ActionContextTypeEnum; import pro.shushi.pamirs.meta.enmu.InformationLevelEnum; import pro.shushi.pamirs.meta.enmu.ViewTypeEnum; @Model.model(PetType.MODEL_MODEL) @Component public class PetTypeAction { @Action(displayName = "消息",bindingType = ViewTypeEnum.TABLE,contextType = ActionContextTypeEnum.CONTEXT_FREE) public PetType message(PetType data){ PamirsSession.getMessageHub().info("info1"); PamirsSession.getMessageHub().info("info2"); PamirsSession.getMessageHub().error("error1"); PamirsSession.getMessageHub().error("error2"); PamirsSession.getMessageHub().msg(new Message().msg("success1").setLevel(InformationLevelEnum.SUCCESS)); PamirsSession.getMessageHub().msg(new Message().msg("success2").setLevel(InformationLevelEnum.SUCCESS)); PamirsSession.getMessageHub().msg(new Message().msg("debug1").setLevel(InformationLevelEnum.DEBUG)); PamirsSession.getMessageHub().msg(new Message().msg("debug2").setLevel(InformationLevelEnum.DEBUG)); PamirsSession.getMessageHub().msg(new Message().msg("warn1").setLevel(InformationLevelEnum.WARN)); PamirsSession.getMessageHub().msg(new Message().msg("warn2").setLevel(InformationLevelEnum.WARN)); return data; } } 图4-1-23-1 为PetType模型新增一个ServerAction Step2 (前端)修改提示级别 在项目初始化时使用CLI构建初始化前端工程,在src/middleware有拦截器的默认实现,修改信息提示的默认级别为【ILevel.SUCCESS】 图4-1-23-2(前端)修改提示级别 const DEFAULT_MESSAGE_LEVEL = ILevel.SUCCESS; 图4-1-23-3(前端)修改提示级别 Step3 重启系统看效果 从页面效果中看到已经不在是只提示错误信息。从协议端看错误级别的信息是在errors下,其他级别的信息是在extensions下。 图4-1-23-4 示例效果 图4-1-23-5 系统提示的返回结果 二、MessageHub的其他说明 是实现上看MessageHub是基于GQL协议,前后端都有配套实现。同时前端还提供了订阅MessageHub的信息功能,以满足前端更多交互要求,前端MessageHub提供的订阅能力使用教程详见4.2.2前端高级特性之【框架之MessageHub】一文。

    2024年5月23日
    1.8K00

Leave a Reply

Please Login to Comment