自定义的复杂字段配置透出字段

学习这篇文章之前,需要先学会使用在界面设计器自定义一个前端组件,如果您还不会,可以先看这篇文章

默认情况下,当开前端发人员自定义了一个复杂字段,比如M2O、O2M、M2M的字段,那么Graphql查询的时候,只会查询id跟name这两个字段,如果还想查询字段的字段,那么可以通过配置化的方式来处理

1: 在界面设计器的组件区域中新增对应的字段
数式Oinone低代码-自定义的复杂字段配置透出字段

2: 设计元件,在模型区域中搜索选项字段列表,拖到设计区域,然后保存
数式Oinone低代码-自定义的复杂字段配置透出字段

数式Oinone低代码-自定义的复杂字段配置透出字段
3: 去对应的设计页面,刷新下页面,选中对应的字段,可以看到右侧有选项字段列表
数式Oinone低代码-自定义的复杂字段配置透出字段
数式Oinone低代码-自定义的复杂字段配置透出字段
4: 输入期望Graphql查询字段,保存发布

数式Oinone低代码-自定义的复杂字段配置透出字段

Oinone社区 作者:汤乾华原创文章,如若转载,请注明出处:https://doc.oinone.top/frontend/4395.html

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

(1)
汤乾华的头像汤乾华数式员工
上一篇 2023年11月8日 pm5:05
下一篇 2023年11月9日 pm3:31

相关推荐

  • 【界面设计器】左树右表

    阅读之前 你应该: 熟悉模型的增删改查相关内容。【界面设计器】模型增删改查基础 名词解释 主体:在视图中提供数据源的主要组件,并且所有动作都围绕着该主体展开。 一级搜索:在表格视图中,上方直观可见的搜索区,为表格提供筛选功能。 二级搜索:与一级搜索不同的是,其搜索条件是通过某些组件的行为追加到一级搜索条件之上的筛选功能。 概述 平台中对于左树右表提供了两种类型的展示形式。 在表格视图中的左树右表,是以表格为主体,树组件为表格提供了二级搜索功能。选中树节点时将对表格追加节点的搜索条件,并重新执行查询。 在树视图中,是以树为主体,其展开的视图可以是表格、表单、详情等其他视图。 PS:不论是树、级联这些视图组件,还是树选择、级联这些字段组件,其配置数据结构的方式是不尽相同的。唯一的区别在于最终到达的目标模型来源不同。 场景1 为了方便接下来的描述,我们需要先构建一个基本的业务场景,这个场景中包含【商品】和【商品类目】两个模型。 在【商品】的表格左侧添加【商品类目】树,选择某个商品类目后,可以根据商品类目进行筛选,查询所属类目下的全部商品。 其中【商品类目】使用【卡片级联】的展示方式进行管理。 其模型定义如下: 商品(Item) 名称 API名称 业务类型 是否多值 长度(单值长度) 关联模型 关联字段 ID id 整数 否 – – – – 编码 code 文本 否 128 – – – 名称 name 文本 否 128 – – 所属类目 category 多对一 否 – 商品类目(ItemCategory) categoryId – id 所属类目ID categoryId 整数 否 – – – 商品类目(ItemCategory) 名称 API名称 业务类型 是否多值 长度(单值长度) 关联模型 关联字段 ID id 整数 否 128 – – 编码 code 文本 否 128 – – – 名称 name 文本 否 128 – – 上级类目 parent 多对一 是 – 商品类目(ItemCategory) parentId – id 上级类目ID parentId 整数 否 – – – PS:实际业务场景中,【商品类目】通常使用编码进行关联,即parentCode – code;不仅如此,通常还会添加treeCode字段,以此来实现高效查询当前节点的所有子节点的能力。在演示模型中,我们不必关注这些内容。 创建【商品】视图 设置联动关系 这里我们需要配置的是【商品类目】的树结构,因此,在【第1级关联】中的模型选择【商品类目】。 在【商品类目】中是通过【上级类目】进行的自关联,因此,在【第1级关联】中的【自关联关系字段】选择【上级类目】。 在选中【商品类目】节点后,需要对右侧表格发起查询。其筛选条件是通过【商品】中的【所属类目】进行筛选的。因此,在【第1级关联】中的【表格关联关系字段】选择【所属类目】。那么,在表格发起查询前,会根据【所属类目】字段的关联关系配置自动添加筛选条件。 配置如下图所示: 创建【商品类目】视图 设置联动关系 这里我们需要配置的是【商品类目】的树结构(级联只是树结构的另一种表现形式) 细心的同学可能发现这里没有【表格关联关系字段】,因此,我们仅需配置【自关联关系字段】即可。 配置如下图所示: 为【商品类目】添加增删改查基础功能 与表格视图不同的是,行内动作区被放在了第一个卡片中的动作区,其他配置方式完全一致。 在这里需要理解的是,一个树节点对应的是表格中的一行。 【商品类目】使用展开视图进行编辑(可选) 打开【支持展开视图】开关,并设置展开视图。这里我们用表单视图进行编辑操作。 和其他表单一样,我们将必要的字段和动作拖入对应区域即可。 由于展开视图只会在选中节点时出现,因此我们仅需提供更新功能即可。 这里需要注意: 提交动作默认打开了【返回上一页】的功能,在当前场景中,更新动作提交数据后,没有上一页需要返回,因此需要关闭【返回上一页】的开关。 提交动作默认打开了【刷新当前视图】的功能,在当前场景中,更新按钮处于【展开视图】中,仅刷新当前视图是不够的,当数据发生变更时,我们需要将级联组件一并刷新,因此需要打开【刷新主视图】的开关。 PS:【编辑】动作和【展开视图编辑】功能是重复的,在使用时应该只选择其中一种。 【商品类目】限制仅支持四级,并为每一个卡片添加标题(可选) 移除【第1级关联】中的【自关联关系字段】,依次添加2、3、4级关联,选择【商品类目】模型,将自动选中【层级关联关系字段】为【上级类目】,并输入每一级关联的标题即可。 这里需要注意的是,在【第1级关联】中需要添加筛选条件,使其只能查询到根节点。 PS:这里的限制仅为交互上的限制,在创建/更新时,如果可以在服务端限制上级类目的选择,以及数据提交时的校验,效果更佳。 【商品类目】的【创建/编辑】使用弹窗打开(可选) 有时我们希望用户在页面中的操作尽可能的流畅,在表单规模较小的情况下,我们也可以使用【弹窗/抽屉】这类交互来优化用户体验。 小贴士: 弹窗打开的方式提供三种页面设计模式,绑定已有页面、使用新页面、复制已有页面。 使用新页面和复制已有页面的方式只能进行一次性的视图设计,无法进行复用。 使用绑定已有页面的方式可以使得视图进行复用,但复用的视图也只能同步弹窗的内容部分,弹窗底部的动作仅会在创建动作时复制一次。 跳转动作的打开方式以及页面设计模式等属于元数据信息,无法通过属性面板进行修改,因此只能通过重新创建新动作的方式进行修改。 鉴于业务的复杂和多变,通常情况下我们只采用【绑定已有页面】的方式为弹窗设计内容部分。这样在交互发生变更时,可以更好的适应变化。 场景2 为了方便接下来的描述,我们需要再构建一个基本的业务场景,这个场景中包含【公司】和【部门】两个模型。 【公司】模型的管理能力使用标准的【增删改查】视图。(此处不进行演示) 在【部门】的表格左侧添加【公司】-【部门】树,在选择某个公司后,可以根据所属公司进行筛选,查询所属公司下的全部部门。在选择某个部门后,可以根据上级部门进行筛选,查询该部门下的子部门(不包含子部门的子部门)。 PS:如这里要求查询该部门下的全部子部门,需要服务端配合。 其模型定义如下: 公司(Company) 名称 API名称 业务类型 是否多值 长度(单值长度) 关联模型 关联字段 ID id 整数 否 – – – – 编码 code 文本 否 128 – – – 名称 name 文本…

    2023年11月1日
    2.3K00
  • 自定义前端拦截器

    某种情况下,我们需要通过自定义请求拦截器来做自己的逻辑处理,平台内置了一些拦截器 登录拦截器LoginRedirectInterceptor 重定向到登录拦截器LoginRedirectInterceptor import { UrlHelper, IResponseErrorResult, LoginRedirectInterceptor } from '@kunlun/dependencies'; export class BizLoginRedirectInterceptor extends LoginRedirectInterceptor { /** * 重定向到登录页 * @param response 错误响应结果 * @return 是否重定向成功 */ public redirectToLogin(response: IResponseErrorResult): boolean { if (window.parent === window) { const redirect_url = location.pathname; // 可自定义跳转路径 location.href = `${UrlHelper.appendBasePath('login')}?redirect_url=${redirect_url}`; } else { // iframe页面的跳转 window.open(`${window.parent.location.origin}/#/login`, '_top'); } return true; } } 请求成功拦截器RequestSuccessInterceptor 请求失败拦截器 RequestErrorInterceptor 网络请求异常拦截器 NetworkErrorInterceptor 当我们需要重写某个拦截器的时候,只需要继承对应的拦截器,然后重写里面的方法即可 // 自定义登录拦截器 export class CustomLoginRedirectInterceptor extends LoginRedirectInterceptor{ public error(response: IResponseErrorResult) { // 自己的逻辑处理 return true // 必写 } } // 自定义请求成功拦截器 export class CustomRequestSuccessInterceptor extends RequestSuccessInterceptor{ public success(response: IResponseErrorResult) { // 自己的逻辑处理 return true // 必写 } } // 自定义请求失败拦截器 export class CustomRequestErrorInterceptor extends RequestErrorInterceptor{ public error(response: IResponseErrorResult) { const { errors } = response; if (errors && errors.length) { const notPermissionCodes = [ SystemErrorCode.NO_PERMISSION_ON_MODULE, SystemErrorCode.NO_PERMISSION_ON_VIEW, SystemErrorCode.NO_PERMISSION_ON_MODULE_ENTRY, SystemErrorCode.NO_PERMISSION_ON_HOMEPAGE ]; /** * 用来处理重复的错误提示 */ const executedMessages: string[] = []; for (const errorItem of errors) { const errorCode = errorItem.extensions?.errorCode; if (!notPermissionCodes.includes(errorCode as any)) { const errorMessage = errorItem.extensions?.messages?.[0]?.message || errorItem.message; if (!executedMessages.includes(errorMessage)) { // 自己的逻辑处理 } executedMessages.push(errorMessage); } } } return true; } } // 自定义网络请求异常拦截器…

    前端 2023年11月1日
    60800
  • 列表视图、卡片视图切换

    在日常项目开发中,我们可能会遇到当前视图是个表格,通过某个操作按钮将它变成卡片的形式. 这篇文章将带大家实现这种功能。通过上面两张图片可以看到出来不管是表格还是卡片,它都在当前的视图里面,所以我们需要一个视图容器来包裹表格跟卡片,并且表格可以使用平台默认的表格渲染,我们只需要自定义卡片即可。 我们用资源模块下面的国家菜单来实现这么一个功能这是对应的URL: http://localhost:8080/page;module=resource;viewType=TABLE;model=resource.ResourceCountry;action=resource%23%E5%9B%BD%E5%AE%B6;scene=resource%23%E5%9B%BD%E5%AE%B6;target=OPEN_WINDOW;menu=%7B%22selectedKeys%22:%5B%22%E5%9B%BD%E5%AE%B6%22%5D,%22openKeys%22:%5B%22%E5%9C%B0%E5%9D%80%E5%BA%93%22,%22%E5%9C%B0%E5%8C%BA%22%5D%7D 源码下载 views 创建外层的视图容器 刚刚我们讲过,不管是表格还是卡片,它都在当前的视图里面,所以我们需要写一个视图容器来包裹它们,并且对应的容器里面允许拆入表格跟卡片,我们先创建TableWithCardViewWidget.ts // TableWithCardViewWidget.ts import { BaseElementWidget, SPI, Widget } from '@kunlun/dependencies'; import TableWithCardView from './TableWithCardView.vue'; enum ListViewType { TABLE = 'table', CARD = 'card' } @SPI.ClassFactory( BaseElementWidget.Token({ widget: 'TableWithCardViewWidget' }) ) export class TableWithCardViewWidget extends BaseElementWidget { @Widget.Reactive() private listViewType: ListViewType = ListViewType.TABLE; // 当前视图展示的类型,是展示卡片还是表格 public initialize(props) { if (!props.slotNames) { props.slotNames = ['tableWidget', 'cardWidget']; } super.initialize(props); this.setComponent(TableWithCardView); return this; } } 在TableWithCardViewWidget中的initialize函数中,我们定义了两个插槽: tableWidget、cardWidget,所以需要在对应的vue文件里面里接收这两个插槽 <template> <div class="list-view-wrapper"> <!– 表格插槽 –> <div style="height: 100%" v-if="listViewType === 'table'"> <slot name="tableWidget" /> </div> <!– 卡片插槽 –> <div v-if="listViewType === 'card'"> <slot name="cardWidget"></slot> </div> </div> </template> <script lang="ts"> import { defineComponent } from 'vue'; export default defineComponent({ props: ['listViewType', 'onChangeViewType'], inheritAttrs: false, setup(props, context) { const onChangeViewType = (listViewType) => { props.onChangeViewType(listViewType); }; } }); </script> 这样一来,我们就定义好了视图容器,接下来就是通过自定义layout的方式注册该容器 layout注册 import { registerLayout, ViewType } from '@kunlun/dependencies'; registerLayout( `<view type="TABLE"> <pack widget="group"> <view type="SEARCH"> <element widget="search" cols="4" slot="search"/> </view> </pack> <pack widget="group" slot="tableGroup" style="position: relative"> <element widget="actionBar" slot="actionBar" slotSupport="action"> <xslot name="actions" slotSupport="action" /> </element> <element widget="TableWithCardViewWidget"> <template slot="tableWidget"> <element widget="table" slot="table" datasource-provider="true"> <element widget="expandColumn" slot="expandRow" /> <xslot name="fields" />…

    2024年10月16日
    1.3K01
  • TS 结合 Vue 实现动态注册和响应式管理

    基础知识 1: 面向对象 面向对象编程是 JavaScript 中一种重要的编程范式,它帮助开发者通过类和对象组织代码,提高代码的复用性。 2: 装饰器 装饰器在 JavaScript 中是用于包装 class 或方法的高阶函数 为了统一术语,下面的内容会把装饰器讲成注解 在 oinone 平台中,无论是字段还是动作,都是通过 ts + vue 来实现的,ts 中是面向对象的写法,所有的属性、方法建议都在对应的 class 中,如果有通用的属性跟方法,可以放在一个公共的 class 中,然后通过继承来实现,这样便于维护。 <!– FormString.vue –> <template> <div> <p>用户名: {{userName}}</p> <button @click="updateUser({name: '王五'})">修改用户名</button> </div> </template> <script lang="ts"> import { defineComponent } from 'vue'; export default defineComponent({ props: { userName: { type: String, default: '' }, userInfo: { type: Object, default: () => ({}) }, updateUser: { type: Function, default: () => () => ({}) } } }); </script> import FormString from './FormString.vue' @SPI.ClassFactory( FormFieldWidget.Token({ viewType: [ViewType.Form, ViewType.Search], ttype: ModelFieldType.String }) ) export class FormCustomStringFieldWidget extends FormFieldWidget { public initialize(props) { super.initialize(props); // 调用父类方法,确保继承的属性和方法正常初始化 this.setComponent(FormString); // 注册 Vue 组件,这样该 Widget 就会渲染 FormString 组件 return this; } public otherInfo = { name:'张三' } @Widget.Reactive() public userInfo = { name:'李四' } @Widget.Reactive() public get userName() { return this.userInfo.name } @Widget.Method() public updateUser userName(user) { this.userInfo = user } public updateOtherUser userName(user) { this.otherUser = user } } 这段代码定义了一个 FormCustomStringFieldWidget 类,用于处理表单中 String 类型字段的展示和交互。该类继承自 FormFieldWidget,并使用了多种注解和特性来实现不同功能。下面是对代码的详细讲解。 SPI 讲解 @SPI.ClassFactory() 无论是自定义字段还是动作,或者是自定义 mask、layout,都会用到@SPI.ClassFactory来注册,@SPI.ClassFactory 是一个注解,它标记了该类是通过工厂模式注册的。 在前端中,所有的注解(装饰器)本质上还是高阶函数,下面是一段伪代码。 const SPI = { ClassFactory(token) { return (targetClass) => {…

    2024年9月21日
    1.3K00
  • 【前端】IOC容器(v4)

    什么是IOC容器? IOC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合,更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IOC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的使程序的整个体系结构变得非常灵活。在运行期,在外部容器动态的将依赖对象注入组件,当外部容器启动后,外部容器就会初始化。创建并管理对象实例,以及销毁,这种应用本身不负责依赖对象的创建和维护,依赖对象的创建和维护是由外部容器负责的称为控制反转。 IOC(控制反转)和DI(依赖注入) IOC(Inversion of Control, 控制反转):通过外部容器管理对象实例的一种思想。DI(Dependency Injection, 依赖注入):IOC的一种实现方式。 作者简述 IOC是Spring框架(一种以Java为语言开发的框架)的核心,并贯穿始终。其面向接口的开发能力,使得服务调用方和服务提供方可以做到完全解耦,只要遵循接口定义的规则进行调用,具体服务的实现可以是多样化的。 对于前端,我们使用inversify进行了IOC的实现。其强大的解耦能力可以使得平台进行大量的抽象,而无需关系具体的实现。 接下来,我们将介绍IOC在开发中的基本运用。 API 为了方便起见,我们将IOC相关功能与组件SPI的调用方式放在了一起。(更高版本的平台版本将自动获得该能力) export class SPI { /** * register singleton service */ public static Service; /** * autowired service property/parameter in service */ public static Autowired; /** * service construct after execute method */ public static PostConstruct; /** * autowired service in widget */ public static Instantiate; /** * autowired services in widget */ public static Instantiates; /** * service construct after execute method in widget */ public static InstantiatePostConstruct; } 创建第一个服务 service/ProductService.ts import { ServiceIdentifier } from '@kunlun/dependencies'; /** * 产品 */ export interface Product { id: string; name: string; } /** * 产品服务 */ export interface ProductService { /** * 获取产品列表 */ getProducts(): Promise<Product[]>; /** * 通过ID获取产品 * @param id 产品ID */ getProductById(id: string): Promise<Product | undefined>; } /** * 产品服务Token */ export const ProductServiceToken = ServiceIdentifier<ProductService>('ProductService'); service/impl/ProductServiceImpl.ts import { SPI } from '@kunlun/dependencies'; import { Product, ProductService, ProductServiceToken } from '../ProductService'; @SPI.Service(ProductServiceToken) export class ProductServiceImpl implements ProductService { public async getProducts(): Promise<Product[]> { // request api get products return []; } public async getProductById(id:…

    前端 2023年11月1日
    75600

Leave a Reply

登录后才能评论