组件SPI机制(v4)

阅读之前

你应该:

组件SPI简介

不论是母版布局还是DSL,所有定义在模板中的标签都是通过组件SPI机制获取到对应Class Component(ts)并继续执行渲染逻辑。

基本概念:

  • 标签:xml中的标签,json中的dslNodeType属性
  • Token组件:用于收集一组Class Component(ts)的基础组件。通常该基础组件包含了对应的一组基础能力(属性、函数等)
  • 维度(dsl属性):用于从Token组件收集的所有Class Component(ts)组件中查找最佳匹配的参数。

组件SPI机制将通过指定维度按照有权重的最长路径匹配算法获取最佳匹配的组件。

组件注册到指定Token组件

BaseFieldWidget这个SPIToken组件为例,可以用如下方式,注册一个可以被field标签处理的自定义组件:

(以下示例仅仅为了体现SPI注册的维度,而并非实际业务中使用的组件代码)

注册一个String类型组件

维度:

  • 视图类型:表单(FORM)
  • 字段业务类型:String类型

说明:

  • 该字段组件可以在表单(FORM)视图中使用
  • 并且该字段的业务类型是String类型
@SPI.ClassFactory(
  BaseFieldWidget.Token({
    viewType: ViewType.Form,
    ttype: ModelFieldType.String
  })
)
export class FormStringFieldWidget extends BaseFieldWidget {
  ......
}
注册一个多值String类型组件

维度:

  • 视图类型:表单(FORM)
  • 字段业务类型:String类型
  • 是否多值:是

说明:

  • 该字段组件可以在表单(FORM)视图中使用
  • 并且该字段的业务类型是String类型
  • 并且该字段为多值字段
@SPI.ClassFactory(
  BaseFieldWidget.Token({
    viewType: ViewType.Form,
    ttype: ModelFieldType.String,
    multi: true
  })
)
export class FormStringMultiFieldWidget extends BaseFieldWidget {
  ......
}
注册一个String类型Hyperlinks组件

维度:

  • 视图类型:表单(FORM)
  • 字段业务类型:String类型
  • 组件名称:Hyperlinks

说明:

  • 该字段组件仅可以在表单(FORM)视图中使用
  • 并且该字段的业务类型是String类型
  • 并且组件名称必须指定为Hyperlinks
@SPI.ClassFactory(
  BaseFieldWidget.Token({
    viewType: ViewType.Form,
    ttype: ModelFieldType.String,
    widget: 'Hyperlinks'
  })
)
export class FormStringHyperlinksFieldWidget extends BaseFieldWidget {
  ......
}

当上述组件全部按顺序注册在BaseFieldWidget这个SPIToken组件中时,将形成一个以BaseFieldWidget为根节点的树:

image.png

``` mermaid
graph TD
BaseFieldWidget ---> FormStringFieldWidget
BaseFieldWidget ---> FormStringMultiFieldWidget
FormStringFieldWidget ---> FormStringHyperlinksFieldWidget
```

树的构建

上述形成的组件树实际并非真实的存储结构,真实的存储结构是通过维度进行存储的,如下图所示:

(圆角矩形表示维度上的属性和值,矩形表示对应的组件)

image.png

``` mermaid
graph TD
viewType([viewType: ViewType.Form]) --->
ttype([ttype: ModelFieldType.Strng]) --->
multi([multi: true]) & widget([widget: 'Hyperlinks'])

direction LR
ttype ---> FormStringFieldWidget
multi ---> FormStringMultiFieldWidget
widget ---> FormStringHyperlinksFieldWidget
```

有权重的最长路径匹配

同样以上述BaseFieldWidget组件为例,该组件可用的维度有:

  • viewType:ViewType[Enum]
  • ttype:ModelFieldType[Enum]
  • multi:[Boolean]
  • widget:[String]
  • model:[String]
  • viewName:[String]
  • name:[String]

field标签被渲染时,我们会组装一个描述当前获取维度的对象:

{
    "viewType": "FORM",
    "ttype": "STRING",
    "multi": false,
    "widget": "", // 在dsl中定义的任意值
    "model": "", // 在dsl编译后自动填充
    "viewName": "", // 当前视图名称
    "name": "" // 字段的name属性,在dsl编译后自动填充
}

当我们需要使用FormStringHyperlinksFieldWidget这个组件时,我们在dsl中会使用如下方式定义:

<view type="FORM" title="演示表单" name="演示模型form" model="demo.DemoModel">
    <field data="name" widget="Hyperlinks" />
</view>

此时,我们虽然没有在dsl中定义维度中的其他信息,但在dsl返回到前端时,经过了后端编译填充了对应元数据相关属性,我们可以得到如下所示的对象:

{
    "viewType": "FORM",
    "ttype": "STRING",
    "multi": false,
    "widget": "Hyperlinks",
    "model": "demo.DemoModel",
    "viewName": "演示模型form",
    "name": "name"
}

通过上述定义的对象,我们在存储结构中按照指定的维度顺序进行查找,就可以获取到我们需要的组件了。

查找过程简述:

  • 匹配第一层viewType为FORM或包含FORM的节点
  • 匹配第二层ttype为STRING或包含STRING的节点(此时,FormStringFieldWidget被插入到待返回队列首位)
  • 匹配第三层multi为false的节点。(此时,没有任何节点匹配,继续匹配当前层)
  • 匹配第三层widget为Hyperlinks的节点。(此时,FormStringHyperlinksFieldWidget被插入到待返回队列首位)
  • 第四层为空,不再继续向下查找。
  • 返回待返回队列首项。

特殊的默认组件

BasePackWidget为例,平台提供的DefaultGroupWidget组件是这样注册的:

@SPI.ClassFactory(BasePackWidget.Token({}))
export class DefaultGroupWidget extends BasePackWidget {
  ......
}

该组件中不包含任何维度属性,我们无法将它添加到树中的任何一个节点,所以我们称这个组件在BasePackWidget这个SPIToken中为默认组件,任何SPIToken中的默认组件有且仅有一个。

因此,在dsl中,我们可以用如下方式直接使用这个默认组件:

<view type="FORM" title="演示表单" name="演示模型form" model="demo.DemoModel">
    <pack>
        <field data="name" widget="Hyperlinks" />
    </pack>
</view>

通常情况下,我们希望dsl尽可能提供足够清楚的描述,因此,有时也可能看到这样的dsl模板:

<view type="FORM" title="演示表单" name="演示模型form" model="demo.DemoModel">
    <pack widget="group">
        <field data="name" widget="Hyperlinks" />
    </pack>
</view>

由于平台并没有提供widget="group"这个组件,因此,这两个dsl模板的最终执行结果是完全一致的。

精确匹配和模糊匹配

当我们希望一个组件可以在多个视图中共用时,我们通常使用这样的注册方式:

维度:

  • 视图类型:表单(FORM)、搜索(SEARCH)
  • 字段业务类型:String类型

说明:

  • 该字段组件可以在表单(FORM)搜索(SEARCH)视图中使用
  • 并且该字段的业务类型是String类型
@SPI.ClassFactory(
  BaseFieldWidget.Token({
    viewType: [ViewType.Form, ViewType.Search],
    ttype: ModelFieldType.String
  })
)
export class FormStringFieldWidget extends BaseFieldWidget {
  ......
}

这样,我们就可以在多个视图中使用同一个组件。

母版中的标签

母版中的所有标签均使用MaskWidget作为SPIToken组件。
(旧版使用ViewWidget作为SPIToken组件,使用方式与下方描述完全不同,可略过组件注册相关内容)

维度:

  • dslNodeType:xml中的标签
  • widget:组件名称

标签组件概览

为了提供更好的灵活性,平台提供的所有标签组件,均支持classstyle属性,在无法满足业务需求的情况下,可以使用这些特性进行处理。

标签 描述
mask 母版根标签
multi-tabs 多选项卡
header 顶部栏
container 水平布局容器
sidebar 侧边栏
content 主内容区
block 块(div)
breadcrumb 面包屑
widget 母版通用组件

母版通用组件

母版通用组件全部使用widget作为标签,使用widget属性查找对应的组件。

例如:

<widget widget="app-switcher" />
标签 功能
app-switcher 应用切换组件
divider 分割线
notification 用户消息通知组件
language 多语言切换组件
user 用户信息展示组件
nav-menu 导航菜单
main-view 主视图组件;用于渲染布局和DSL等相关内容

默认母板

<mask>
    <multi-tabs />
    <header>
        <widget widget="app-switcher" />
        <block>
            <widget widget="notification" />
            <widget widget="divider" />
            <widget widget="language" />
            <widget widget="divider" />
            <widget widget="user" />
        </block>
    </header>
    <container>
        <sidebar>
            <widget widget="nav-menu" height="100%" />
        </sidebar>
        <content>
            <breadcrumb />
            <block width="100%">
                <widget width="100%" widget="main-view" />
            </block>
        </content>
    </container>
</mask>

母版标签注册

例如mask标签的注册:

@SPI.ClassFactory(MaskWidget.Token({ dslNodeType: 'mask' }))
export class MaskRootWidget extends MaskWidget {
  ......
}

在母版中是这样使用的:

<mask>
  ......
</mask>

例如multi-tabs标签的注册:

@SPI.ClassFactory(MaskWidget.Token({ dslNodeType: 'multi-tabs' }))
export class MultiTabsWidget extends MaskWidget {
  ......
}

在母版中是这样使用的:

<mask>
  <multi-tabs />
  ......
</mask>

通用母版组件的注册

例如widget="main-view"这样的组件注册:

@SPI.ClassFactory(MaskWidget.Token({ widget: 'main-view' }))
export class MainViewWidget extends MaskWidget {
  ......
}

在母版中是这样使用的:

<mask>
  <widget widget="main-view" />
  ......
</mask>

替换母版中的平台内置组件

当使用与平台内置组件注册条件一致的SPIToken进行注册时,将实现内置组件的替换。

以多选项卡(multi-tabs)为例:

@SPI.ClassFactory(MaskWidget.Token({ dslNodeType: 'multi-tabs' }))
export class CustomMultiTabsWidget extends MaskWidget {
  ......
}

标签组件和通用母版组件的区别

使用标签组件时,该组件将完全控制当前组件的渲染逻辑,与框架本身的渲染逻辑是完全一致的。

使用通用模板组件时,该组件将被默认包裹在一个特定div标签下。

下面我们通过示例来了解一下。

先定义一个vue组件,在ts组件中通过不同的注册方式,将获得不同的渲染结果。

CustomMaskHelloWorld.vue
<template>
  <div>hello world !</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'CustomMaskHelloWorld'
});
</script>

使用标签组件

CustomMaskHelloWorldWidget.ts
@SPI.ClassFactory(MaskWidget.Token({ dslNodeType: 'custom-mask-widget' }))
export class CustomMaskHelloWorldWidget extends MaskWidget {
  public initialize(props) {
    super.initialize(props);
    this.setComponent(CustomMaskHelloWorld);
    return this;
  }
}
<mask>
  <custom-mask-widget />
  ......
</mask>
最终渲染结果
<div class="k-layout-mask">
  <div>hello world !</div>
  ......
</div>

使用通用母版组件注册

CustomMaskHelloWorldWidget.ts
@SPI.ClassFactory(MaskWidget.Token({ widget: 'custom-mask-widget' }))
export class CustomMaskHelloWorldWidget extends MaskWidget {
  public initialize(props) {
    super.initialize(props);
    this.setComponent(CustomMaskHelloWorld);
    return this;
  }
}
<mask>
  <widget widget="custom-mask-widget" />
  ......
</mask>
最终渲染结果
<div class="k-layout-mask">
  <div class="k-layout-widget">
    <div>hello world !</div>
  </div>
  ......
</div>

布局中的标签

标签 Token组件 维度(dsl属性) 可选项 描述
view BaseView type ViewType[Enum] 视图标签;主要用于元数据隔离;
pack BasePackWidget viewType
widget
inline
ViewType[Enum]
[String]
[Boolean]
容器类组件标签
element BaseElementWidget viewType
widget
inline
ViewType[Enum]
[String]
[Boolean]
任意元素组件标签

默认布局

平台根据不同的视图类型内置了一些默认布局模板。参考文档

DSL中的标签

标签 Token组件 维度(dsl属性) 可选项 描述
view BaseView type ViewType[Enum] 视图标签;主要用于元数据隔离;
field BaseFieldWidget viewType
ttype
multi
widget
model
viewName
name
ViewType[Enum]
ModelFieldType[Enum]
[Boolean]
[String]
[String]
[String]
[String]
字段元数据标签
action BaseActionWidget viewType
actionType
target
widget
viewName
model
name
ViewType[Enum]
ActionType[Enum]
ViewActionTarget[Enum]
[String]
[String]
[String]
[String]
动作元数据标签
pack BasePackWidget viewType
widget
inline
ViewType[Enum]
[String]
[Boolean]
容器类组件标签
element BaseElementWidget viewType
widget
inline
ViewType[Enum]
[String]
[Boolean]
任意元素组件标签

在上表中,我们可以看到,pack组件element组件的获取维度虽然是类似的,但我们依然将其进行了拆分。原因在于,当pack组件中不包含任何元素或所有元素都隐藏时,我们希望pack组件可以同时隐藏,但element组件则无法确定是否需要这样的特性,因此element组件默认没有进行这样的隐藏处理。

写在最后

在渲染母版布局DSL时,组件SPI机制使得动态组件可以按照设计好的维度进行获取,使得动态渲染可以按照一定的规范进行二次改造。

为了使得动态渲染可以更加灵活,我们提供了自定义标签注册自定义创建SPIToken等功能,开发者们完全可以根据自己的理解设计出一套全新的模板语法,以弥补我们在平台内置方面的不足。

HTML标签、Vue组件,都是按照标签进行简单的获取组件,这在框架层面来说是完全足够的。但美中不足的是,如果全部使用标签形式来设计我们的模板语法,会导致模板语法难以阅读和理解。每个人都需要知道这个标签背后的实现逻辑才可能清楚的理解这段模板所描述的主要内容,再加上开发人员的独特理解风格,最终必然会导致对这段模板语法的解释只能由为数不多的开发人员理解。为了避免这些语法理解上的差异化,使用被设计好的维度来保证每个人的理解是尽可能保持一致的。

例如一个“糟糕”的模板:

<view type="FORM" title="演示表单" name="演示模型form" model="demo.DemoModel">
    <field data="id" invisible="true" />
    <my-widget1>
        <field data="name" />
    </my-widget1>
    <my-widget2>
        <field data="isEnabled" />
    </my-widget2>
</view>

这段模板很难推断my-widget1my-widget2这两个组件所承担的主要功能。

但如果使用这样的模板进行描述:

<view type="FORM" title="演示表单" name="演示模型form" model="demo.DemoModel">
    <field data="id" invisible="true" />
    <pack widget="my-widget1">
        <field data="name" />
    </pack>
    <pack widget="my-widget2">
        <field data="isEnabled" />
    </pack>
</view>

我们虽然同样无法确定这两个组件所承担的具体功能,但pack标签的特性将告诉我们,这两个组件至少是一个容器类的组件,它本身所承担的是描述表单布局相关功能的组件。

不过,显而易见的是,虽然我们提供了一整套标准的模板语法来描述一个页面是如何呈现的,但仍然无法阻止开发人员在实现一个具体功能的组件时,一定要按照设计规范来编码。

因此,我们希望所有的开发者们,可以遵循这套设计规范来定义组件以及实现组件。

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

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

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

相关推荐

  • 页面出现中文乱码,该怎么解决?

    可能性1: 后端读取视图的xml解析时,由于系统缺少中文字体,导致解析后出现乱码,这种问题常见于采用docker镜像部署的情况,很多基础镜像不带中文字体。 解决方案:在物理系统或者docker镜像内安装中文字体 可能性2: win环境下未指定文件的编码类型 解决方案: 启动命令中加上-Dfile.encoding=UTF-8参数 # 示例命令 java -jar -Dfile.encoding=UTF-8 pamirs-demo-boot-1.0.0-SNAPSHOT.jar -Plifecycle=INSTALL

    2023年11月1日
    1.4K00
  • 自定义视图部分区域渲染设计器的配置

    自定义视图与界面设计器配置对接 在日常开发中,我们经常会遇到自定义视图的需求。自定义视图不仅需要与平台机制结合,还要实现与界面设计器中配置的字段和动作的无缝对接。本文将介绍如何将自定义视图与界面设计器中配置的字段和动作的无缝对接,实现字段和动作的渲染。 用大白话来讲就是:当前页面一部分是自定义的,一部分是设计器生成的 代码地址 目录 自定义表单视图与字段、动作的结合 自定义表格视图与字段、动作的结合 自定义表单视图与字段、动作的结合 首先需要在界面设计器配置好对应界面,虽然配置的页面样式跟期望展示的 UI 的不一样,但是数据的分发、汇总以及动作的交互也是一致的,所以我们可以通过自定义的方式替换这个页面的 UI,但是数据以及动作,是完全可以通过平台的能力获取的。 注册表单对应的 SPI // CustomFormWidget.ts import CustomForm from './CustomForm.vue'; @SPI.ClassFactory( BaseElementWidget.Token({ viewType: ViewType.Form, widget: 'CustomFormWidget' }) ) export class CustomFormWidget extends FormWidget { public initialize(props: BaseElementObjectViewWidgetProps): this { super.initialize(props); this.setComponent(CustomForm); return this; } } <!– CustomForm.vue –> <template> <div class="custom-form-container"> <div class="custom-form-tip">自定义视图</div> </div> </template> <script lang="ts"> import { DslRender, DslRenderDefinition, PropRecordHelper } from '@kunlun/dependencies'; import { createVNode, defineComponent, onMounted, PropType, ref, VNode } from 'vue'; export default defineComponent({ inheritAttrs: false, props: { formData: { type: Object as PropType<Record<string, any>>, default: () => ({}) } } }); </script> 在上述的代码中,我们继承的是 FormWidget,那么这个页面会自动发起对应的请求,所有的数据都在 formData 中。 表单视图渲染动作 在最开始我们讲到过,当前页面是在界面设计器配置过,所有在CustomFormWidget里面是可以拿到当前页面配置的元数据信息,所以我们可以拿到界面设计器配置的字段跟动作 /// CustomFormWidget.ts @Widget.Method() protected renderActionVNodes() { // 从dsl中获取actionBar,actionBar里面包含了界面设计器配置的动作 const actionBar = this.metadataRuntimeContext.viewDsl?.widgets.find((w) => w.slot === 'actionBar'); if (actionBar) { // actionBar.widgets 就是界面设计器配置的动作,借助平台提供的DslRender.render方法,将对应的dsl转换成VNode return actionBar.widgets.map((w, index) => DslRender.render({ …w, __index: index }) ); } return null; } 因为 renderActionVNodes 方法返回的是 VNode,所以我们必须借助 vue 的 render 函数才能渲染。 <!– ActionRender.vue –> <script lang="ts"> import { defineComponent } from 'vue'; export default defineComponent({ inheritAttrs: false, props: { renderActionVNodes: { type: Function, required: true } }, render() { return this.renderActionVNodes(); } });…

    2024年9月12日
    1.4K01
  • 移动端端默认布局模板

    默认布局 表格视图(TABLE) <view type="TABLE"> <view type="SEARCH"> <element widget="search" slot="search" slotSupport="field"> <xslot name="searchFields" slotSupport="field" /> </element> </view> <pack widget="group" class="oio-m-default-view-element" style="height: 100%;flex: 1;overflow-y: hidden;" wrapperStyle="height: 100%;box-sizing:border-box;"> <pack widget="row" style="height: 100%;"> <pack widget="col" mode="full" style="min-height: 234px;height: 100%;"> <element widget="table" slot="table" slotSupport="field" checkbox="false"> <xslot name="fields" slotSupport="field" /> <element widget="rowActions" slot="rowActions" /> </element> </pack> </pack> </pack> <element widget="actionBar" slot="actionBar" slotSupport="action"> <xslot name="actions" slotSupport="action" /> </element> </view> 表单视图(FORM) <view type="FORM"> <element widget="form" slot="form"> <xslot name="fields" slotSupport="pack,field" /> </element> <element widget="actionBar" slot="actionBar" slotSupport="action"> <xslot name="actions" slotSupport="action" /> </element> </view> 详情视图(DETAIL) <view type="DETAIL"> <element widget="detail" slot="detail"> <xslot name="fields" slotSupport="pack,field" /> </element> <element widget="actionBar" slot="actionBar" slotSupport="action"> <xslot name="actions" slotSupport="action" /> </element> </view> 画廊视图(GALLERY) 默认内嵌布局(inline=true) 内嵌表格视图(TABLE) 内嵌表单视图(FORM) 内嵌详情视图(DETAIL) <view type="DETAIL"> <element widget="detail" slot="detail"> <xslot name="fields" slotSupport="pack,field" /> </element> </view>`

    2024年12月11日
    2.5K00
  • 【界面设计器】自定义字段组件实战——轮播图

    阅读之前 此文章为实战教程,已假定你熟悉了【界面设计器】较为完整的【自定义组件】相关内容。 如果在阅读过程中出现的部分概念无法理解,请自行学习相关内容。【前端】文章目录 业务背景 用户需要从【创建/编辑】页面中上传多张图片,并且在【详情】页面将这多张图片进行【轮播】展示。 业务分析 从需求来看,我们需要实现一个【轮播图】组件,并且该组件允许在【详情】视图中使用。在其他视图中,我们可以直接使用平台内置的【图片】组件,实现基础的编辑和展示功能。 名词解释 业务模型:需要进行可视化管理的存储模型或代理模型。 准备工作 你需要在某个业务模型下创建一个【表格视图】用于查看全部数据,创建【表单视图】用于创建/编辑数据,并创建【详情视图】展示必要的信息。(为了方便起见,你可以在所有视图中仅使用编码和名称两个字段) 你需要将【表格视图】绑定到某个菜单上,并通过【跳转动作】将三个视图进行关联,可以完整执行当前模型的全部【增删改查】操作。 业务模型定义 (以下仅展示本文章用到的模型字段,忽略其他无关字段。) 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
  • 前端视图的元数据与数据的传递、交互

    在阅读本篇文章之前,您需要学习以下知识点: 1: 元数据 视图的元数据 在日常开发中,我们会经常遇到自定义的字段、动作、视图需要界面设计器配置的数据,这些数据可能是当前页面的字段,也有可能动作,那么如何获取呢? 视图元数据分为两种:1: 当前视图(metadataRuntimeContext)2: 根视图(rootRuntimeContext) 那么这两种类型怎么区分呢? 举个例子:1: 如果当前字段是在表单中,那么当前视图就是表单,根视图就表单的父级视图,如果只有一个表单视图,那么当前视图就是根视图。2: 如果当前视图是表单,但是表单里面有个表格,对于表格字段而言,当前视图就是表格,根视图就是表单。 当前视图的元数据(metadataRuntimeContext) 在前端,我们通过 metadataRuntimeContext 来获取视图的元数据,例如: export class CustomFormStringFieldSingleWidget extends FormStringFieldSingleWidget { protected mounted(): void { console.log(this.metadataRuntimeContext); } /** * 界面设计器配置的动作 */ @Widget.Reactive() protected get modelActions() { return this.metadataRuntimeContext.model.modelActions } /** * 界面设计器配置的字段 */ @Widget.Reactive() protected get modelFields() { return this.metadataRuntimeContext.model.modelFields } } 属性名 类型 可选性 描述 viewAction RuntimeViewAction 是 运行时跳转动作(通过跳转动作创建的运行时上下文具备该属性) module RuntimeModule 否 运行时模块 model RuntimeModel 否 运行时模型 view RuntimeView 否 运行时视图 viewLayout DslDefinition \| undefined 否 视图布局 DSL,从运行时视图解析获得 viewDsl DslDefinition \| undefined 否 视图模板 DSL,从运行时视图解析获得 viewTemplate DslDefinition 否 视图最终执行 DSL,从运行时视图解析获得或根据布局 DSL 和模板 DSL 合并生成 getModel (model: string, isBelong?: boolean) => GetModelResult \| undefined 否 获取模型,返回获取的模型和所在的运行时上下文 getModelField (data: string, isBelong?: boolean) => GetModelFieldResult \| undefined 否 获取模型字段,返回获取的模型字段和所在的运行时上下文 getRequestModelFields (options?: GetRequestModelFieldsOptions) => RequestModelField[] 否 获取请求字段 getDefaultValue () => Promise<Record<string, unknown>> 否 获取默认值 getInitialValue () => Promise<Record<string, unknown>> 否 获取初始值 运行时模型(model) 属性名 类型 可选性 描述 id string 是 模型 id model string 否 模型编码 name string 否 技术名称 modelFields RuntimeModelField[] 否 模型字段 modelActions RuntimeAction[] 否 模型动作 type ModelType 是 模型类型 module string 是 模块编码 moduleName string 否 模块名称 moduleDefinition RuntimeModule 是…

    2024年10月8日
    2.9K00

Leave a Reply

登录后才能评论