组件数据交互基础(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

相关推荐

  • 前端密码加密

    在项目开发中,我们可能会遇到自定义登录页,密码需要加密,或者是数据提交的时候,某个数据需要加密,在平台的前端中,提供了默认的全局加密 API 在 oinone 前端工程使用 // pc端工程使用 import { encrypt } from '@kunlun/dependencies'; // 移动端端工程使用 import { encrypt } from '@kunlun/mobile-dependencies'; // 加密后的密码 const password = encrypt('123456'); 其他工程使用 如果是其他工程,前端没有用到 oinone 这一套,比如小程序,或者是其他工程,可以使用下面的代码记得安装 crypto-js import CryptoJS from 'crypto-js'; const key = CryptoJS.enc.Utf8.parse('1234567890abcdefghijklmnopqrstuv'); const iv = CryptoJS.enc.Utf8.parse('1234567890aabbcc'); export const encrypt = (content: string): string => { if (typeof content === 'string' && content) { const encryptedContent = CryptoJS.AES.encrypt(content, key, { iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); return encryptedContent.ciphertext.toString(); } return ''; };

    2025年3月24日
    69300
  • 前端自定义字段与视图最佳方案

    自定义视图获取数据 在某些情况下,oinone 提供的默认视图无法满足需求,这时我们就需要自定义视图。通常,虽然视图的 UI 不足以满足要求,但数据结构是不变的。此时,重点是修改页面 UI,数据的请求与回填可以利用平台默认的能力。 如何实现? 界面设计器的使用 你可以通过界面设计器先配置页面,平台在运行时会根据设计器生成对应的 GraphQL 请求,并自动回填数据。 视图的数据结构 视图的数据类型分为可以分为 Object 跟 List Object 代表当前视图的数据结构是对象 List 代表当前视图的数据结构是数组。 如果我们将 Object 跟 List 分的更细一点就变成了这样: 1: Object: 对象,代表当前视图的数据结构是单个对象,例如:表单视图、详情视图1: List: 对象数组,代表当前视图的数据结构数组,例如:表格视图、卡片视图、画廊视图 视图类型 平台组件 数据属性 表格视图 TableWidget dataSource 画廊视图 GalleryWidget dataSource 表单视图 FormWidget formData 详情视图 DetailWidget formData 自定义视图时,需要先确认当前视图的类型,再通过界面设计器进行页面配置。前端部分只需继承相应的组件,平台底层会自动处理接口数据的获取与回填。 表单视图示例: import Form from './Form.vue'; // 自定义表单视图 @SPI.ClassFactory( BaseElementWidget.Token({ widget: 'custom-form' }) ) export class CustomForm extends FormWidget { public initialize(props: Props) { super.initialize(props); this.setComponent(Form); return this; } } Vue 组件: <template></template> <script lang="ts"> export default defineComponent({ props: { formData: { // 当前表单的数据 type: Object, default: () => ({}) } } }); </script> 自定义layout // 原始的layout模版 <view type="FORM"> <element widget="actionBar" slot="actionBar" slotSupport="action"> <xslot name="actions" slotSupport="action" /> </element> <element widget="form" slot="form"> <xslot name="fields" slotSupport="pack,field" /> </element> </view> //自定义的layout模版 <view type="FORM"> <element widget="actionBar" slot="actionBar" slotSupport="action"> <xslot name="actions" slotSupport="action" /> </element> <element widget="custom-form" slot="form"> <xslot name="fields" slotSupport="pack,field" /> </element> </view> 其实就是把 widget="form" 改成 widget="custom-form" 表格视图示例: import Table from './Table.vue'; // 自定义表格视图 @SPI.ClassFactory( BaseElementWidget.Token({ widget: 'custom-table' }) ) export class CustomTable extends TableWidget { public initialize(props: Props) { super.initialize(props); this.setComponent(Table); return this; } } Vue 组件: <template></template> <script…

    2024年10月17日
    1.8K00
  • 【界面设计器】树形表格

    阅读之前 你应该: 熟悉模型的增删改查相关内容。【界面设计器】模型增删改查基础

    2024年4月19日
    1.2K00
  • 【前端】项目开发前端知识要点地图

    概述 下面整理了目前现有的所有文章,并提供了基本的学习路径。所有使用*标记的文章属于推荐必读文章。 目录 基础篇 【路由】浏览器地址栏url参数介绍 母版-布局-DSL 渲染基础(v4)* 组件SPI机制(v4)* 组件数据交互基础(v4)* 组件生命周期(v4) 入门篇 自定义视图组件(v4)* 如何通过浏览器开发者工具提高调试效率* 如何提高自定义组件的开发效率* 自定义组件之自动渲染(组件插槽的使用)(v4)* GraphQL请求详解(v4)* 上下文在字段和动作中的应用 如何实现页面间的跳转 如何自定义指定页面的样式 进阶篇 自定义组件之手动渲染基础(v4) 自定义组件之手动渲染弹出层(v4) 自定义组件之手动渲染任意视图(v4) 【前端】IOC容器(v4) 最佳实践篇 【前端】工程结构最佳实践(v4)* 【前端】移动端工程结构最佳实践(v4)* 界面设计器实战篇 基础篇 【界面设计器】模型增删改查基础 【界面设计器】他表字段 【界面设计器】左树右表 【界面设计器】树形表格 【界面设计器】树下拉/级联选择 【界面设计器】自定义字段组件基础 展示篇 【界面设计器】自定义字段组件实战——轮播图 【界面设计器】自定义字段组件实战——表格字段组合展示 【界面设计器】自定义字段组件实战——表格字段内嵌表格 交互篇 【界面设计器】自定义字段组件实战——千分位输入框 其他 前端低无一体使用教程 如何自定义表格字段? 【界面设计器】组件开发常见问题 【前端】低无一体部署常见问题 【前端】生产环境性能调优 API文档 OioProvider详解(v4.3.0)* 前端环境配置(v4)* 默认布局模板(v4) 表格主题配置(v4) 运行时上下文API文档(v4) Class Component(ts)(v4)

    2024年5月25日
    4.3K00
  • 自定义字段组件如何处理vue组件内的表单校验

    介绍 本示例以字符串字段为业务场景,将输入框用element-plus的组件实现了一遍,vue组件内在onMounted生命周期内将ElForm表单实例通过ts组件内提供到props的setFormInstance方法设置到了ts组件的属性formInstance上,这样就可以在ts组件校验方法validator()触发的时候直接调用表单组件实例formInstance的校验方法validate() 适用场景 当前字段存储了动态表单的配置json,vue组件内自行实现了一套表单渲染逻辑,需要在vue组件和ts组件内同时触发校验 参考文档 element-plus表单组件文档 如何编写自定义字段组件的校验逻辑 示例代码 ts组件 import { BaseFieldWidget, FormStringFieldSingleWidget, isValidatorSuccess, ModelFieldType, SPI, ValidatorInfo, ViewType, Widget } from '@kunlun/dependencies'; import { FormInstance } from 'element-plus'; import MyFormStringField from './MyFormStringField.vue'; @SPI.ClassFactory( BaseFieldWidget.Token({ viewType: [ViewType.Form, ViewType.Search], ttype: ModelFieldType.String, widget: 'Input', model: 'resource.k2.Model0000000109', name: 'code', }) ) export class MyFormStringFieldWidget extends FormStringFieldSingleWidget { public initialize(props) { super.initialize(props); this.setComponent(MyFormStringField); return this; } /** * ElementPlus的表单vue组件实例 * @private */ private formInstance?: FormInstance; @Widget.Method() private setFormInstance(formInstance: FormInstance | undefined) { this.formInstance = formInstance; } /** * 字段校验方法 */ public async validator(): Promise<ValidatorInfo> { const validRes = await this.formInstance?.validate((valid, fields) => {}); console.log('validRes', validRes) if (!validRes) { return this.validatorError('校验失败'); } const res = await super.validator(); if (!isValidatorSuccess(res)) { return res; } if (this.value == null) { return this.validatorSuccess(); } return this.validateLength(this.value); } } vue组件 <template> <ElForm ref="formInstance" :model="model" :rules="rules"> <ElFormItem label="编码" prop="code"> <ElInput v-model="model.code" @input="onValueChange"></ElInput> </ElFormItem> </ElForm> </template> <script lang="ts"> import { defineComponent, reactive, ref, onMounted, watch } from 'vue'; import { ElForm, ElFormItem, ElInput, FormInstance } from 'element-plus'; export default defineComponent({ name: 'MyFormStringField', components: { ElForm, ElFormItem, ElInput }, props: ['value', 'setFormInstance', 'onChange'],…

    2024年9月6日
    1.6K00

Leave a Reply

登录后才能评论