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

阅读之前

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

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

业务背景

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

业务分析

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

名词解释

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

准备工作

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

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

业务模型定义

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

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

``` html
<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 &#039;ant-design-vue&#039;;
import { computed, defineComponent, PropType } from &#039;vue&#039;;

export default defineComponent({
name: &#039;Carousel&#039;,
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

``` ts
export enum CarouselPosition {
top = &#039;top&#039;,
bottom = &#039;bottom&#039;,
left = &#039;left&#039;,
right = &#039;right&#039;
}
```

DetailStringMultiCarouselFieldWidget.ts

``` ts
import { BooleanHelper, FormFieldWidget, ModelFieldType, SPI, ViewType, Widget } from &#039;@kunlun/dependencies&#039;;
import Carousel from &#039;./Carousel.vue&#039;;
import { CarouselPosition } from &#039;./typing&#039;;

@SPI.ClassFactory(
FormFieldWidget.Token({
viewType: ViewType.Detail,
ttype: ModelFieldType.String,
widget: &#039;Carousel&#039;,
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

``` html
<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 &#039;ant-design-vue&#039;;
import { computed, defineComponent, PropType } from &#039;vue&#039;;
import { CarouselPosition } from &#039;./typing&#039;;

export default defineComponent({
name: &#039;Carousel&#039;,
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

``` html
<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 &#039;@kunlun/dependencies&#039;;
import { Carousel as ACarousel } from &#039;ant-design-vue&#039;;
import { computed, defineComponent, PropType } from &#039;vue&#039;;
import { CarouselPosition } from &#039;./typing&#039;;

export default defineComponent({
name: &#039;Carousel&#039;,
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) || &#039;160px&#039;);

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

``` ts
import { BooleanHelper, FormFieldWidget, ModelFieldType, SPI, ViewType, Widget } from &#039;@kunlun/dependencies&#039;;
import Carousel from &#039;./Carousel.vue&#039;;
import { CarouselPosition } from &#039;./typing&#039;;

@SPI.ClassFactory(
FormFieldWidget.Token({
viewType: ViewType.Detail,
ttype: ModelFieldType.String,
widget: &#039;Carousel&#039;,
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低代码应用平台体验

(0)
数式-海波的头像数式-海波数式管理员
上一篇 2023年6月20日 下午4:07
下一篇 2023年11月2日 下午1:58

相关推荐

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

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

    2023年11月1日
    4200
  • oio-spin 加载中

    用于页面和区块的加载中状态。 何时使用 页面局部处于等待异步数据或正在渲染过程时,合适的加载动效会有效缓解用户的焦虑。 API 参数 说明 类型 默认值 版本 delay 延迟显示加载效果的时间(防止闪烁) number (毫秒) – loading 是否为加载中状态 boolean true wrapperClassName 包装器的类属性 string …

    2023年12月18日
    6300
  • 【界面设计器】自定义字段组件实战——千分位输入框

    阅读之前 此文章为实战教程,已假定你熟悉了【界面设计器】较为完整的【自定义组件】相关内容。 如果在阅读过程中出现的部分概念无法理解,请自行学习相关内容。【前端】文章目录 业务背景 用户在输入【金额】字段时,实时展示千分位格式。 业务分析 从需求来看,我们需要实现一个【千分位】组件,并且该组件允许在【表单】视图中使用。 扩展实现:该组件虽然仅要求在【表单】中使…

    2023年11月1日
    3300
  • 【前端】移动端工程结构最佳实践(v4)

    阅读之前 你应该: 了解node与npm相关内容 了解lerna包管理工具的相关内容 官方文档 了解git仓库的相关内容 了解rollup的相关内容 工程结构包示例 Vue项目结构包下载 工程结构详解 工程结构 ├── packages │   ├── kunlun-mobile-boot │   │   ├── package.json │   │   ├…

    前端 2023年11月1日
    8300
  • 如何在Oinone 根据主题实现自定义组件样式

    在页面交互中,样式的变化是前端核心工作之一。本文介绍如何在Oinone平台中根据主题变化自定义组件样式。 介绍 Oinone平台提供了六种不同的主题设置,浅色大主题、浅色中主题、浅色小主题、深色大主题、深色中主题、深色小主题,默认采用浅色中主题。本文旨在指导如何在线或通过代码修改这些主题,以满足个性化需求。 基础知识 Oinone平台的默认主题为浅色中主题,…

    2024年2月26日
    4100

发表回复

登录后才能评论