左树右表,支撑不同场景的树表结构

左树右表俩种情况 假设存在 A模型 B模型

1: 左树为A模型,右表为B模型

举例 A模型为类目 B模型为类目属性
模型代码实例:

@Model.model(AriesPlatformCategory.MODEL_MODEL)
@Model(displayName = "平台后台类目", labelFields = "name")
@Model.Advanced(type = ModelTypeEnum.PROXY)
public class AriesPlatformCategory extends AriesCategory {
    public static final String MODEL_MODEL = "aries.item.AriesPlatformCategory";

    @Field.many2one
    @Field.Relation(relationFields = {"parentCateCode"}, referenceFields = {"code"},store = true)
    @Field(displayName = "平台父类目")
    private AriesPlatformCategory platformCategory;

    @Field.one2many
    @Field(displayName = "类目属性")
    @Field.Relation(relationFields = "code", referenceFields = "categoryCode", store = true)
    private List<AriesPlatformCategoryAttr> platformCategoryAttrs;

}
@Model.model(AriesPlatformCategoryAttr.MODEL_MODEL)
@Model(displayName = "Aries_平台类目属性", labelFields = "name")
@Model.Advanced(type = ModelTypeEnum.PROXY)
public class AriesPlatformCategoryAttr extends CategoryAttr {
    public static final String MODEL_MODEL = "aries.item.AriesPlatformCategoryAttr";

    @Field.many2one
    @Field(displayName = "平台后台类目")
    @Field.Relation(relationFields = "categoryCode", referenceFields = "code", store = true)
    private AriesPlatformCategory platformCategory;

}

在设计器设计左树右表之前,需要在模型 中配置好关联关系 。如下部分代码 配置好类目与父类目的关联关系。

    @Field.many2one
    @Field.Relation(relationFields = {"parentCateCode"}, referenceFields = {"code"},store = true)
    @Field(displayName = "平台父类目")
    private AriesPlatformCategory platformCategory;

配置好 类目与类目属性的关联关系。一个类目可以有多个类目属性,一对多one2many

    @Field.one2many
    @Field(displayName = "类目属性")
    @Field.Relation(relationFields = "code", referenceFields = "categoryCode", store = true)
    private List<AriesPlatformCategoryAttr> platformCategoryAttrs;

在类目属性模型中,配置好属性与类目的关联关系,一个类目属性只属于一个类目,一个类目可以有多个类目属性。类目属性对类目多对一many2one

    @Field.many2one
    @Field(displayName = "平台后台类目")
    @Field.Relation(relationFields = "categoryCode", referenceFields = "code", store = true)
    private AriesPlatformCategory platformCategory;

设计器实例:
1.需要选择 平台类目属性 做为主模型创建树表页面
image.png
2.构建关联关系 选择平台后台类目 第一级的筛选条件 上级编码为空 表格关联关系字段 选择 平台类目属性。image.png

3.表格拖拽好需要的属性字段
image.png

2: 左树为A模型,右表也为A模型

举例 左A模型 组织结构管理 右A模型 组织结构管理
模型代码实例:

@Model.model(BasicOrg.MODEL_MODEL)
@Model(displayName = "组织结构管理", summary = "组织结构管理", labelFields = {"orgName"})
@Model.Code(sequence = "ORDERLY_SEQ", prefix = "Org", size = 5, initial = 0)
public class BasicOrg {
    public static final String MODEL_MODEL = "basic.BasicOrg";

    @Field.String
    @Field(displayName = "名称")
    private String orgName;

    @Field.Enum
    @Field(displayName = "组织类型", summary = "组织类型")
    private OrgTypeEnum deptType;

    @Field.Enum
    @Field(displayName = "工厂类型", summary = "当组织类型为工厂时可见")
    private FactoryTypeEnum factoryType;

    @Field.Enum
    @Field(displayName = "仓库类型", summary = "当组织类型为仓库时可见")
    private WarehouseTypeEnum warehouseType;

    @Field.String
    @Field(displayName = "负责人", summary = "负责人")
    private String principal;

    @Field.String
    @Field(displayName = "联系号码", summary = "联系号码")
    private String phone;

    @Field(displayName = "上级组织")
    @Field.many2one
    @Field.Relation(relationFields = {"parentCode"}, referenceFields = {"code"})
    private BasicOrg parent;

    @Field.Text
    @Field(displayName = "备注")
    private String remark;

    @Field.Enum
    @Field(displayName = "数据状态", required = true, defaultValue = "ENABLED")
    private DataStatusEnum dataStatus;

    @Field.String
    @Field(displayName = "上级组织编码")
    private String parentCode;

    @Field.String(size = 256)
    @Field(displayName = "树编码", required = true)
    private String treeCode;

    @Field.one2many
    @Field.Relation(relationFields = "id", referenceFields = "orgId")
    @Field(displayName = "基础-所属组织员工")
    private List<BasicEmployee> basicEmployees;
}

配置好自我关联

    @Field(displayName = "上级组织")
    @Field.many2one
    @Field.Relation(relationFields = {"parentCode"}, referenceFields = {"code"})
    private BasicOrg parent;

设计器操作:

  1. 选择好模型 组织结构管理 创建页面 试图类型选择 表格 布局选择树表

image.png

2.设置联动关系 模型选择 组织结构管理 模型 ; 第一级的筛选条件 上级编码为空 表格关联关系字段 选择 组织结构管理-上级组织 字段名parent
image.png

  1. 表格拖拽好需要的属性字段
    image.png

Oinone社区 作者:shao原创文章,如若转载,请注明出处:https://doc.oinone.top/dai-ma-shi-jian/5576.html

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

Like (0)
shao's avatarshao数式管理员
Previous 2024年2月20日 pm6:40
Next 2024年2月20日 pm6:49

相关推荐

  • 蓝绿发布

    背景 应用程序升级面临最大挑战是新旧业务切换,将软件从测试的最后阶段带到生产环境,同时要保证系统不间断提供服务。长期以来,业务升级渐渐形成了几个发布策略:蓝绿发布、灰度发布和滚动发布,目的是尽可能避免因发布导致的流量丢失或服务不可用问题。 本文主要介绍Oinone平台如何实现蓝绿发布。蓝绿发布:项目逻辑上分为AB组,在项目系统时,首先把A组从负载均衡中摘除,进行新版本的部署。B组仍然继续提供服务。 需求 统一权限统一登录信息不同业务数据 实现方案 首先需要两个环境并统一流量入口,这里使用Nginx配置负载均衡:nginx如何配置后端服务的负载均衡 统一权限配置 在蓝绿环境添加不同的redis前缀 spring: redis: prefix: xxx 在蓝绿环境的修改AuthRedisTemplate Bean,利用setKeySerializer去掉redis前缀。 可以使用@Profile注解指定仅线上环境生效。 @Configuration // @Profile("prod") public class AuthRedisTemplateConfig { @Bean(AuthConstants.REDIS_TEMPLATE_BEAN_NAME) public AuthRedisTemplate<?> authRedisTemplate( RedisConnectionFactory redisConnectionFactory, PamirsStringRedisSerializer pamirsStringRedisSerializer ) { AuthRedisTemplate<Object> template = new AuthRedisTemplate<Object>(redisConnectionFactory, pamirsStringRedisSerializer) { @Override public void afterPropertiesSet() { // 重写 key serializer,去掉前缀隔离 this.setKeySerializer(new PamirsStringRedisSerializer(null)); super.afterPropertiesSet(); } }; return template; } } 统一登录 在蓝绿环境自定义实现pro.shushi.pamirs.user.api.spi.UserCacheApi SPI,去除redis前缀 package pro.shushi.pamirs.top.core.spi; import com.alibaba.fastjson.JSON; import com.google.common.collect.Sets; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.annotation.Order; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.core.RedisOperations; import org.springframework.data.redis.core.SessionCallback; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import pro.shushi.pamirs.auth.api.cache.redis.AuthRedisTemplate; import pro.shushi.pamirs.meta.api.dto.model.PamirsUserDTO; import pro.shushi.pamirs.meta.api.dto.protocol.PamirsRequestVariables; import pro.shushi.pamirs.meta.api.session.PamirsSession; import pro.shushi.pamirs.meta.common.spi.SPI; import pro.shushi.pamirs.user.api.cache.UserCache; import pro.shushi.pamirs.user.api.configure.UserConfigure; import pro.shushi.pamirs.user.api.spi.UserCacheApi; import java.net.URI; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; @Order(1) @Component @SPI.Service("MyUserCache") public class MyUserCache implements UserCacheApi { private static final Set<String> DEFAULT_FILTER_URIS = Collections.singleton("/pamirs/message"); @Autowired private AuthRedisTemplate redisTemplate; @Override public PamirsUserDTO getSessionUser(String key) { String objectValue = getUserCacheAndRenewed(key); if (StringUtils.isNotBlank(objectValue)) { return JSON.parseObject(objectValue, PamirsUserDTO.class); } return null; } @Override public void setSessionUser(String key, PamirsUserDTO user, Integer expire) { user.setPassword(null); expire = getExpire(expire); redisTemplate.opsForValue().set(key.replace("'", " "), JSON.toJSONString(user), expire, TimeUnit.SECONDS); // 当前的实现是一个user可以在多个客户端登录,需要在管理端修改user权限后强制清除掉该用户已登录的session,所以需要记录uid对应所有已登录的sessionId String userRelSessionKey = UserCache.createUserRelSessionKey(user.getUserId()); redisTemplate.opsForSet().add(userRelSessionKey, key); redisTemplate.expire(userRelSessionKey,…

    2025年9月18日
    82000
  • 字段类型之关系描述的特殊场景(常量关联)

    场景概述 【字段类型之关系与引用】一文中已经描述了各种关系字段的常规写法,还有一些特殊场景如:关系映射中存在常量,或者M2M中间表是大于两个字段构成。 场景描述 1、PetTalent模型增加talentType字段2、PetItem与PetTalent的多对多关系增加talentType(达人类型),3、PetItemRelPetTalent中间表维护petItemId、petTalentId以及talentType,PetDogItem和PetCatItem分别重写petTalents字段,关系中增加常量描述。示意图如下: 实际操作步骤 Step1 新增 TalentTypeEnum package pro.shushi.pamirs.demo.api.enumeration; import pro.shushi.pamirs.meta.annotation.Dict; import pro.shushi.pamirs.meta.common.enmu.BaseEnum; @Dict(dictionary = TalentTypeEnum.DICTIONARY,displayName = "达人类型") public class TalentTypeEnum extends BaseEnum<TalentTypeEnum,Integer> { public static final String DICTIONARY ="demo.TalentTypeEnum"; public final static TalentTypeEnum DOG =create("DOG",1,"狗达人","狗达人"); public final static TalentTypeEnum CAT =create("CAT",2,"猫达人","猫达人"); } Step2 PetTalent模型增加talentType字段 package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.demo.api.enumeration.TalentTypeEnum; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; @Model.model(PetTalent.MODEL_MODEL) @Model(displayName = "宠物达人",summary="宠物达人",labelFields ={"name"}) public class PetTalent extends AbstractDemoIdModel{ public static final String MODEL_MODEL="demo.PetTalent"; @Field(displayName = "达人") private String name; @Field(displayName = "达人类型") private TalentTypeEnum talentType; } Step3 修改PetItem的petTalents字段,在关系描述中增加talentType(达人类型) @Field.many2many(relationFields = {"petItemId"},referenceFields = {"petTalentId","talentType"},through = PetItemRelPetTalent.MODEL_MODEL ) @Field.Relation(relationFields = {"id"}, referenceFields = {"id","talentType"}) @Field(displayName = "推荐达人",summary = "推荐该商品的达人们") private List<PetTalent> petTalents; Step4 PetDogItem增加petTalents字段,重写父类PetItem的关系描述 talentType配置为常量,填入枚举的值 增加domain描述用户页面选择的时候自动过滤出特定类型的达人,RSQL用枚举的name @Field(displayName = "推荐达人") @Field.many2many( through = "PetItemRelPetTalent", relationFields = {"petItemId"}, referenceFields = {"petTalentId","talentType"} ) @Field.Relation(relationFields = {"id"}, referenceFields = {"id", "#1#"}, domain = " talentType == DOG") private List<PetTalent> petTalents; Step5 PetCatItem增加petTalents字段,重写父类PetItem的关系描述 talentType配置为常量,填入枚举的值 增加domain描述用户页面选择的时候自动过滤出特定类型的达人,RSQL用枚举的name @Field(displayName = "推荐达人") @Field.many2many( through = "PetItemRelPetTalent", relationFields = {"petItemId"}, referenceFields = {"petTalentId","talentType"} ) @Field.Relation(relationFields = {"id"}, referenceFields = {"id", "#2#"}, domain = " talentType == CAT") private List<PetTalent> petTalents; Step6 PetCatItem增加petTalents字段,many2one关系示例 talentType配置为常量,填入枚举的值 增加domain描述用户页面选择的时候自动过滤出特定类型的达人,RSQL用枚举的name @Model.model(PetPet.MODEL_MODEL) @Model(displayName…

    2024年5月25日
    2.0K00
  • 部分模型不动态修改表结构(由单独DDL处理)

    需求描述 实际项目中, 存在部分模型不动态修改表结构,由单独DDL脚本处理,常见的场景有: 已存在库和表中使用Oinone进行功能开发,此时对于已经存在的表对应的模型不允许改表结构 其他情况不希望动态改变表结构的情况 实现步骤 新建NODDL的基础模型 模型公共字段 公共字段说明:使用Oinone进行开发时,业务模型需继承基础IdModel(或者由IdModel衍生出的子类),这些基础模型有createDate(创建时间)、writeDate(更新时间)、createUid(创建人ID)和writeUid(更新人ID)等公共字段;实际表中公共字段可能与Oinone有所不同。 实现方式 方式1:公共属性字段用平台提供的createDate、writeDate、createUid和writeUid,通过指定column与表中的实际字段对应.【推荐】该方式,公共字段的处理可以继续使用平台的默认赋值处理方式; 方式2:继承平台的时候,把公共字段排除掉(配置unInheritedFields),然后自行加通用字段:排除字段:@Model.Advanced(type= ModelTypeEnum.ABSTRACT, ordering = "createAt DESC, id DESC", unInheritedFields = {"createUid","writeUid","createDate","writeDate"})【不推荐】该方式,公共字段的赋值逻辑需要自行处理,略显复杂; 实现方式举例 下面的示例以方式1举例;假设表的基础字段分别是:createAt、updateAt、createId和updateId 与平台的不同. 不自动DDL的抽象模型示例 import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; import pro.shushi.pamirs.meta.base.IdModel; import pro.shushi.pamirs.meta.enmu.FieldStrategyEnum; import pro.shushi.pamirs.meta.enmu.ModelTypeEnum; import java.util.Date; /** * 假设表的基础字段分别是:createAt、updateAt、createId和updateId 与平台的不同 */ @Model.model(BaseNoDdlModel.MODEL_MODEL) @Model(displayName = "不自动DDL的抽象模型") @Model.Advanced(type= ModelTypeEnum.ABSTRACT, ordering = "createAt DESC, id DESC") public abstract class BaseNoDdlModel extends IdModel { public static final String MODEL_MODEL = "hr.std.BaseNoDdlModel"; // 如果原表的主键的列名不是ID的情况,这里可以定义column指定ID属性对应的列名 /** @Field.PrimaryKey @Field(displayName = "主键ID") @Field.Advanced(column = "XLH") private Long id; **/ // 下面这几个字段按实际项目中的情况来增加,包括字段名 @Field.Advanced(columnDefinition = "DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP", column = "createAt", insertStrategy = FieldStrategyEnum.NEVER, updateStrategy = FieldStrategyEnum.NEVER, batchStrategy = FieldStrategyEnum.NEVER) @Field(displayName = "创建时间", priority = 200) private Date createDate; @Field.Advanced(columnDefinition = "DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP", column = "updateAt", batchStrategy = FieldStrategyEnum.NEVER) @Field(displayName = "更新时间", priority = 210) private Date writeDate; @Field.Advanced(column = "createId") @Field(displayName = "创建人ID", priority = 220, invisible = true) private Long createUid; @Field.Advanced(column = "updateId") @Field(displayName = "更新人ID", priority = 230, invisible = true) private Long writeUid; } 不需动态DDL的业务模型,业务模型继承BaseNoDdlModel。 其他业务模型如果有相同的需求类似的做法 /** * 测试合同表 */ @Model.model(InspectionInfo.MODEL_MODEL) @Model(displayName = "合同", labelFields…

    2025年2月22日
    1.2K00
  • 如何扩展行为权限

    注意:5.2.8 以上版本适用 需求 我们的权限控制需要在页面上有交互才可以在管理中心控制该动作权限,在没有页面交互的动作是不能进行授权操作的。所以本文章将介绍如何将页面上没有交互的动作接入到系统权限中管理。 一、扩展系统权限的菜单页面 实现 步骤: 创建授权节点实现权限节点扩展接口:pro.shushi.pamirs.auth.api.extend.load.PermissionNodeLoadExtendApi#buildRootPermissions @Component @Order(88) public class MyTestNodeLoadExtend implements PermissionNodeLoadExtendApi { public static final String MODEL = AuthTest.MODEL_MODEL; public static final String MODULE = TopModule.MODULE_MODULE; public static final String FUN = "dataStatus"; @Override public List<PermissionNode> buildRootPermissions(PermissionLoadContext loadContext, List<PermissionNode> nodes) { //创建授权根节点 PermissionNode root = createMyNode(); List<PermissionNode> newNodes = new ArrayList<>(); //从缓存中读取需要授权的Action Action cacheAction = PamirsSession.getContext().getExtendCache(ActionCacheApi.class).get(MODEL, FUN); if (cacheAction != null) { //将该Action放入权限树 //权限鉴权的path路径是根据【cacheAction.getModel() + cacheAction.getName()】拼接的。和MODULE没有关系,这里MODULE可以自定义。 AuthNodeHelper.addNode(newNodes, root, AuthNodeHelper.createActionNode(MODULE, cacheAction, root)); } nodes.add(0, root); return newNodes; } private PermissionNode createMyNode() { return AuthNodeHelper.createNodeWithTranslate("MyNode", "自定义节点"); } } 在管理中心中我们可以看到代码里创建的授权节点。 给角色分配该动作的权限,调用我们配置的AuthTest模型的dataStatus动作看效果。 二、扩展菜单下的动作权限 实现 步骤: 创建viewAction用于作为权限菜单"permissionExtension"是自定义的viewAction的name,用于下面拼path路径鉴权。因为这里只需要在系统权限那边利用这个viewAction创建出授权节点。所以”权限扩展form“可以随意定义名字,系统会拿默认视图。 @Model.model(AuthTest.MODEL_MODEL) @Component @UxRouteButton( action = @UxAction(name = "permissionExtension", displayName = "权限扩展", label = "权限扩展", contextType = ActionContextTypeEnum.CONTEXT_FREE), value = @UxRoute(model = AuthTest.MODEL_MODEL, viewName = "权限扩展form", openType = ActionTargetEnum.ROUTER)) public class AuthTestAction { @Action(displayName = "启用", contextType = ActionContextTypeEnum.SINGLE) public AuthTest dataStatus(AuthTest data) { data.setOrgName("给个值"); return data; } } 创建授权节点实现权限节点扩展接口:pro.shushi.pamirs.auth.api.extend.load.PermissionNodeLoadExtendApi#buildRootPermissions @Component @Order(88) public class MyTestNodeLoadExtend implements PermissionNodeLoadExtendApi { //创建授权根节点 @Override public List<PermissionNode> buildRootPermissions(PermissionLoadContext loadContext, List<PermissionNode> nodes) { PermissionNode root = AuthNodeHelper.createNodeWithTranslate("CustomNode", "自定义节点"); List<PermissionNode> newNodes = new ArrayList<>(); newNodes.add(root); ViewAction viewAction = new ViewAction().setModel(AuthTest.MODEL_MODEL).setName("permissionExtension").queryOne(); //将该Action放入权限树 //权限鉴权的path路径是根据【viewAction.getModel(),…

    2024年11月1日
    1.2K00
  • 如何给角色增加菜单权限

    对接第三方的权限时,第三方传过来菜单项,需要拿着这些菜单在平台这边进行授权,可以使用代码的方式给指定菜单创建权限代码示例: public class demo { @Autowired private PermissionNodeLoader permissionNodeLoader; @Autowired private AuthRbacRolePermissionServiceImpl authRbacRolePermissionService; public void roleAuthorization() { ArrayList<Menu> menus = new ArrayList<>(); menus.add(new Menu().queryOneByWrapper(Pops.<Menu>lambdaQuery() .from(Menu.MODEL_MODEL) .eq(Menu::getName, "uiMenu90dd10ae7cc4459bacd2845754b658a8") .eq(Menu::getModule, TopModule.MODULE_MODULE))); menus.add(new Menu().queryOneByWrapper(Pops.<Menu>lambdaQuery() .from(Menu.MODEL_MODEL) .eq(Menu::getName, "TopMenus_shoppMenu_Shop3Menu_ShopSayHello52eMenu") .eq(Menu::getModule, TopModule.MODULE_MODULE))); //加载指定角色的全部资源权限项 ResourcePermissionNodeLoader loader = permissionNodeLoader.getManagementLoader(); List<PermissionNode> nodes = loader.buildRootPermissions(); List<AuthRbacResourcePermissionItem> authRbacRolePermissionProxies = new ArrayList<>(); //给指定角色创建权限,如果需要多个角色,可以for循环依次执行authRbacRolePermissionService.update(authRbacRolePermissionProxy) AuthRole authRole = new AuthRole().queryOneByWrapper(Pops.<AuthRole>lambdaQuery() .from(AuthRole.MODEL_MODEL) .eq(AuthRole::getCode, "R003") .eq(AuthRole::getName, "R003")); AuthRbacRolePermissionProxy authRbacRolePermissionProxy = new AuthRbacRolePermissionProxy(); AuthRole.transfer(authRole, authRbacRolePermissionProxy); for (PermissionNode node : nodes) { traverse(node, authRbacRolePermissionProxies, menus); } authRbacRolePermissionProxy.setResourcePermissions(authRbacRolePermissionProxies); authRbacRolePermissionService.update(authRbacRolePermissionProxy); } private void traverse(PermissionNode node, List<AuthRbacResourcePermissionItem> authRbacRolePermissionProxies, ArrayList<Menu> menus) { if (node == null) { return; } //按照指定菜单进行过滤,如果不是指定菜单,则设置菜单项不可访问,如果是指定菜单,则设置可访问 Set<Long> menuIds = new HashSet<>(); for (Menu menu : menus) { menuIds.add(menu.getId()); } if (node instanceof MenuPermissionNode) { AuthRbacResourcePermissionItem item = new AuthRbacResourcePermissionItem(); if (menuIds.contains(Long.parseLong(node.getId()))) { item.setCanAccess(Boolean.TRUE); } else { item.setCanAccess(Boolean.FALSE); } item.setCanManagement(node.getCanManagement()); item.setPath(node.getPath()); item.setSubtype(node.getNodeType()); item.setType(AuthEnumerationHelper.getResourceType(node.getNodeType())); item.setDisplayName(node.getDisplayValue()); item.setResourceId(node.getResourceId()); authRbacRolePermissionProxies.add(item); } List<PermissionNode> childNodes = node.getNodes(); if (CollectionUtils.isNotEmpty(childNodes)) { for (PermissionNode child : childNodes) { traverse(child, authRbacRolePermissionProxies, menus); } } } } 执行看效果

    2024年11月14日
    1.1K00

Leave a Reply

Please Login to Comment