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

阅读之前

此文章为实战教程,已假定你熟悉了【界面设计器】较为完整的【自定义组件】相关内容。

如果在阅读过程中出现的部分概念无法理解,请自行学习相关内容。【前端】文章目录

业务背景

用户需要从【创建/编辑】页面中上传多张图片,并且在【详情】页面将这多张图片进行【轮播】展示。

业务分析

从需求来看,我们需要实现一个【轮播图】组件,并且该组件允许在【详情】视图中使用。在其他视图中,我们可以直接使用平台内置的【图片】组件,实现基础的编辑和展示功能。

名词解释

  • 业务模型:需要进行可视化管理的存储模型或代理模型。

准备工作

你需要在某个业务模型下创建一个【表格视图】用于查看全部数据,创建【表单视图】用于创建/编辑数据,并创建【详情视图】展示必要的信息。(为了方便起见,你可以在所有视图中仅使用编码和名称两个字段)

你需要将【表格视图】绑定到某个菜单上,并通过【跳转动作】将三个视图进行关联,可以完整执行当前模型的全部【增删改查】操作。

业务模型定义

(以下仅展示本文章用到的模型字段,忽略其他无关字段。)

DemoModel
名称 API名称 业务类型 是否多值 长度(单值长度)
编码 code 文本 否 128
名称 name 文本 否 128
轮播图 carouselImages 文本 是 512

实现页面效果展示

表格视图

image.png

表单视图-创建

image.png

表单视图-编辑

image.png

详情视图

image.png

根据业务背景添加轮播图字段到所有视图

轮播图字段信息:

  • 字段业务类型:文本
  • 多值:是

使用组件:图片

无代码模型

在模型设计器创建轮播图字段,并从【组件库】-【模型】拖放至视图中即可。

PS:这里需要注意的是,在模型设计器中需要切换至专家模式,并确认字段长度为512,否则当URL超长时将无法保存。

低代码模型

与服务端同学确认字段,并从【组件库】-【模型】中拖放至视图中即可。

将上图中的【演示】数据进行【编辑】,并上传三张图片,在【详情视图】查看默认展示效果。

演示图片下载

image.png

创建组件、元件

准备工作完成后,我们需要根据【业务背景】确定【组件】以及【元件】相关信息,并在【界面设计器】中进行创建。

以下操作过程将省略详细步骤,仅展示可能需要确认的关键页面。

创建轮播图组件

image.png

创建轮播图元件

根据业务背景,我们需要根据模型中的字段确定业务类型,在这个场景中,可以使用如下配置。(暂时可以不进行属性面板的设计)

image.png

在【详情视图】中将【轮播图字段】的组件切换为我们新创建的【轮播图组件】

image.png

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文件在【界面设计器】的【低无一体】进行上传,就可以在【设计器环境】中正常使用了。

image.png

设计轮播图的属性面板

通过我们使用的a-carousel组件,我们发现组件中提供了很多【属性】或【功能】可以进行配置,比如是否自动切换(autoplay)、面板指示点位置(dotPosition)、是否显示面板指示点(dots)等。在这里我们将对这三个属性的配置方式进行演示,其他更多属性可以自行设计并开发。

我们可以在【界面设计器】的【属性面板设计】中根据这三个属性的字段类型确定以下信息:

功能 API名称 业务类型 选用组件 可选项
是否自动切换 autoplay 布尔 开关 -
是否显示面板指示点 dots 布尔 开关 -
面板指示点位置 dotPosition 数据字典 下拉单选 上方(top)、下方(bottom)、左侧(left)、右侧(right)

确定了这些信息后,我们在【属性面板设计】中拖入对应组件,并创建【指定API名称】的字段。

PS:数据字典类型的字段需要先在模型设计器中创建对应的数据字典,才能创建该字段。由于设计器本身的依赖关系,建议将数据字典创建在【资源】模块中,这样才可以在设计器被选中。

数据字典

image.png

image.png

PS:前端获取的值为【API名称】,并非【字典项值】。

属性面板设计

image.png

在SDK中为组件新增的属性补充代码实现

typing.ts
export enum CarouselPosition {
  top = 'top',
  bottom = 'bottom',
  left = 'left',
  right = 'right'
}
DetailStringMultiCarouselFieldWidget.ts
import { BooleanHelper, FormFieldWidget, ModelFieldType, SPI, ViewType, Widget } from '@kunlun/dependencies';
import Carousel from './Carousel.vue';
import { CarouselPosition } from './typing';

@SPI.ClassFactory(
  FormFieldWidget.Token({
    viewType: ViewType.Detail,
    ttype: ModelFieldType.String,
    widget: 'Carousel',
    multi: true
  })
)
export class DetailStringMultiCarouselFieldWidget extends FormFieldWidget {
  public initialize(props) {
    super.initialize(props);
    this.setComponent(Carousel);
    return this;
  }

  @Widget.Reactive()
  protected get autoplay() {
    return BooleanHelper.toBoolean(this.getDsl().autoplay);
  }

  @Widget.Reactive()
  protected get dots() {
    return BooleanHelper.toBoolean(this.getDsl().dots);
  }

  @Widget.Reactive()
  protected get dotPosition(): CarouselPosition | undefined {
    return this.getDsl().dotPosition;
  }
}
Carousel.vue
<template>
  <a-carousel class="carousel" effect="fade" :autoplay="autoplay" :dots="dots" :dotPosition="dotPosition">
    <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';
import { CarouselPosition } from './typing';

export default defineComponent({
  name: 'Carousel',
  components: {
    ACarousel
  },
  props: {
    value: {
      type: Array as PropType<string[]>
    },
    autoplay: {
      type: Boolean
    },
    dots: {
      type: Boolean
    },
    dotPosition: {
      type: String as PropType<CarouselPosition>
    }
  },
  setup(props) {
    const images = computed(() => props.value || []);

    const dotPosition = computed(() => props.dotPosition?.toLowerCase());

    return {
      images,
      dotPosition
    };
  }
});
</script>
<style lang="scss">
.carousel.ant-carousel {
  .slick-slide,
  .slick-vertical {
    height: 160px;
  }

  .slick-slide {
    & > div,
    .carousel-item {
      width: 100%;
      height: 100%;
    }

    img {
      max-width: 100%;
      max-height: 100%;
      margin: auto;
    }
  }
}
</style>

开发完成后,我们将重新打包生成的JS文件和CSS文件在【界面设计器】的【低无一体】进行上传,就可以在【设计器环境】中正常使用了。

设计组件优化

在执行到以上步骤之后,我们发现执行页面和设计页面的属性面板都可以正常运行,美中不足的是,我们无法在设计器直观看到预览效果。为了解决这一问题,我们需要对设计组件进行相关的优化。

细心的同学可能也会发现,我们在组件中对高度的设定是160px,这个设置会导致用户无法根据需求进行定制。不仅如此,由于宽度属性未进行配置,用户也无法根据需求调整宽度。

为了解决上述问题,我们可以将属性面板稍作调整。

宽度使用内置的数据字典类型的宽度即可,高度需要我们新建一个高度(height)的整数字段,并添加后缀提示用户高度的单位。

效果如下图所示:

image.png

接下来,我们仅需实现【预览效果】和【高度】属性即可,内置的【宽度】属性是通过外部控制的,组件本身无需关心。

实现思路:

  • 由于设计器的预览功能移除了Class Component(ts)组件的相关功能,而是直接将属性面板的值传递到Vue组件中,因此,我们需要在Vue组件中判断当前组件是否在设计器的预览环境中,并且根据这个判断提供相应的预览效果。我们可以使用代码示例中的isDesignComponent属性,来实现这一功能。
  • 高度在用户输入时为【整数】,因此是没有单位的,可以使用平台内置的StyleHelper#px方法进行转换。
Carousel.vue
<template>
  <a-carousel class="carousel" effect="fade" :autoplay="autoplay" :dots="dots" :dotPosition="dotPosition">
    <template v-if="isDesignComponent">
      <div class="carousel-item carousel-item-demo"><h3>1</h3></div>
      <div class="carousel-item carousel-item-demo"><h3>2</h3></div>
      <div class="carousel-item carousel-item-demo"><h3>3</h3></div>
    </template>
    <template v-else>
      <div class="carousel-item" v-for="image in images" :key="image">
        <img :src="image" :alt="image" />
      </div>
    </template>
  </a-carousel>
</template>
<script lang="ts">
import { StyleHelper } from '@kunlun/dependencies';
import { Carousel as ACarousel } from 'ant-design-vue';
import { computed, defineComponent, PropType } from 'vue';
import { CarouselPosition } from './typing';

export default defineComponent({
  name: 'Carousel',
  components: {
    ACarousel
  },
  props: {
    value: {
      type: Array as PropType<string[]>
    },
    autoplay: {
      type: Boolean
    },
    dots: {
      type: Boolean
    },
    dotPosition: {
      type: String as PropType<CarouselPosition>
    },
    height: {
      type: [Number, String]
    },
    isDesignComponent: {
      type: Boolean,
      default: true
    }
  },
  setup(props) {
    const images = computed(() => props.value || []);

    const dotPosition = computed(() => props.dotPosition?.toLowerCase());

    const height = computed(() => StyleHelper.px(props.height) || '160px');

    return {
      images,
      dotPosition,
      height
    };
  }
});
</script>
<style lang="scss">
.carousel.ant-carousel {
  .slick-slide,
  .slick-vertical {
    height: v-bind(height);
  }

  .slick-slide {
    & > div,
    .carousel-item {
      width: 100%;
      height: 100%;
    }

    img {
      max-width: 100%;
      max-height: 100%;
      margin: auto;
    }
  }

  .carousel-item-demo {
    display: flex !important;
    align-items: center;
    justify-content: center;
    background-color: #364d79;

    h3 {
      color: #ffffff;
    }
  }
}
</style>
DetailStringMultiCarouselFieldWidget.ts
import { BooleanHelper, FormFieldWidget, ModelFieldType, SPI, ViewType, Widget } from '@kunlun/dependencies';
import Carousel from './Carousel.vue';
import { CarouselPosition } from './typing';

@SPI.ClassFactory(
  FormFieldWidget.Token({
    viewType: ViewType.Detail,
    ttype: ModelFieldType.String,
    widget: 'Carousel',
    multi: true
  })
)
export class DetailStringMultiCarouselFieldWidget extends FormFieldWidget {
  public initialize(props) {
    super.initialize(props);
    this.setComponent(Carousel);
    return this;
  }

  @Widget.Reactive()
  protected get autoplay() {
    return BooleanHelper.toBoolean(this.getDsl().autoplay);
  }

  @Widget.Reactive()
  protected get dots() {
    return BooleanHelper.toBoolean(this.getDsl().dots);
  }

  @Widget.Reactive()
  protected get dotPosition(): CarouselPosition | undefined {
    return this.getDsl().dotPosition;
  }

  @Widget.Reactive()
  protected get height(): number | string | undefined {
    return this.getDsl().height;
  }

  @Widget.Reactive()
  protected get isDesignComponent() {
    return false;
  }
}

开发完成后,我们将重新打包生成的JS文件和CSS文件在【界面设计器】的【低无一体】进行上传,就可以在【设计器环境】中正常使用了。

除了上述我们演示的功能实现外,其实轮播图组件还可以有其他更多的功能。比如:轮播图中的图片展示方式可以是拉伸、平铺、等比缩放等,轮播图分页样式,轮播图是否展示左右翻页按钮以及翻页按钮的样式,轮播动画效果等等。

结语

至此,我们已经基本完成了一个简单的轮播图组件。

从轮播图组件的实现来看,对我们传统开发思维提出了一些挑战。

在传统组件开发中,我们开发的一个一个Vue组件都是提供给其他开发人员使用的,甚至绝大多数组件是由于单个组件的复杂性进行拆分的一个一个小组件。

在传统开发思维中,我们很难感受到当一个组件具备用户输入能力时,它所展示的最终效果。甚至在某些场景中,用户输入的结果并非是我们所能预知的。

对于低代码组件而言,其使得用户对组件可以有一定程度的输入能力。正是由于存在这样的差异,我们更需要站在用户角度去思考组件该通过怎样的输入得到怎样的输出。

从结果上来看,低代码组件为用户提供的输入能力可以使得组件应用的场景更加广泛,再结合界面设计器的在线设计能力,可以促使我们在开发组件时使其具有更高的复用能力。

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

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

Like (0)
数式-海波's avatar数式-海波数式管理员
Previous 2023年6月20日 pm4:07
Next 2023年11月2日 pm1:58

相关推荐

  • 前端视图的元数据与数据的传递、交互

    在阅读本篇文章之前,您需要学习以下知识点: 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日
    3.9K00
  • oio-dropdown 下拉菜单

    向下弹出的列表。 何时使用 当页面上的操作命令过多时,用此组件可以收纳操作元素。点击或移入触点,会出现一个下拉菜单。可在列表中进行选择,并执行相应的命令。 用于收罗一组命令操作。 Select 用于选择,而 Dropdown 是命令集合。 API 属性如下 参数 说明 类型 默认值 destroyOnHide 关闭后是否销毁 Dropdown boolean false disabled 菜单是否禁用 boolean – getTriggerContainer 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。 Function(triggerNode) () => document.body overlay(v-slot) 菜单 Menu – overlayClassName 下拉根元素的类名称 string – overlayStyle 下拉根元素的样式 object – placement 菜单弹出位置 bottomLeft | bottom | bottomRight | topLeft | top | topRight bottomLeft trigger 触发下拉的行为, 移动端不支持 hover Array<click|hover|contextmenu> ['hover'] visible(v-model:visible) 菜单是否显示 boolean – 事件 事件名称 说明 回调参数 onUpdateValue 菜单显示状态改变时调用,参数为 visible。点击菜单按钮导致的消失不会触发 function(visible)

    2023年12月18日
    2.1K00
  • 母版-布局-DSL 渲染基础(v4)

    概述 不论是母版、布局还是DSL,我们统一使用XML进行定义,可以更好的提供结构化表述。 参考文档: XML百度百科 XML语法参考 下面文档中未介绍到的Mask母版和Layout布局,可以去数据库中base库的表base_layout_definition和base_mask_definition的template字段查看 母版 确定了主题、非主内容分发区域所使用组件和主内容分发区域联动方式的页面配置。 母版内容分为主内容分发区域与非主内容分发区域。非主内容分发区域一般包含顶部栏、底部栏和侧边栏。侧边栏可以放置菜单,菜单与主内容分发区域内容进行联动。 默认母板 <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:母版根标签 multi-tabs:多选项卡 header:顶部栏 container:容器 sldebar:侧边栏 nav-menu:导航菜单 content:主内容 breadcrumb:面包屑 block:div块 main-view:主视图;用于渲染布局和DSL等相关内容; 母版将整个页面的大体框架进行了描述,接下来将主要介绍布局和DSL是如何在main-view中进行渲染的。关于自定义母版组件的相关内容 点击查看 布局 布局是将页面拆分成一个一个的小单元,按照从上到下、从左到右进行顺序排列 布局主要用于控制页面中元素的展示的相对位置,原则上不建议将元数据相关内容在布局中进行使用,可最大化布局的利用率。 默认表格视图(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:视图;用于定义当前视图类型,不同的视图类型会有不同的数据交互,以及渲染不同的组件。 pack:容器类型相关组件。 element:元素组件;包含各种各样的组件,根据组件实现有不同的作用。 xslot:DSL插槽;用于将DSL中定义的模板分别插入到对应的槽中; 特别的,任何XML标签上的slot属性都具备DSL插槽的全部能力。当学习完DSL相关内容后,我们将会对DSL插槽有比较清晰的理解。 PS:在下面的内容中,将使用该布局进行描述。 DSL 准备工作 为了方便描述DSL和元数据之间的关系,我们需要先定义一个简单模型,这个模型里面包含字段和动作。这些通常是服务端定义的。(对服务端不感兴趣的同学可以跳过代码部分) DemoModel.java @Model.model(DemoModel.MODEL_MODEL) @Model(displayName = "演示模型", labelFields = {"name"}) public class DemoModel extends IdModel { private static final long serialVersionUID = -7211802945795866154L; public static final String MODEL_MODEL = "demo.DemoModel"; @Field(displayName = "名称") private String name; @Field(displayName = "是否启用") private Boolean isEnabled; } DemoModelAction.java @Model.model(DemoModel.MODEL_MODEL) @UxRouteButton( action = @UxAction(name = "redirectCreatePage", displayName = "创建", contextType = ActionContextTypeEnum.CONTEXT_FREE), value = @UxRoute(model =…

    2023年11月1日
    3.1K10
  • 前端表格复制

    我们可能会遇到表格复制的需求,也就是表格填写的时候,不是增加一行数据,而是增加一个表格。以下是代码实现和原理分析。 代码实现 在 boot 工程的 main.ts 中加入以下代码 import { registerDesignerFieldWidgetCreator, selectorDesignerFieldWidgetCreator } from '@oinone/kunlun-ui-designer-dependencies'; // 注册无代码组件,将表头分组的无代码组件,注册成字段表格组件 registerDesignerFieldWidgetCreator( { widget: 'DynamicCreateTable' }, selectorDesignerFieldWidgetCreator({ widget: TABLE_WIDGET })! ); DynamicCreateTableWidget 动态添加表格 ts 组件 import { FormO2MTableFieldWidget, Widget, DslDefinition, RuntimeView, SubmitValue, BaseFieldWidget, ModelFieldType, SPI, ViewType, ActiveRecord, uniqueKeyGenerator } from '@oinone/kunlun-dependencies'; import { MyMetadataViewWidget } from './MyMetadataViewWidget'; import DynamicCreateTable from './DynamicCreateTable.vue'; @SPI.ClassFactory( BaseFieldWidget.Token({ viewType: ViewType.Form, ttype: ModelFieldType.OneToMany, widget: 'DynamicCreateTable' }) ) export class DynamicCreateTableWidget extends FormO2MTableFieldWidget { public myMetadataViewWidget: MyMetadataViewWidget[] = []; @Widget.Reactive() public myMetadataViewWidgetLength = 0; @Widget.Reactive() public myMetadataViewWidgetKeys: string[] = []; protected props: Record<string, unknown> = {}; public initialize(props) { super.initialize(props); this.props = props; this.setComponent(DynamicCreateTable); return this; } // region 创建动态表格 @Widget.Method() public async createTableWidget(record: ActiveRecord) { const index = this.myMetadataViewWidget.length; const handle = uniqueKeyGenerator(); const slotKey = `MyMetadataViewWidget_${handle}`; const widget = this.createWidget( new MyMetadataViewWidget(handle), slotKey, // 插槽名称 { subIndex: index, metadataHandle: this.metadataHandle, rootHandle: this.rootHandle, automatic: true, internal: true, inline: true } ); this.initDynamicSubview(this.props, widget); widget.setData(record); this.myMetadataViewWidgetLength++; this.myMetadataViewWidgetKeys.push(slotKey); this.myMetadataViewWidget.push(widget); } protected initDynamicSubview(props: Record<string, unknown>, widget: MyMetadataViewWidget) { const { currentViewDsl } = this; let viewDsl = currentViewDsl; if (!viewDsl) { viewDsl = this.getViewDsl(props)…

    2025年7月21日
    1.0K00
  • 表格字段配置Switch开关

    在业务开发中,我们经常会遇到表格中有个Switch开关组件: 那么如何通过界面设计器配置这个组件呢,下面让我们一起来学习下吧。 设计布尔类型的组件 1: 首先在界面设计器的组件区域添加一个组件。 2: 我们给这个组件添加一个元件,元件的配置必须跟下面的一致 3: 给元件添加属性配置 拖拽一个单行文本字段, 字段编码必须是truthyAction,代表的是该字段为true的时候要执行的动作 再拖拽一个单行文本字段, 字段编码必须是falsyAction,代表的是该字段为false的时候要执行的动作 4: 发布元件,然后再去对应的界面设计器页面,将对应的字段切换成刚刚设计的组件 5: 发布界面设计器,染红我们可以在运行时的页面看到效果 当switch切换的时候,会执行对应的action

    2023年11月21日
    3.0K00

Leave a Reply

Please Login to Comment