【界面设计器】左树右表

阅读之前

你应该:

名词解释

  • 主体:在视图中提供数据源的主要组件,并且所有动作都围绕着该主体展开。
  • 一级搜索:在表格视图中,上方直观可见的搜索区,为表格提供筛选功能。
  • 二级搜索:与一级搜索不同的是,其搜索条件是通过某些组件的行为追加到一级搜索条件之上的筛选功能。

概述

平台中对于左树右表提供了两种类型的展示形式。

表格视图中的左树右表,是以表格为主体,树组件为表格提供了二级搜索功能。选中树节点时将对表格追加节点的搜索条件,并重新执行查询。

树视图中,是以为主体,其展开的视图可以是表格表单详情等其他视图。

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字段,以此来实现高效查询当前节点的所有子节点的能力。在演示模型中,我们不必关注这些内容。

创建【商品】视图

image.png

设置联动关系

这里我们需要配置的是【商品类目】的树结构,因此,在【第1级关联】中的模型选择【商品类目】。

在【商品类目】中是通过【上级类目】进行的自关联,因此,在【第1级关联】中的【自关联关系字段】选择【上级类目】。

在选中【商品类目】节点后,需要对右侧表格发起查询。其筛选条件是通过【商品】中的【所属类目】进行筛选的。因此,在【第1级关联】中的【表格关联关系字段】选择【所属类目】。那么,在表格发起查询前,会根据【所属类目】字段的关联关系配置自动添加筛选条件。

配置如下图所示:

image.png

创建【商品类目】视图

image.png

设置联动关系

这里我们需要配置的是【商品类目】的树结构(级联只是树结构的另一种表现形式)

细心的同学可能发现这里没有【表格关联关系字段】,因此,我们仅需配置【自关联关系字段】即可。

配置如下图所示:

image.png

为【商品类目】添加增删改查基础功能

与表格视图不同的是,行内动作区被放在了第一个卡片中的动作区,其他配置方式完全一致。

在这里需要理解的是,一个树节点对应的是表格中的一行。

image.png

【商品类目】使用展开视图进行编辑(可选)

打开【支持展开视图】开关,并设置展开视图。这里我们用表单视图进行编辑操作。

image.png

和其他表单一样,我们将必要的字段和动作拖入对应区域即可。

由于展开视图只会在选中节点时出现,因此我们仅需提供更新功能即可。

image.png

这里需要注意:

  • 提交动作默认打开了【返回上一页】的功能,在当前场景中,更新动作提交数据后,没有上一页需要返回,因此需要关闭【返回上一页】的开关。
  • 提交动作默认打开了【刷新当前视图】的功能,在当前场景中,更新按钮处于【展开视图】中,仅刷新当前视图是不够的,当数据发生变更时,我们需要将级联组件一并刷新,因此需要打开【刷新主视图】的开关。

image.png

PS:【编辑】动作和【展开视图编辑】功能是重复的,在使用时应该只选择其中一种。

【商品类目】限制仅支持四级,并为每一个卡片添加标题(可选)

移除【第1级关联】中的【自关联关系字段】,依次添加2、3、4级关联,选择【商品类目】模型,将自动选中【层级关联关系字段】为【上级类目】,并输入每一级关联的标题即可。

这里需要注意的是,在【第1级关联】中需要添加筛选条件,使其只能查询到根节点。

image.png

image.png

image.png

PS:这里的限制仅为交互上的限制,在创建/更新时,如果可以在服务端限制上级类目的选择,以及数据提交时的校验,效果更佳。

【商品类目】的【创建/编辑】使用弹窗打开(可选)

有时我们希望用户在页面中的操作尽可能的流畅,在表单规模较小的情况下,我们也可以使用【弹窗/抽屉】这类交互来优化用户体验。

image.png

小贴士:

  • 弹窗打开的方式提供三种页面设计模式,绑定已有页面、使用新页面、复制已有页面。
  • 使用新页面和复制已有页面的方式只能进行一次性的视图设计,无法进行复用。
  • 使用绑定已有页面的方式可以使得视图进行复用,但复用的视图也只能同步弹窗的内容部分,弹窗底部的动作仅会在创建动作时复制一次。
  • 跳转动作的打开方式以及页面设计模式等属于元数据信息,无法通过属性面板进行修改,因此只能通过重新创建新动作的方式进行修改。
  • 鉴于业务的复杂和多变,通常情况下我们只采用【绑定已有页面】的方式为弹窗设计内容部分。这样在交互发生变更时,可以更好的适应变化。

场景2

为了方便接下来的描述,我们需要再构建一个基本的业务场景,这个场景中包含【公司】和【部门】两个模型。

【公司】模型的管理能力使用标准的【增删改查】视图。(此处不进行演示)

在【部门】的表格左侧添加【公司】-【部门】树,在选择某个公司后,可以根据所属公司进行筛选,查询所属公司下的全部部门。在选择某个部门后,可以根据上级部门进行筛选,查询该部门下的子部门(不包含子部门的子部门)。

PS:如这里要求查询该部门下的全部子部门,需要服务端配合。

其模型定义如下:

公司(Company)
名称 API名称 业务类型 是否多值 长度(单值长度) 关联模型 关联字段
ID id 整数
编码 code 文本 128
名称 name 文本 128
部门(Department)
名称 API名称 业务类型 是否多值 长度(单值长度) 关联模型 关联字段
ID id 整数 128
编码 code 文本 128
所属公司 company 多对一 公司(Company) companyId – id
所属公司ID companyId 整数
上级部门 parent 多对一 部门(Department) parentId – id
上级部门ID parentId 整数

创建【部门】视图

image.png

设置联动关系

这里我们需要配置的是【公司】-【部门】的树结构,因此,在【第1级关联】中的模型选择【公司】,在【第2级关联】中的模型选择【部门】。

在【第2级关联】中的【层级关联关系字段】默认会选择一个可用字段,这里我们是通过【部门】中的【所属公司】进行关联的。

在【部门】中是通过【上级部门】进行的自关联,因此,在【第2级关联】中的【自关联关系字段】选择【上级部门】。

在选中【公司】节点后,需要对右侧表格发起查询。其筛选条件是通过【部门】中的【所属公司】进行筛选的。因此,在【第1级关联】中的【表格关联关系字段】选择【所属公司】。那么,在表格发起查询前,会根据【所属公司】字段的关联关系配置自动添加筛选条件。

在选中【部门】节点后,同样需要对右侧表格发起查询。其筛选条件是通过【部门】中的【上级部门】进行筛选的。因此,在【第2级关联】中的【表格关联关系字段】选择【上级部门】。那么,在表格发起查询前,会根据【上级部门】字段的关联关系配置自动添加筛选条件。

配置如下图所示:

image.png

树/级联配置详解

在体验过树/级联配置之后,我们需要具体的介绍每个配置在树构建时的作用,以便于理解一系列类似组件的配置。

配置解释
  • 模型:当前层级的模型。
  • 数据标题:树节点展示标题。
  • 筛选条件:当设置【自关联关系字段】时,会根据【自关联关系字段】的关联关系自动添加筛选条件,此时如果配置了筛选条件,将进行追加。当未设置【自关联关系字段】时,为查询单层级树节点的筛选条件。
  • 自关联关系字段:通过字段配置以及所在层级自动添加筛选条件用于查询下级节点的一种方式。
  • 层级关联关系字段:通过字段配置将当前层级与上一层级进行关联,自动添加筛选条件用于查询下级节点的一种方式。
  • 表格关联关系字段:在树节点选中时,通过字段配置自动添加筛选条件用于表格查询。
名词解释
  • 树(Tree):一种数据结构,用于描述一组具备父子关系的数据。
  • 节点(TreeNode):对于树中的每一个节点存储必要属性的对象。主要包括唯一标识(key)数据(value)父(parent)子(children)以及层级(level)等属性。
  • 根节点:没有父节点的节点。
  • 父节点:相对于子节点而言的节点。
  • 子节点:相对于父节点而言的节点。
  • 当前节点:用户行为触发时所操作的对于节点。
  • 选中节点:通过当前节点中的某些配置为数据提供者追加筛选条件,并让数据提供者执行刷新操作。
  • 展开节点:通过当前节点中的某些配置查询该节点的子节点。

树的层级配置

在配置面板,我们可以看到有第x级关联这样的分组。每一个分组中的全部配置都对应了树的一个层级所需的配置。

但需要理解的是,这里的层级并不是指运行时看到的树的真实层级,而是指配置时所需的层级。

比如:通过设置【自关联关系字段】,我们可以在这配置的一层级中,在运行时展开多个层级,直到无法查询到自关联的子节点为止。

自关联关系字段影响查询下级节点

在未配置【自关联关系字段】时,我们在运行时查询第一层树节点(通常也称为根节点)时,其查询条件可以表示为:

rsql: [${筛选条件}]

当配置了【自关联关系字段】时,我们在运行时查询第一层树节点时,其查询条件可以表示为:

rsql: parentId =isnull= true [and ${筛选条件}]

展开查询第二层树节点时,其查询条件可以表示为:

rsql: parentId == ${parent.id} [and ${筛选条件}]

PS:这里的parentId泛指【自关联关系字段】的relationFields属性,而不是固定的某个字段。当存在多个时,将通过and连接。这里的parent.id泛指上级节点中的对应的【自关联关系字段】的referenceFields属性。

字段的关联关系属性

类型定义:(这里仅展示必要的元数据类型定义)

export interface RuntimeRelationField {
    /**
     * 是否关系存储
     */
    relationStore: boolean;
    /**
     * 关联模型编码
     */
    references: string;
    /**
     * 关系字段(在当前模型中的字段)
     */
    relationFields: string[];
    /**
     * 关联字段(在关联模型中的字段)
     */
    referenceFields: string[];
}

export type RuntimeO2OField = RuntimeRelationField;

export interface RuntimeO2MField extends RuntimeRelationField {
    /**
     * 限制数量
     */
    limit?: number | string;
}

export type RuntimeM2OField = RuntimeRelationField;

export interface RuntimeM2MField extends RuntimeRelationField {
    /**
     * 多对多中间模型编码
     */
    through: string;
    /**
     * 多对多中间模型关系字段(与关系字段对应)
     */
    throughRelationFields: string[];
    /**
     * 多对多中间模型关联字段(与关联字段对应)
     */
    throughReferenceFields: string[];
    /**
     * 限制数量
     */
    limit?: string | number;
}

一对一(O2O)多对一(M2O)以及一对多(O2M)这三种类型的关联关系字段中,relationFieldsreferenceFields总是长度相等,且相同索引位置的字段总是配对的。

多对多(M2M)这种类型的关联关系字段中,relationFieldsthroughRelationFields总是长度相等,且相同索引位置的字段总是配对的,,referenceFieldsthroughReferenceFields总是长度相等,且相同索引位置的字段总是配对的。

其中relationFields表示当前模型中的字段,referenceFields表示关联模型中的字段。throughRelationFields表示在中间模型中与当前模型字段对应的字段,throughReferenceFields表示在中间模型中与关联模型字段对应的字段。

层级关联关系字段影响查询下级节点

从【第2级关联】开始,需要配置【层级关联关系字段】,将其与上一层级进行关联。

这里需要注意的是,如果【第1级关联】使用了【自关联关系字段】进行自关联,那么只有当无法通过自关联条件查询到子节点时,会自动转换为使用【层级关联关系字段】查询当前节点的子节点。

其查询条件可以表示为:

rsql: parentId == ${parent.id} [and ${筛选条件}]

PS:这里的parentId泛指【层级关联关系字段】的relationFields属性,而不是固定的某个字段。当存在多个时,将通过and连接。这里的parent.id泛指上级节点中的对应的【层级关联关系字段】的referenceFields属性。

构成完整关联关系*

在配置【联动关系】时,我们必然需要一些条件来规定一种怎样的配置才能在运行时正常运行。下面将介绍构成完整关联关系的一些基本条件和限制。

最终到达的目标模型

为了构建完整的关联关系,并且可以在业务使用中可以正常运行,我们对【联动关系】的配置进行了基础的限制。最终到达的目标模型就是保障配置有效性的基础。

在不同的视图组件中,最终到达的目标模型也有所区别。下面列举了目前现有的几种情况。

  • 表格视图中的左树右表,其最终到达的模型为表格对应的模型,也就是当前视图模型。其要求最后一级关联必须通过【表格关联关系字段】与表格建立关联关系。

  • 树视图中,其最终到达的模型为对应的模型,也就是当前视图模型。其要求最后一级关联中的【模型】必须是该模型。

  • 字段树下拉级联组件中,其最终到达的模型为字段对应的关联模型,也就是字段的references对应的模型。其要求最后一级关联中的【模型】必须是该模型。

PS:一般而言,未构成完整关联关系的提示都是由于最终到达的目标模型限制引起的。

层级关联关系的模型限制

平台中的模型元数据定义了模型的拓扑结构,模型与模型之间通过关联关系字段产生联系,然后形成模型的拓扑结构。在这个基础上,每一层级的模型也就有了可选范围。

在【第1级关联】中的【模型】可以选择任意模型。

从【第2级关联】开始,可选的【模型】就有了限制。其只能选择与上一级关联的模型可以通过关联关系字段关联的模型。由于元数据定义的模型的拓扑结构过于复杂,我们无法通过适当的条件进行筛选。因此,在用户交互上我们并没有限制这一点,这就要求用户明确需要构建一个怎样的树结构,并根据树结构进行合理的配置。

PS:当模型选择错误时,【层级关联关系字段】未选择的情况,也是未构成完整关联关系的情况之一。

层级关联关系的字段限制

由于字段的关联关系具有方向性,在四种关联关系类型中,具备明确方向性的类型为一对多(O2M)多对一(M2O),在子节点的查询时,我们假定节点展开一定是向的一方进行展开的。因此,【层级关联关系字段】也只能使用这两种关联关系类型的字段。

在其他两种关联关系类型中,我们无法确定其展开的方向,也就无法提供自动化的查询机制。

元数据模型的拓扑结构复杂性(扩展内容,仅作了解即可)
  • 模型继承:模型存在扩展继承、代理继承等继承方式,那么当前模型就不仅仅是这一个模型,包括它的全部父模型和子模型。
  • 单向关联:当模型是通过单向关联到当前模型时,我们不仅需要从当前模型出发查找一些对应的模型外,还需要从其他模型出发查找一些与当前模型关联的模型。

由于这些复杂性共同导致最终筛选的结果集和所有模型的结果集几乎一致,其在便于用户选择的需求下表现的并不好,因此我们放弃了筛选。

选择和配置的一般步骤
  • 明确业务需求,需要构建一个怎样的树结构
  • 确定最终到达的目标模型
  • 定义模型元数据,以符合业务需求。
  • 确定展示主体是表格还是,分别选择不同的视图。(字段组件可跳过)
  • 确定从什么模型开始选择,最终可以通过层级关联关系字段到达最终目标模型的。

Oinone社区 作者:nation原创文章,如若转载,请注明出处:https://doc.oinone.top/designer/uidesigner/63.html

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

(2)
nationnation
上一篇 2023年6月20日 pm4:07
下一篇 2023年11月2日 pm1:58

相关推荐

  • 如何实现页面间的跳转

    介绍 在日常的业务中,我们经常需要在多个模型的页面之间跳转,例如从商品的行可以直接点击链接跳转到类目详情,还有查看该订单发起的售后单列表,这里将给大家展示如何在oinone中如何实现这些功能。 方法一、通过界面设计器的无代码能力配置 表格行跳转到表单页/详情页 拖入一个跳转动作到表格行,保存动作后,在左侧的动作属性面板底部有个请求配置,里面的上下文属性就是配置跳转参数的地方,点击添加按钮可以增加一行参数 点击添加按钮后,可以看到新增了一行,行内有2个输入框,左侧输入框为目标视图模型的字段,右侧输入框为当前视图模型的表达式 注意 表达式中activeRecord关键字代表当前行的数据对象 “上下文”相关知识点 当前页面的模型和跳转后的页面模型相同的情况下,会字段带上当前行数据的id作为路由参数 上下文是从当前页面跳转到下个页面带的自定义参数 上下文会作为跳转后的页面数据加载函数的入参,后端的该函数需要根据该条件查询到数据返回给前端,典型的例子就是编辑页,根据id查询对象的其他字段信息返回 跳转后页面的数据加载函数可以在动作场景的时候选择加载函数,也可以在页面的加载函数处设置 方法二、通过低代码方式在自定义代码中调用 oinone提供了内置函数executeViewAction实现该功能 import { DefaultComparisonOperator, executeViewAction, QueryExpression, RuntimeViewAction, ViewActionTarget, ViewType } from '@kunlun/dependencies'; export class JumpActionWidget { protected goToObjectView() { executeViewAction( { viewType: ViewType.Form, moduleName: 'resource', model: 'resource.ResourceCountry', name: 'redirectUpdatePage', target: ViewActionTarget.Router } as RuntimeViewAction, undefined, undefined, { // 此处为id参数,目前只有表单和详情页需要 id: '12223', // 此处为上下文参数,context内对象的key是目标页面需要传递的默认值 context: JSON.stringify({code: 'xxx'}), // 此处为跳转后左侧菜单展开选中的配置 menu: JSON.stringify({"selectedKeys":["国家"],"openKeys":["地址库","地区"]}) } ); } protected goToListView() { const searchConditions: QueryExpression[] = []; searchConditions.push({ leftValue: ['countryCode'], // 查询条件的字段 operator: DefaultComparisonOperator.EQUAL, right: 'CN' // 字段的值 }); executeViewAction( { viewType: ViewType.Table, moduleName: 'resource', model: 'resource.ResourceCity', name: 'resource#市', target: ViewActionTarget.OpenWindow } as RuntimeViewAction, undefined, undefined, { // searchConditions相当于domain,不会随页面搜索项重置动作一起被清空 searchConditions: encodeURIComponent(JSON.stringify(searchConditions)), // searchBody的字段会填充搜索区域的字段组件,会随页面搜索项重置动作一起被清空 searchBody: JSON.stringify({code: 'CN'}), menu: JSON.stringify({"selectedKeys":["国家"],"openKeys":["地址库","地区"]}) } ); } } 扩展知识点 为什么executeViewAction跳转到的新页面不是入参的moduleName属性对应的模块? 答:跳转后所在的模块优先级为: 第一个入参的resModuleName属性对应的模块 执行executeViewAction时所在的模块 第一个入参的moduleName属性对应的模块 如何快速获取选中菜单的值? 答:先通过页面菜单手动打开页面,然后在浏览器自带调试工具的控制台执行decodeURIComponent(location.href),其中的menu参数就是我们需要的值

    2024年5月13日
    00
  • 创建与编辑一体化

    在业务操作中,用户通常期望能够在创建页面后立即进行编辑,以减少频繁切换页面的步骤。我们可以充分利用Oinone平台提供的创建与编辑一体化功能,使操作更加高效便捷。 通过拖拽实现表单页面设计 在界面设计器中,我们首先需要设计出对应的页面。完成页面设计后,将需要的动作拖入设计好的页面。这个动作的关键在于支持一个功能,即根据前端传入的数据是否包含id来判断是创建操作还是编辑操作。 动作的属性配置如下: 前端自定义动作 一旦页面配置完成,前端需要对这个动作进行自定义。以下是一个示例的代码: @SPI.ClassFactory( ActionWidget.Token({ actionType: [ActionType.Server], model: '模型', name: '动作的名称' }) ) export class CreateOrUpdateServerActionWidget extends ServerActionWidget { @Widget.Reactive() protected get updateData(): boolean { return true; } } 通过以上步骤,我们实现了一个更智能的操作流程,使用户能够在创建页面的同时进行即时的编辑,从而提高了整体操作效率。这种创建与编辑一体化的功能不仅使操作更加顺畅,同时也为用户提供了更灵活的工作流程。

    2023年11月21日
    00
  • 计算属性如何配置按条件展示不同的值

    1.打开计算公式,点击“fx”字样的图标添加函数 2.选择逻辑函数分类下的条件函数 3.在第一个输入框填写条件,这里可以手写条件的表达式,后面2个输入框分别是条件成立时的值和条件不成立时的值,可以手动输入表达式 表达式中的activeRecord,在表格中代表当前表格行的数据,在表单中为整个表单的数据

    2024年5月16日
    00
  • 自定义字段的数据联动

    某种情况下,开发人员期望自定以的字段发生变化后,需要修改其他的字段,这篇文章从两个维度来讲解如果处理数据的联动 界面设计器配置 1: 在界面设计器页面中的的组件区域找到自定义的字段,设计元件 2: 在模型区域,搜索提交方式,如果找到了,就把该字段拖拽进来, 如果找不到,就在元件中的组件区域,拖拽一个文本字段,按照下面的配置进行配置,然后保存 图一是找到了对应的字段图二是找不到对应的字段 【图一】 【图二】 图二的字段编码必须是constructDataTrigger 3: 从模型区搜索联动函数,将其拖拽进来 3: 从模型区搜索提交数据,将其拖拽进来4: 从模型区搜索提交字段,将其拖拽进来 5: 发布 (记得刷新页面哦) 最后再到对应的设计器页面,选中该字段,进行配置 提交方式为blur或者change , 需要开发者手动调用该方法 this.blur()或者this.change(value) // 字段对应的ts文件 class MyField extends FormFieldWidget { onChangeValue(val) { // this.change(val) // this.blur() } } 联动函数就是要调用的后端函数 提交数据分为:变更字段 -> 发生变化后的字段当前视图字段 -> 当前视图所有的字段指定字段 -> 指定字段,如果配置的指定字段,那么提交字段的配置就要输入对应的字段 代码配置 平台也支持通过代码的方式修改字段 // 字段对应的ts文件 class MyField extends FormFieldWidget { onChangeValue(val) { // 修改字段本身的值 this.change(val) // 修改其他字段的值 this.formData.otherField = 'value' this.reloadFormData$.subject.next(true); } }

    2023年11月9日
    00
  • 界面设计器创建批量更新的动作

    界面设计器如果创建批量更新的动作 一、拖拽批量动作到表格区域 拖拽批量动作至表格区域,创建批量更新动作设置动作信息,设置完成之后点击保存 二、设置批量动作弹窗视图 点击设计弹窗,出现弹窗设计器界面将需要批量更新的字段拖入表单区,将待提交的数据需要展示字段拖入表格区域,比如更新名称,展示待提交数据的ID、名称、创建时间(注意:创建时间需要在表格中也展示)拖拽客户端动作进入动作区域,并设置动作信息,客户端行为选择批量更新动作设置成功,点击保存 三、发布设计好的批量动作,在运行页面看看效果 选择批量更新的数据之后点击批量更新将名称修改为‘批量修改名称’查看最后实现效果

    2024年4月22日
    00

Leave a Reply

登录后才能评论