Oinone社区 作者:史, 昂原创文章,如若转载,请注明出处:https://doc.oinone.top/oio4/9203.html
访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验
Oinone社区 作者:史, 昂原创文章,如若转载,请注明出处:https://doc.oinone.top/oio4/9203.html
访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验
在日常需求中也经常需要调整Layout的情况,如出现树表结构(左树右表、级联),我们则需要通过修改View的Layout来完成。今天就带您学习下Layout的自定义 第一个表格Layout 如果我们想去除表格视图区域的搜索区、ActionBar(操作区),就可以为视图自定义一个简单的Layout就行啦 Step1 新建一个表格的Layout 在views/demo_core/layout路径下增加一个名为sample_table_layout.xml文件,name设置为sampleTableLayout <view type="TABLE" name="sampleTableLayout"> <!– <view type="SEARCH">–> <!– <pack widget="fieldset">–> <!– <element widget="search" slot="search" slotSupport="field"/>–> <!– </pack>–> <!– </view>–> <pack widget="fieldset" style="height: 100%" wrapperStyle="height: 100%"> <pack widget="row" style="height: 100%; flex-direction: column"> <!– <pack widget="col" mode="full" style="flex: 0 0 auto">–> <!– <element widget="actionBar" slot="actionBar" slotSupport="action">–> <!– <xslot name="actions" slotSupport="action" />–> <!– </element>–> <!– </pack>–> <pack widget="col" mode="full" style="min-height: 234px"> <element widget="table" slot="table" slotSupport="field"> <xslot name="fields" slotSupport="field" /> <element widget="rowAction" slot="rowActions" slotSupport="action"/> </element> </pack> </pack> </pack> </view> 图3-5-2-27 新建一个表格的Layout Step2 修改宠物达人自定义表格Template 在view标签上增加layout属性值为"sampleTableLayout" <view name="tableView" model="demo.PetTalent" cols="1" type="TABLE" enableSequence="true" layout="sampleTableLayout"> ……省略其他 </view> 图3-5-2-28 修改宠物达人自定义表格Template Step3 重启看效果 图3-5-2-29 示例效果 Step4 修改宠物达人自定义表格Template 去除在view标签上的layout属性配置,让其回复正常 第一个树表Layout 本节以“给商品管理页面以树表的方式增加商品类目过滤”为例 Step1 增加商品类目模型 增加PetItemCategory模型继承CodeModel,新增两个字段定义name和parent,其中parent字段M2O关联自身模型,非必填字段(如字段值为空即为一级类目): 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.common.CodeModel; @Model.model(PetItemCategory.MODEL_MODEL) @Model(displayName = "宠物商品类目",summary="宠物商品类目",labelFields={"name"}) public class PetItemCategory extends CodeModel { public static final String MODEL_MODEL="demo.PetItemCategory"; @Field(displayName = "类目名称",required = true) private String name; @Field(displayName = "父类目") @Field.many2one private PetItemCategory parent; } 图3-5-2-30 增加商品类目模型 Step2 修改自定义商品模型 为商品模型PetItem增加一个category字段m2o关联PetItemCategory @Field(displayName = "类目") @Field.many2one private PetItemCategory category; 图3-5-2-31 修改宠物商品模型 Step3 新增名为treeTableLayout的Layout 在views/demo_core/layout路径下增加一个名为tree_table_layout.xml文件,name设置为treeTableLayout 图3-5-2-32 新增名为treeTableLayout的Layout <view type="TABLE" name="treeTableLayout"> <view type="SEARCH"> <pack widget="fieldset"> <element widget="search" slot="search" slotSupport="field"/> </pack> </view> <pack…
在日常开发中,我们经常需要把一些通用的信息放入程序执行的上下文中,以便业务开发人员快速获取。那么oinone的PamirsSession就是来解决此类问题的。 一、PamirsSession介绍 在oinone的体系中PamirsSession是执行上下文的承载,您能从中获取业务基础信息、指令信息、元数据信息、环境信息、请求参数,以及前后端MessageHub等。在前面的学习过程中我们已经多次接触到了如何使用PamirsSession: 在4.1.19【框架之网关协议-后端占位符】一文中,使用PamirsSession.getUserId()来获取当前登入用户Id,诸如此类的业务基础信息; 在4.1.18【框架之网关协议-variables变量】一文中,使用PamirsSession.getRequestVariables()得到PamirsRequestVariables对象,进而获取前端请求的相关信息; 在4.1.5【模型之持久层配置】一文中,使用PamirsSession.directive(),来操作元位指令系统,进而影响执行策略; 在4.1.13【Action之校验】、3.4.1【构建第一个Function】等文章中,都用到PamirsSession.getMessageHub()来设置返回消息。 二、构建模块自身Session(举例) 不同的应用场景对PamirsSession的诉求是不一样的,这个时候我们就可以去扩展PamirsSession来达到我们的目的 构建模块自身Session的步骤 构建自身特有的数据结构XSessionData 对XSessionData进行线程级缓存封装 利用Hook机制初始化XSessionData并放到ThreadLocal中 定义自身XSessionApi 实现XSessionApi接口、SessionClearApi。在请求结束时会调用SessionClearApi的clear方法 定义XSession继承PamirsSession 扩展PamirsSession的经典案例设计图 图4-1-20-1 扩展PamirsSession的经典案例设计图 构建Demo应用自身Session 下面的例子为给Session放入当前登陆用户 Step1 新建DemoSessionData类 构建自身特有的数据结构DemoSessionData,增加一个模型为PamirsUser的字段user,DemoSessionData用Data注解,注意要用Oinone平台提供的@Data package pro.shushi.pamirs.demo.core.session; import pro.shushi.pamirs.meta.annotation.fun.Data; import pro.shushi.pamirs.user.api.model.PamirsUser; @Data public class DemoSessionData { private PamirsUser user; } 图4-1-20-2 新建DemoSessionData类 Step2 新建DemoSessionCache 对DemoSessionData进行线程级缓存封装 package pro.shushi.pamirs.demo.core.session; import pro.shushi.pamirs.meta.api.CommonApiFactory; import pro.shushi.pamirs.meta.api.session.PamirsSession; import pro.shushi.pamirs.user.api.model.PamirsUser; import pro.shushi.pamirs.user.api.service.UserService; public class DemoSessionCache { private static final ThreadLocal<DemoSessionData> BIZ_DATA_THREAD_LOCAL = new ThreadLocal<>(); public static PamirsUser getUser(){ return BIZ_DATA_THREAD_LOCAL.get()==null?null:BIZ_DATA_THREAD_LOCAL.get().getUser(); } public static void init(){ if(getUser()!=null){ return ; } Long uid = PamirsSession.getUserId(); if(uid == null){ return; } PamirsUser user = CommonApiFactory.getApi(UserService.class).queryById(uid); if(user!=null){ DemoSessionData demoSessionData = new DemoSessionData(); demoSessionData.setUser(user); BIZ_DATA_THREAD_LOCAL.set(demoSessionData); } } public static void clear(){ BIZ_DATA_THREAD_LOCAL.remove(); } } 图4-1-20-3 对DemoSessionData进行线程级缓存封装 Step3 新建DemoSessionHook 利用Hook机制,调用DemoSessionCache的init方法初始化DemoSessionData并放到ThreadLocal中。 @Hook(module= DemoModule.MODULE_MODULE), 规定只有增对DemoModule模块访问的请求该拦截器才会生效,不然其他模块的请求都会被DemoSessionHook拦截。 package pro.shushi.pamirs.demo.core.hook; import org.springframework.stereotype.Component; import pro.shushi.pamirs.demo.api.DemoModule; import pro.shushi.pamirs.demo.core.session.DemoSessionCache; import pro.shushi.pamirs.meta.annotation.Hook; import pro.shushi.pamirs.meta.api.core.faas.HookBefore; import pro.shushi.pamirs.meta.api.dto.fun.Function; @Component public class DemoSessionHook implements HookBefore { @Override @Hook(priority = 1,module = DemoModule.MODULE_MODULE) public Object run(Function function, Object… args) { DemoSessionCache.init(); return function; } } 图4-1-20-4 新建DemoSessionHook Step4 新建DemoSessionApi package pro.shushi.pamirs.demo.core.session; import pro.shushi.pamirs.meta.api.CommonApi; import pro.shushi.pamirs.user.api.model.PamirsUser; public interface DemoSessionApi extends CommonApi { PamirsUser getUser(); } 图4-1-20-5 新建DemoSessionApi Step5…
1. 创建用户 进入用户中心应用,在用户列表中点击创建。 填写表单中的必填信息。 若未设置昵称,则右上角头像右侧展示名称。若设置了昵称,则右上角头像右侧展示昵称。 是否激活账号选择是,选择否时用户登录会显示“未找到首页”。 角色分组中,选择创建的用户的角色,默认选择了超级管理员(包含所有权限)。 点击确定,用户创建完成。 用户登录时可用登录账号/邮箱/手机号登录。 2. 用户相关操作 表格页中包含常规的搜索、批量删除功能。 冻结:当将“是否有效”状态为“是”时展示,将用户“是否有效”修改为“否”。 解冻:当将“是否有效”状态为“否”时展示,将用户“是否有效”修改为“是”。 修改:进入用户信息修改页面,“编码、登录账号、注册时间”只读。 重置密码:点击后在弹窗“账号确认”中输入账号,点击重置密码后,展示新密码。
函数的触发和定时在很多场景中会用到,也是一个oinone的基础能力。比如我们的流程产品中在定义流程触发时就会让用户选择模型触发还是时间触发,就是用到了函数的触发与定时能力。 整体链路示意图(如下图4-1-10-1 所示),本文只讲trigger里的两类任务,一个是触发任务,一个是定时任务,异步任务放在4.1.11【函数之异步执行】一文中单独去介绍。 图4-1-10-1 整体链路示意图 一、触发任务TriggerTaskAction(举例) 触发任务的创建,使用pamirs-middleware-canal监听mysql的binlog事件,通过rocketmq发送变更数据消息,收到MQ消息后,创建TriggerAutoTask。 触发任务的执行,使用TBSchedule拉取触发任务后,执行相应函数。 注意:pamirs-middleware-canal监听的数据库表必须包含触发模型的数据库表。 Step1 下载canal中间件 下载pamirs-middleware-canal-deployer-3.0.1.zip,去.txt后缀为pamirs-middleware-canal-deployer-3.0.1.zip,解压文件如下: 图4-1-10-2 下载canal中间件 Step2 引入依赖pamirs-core-trigger模块 pamirs-demo-api增加pamirs-trigger-api <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-trigger-api</artifactId> </dependency> 图4-1-10-3 pamirs-trigger-api依赖包 DemoModule在模块依赖定义中增加@Module(dependencies={TriggerModule.MODULE_MODULE}) @Component @Module( name = DemoModule.MODULE_NAME, displayName = "oinoneDemo工程", version = "1.0.0", dependencies = {ModuleConstants.MODULE_BASE, CommonModule.MODULE_MODULE, UserModule.MODULE_MODULE, TriggerModule.MODULE_MODULE} ) @Module.module(DemoModule.MODULE_MODULE) @Module.Advanced(selfBuilt = true, application = true) @UxHomepage(PetShopProxy.MODEL_MODEL) public class DemoModule implements PamirsModule { ……其他代码 } 图4-1-10-4 模块依赖中增加Trigger模块 pamirs-demo-boot 增加pamirs-trigger-core和pamirs-trigger-bridge-tbschedule的依赖 <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-trigger-core</artifactId> </dependency> <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-trigger-bridge-tbschedule</artifactId> </dependency> 图4-1-10-5 增加pamirs-trigger-core和pamirs-trigger-bridge-tbschedule的依赖 修改pamirs-demo-boot的applcation-dev.yml 修改pamris.event.enabled和pamris.event.schedule.enabled为true pamirs_boot_modules增加启动模块:trigger pamirs: event: enabled: true schedule: enabled: true rocket-mq: namesrv-addr: 127.0.0.1:9876 boot: init: true sync: true modules: – base – common – sequence – resource – user – auth – message – international – business – trigger – demo_core 图4-1-10-6 启动模块中增加trigger模块 Step3 启动canal中间件 canal的库表需要手工建 create schema canal_tsdb collate utf8mb4_bin 图4-1-10-7 canal的建库语句 CREATE TABLE IF NOT EXISTS `meta_snapshot` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', `gmt_create` datetime NOT NULL COMMENT '创建时间', `gmt_modified` datetime NOT NULL COMMENT '修改时间', `destination` varchar(128) DEFAULT NULL COMMENT '通道名称', `binlog_file` varchar(64) DEFAULT NULL COMMENT 'binlog文件名', `binlog_offest` bigint(20) DEFAULT NULL COMMENT 'binlog偏移量', `binlog_master_id` varchar(64) DEFAULT NULL COMMENT 'binlog节点id', `binlog_timestamp` bigint(20) DEFAULT NULL…
定义模型,并配上相应的菜单或配置模块的homepage为该模型,模块就具备了可访问的该模型对应的列表页、新增页、修改页、删除记录和导入导出功能。 都说oinone是以model为驱动,对于模型的理解,希望通过本节跟给大家带来一个初步的认识。 一、构建宠物店铺模型 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; import java.sql.Time; @Model.model(PetShop.MODEL_MODEL) @Model(displayName = "宠物店铺",summary="宠物店铺") public class PetShop extends IdModel { 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-1-1 构建宠物店铺模型 配置注解 模型类必需使用@Model注解来标识当前类为模型类。 可以使用@Model.model、@Fun注解模型的模型编码(也表示命名空间),先取@Model.model注解值,若为空则取@Fun注解值,若皆为空则取全限定类名。 使用@Model.model注解配置模型编码,模型编码唯一标识一个模型。 请勿使用Query和Mutation作为模型编码和技术名称的结尾。 上方示例使用Model注解和Field注解来定义一个实体模型。displayName属性最终会作为label展现在前端界面上。 更多Model的详细元数据描述介绍参见4.1.6【模型元数据详解】一文 模型命名规范 模型属性 默认取值规范 命名规则规范 name 默认取model.model的点分割最后一位 1仅支持数字、字母2必须以字母开头3长度必须小于等于128个字符 module 无默认值开发人员定义规范示例:{项目名称}_{模块功能示意名称} 1仅支持数字、大写或小写字母、下划线2必须以字母开头3不能以下划线结尾4长度必须小于等于128个字符 model 默认使用全类名,取lname的值开发人员定义规范示例:{项目名称}.{模块功能示意名称}.{简单类名} 1仅支持数字、字母、点2必须以字母开头3不能以点结尾4长度必须小于等于128个字符 display_name 空字符串 1长度必须小于等于128个字符 lname 符合java命名规范,真实的java全类名,无法指定,要符合model的约束,即为包名+类名 lname是不能定义的,为全类名:包名+类名,和model一样的校验规则:包名和类名的校验 summary 默认使用displayName属性 1不能使用分号2长度必须小于等于128个字符 descripition NULL,注解无法定义 1长度必须小于等于65535个字符 table 默认使用name字段生成表名时,table字段的命名规则约束同样生效(大小驼峰命名转为下划线分割的表名称) 1仅支持数字、字母、下划线2长度必须小于等于128个字符(此限制为系统存储约束,与数据库本身无关) type java属性类型与数据库存储类型可执行转换即可 ModelTypeEnum枚举值 表3-3-1-1 模型命名规范 字段命名规范 字段属性 默认取值规范 命名规则规范 name 默认使用java属性名 1仅支持数字、字母2必须以小写字母开头3长度必须小于等于128个字符 field 默认使用java属性名 与name使用相同命名规则约束 display_name 默认使用name属性 1长度必须小于等于128个字符 lname 使用java属性名,符合java命名规范,真实的属性名称,无法指定 与name使用相同命名规则约束 column 列名为属性名的小驼峰转下划线格式 1仅支持数字、字母、下划线2长度必须小于等于128个字符(此限制为系统存储约束,与数据库本身无关) summary 默认使用displayName属性 1不能使用分号2长度必须小于等于500个字符 表3-3-1-2 字段命名规范 我们重启Demo应用以后,打开Insomnia刷新GraphQL的schema,就可以看到PetShop默认对应的读写服务了 图3-3-1-2 PetShopQuery默认读写服务 图3-3-1-3 PetShopMutatiom默认读写服务 配置模块的主页为宠物商店的列表页 为了方便大家对模型有个更加直观的了解,接下来我们通过前端交互来感受下。 在3.2.2【启动前端工程】一文中,在模块下拉列表中“oinoneDemo工程”还是不能点击的。是因为该模块没有配置主页,我们现在把主页设置为宠物商店的列表页,只需要在DemoModule这个类上增加一个注解@UxHomepage(@UxRoute(PetShop.MODEL_MODEL)),关于该注解的更多说明请查看4.1.4一文中的【UxHomepage注解详解】部分 package pro.shushi.pamirs.demo.api; import org.springframework.stereotype.Component; import pro.shushi.pamirs.boot.base.ux.annotation.action.UxRoute; import pro.shushi.pamirs.boot.base.ux.annotation.navigator.UxHomepage; import pro.shushi.pamirs.demo.api.model.PetShop; import pro.shushi.pamirs.meta.annotation.Module; import pro.shushi.pamirs.meta.base.PamirsModule; import pro.shushi.pamirs.meta.common.constants.ModuleConstants; @Component @Module( name = DemoModule.MODULE_NAME, displayName = "oinoneDemo工程", version = "1.0.0", dependencies = {ModuleConstants.MODULE_BASE} ) @Module.module(DemoModule.MODULE_MODULE) @Module.Advanced(selfBuilt = true, application = true) @UxHomepage(@UxRoute(PetShop.MODEL_MODEL)) public class DemoModule implements PamirsModule { public static final String MODULE_MODULE = "demo_core"; public…