组件数据交互基础(v4)

阅读之前

你应该:

组件数据交互概述

数据结构设计

数据结构分为三大类,列表(List)对象(Object)以及弹出层(Popup)

  • 列表(List):用于多条数据的展示,主要包括搜索(用户端)自定义条件(产品端)排序分页数据选中数据提交数据校验功能。
  • 对象(Object):用于单条数据的展示,主要包括数据提交数据校验功能。
  • 弹出层(Popup):用于在一块独立的空间展示对应类型的数据。

数据结构与对于的内置视图类型:

  • 列表(List):表格视图(TABLE)画廊视图(GALLERY)
  • 对象(Object):表单视图(FORM)详情视图(DETAIL)

数据交互设计原则

组件与组件之间的结构关系是独立的,组件与组件之间的数据是关联的。因此,数据交互整体采用“作用域隔离(View),行为互通(CallChaining),数据互通(ActiveRecords)”这样的基本原则进行设计。实现时,围绕不同视图类型定义了一类数据结构所需的基本属性。

在弹出层进行设计时,使用Submetadata的方式,将包括弹出层在内的所有组件包含在内,以形成新的作用域。

通用属性及方法

属性

  • rootData:根数据源
  • dataSource:当前数据源
  • activeRecords:当前激活数据源

为了在实现时包含所有数据结构,我们统一采用ActiveRecord[]类型为所有数据源的类型,这些数据源在不同类型的视图中表示不同的含义:

  • 列表(List):dataSource为列表当前数据源,activeRecords为列表中被选中的数据。特别的,showDataSource为当前展示的数据源,它是dataSource经过搜索、排序、分页等处理后的数据源,也是我们在组件中真正使用的数据源。
  • 对象(Object):daraSourceactiveRecords总是完全一致的,且长度永远为1。因此我们有时也在组件中定义formData属性,并提供默认实现:this.activeRecords?.[0] || {}

方法

  • reloadDataSource:重载当前数据源
  • reloadActiveRecords:重载当前激活数据源

由于底层实现并不能正确判断当前使用的数据类型,因此我们无法采用统一标准的数据源修改方法,这时候需要开发者们自行判断。

重载列表数据源
cosnt dataSource: ActiveRecord[] = 新的数据源
// 重载数据源
this.reloadDataSource(dataSource);
// 重置选中行
this.reloadActiveRecords([]);
重载表单数据源
cosnt dataSource: ActiveRecord[] = 新的数据源(数组中有且仅有一项)
// 重载数据源
this.reloadDataSource(dataSource);
// 此处必须保持相同引用
this.reloadActiveRecords(dataSource);

内置CallChaining(链式调用)

在自动化渲染过程中,我们通常无法明确知道当前组件子组件交互的具体情况,甚至我们在定义当前组件时,并不需要关心(某些情况下可能无法关心)子组件的具体情况。这也决定了我们无法在任何一个组件中完整定义所需的一切功能。

为了保证组件行为的一致性,我们需要某些行为在各个组件的实现需要做到组件自治。以表格视图为例:当view标签在挂载时,我们无法确定应该怎样正确的加载数据,因此,我们需要交给一个具体的element标签来完成这一功能。当element标签对应的组件发生变化时,只需按照既定的重载方式将数据源提交给view标签即可。

除了保证组件行为的一致性外,我们不能完全的信任第三方框架对组件生命周期的处理顺序。因此,我们还需要对组件行为进行进一步的有序处理。以表格视图为例:我们希望搜索视图(SEARCH)的处理总是在加载数据前就处理完成的,这样将可以保证我们加载数据可以正确处理搜索条件,而这一特性并不随着模板结构的变化而发生变化。

平台内置的CallChaining
  • mountedCallChaining:挂载时钩子;
  • refreshCallChaining:刷新时钩子;
  • submitCallChaining:提交时钩子;
  • validatorCallChaining:验证时钩子;
优先级常量
  • VIEW_WIDGET_PRIORITY(0):视图组件优先级
  • FETCH_DATA_WIDGET_PRIORITY(100):数据提供者组件优先级
  • SUBVIEW_WIDGET_PRIORITY(200):子视图组件优先级

未设置优先级的hook将最后执行,在通常情况下,你无需关心优先级的问题。

注意事项
  • CallChaining通常不需要手动初始化,仅需通过inject方式获取即可。
  • CallChaining的hook/unhook方法需要在组件生命周期的mounted/unmounted分别执行,如无特殊情况,一般通过this.path作为挂载钩子的唯一键。
字段组件中使用mountedCallChaing
@Widget.Reactive()
@Widget.Inject()
protected mountedCallChaining: CallChaining | undefined;

protected mountedProcess() {
  // 挂载时处理
}

protected mounted() {
  super.mounted();
  this.mountedCallChaining?.hook(this.path, () => {
    this.mountedProcess();
  });
}

protected unmounted() {
  super.unmounted();
  this.mountedCallChaining?.unhook(this.path);
}

字段组件mountedCallChaing并不是必须的,因此我们未进行内置处理。

一般的,当视图数据被加载完成时,字段组件formDatavalue等属性,将通过响应式自动获取对应的值,因此在大多数情况下是不需要使用这一特性的。

当我们需要对字段获取的值做进一步初始化处理时,我们将需要使用这一特性。例如TreeSelect组件,必须在初始化时填充树下拉所需的结构化数据,这样才能正确展示对应的值。

字段组件mounted方法被执行时,我们还未执行视图数据加载,因此,在我们无法在mounted方法中操作formDatavalue等属性,只有在mountedCallChainingview标签执行时,按照执行顺序,此时字段的mountedChaining将在视图数据被加载完成后执行。

数据源持有者和数据源提供者

在设计上,我们通常将view标签设计为数据源持有者,将element标签设计为数据源提供者

原则上,在一个视图中有且仅有一个数据源提供者。

即:当一个element标签的实现组件通过reloadDataSource方法view标签设置数据源,我们就称该实现组件为当前view标签数据源提供者view标签数据源持有者

provider/inject

阅读该章节需要理解vue的依赖注入原理

在实现上,我们通过provider/inject机制将上述通用属性/方法进行交替处理,就可以实现根据模板定义的结构进行隔离和共享功能。

例如dataSource属性的实现:

/**
 * 上级数据源
 * @protected
 */
@Widget.Reactive()
@Widget.Inject('dataSource')
protected parentDataSource: ActiveRecord[] | undefined;

/**
 * 当前数据源
 * @protected
 */
@Widget.Reactive()
private currentDataSource: ActiveRecord[] | null | undefined;

/**
 * 提供给下级的数据源
 * @protected
 */
@Widget.Reactive()
@Widget.Provide()
public get dataSource(): ActiveRecord[] | undefined {
  return this.currentDataSource || this.parentDataSource;
}

不足的是,由于provider/inject机制特性决定,通过provider提供的属性和方法,在某些情况下可能会进行穿透,导致组件通过inject获取的属性和方法并非是我们所期望的那样,因此,我们仍然需要进行一些特殊的处理,才能正确的处理子视图的数据交互,这一点在对象(Object)类型的视图中会详细介绍。

运行时上下文(metadataHandle/rootHandle)

在之前的文章中,我们知道前端的字段/动作组件渲染和后端元数据之间是密不可分的。

在数据交互方面,后端元数据对于字段类型的定义,将决定从API接口中获取的字段、数据类型和格式,以及通过API接口提交数据到后端时的字段、数据类型和格式

数据类型和格式可以通过field标签data属性,获取到经过后端编译的相关字段元数据。

那么,我们该如何决定,当数据提供者向后端发起请求时,应该获取哪些字段呢?

因此,我们设计了RuntimeContext机制,并通过metadataHandle/rootHandle机制,在任何一个组件都可以通过view标签正确获取已经实现隔离的运行时上下文机制。

以表单视图为例,我们来看这样一个经过合并后的完整视图模板:

(以下模板所展示的并非实际的运行时的结果,而是为了方便描述所提供的完整视图模板)

<view type="FORM">
    <element widget="actions">
        <action name="create" invisible="activeRecord.id" />
        <action name="update" invisible="!activeRecord.id" />
    </element>
    <element widget="fields">
        <field data="id" invisible="true" />
        <field data="code" />
        <field data="name" />
        <field data="relationOne" widget="Form">
            <view type="FORM">
                <element widget="form">
                    <field data="id" invisible="true" />
                    <field data="code" />
                    <field data="name" />
                </element>
            </view>
        </field>
        <field data="relationMany" widget="Table">
            <view type="TABLE">
                <element widget="actionBar">
                    <action name="openCreateDialog">
                        <view type="FORM">
                            <element widget="fields">
                                <field data="id" invisible="true" />
                                <field data="code" />
                                <field data="name" />
                            </element>
                        </view>
                    </action>
                </element>
                <element widget="table">
                    <field data="id" invisible="true" />
                    <field data="code" />
                    <field data="name" />
                    <element widget="rowActions">
                        <action name="openEditDialog">
                            <view type="FORM">
                                <element widget="fields">
                                    <field data="id" invisible="true" />
                                    <field data="code" />
                                    <field data="name" />
                                </element>
                            </view>
                        </action>
                    </element>
                </element>
            </view>
        </field>
    </element>
</view>

其中:

  • relationOne字段:代表M2O字段,它使用Form组件将字段展开为子表单视图。(O2O类型基本一致)
  • relationMany字段:代表O2M字段,它使用Table组件将字段展开为子表格视图。(M2M类型基本一致)
    • openCreateDialog:代表打开一个创建弹窗,它是一个上下文类型为上下文无关的跳转动作。
    • openEditDialog:代表打开一个编辑弹窗,它是一个上下文类型为单行的跳转动作。

PS:这里我们关注的是组件之间的数据交互,至于动作相关内容,我们会在其他文章中做进一步介绍。在这里我们只需要了解,当按钮的上下文类型为上下文无关时,该按钮将不会携带任何数据到下一个视图中;当按钮的上下文类型为单行时,该按钮将携带activeRecords[0]的数据到下一个视图中。

通过这个视图模板,我们将构建出如下图所示的RuntimeContext结构:

image.png

``` mermaid
graph TD
metadataHandle-1("metadataHandle-1(ViewAction)") ---> rootHandle-1("rootHandle-1(view type=#quot;FORM#quot;)")

rootHandle-1 ---> metadataHandle-2 & metadataHandle-3

metadataHandle-2("metadataHandle-2(field data=#quot;relationOne#quot;)") ---> rootHandle-2("rootHandle-2(view type=#quot;FORM#quot;)")
metadataHandle-3("metadataHandle-3(field data=#quot;relationMany#quot;)") ---> rootHandle-3("rootHandle-3(view type=#quot;TABLE#quot;)")

rootHandle-3 ---> metadataHandle-4 & metadataHandle-5

metadataHandle-4("metadataHandle-4(action name=#quot;openCreateDialog#quot;)") ---> rootHandle-4("rootHandle-4(view type=#quot;FORM#quot;)")
metadataHandle-5("metadataHandle-5(action name=#quot;openEditDialog#quot;)") ---> rootHandle-5("rootHandle-5(view type=#quot;FORM#quot;)")
```

由此可见,我们的metadataHandle可能通过页面入口(ViewAction)提供,也可能通过字段(field标签)提供,还可能通过动作(action标签)提供。无一例外的是,所有的view标签都将生成一个与之对应的rootHandle

在编码过程中,我们可以通过this.metadataRuntimeContextthis.rootRuntimeContext获取到对应的运行时上下文。前端运行时上下文API文档

列表(List)数据交互

对于列表(List)数据结构来说,我们目前提供了两种具体的视图:表格视图(TABLE)画廊视图(GALLERY),这两种视图分别对应不同的数据展示形态。

  • 表格视图(TABLE):字段代表表格中的列(Column),每一列的数据使用相同方式展示,无法使用布局特性。
  • 画廊视图(GALLERY):使用list - item形式进行渲染,每一个item可以通过DSL描述其布局特性。

特别的是,搜索视图(SEARCH)是对列表(List)数据结构提供搜索(用户端)相关功能的子视图,其独立存在于页面中是没有意义的。

默认表格视图(TABLE)

<view type="TABLE">
    <pack widget="group">
        <view type="SEARCH">
            <element widget="search" slot="search" />
        </view>
    </pack>
    <pack widget="group" slot="tableGroup">
        <element widget="actionBar" slot="actionBar">
            <xslot name="actions" />
        </element>
        <element widget="table" slot="table">
            <element widget="expandColumn" slot="expandRow" />
            <xslot name="fields" />
            <element widget="rowActions" slot="rowActions" />
        </element>
    </pack>
</view>

在之前的文章中,我们了解了布局和标签的基本概念,为了能更好的说明数据交互相关的特征,我们将上述视图进行简化,仅留下与数据交互相关的标签。

<view type="TABLE">
    <view type="SEARCH">
        <element widget="search" slot="search" />
    </view>
    <element widget="actionBar" slot="actionBar">
        <xslot name="actions" />
    </element>
    <element widget="table" slot="table">
        <xslot name="fields" />
        <element widget="rowActions" slot="rowActions" />
    </element>
</view>

在这个视图中,我们可以获得如下信息:

  • 主视图为表格(TABLE)视图。
  • 包含一个搜索(SEARCH)子视图。
  • 包含两个主要的element组件widget="actionBar"widget="table"
  • widget="table"中内置了一个element组件widget="rowActions"

在我们内置的组件实现中,widget="table"这个组件通过调用后端接口,为整个视图提供数据源。

组件挂载时序图

image.png

``` mermaid
sequenceDiagram

participant TableView as TableView("view type=#quot;TABLE#quot;")

participant SearchView as SearchView("view type=#quot;SEARCH#quot;")
participant Search as SearchWidget("element widget=#quot;search#quot;")

participant ActionBar as ActionBarWidget("element widget=#quot;actionBar#quot;")

participant Table as TableWidget("element widget=#quot;table#quot;")
participant RowActionBar as RowActionBarWidget("element widget=#quot;rowActions#quot;")

TableView ->> TableView : before mount
TableView ->> SearchView : before mount
SearchView ->> Search : before mount
TableView ->> ActionBar : before mount
TableView ->> Table : before mount
Table ->> RowActionBar : before mount

Search ->> SearchView : mounted
SearchView ->> TableView : mounted
ActionBar ->> TableView : mounted
RowActionBar ->> Table : mounted
Table ->> TableView : mounted
TableView ->> TableView : mounted

TableView ->> TableView : mountedCallChaing hook (VIEW_WIDGET_PRIORITY)

TableView ->> SearchView : mountedCallChaing hook (VIEW_WIDGET_PRIORITY)
SearchView ->> Search : mountedCallChaing hook (FETCH_DATA_WIDGET_PRIORITY)

TableView ->> Table : mountedCallChaing hook (FETCH_DATA_WIDGET_PRIORITY)

```

在上图中,我们可以看到,mountedCallChaining是在所有组件全部渲染并挂载完成后,通过最上层的view标签进行调用的,并且按照一定的优先级顺序执行了每个组件实现的挂载钩子函数

组件数据源加载

widget="search"组件执行了挂载钩子函数时,将从url参数中获取所需的searchBodysearchCondition属性,并通过flushSearchParameters方法将这两个参数提交到表格视图(TABLE)

widget="table"组件执行了挂载钩子函数时,将执行加载数据相关功能,并通过reloadDataSourcereloadActiveRecords方法对数据源进行向上提交。

image.png

``` mermaid
sequenceDiagram

participant TableView as TableView("view type=#quot;TABLE#quot;")

participant SearchView as SearchView("view type=#quot;SEARCH#quot;")
participant Search as SearchWidget("element widget=#quot;search#quot;")

participant Table as TableWidget("element widget=#quot;table#quot;")

TableView ->> SearchView : mountedCallChaing hook (VIEW_WIDGET_PRIORITY)
SearchView ->> Search : mountedCallChaing hook (FETCH_DATA_WIDGET_PRIORITY)
Search ->> Search : resolve url parameters
Search ->> SearchView : reloadActiveRecords
Search ->> TableView : flushSearchParameters

TableView ->> Table : mountedCallChaing hook (FETCH_DATA_WIDGET_PRIORITY)
Table ->> Table : mounted process (call api)
Table ->> TableView : reloadDataSource
Table ->> TableView : reloadActiveRecords

```

用户行为触发的数据交互

这里列举了一些列表(List)常用的数据交互时序图,用于帮助大家对数据交互的整体逻辑可以有更清晰的理解。

点击搜索

image.png

``` mermaid
sequenceDiagram

participant TableView as TableView("view type=#quot;TABLE#quot;")

participant SearchView as SearchView("view type=#quot;SEARCH#quot;")
participant Search as SearchWidget("element widget=#quot;search#quot;")

participant Table as TableWidget("element widget=#quot;table#quot;")

Search ->> TableView : flushSearchParameters
Search ->> SearchView : refreshCallChaining (call)
SearchView ->> TableView : refreshCallChaining (call)

TableView ->> Table : refreshCallChaining hook (FETCH_DATA_WIDGET_PRIORITY)
Table ->> Table : refresh process (call api)
Table ->> TableView : reloadDataSource
Table ->> TableView : reloadActiveRecords

```

排序/分页

image.png

``` mermaid
sequenceDiagram

participant TableView as TableView("view type=#quot;TABLE#quot;")

participant Table as TableWidget("element widget=#quot;table#quot;")

Table ->> Table : refresh process (call api)
Table ->> TableView : reloadDataSource
Table ->> TableView : reloadActiveRecords

```

表格勾选(table checkbox/radio)

image.png

``` mermaid
sequenceDiagram

participant TableView as TableView("view type=#quot;TABLE#quot;")

participant Table as TableWidget("element widget=#quot;table#quot;")

Table ->> TableView : reloadActiveRecords

```

对象(Object)数据交互

对于对象(Object)数据结构来说,我们目前提供了两种具体的视图:表单视图(FORM)详情视图(DETAIL),这两种视图分别对应不同的数据展示形态。

  • 表单视图(FORM):主要用于数据的填写和提交;可以通过DSL描述其布局特性。
  • 详情视图(DETAIL):主要用于数据的展示,所有字段强制为只读态(与表单的只读态有所不同);可以通过DSL描述其布局特性。

特别的是,表单视图(FORM)详情视图(DETAIL)在对子视图的处理上并不完全相同。主要体现在数据提交类型(submitType)关联关系更新类型(relationUpdateType)这两个属性的处理上。

数据提交类型(submitType)
/**
 * 数据提交类型
 */
export enum SubmitType {
  /**
   * <h3>默认</h3>
   * <p>
   * 基础字段使用全量提交,关联关系字段使用关联关系提交
   * </p>
   */
  default = 'default',
  /**
   * 不提交
   */
  none = 'none',
  /**
   * 关联关系字段关系提交(仅对关联关系字段有效)
   */
  relation = 'relation',
  /**
   * 关联关系字段增量提交(仅对关联关系字段有效)
   */
  increment = 'increment',
  /**
   * 全量提交
   */
  all = 'all'
}
关联关系更新类型(relationUpdateType)
/**
 * 关联关系数据更新类型
 */
export enum RelationUpdateType {
  /**
   * <h3>默认</h3>
   * <p>
   * 默认根据数据提交类型进行自动识别
   * </p>
   */
  default = 'default',
  /**
   * 使用特定接口进行差量更新(关联关系字段强制使用增量提交)
   */
  diff = 'diff',
  /**
   * 使用batch接口进行差量更新(关联关系字段强制使用增量提交)
   */
  batch = 'batch',
  /**
   * 全量更新
   */
  all = 'all'
}

下面分别列举了一些字段组件在不同视图中的默认功能。

表单视图(FORM)
组件 (widget) 一对一(O2O) 多对一(M2O) 一对多(O2M) 多对多(M2M)
Table / / 前端搜索、分页、排序
submitType="all"
relationUpdateType="all"
前端搜索、分页、排序
submitType="relation"
relationUpdateType="all"
Form submitType="all"
relationUpdateType="all"
submitType="all"
relationUpdateType="all"
/ /
Select submitType="relation"
relationUpdateType="all"
submitType="relation"
relationUpdateType="all"
submitType="relation"
relationUpdateType="all"
submitType="relation"
relationUpdateType="all"
详情视图(DETAIL)
组件 (widget) 一对一(O2O) 多对一(M2O) 一对多(O2M) 多对多(M2M)
Table / / 后端搜索、分页、排序
不支持数据提交
后端搜索、分页、排序
不支持数据提交
Form 仅用于查看
submitType="relation"
relationUpdateType="all"
仅用于查看
submitType="relation"
relationUpdateType="all"
/ /
Select 仅用于查看
submitType="relation"
relationUpdateType="all"
仅用于查看
submitType="relation"
relationUpdateType="all"
仅用于查看
submitType="relation"
relationUpdateType="all"
仅用于查看
submitType="relation"
relationUpdateType="all"

默认表单视图(FORM)

<view type="FORM">
    <element widget="actionBar" slot="actionBar">
        <xslot name="actions" />
    </element>
    <element widget="form" slot="form">
        <xslot name="fields" />
    </element>
</view>

在这个视图中,我们可以获得如下信息:

  • 主视图为表单(FORM)视图。
  • 包含两个主要的element组件widget="actionBar"widget="form"

在我们内置的组件实现中,widget="form"这个组件通过调用后端接口,为整个视图提供数据源。

组件挂载时序图

image.png

``` mermaid
sequenceDiagram

participant FormView as FormView("view type=#quot;FORM#quot;")

participant ActionBar as ActionBarWidget("element widget=#quot;actionBar#quot;")

participant Form as FormWidget("element widget=#quot;form#quot;")

FormView ->> FormView : before mount
FormView ->> ActionBar : before mount
FormView ->> Form : before mount

ActionBar ->> FormView : mounted
Form ->> FormView : mounted
FormView ->> FormView : mounted

FormView ->> FormView : mountedCallChaing hook (VIEW_WIDGET_PRIORITY)

FormView ->> Form : mountedCallChaing hook (FETCH_DATA_WIDGET_PRIORITY)

```

组件数据源加载

image.png

``` mermaid
sequenceDiagram

participant FormView as FormView("view type=#quot;FORM#quot;")

participant Form as FormWidget("element widget=#quot;form#quot;")

FormView ->> Form : mountedCallChaing hook (FETCH_DATA_WIDGET_PRIORITY)
Form ->> Form : mounted process (call api)
Form ->> FormView : reloadDataSource
Form ->> FormView : reloadActiveRecords

```

用户行为触发的数据交互

这里列举了一些对象(Object)常用的数据交互时序图,用于帮助大家对数据交互的整体逻辑可以有更清晰的理解。

点击提交动作(ServerAction)(创建/编辑)

image.png

``` mermaid
sequenceDiagram

participant FormView as FormView("view type=#quot;FORM#quot;")

participant Form as FormWidget("element widget=#quot;form#quot;")

participant ServerAction as ServerAction("action actionType=#quot;ServerAction#quot;")

ServerAction ->> FormView : submitCallChaining (call)
FormView ->> Form : submitCallChaining (call)
FormView ->> ServerAction : submitCallChaining (return)

ServerAction ->> ServerAction : execute action (call api)

```

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

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

(0)
oinone的头像oinone
上一篇 2023年6月20日 pm4:07
下一篇 2023年11月2日 pm1:58

相关推荐

  • 【界面设计器】自定义字段组件实战——轮播图

    阅读之前 此文章为实战教程,已假定你熟悉了【界面设计器】较为完整的【自定义组件】相关内容。 如果在阅读过程中出现的部分概念无法理解,请自行学习相关内容。【前端】文章目录 业务背景 用户需要从【创建/编辑】页面中上传多张图片,并且在【详情】页面将这多张图片进行【轮播】展示。 业务分析 从需求来看,我们需要实现一个【轮播图】组件,并且该组件允许在【详情】视图中使用。在其他视图中,我们可以直接使用平台内置的【图片】组件,实现基础的编辑和展示功能。 名词解释 业务模型:需要进行可视化管理的存储模型或代理模型。 准备工作 你需要在某个业务模型下创建一个【表格视图】用于查看全部数据,创建【表单视图】用于创建/编辑数据,并创建【详情视图】展示必要的信息。(为了方便起见,你可以在所有视图中仅使用编码和名称两个字段) 你需要将【表格视图】绑定到某个菜单上,并通过【跳转动作】将三个视图进行关联,可以完整执行当前模型的全部【增删改查】操作。 业务模型定义 (以下仅展示本文章用到的模型字段,忽略其他无关字段。) DemoModel 名称 API名称 业务类型 是否多值 长度(单值长度) 编码 code 文本 否 128 名称 name 文本 否 128 轮播图 carouselImages 文本 是 512 实现页面效果展示 表格视图 表单视图-创建 表单视图-编辑 详情视图 根据业务背景添加轮播图字段到所有视图 轮播图字段信息: 字段业务类型:文本 多值:是 使用组件:图片 无代码模型 在模型设计器创建轮播图字段,并从【组件库】-【模型】拖放至视图中即可。 PS:这里需要注意的是,在模型设计器中需要切换至专家模式,并确认字段长度为512,否则当URL超长时将无法保存。 低代码模型 与服务端同学确认字段,并从【组件库】-【模型】中拖放至视图中即可。 将上图中的【演示】数据进行【编辑】,并上传三张图片,在【详情视图】查看默认展示效果。 演示图片下载 创建组件、元件 准备工作完成后,我们需要根据【业务背景】确定【组件】以及【元件】相关信息,并在【界面设计器】中进行创建。 以下操作过程将省略详细步骤,仅展示可能需要确认的关键页面。 创建轮播图组件 创建轮播图元件 根据业务背景,我们需要根据模型中的字段确定业务类型,在这个场景中,可以使用如下配置。(暂时可以不进行属性面板的设计) 在【详情视图】中将【轮播图字段】的组件切换为我们新创建的【轮播图组件】 PS:这里会发现组件变成了【输入框】的样式,这是由于我们没有提供对应元件的代码实现,使得SPI找到了默认组件。 启动SDK工程进行组件基本功能开发 (npm相关操作请自行查看SDK工程中内置的README.MD) Carousel.vue <template> <a-carousel class="carousel" effect="fade" autoplay> <div class="carousel-item" v-for="image in images" :key="image"> <img :src="image" :alt="image" /> </div> </a-carousel> </template> <script lang="ts"> import { Carousel as ACarousel } from 'ant-design-vue'; import { computed, defineComponent, PropType } from 'vue'; export default defineComponent({ name: 'Carousel', components: { ACarousel }, props: { value: { type: Array as PropType<string[]> } }, setup(props) { const images = computed(() => props.value || []); return { images }; } }); </script> <style lang="scss"> .carousel { .slick-slide { height: 160px; & > div, .carousel-item { width: 100%; height: 100%; } img { max-width: 100%; max-height: 100%; margin: auto; } } } </style> 效果展示 开发完成后,我们将重新打包生成的JS文件和CSS文件在【界面设计器】的【低无一体】进行上传,就可以在【设计器环境】中正常使用了。 设计轮播图的属性面板 通过我们使用的a-carousel组件,我们发现组件中提供了很多【属性】或【功能】可以进行配置,比如是否自动切换(autoplay)、面板指示点位置(dotPosition)、是否显示面板指示点(dots)等。在这里我们将对这三个属性的配置方式进行演示,其他更多属性可以自行设计并开发。 我们可以在【界面设计器】的【属性面板设计】中根据这三个属性的字段类型确定以下信息: 功能 API名称 业务类型 选用组件 可选项 是否自动切换 autoplay 布尔 开关 -…

    2023年11月1日
    4.1K00
  • 表单字段API

    FormFieldWidget 表单字段的基类,包含了表单字段通用的属性跟方法 示例 class MyFieldClass extends FormFieldWidget{ } 字段属性 属性名 说明 类型 可选值 默认值 value 当前字段的值 any – null formData 当前表单视图的数据 Object – {} rootData 跟视图的数据,如果当前只有一个视图,那么与formData是一样的 Array – [] metadataRuntimeContext 当前视图运行时的上下文,可以获取当前模型、字段、动作、视图等所有的数据 Object – – urlParameters 当前url参数 Object – – field 当前字段的元数据 Object – – model 当前模型 Object – – view 当前视图 Object – – disabled 是否禁用 Boolean – false invisible 当前字段是否不可见 Boolean – false required 当前字段是否必填,如果当前字段是在详情页,那么是false Boolean – false readonly 当前字段是否只读,如果当前字段是在详情页、搜索,那么是false Boolean – false placeholder 占位符 String – 当前字段的displayName label 字段的标题 String – 当前字段的displayName 方法 方法名 说明 参数 例子 getDsl 获取当前字段所有的配置 – change 修改当前字段的值 any focus 获取焦点触发的方法 – blur 失去焦点触发的方法 – executeValidator 执行当前字段的校验,异步的 – submit 重写当前字段的提交逻辑 – submit() { return ‘value’ } reloadActiveRecords 替换当前视图的数据 Array this.reloadActiveRecords([{code: xxx, name: 111}]) reloadRootData 替换根视图的数据 Array this.reloadRootData([{code: xxx, name: 111}])

    2023年11月15日
    1.6K00
  • 如何提高自定义组件的开发效率

    引言 本文将通过前端的开发者模式带领大家提高自定义组件的开发效率 支持2024年9月6日之后用npm i安装的4.7.x之后的所有版本 介绍前端开发者模式 开发者模式的特性 浏览器控制台可以看到更多利于开发的日志输出 页面顶部状态栏消息模块的轮询接口,将只在页面刷新后请求一次,这样会减少开发阶段不必要的请求,以及解决后端断点调试的时候被消息轮询干扰的问题 页面视图的数据将不在走缓存,而是每次实时从base_view表里查询template字段获取,方便需要实时看通过xml修改的页面效果 如何进入开发者模式 通过在浏览器地址栏后追加;mode=dev刷新页面进入开发者模式,页面加载后可以在浏览器开发者工具的应用标签下的本地存储空间(即localStorage)中看到本标识的存储,如果想退出开发者模式可以手动清除该值 开发者模式的应用场景 1.通过控制台跳转到视图动作(ViewAction)的调试页面 页面刷新后,可以在浏览器开发者工具的控制台看到如下图的日志输出,直接点击该链接可以跳转到当前页面的调试链接弹窗视图动作、抽屉视图动作等以弹出层为展示效果的页面,之前的版本是无法进入调试链接查看的,现在可以在弹窗等视图动作点击后在控制台看到该链接点击进入调试页面 2.查看页面母版(mask)、布局(layout)的匹配情况 母版(mask)匹配结果布局(layout)匹配结果 3.查看视图组件(View)、字段组件(Field)、动作组件(Action)的匹配情况 通过浏览器开发者工具的控制台,我们可以看到每个视图、字段、字段匹配到的组件的class类名,这样我们就知道当前用的是那个组件,在自定义的时候可以在源码查看该组件类的实现代码,然后再选择是否继承该父类。另外我们在给组件定义SPI条件的时候,可以参考匹配入参和匹配结果内的属性。 匹配入参是当前元数据SPI的最全量注册条件 匹配结果是当前匹配到的组件的注册条件,一般情况下我们只需要在匹配结果的条件加上当前模型编码(model)、视图名称(viewName)、字段名称(name)、动作名称(actionName)等条件就可以完成自定义组件的覆盖。 在浏览器开发者工具的控制台,通过模型编码、视图名称、字段名称、动作名称等关键字快速搜索定位到需要查找的组件 控制台搜索快捷键:win系统通过ctrl+f,mac系统通过cmd+f 总结 在确保自定义部分代码最终正确被import导入到启动工程main.ts内的情况下,可以通过开发者模式在浏览器控制台打印的日志来排查组件未正确覆盖的问题。

    2024年9月6日
    1.8K00
  • 如果让表单支持单选 ?

    本文将介绍在代码和XML配置中的修改,支持表格的单选功能。 先自定义一个widget,继承平台默认的table @SPI.ClassFactory( BaseElementWidget.Token({ viewType: ViewType.Table, widget: 'MyRadioTable' }) ) export class MyRadioTable extends TableWidget { @Widget.Reactive() protected get selectMode(): ListSelectMode { return ListSelectMode.radio; } } 注册layout 如果当前是主表格,那么xml配置如下 const xml = `<view type="TABLE"> <pack widget="group"> <view type="SEARCH"> <element widget="search" slot="search" slotSupport="field" /> </view> </pack> <pack widget="group" slot="tableGroup"> <element widget="actionBar" slot="actionBar" slotSupport="action"> <xslot name="actions" slotSupport="action" /> </element> <element widget="MyRadioTable" slot="table" slotSupport="field"> <element widget="expandColumn" slot="expandRow" /> <element widget="RadioColumn" /> <xslot name="fields" slotSupport="field" /> <element widget="rowActions" slot="rowActions" slotSupport="action" /> </element> </pack> </view>` registerLayout(xml, { moduleName: '模块名称', model: '模型', viewType: ViewType.Table }) 如果当前的表格是form表单页面的表格,那么xml配置如下 const xml = `<view type="TABLE"> <view type="SEARCH"> <element widget="search" slot="search" slotSupport="field" /> </view> <element widget="actionBar" slot="actionBar" slotSupport="action"> <xslot name="actions" slotSupport="action" /> </element> <element widget="MyRadioTable" slot="table"> <element widget="expandColumn" slot="expandRow" /> <element widget="RadioColumn" /> <xslot name="fields" slotSupport="field" /> <element widget="rowActions" slot="rowActions" /> </element> </view>` registerLayout(xml, { moduleName: '模块名称', model: '模型', viewType: ViewType.Table })

    2023年11月27日
    91300
  • 在前端视图添加自定义的区域块

    添加自定义区域块 平台提供了一系列默认的视图布局,可以帮助开发人员快速构建出复杂的企业应用系统。当然,我们可以使用自定义区域块来扩展表格、表单、画廊、树形等视图。 自定义区域块概述 平台视图布局都是通过XML配置实现的。在视图布局中,我们可以使用一些特定的元素标签来构建视图的表头、表单、搜索区域等部分。而自定义区域块,就是这些元素标签之外的部分。我们可以通过在视图布局的XML中添加自定义区域块,来扩展页面功能。 视图类型及相关元素 视图类型分为表格(TABLE)、表单(FORM)、画廊(GALLERY)、树形(TREE)等。不同类型的视图布局,包含的元素也有所不同。 下面是几种视图类型及其对应的元素: 表格:搜索区域、表格主体,其中表格主体包含了表格上面的动作、表格区域等部分。 表单:表单区域,包含了表单动作、表单区域等部分。 画廊:动作、卡片详细信息。 在表格页面添加自定义区域块 以下是一个示例,演示如何在表格页面顶部添加自定义区域块。 1. 修改视图布局XML 首先,我们需要修改表格视图的XML布局,添加自定义区域块元素标签。 <view type="TABLE"> <!– 这是搜索区域 –> <pack widget="group"> <view type="SEARCH"> <element widget="search" slot="search" slotSupport="field" /> </view> </pack> <!– 这是表格主体 –> <pack widget="group" slot="tableGroup"> <!– 在这里添加自定义区域块元素标签 –> <element widget="MyCustomElement"></element> <!– 这是表格上面的动作 –> <element widget="actionBar" slot="actionBar" slotSupport="action"> <xslot name="actions" slotSupport="action" /> </element> <!– 这是表格区域 –> <element widget="table" slot="table" slotSupport="field"> <element widget="expandColumn" slot="expandRow" /> <xslot name="fields" slotSupport="field" /> <element widget="rowActions" slot="rowActions" slotSupport="action" /> </element> </pack> </view> 在上述代码中,我们添加了一个名为MyCustomElement的元素标签。这将作为我们自定义区域块的容器。 2. 创建自定义Vue组件 接下来,我们需要创建一个Vue组件,并将其指定为自定义元素标签MyCustomElement的模板。 <template> <div> <!– 在这里编写自定义区域块的内容 –> <p>Hello, world!</p> </div> </template> <script lang="ts"> import { defineComponent } from 'vue'; export default defineComponent({ components: { }, props: [], setup(props) { return {}; } }); </script> 在上述代码中,我们定义了一个非常简单的Vue组件,它在页面上显示一个“Hello, world!”的文本信息。 3. 创建自定义Element Widget 为了使自定义Vue组件与XML布局文件关联起来,我们需要创建一个对应的Element Widget。 import { BaseElementWidget, SPI, BaseElementViewWidget, Widget, ViewMode, FormWidget, BaseElementWidgetProps } from '@kunlun/dependencies'; import MyCustomElement from './MyCustomElement.vue'; @SPI.ClassFactory(BaseElementWidget.Token({ widget: 'MyCustomElementWidget' })) export class MyCustomElementWidget extends BaseElementWidget { public initialize(props: BaseElementWidgetProps): this { super.initialize(props) this.setComponent(MyCustomElement) return this } } 在上述代码中,我们继承了BaseElementWidget类,并在其中指定了Vue组件MyCustomElement。这样,XML布局文件中的元素标签就能够正确地与Vue组件关联起来。 4. 注册视图布局 最后,我们需要将上述代码配置注册。具体而言,我们需要调用registerLayout方法来将XML布局文件、模块名和视图类型进行关联。 import { registerLayout, ViewType } from '@kunlun/dependencies'; registerLayout( `<view type="TABLE"> <pack widget="group"> <view type="SEARCH"> <element widget="search" slot="search" slotSupport="field" />…

    2023年11月1日
    2.7K00

Leave a Reply

登录后才能评论