455 lines
15 KiB
Markdown
455 lines
15 KiB
Markdown
|
|
# 低码移动端组件开发指南
|
|||
|
|
|
|||
|
|
## 概述
|
|||
|
|
|
|||
|
|
本文档介绍低码平台的移动端组件开发指南。移动端组件基于 Vue 进行开发,以 H5 形式运行,组件库采用 Vant。
|
|||
|
|
|
|||
|
|
**阅读前须知:** 在阅读本文档前,请先阅读《字段类型与物料组件开发指南.md》。移动端开发与浏览器端在整体设计、开发流程上保持一致,主要差异在于底层规划、文件目录、运行和初始化等差异。对于组件开发者,只需了按移动端的目录标准开发组件、配置组件即可。
|
|||
|
|
|
|||
|
|
本文档主要介绍移动端开发中的差异部分。关于功能开发的详细说明、开发标准与规范,请参考《字段类型与物料组件开发指南.md》。
|
|||
|
|
|
|||
|
|
## 目录结构
|
|||
|
|
|
|||
|
|
移动端组件的相关配置文件位于 `packages/widget-mobile/src/config` 目录下,主要包含以下文件:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
packages/widget-mobile/src/config/
|
|||
|
|
├── index.ts # 配置入口文件,注册移动端设计器和预览器配置
|
|||
|
|
├── Material.ts # 物料面板配置,定义组件在物料面板中的分类和展示
|
|||
|
|
├── RegisterWidget.ts # 组件注册配置,注册移动端组件的实现
|
|||
|
|
├── RegisterWidgetHelper.ts # 组件辅助工具注册配置
|
|||
|
|
├── RegisterSettings.ts # 组件设置配置
|
|||
|
|
├── WidgetType.ts # 组件类型枚举定义
|
|||
|
|
├── widget/ # 组件实现目录
|
|||
|
|
│ ├── Root.vue # 页面根容器组件
|
|||
|
|
│ └── TestWidget.vue # 测试组件示例
|
|||
|
|
└── widget-helper/ # 组件辅助工具目录
|
|||
|
|
└── TestWidgetHelper.ts # 测试组件辅助工具示例
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 主要文件说明
|
|||
|
|
|
|||
|
|
- **index.ts**: 配置入口,提供 `setMobileConfigAll()` 函数,用于注册设计器和预览器的配置。
|
|||
|
|
- **Material.ts**: 定义组件在物料面板中的分类组织,通过 `getMaterialPanelConfig()` 返回物料配置数组。
|
|||
|
|
- **RegisterWidget.ts**: 注册移动端组件的实现,将组件类型与对应的 Vue 组件进行映射。
|
|||
|
|
- **WidgetType.ts**: 定义所有移动端组件的类型枚举,需要与后端定义的控件类型保持一致。
|
|||
|
|
|
|||
|
|
## 开发流程
|
|||
|
|
|
|||
|
|
添加一个新的移动端物料组件,需要完成以下 6 个步骤的配置。下面以 `TestWidget` 组件为例,详细说明每个步骤的操作。
|
|||
|
|
|
|||
|
|
### 步骤 1:添加组件类型枚举
|
|||
|
|
|
|||
|
|
在 `WidgetType.ts` 文件中添加组件类型枚举,枚举值需要与后端定义的控件类型保持一致。
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
export enum WidgetType {
|
|||
|
|
// 测试组件
|
|||
|
|
TestWidget = "TEST_WIDGET",
|
|||
|
|
|
|||
|
|
// 你的新组件
|
|||
|
|
// YourWidget = 'YOUR_WIDGET',
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 步骤 2:创建 Vue 组件
|
|||
|
|
|
|||
|
|
在 `widget/` 目录下创建组件的 Vue 文件,例如 `YourWidget.vue`。
|
|||
|
|
|
|||
|
|
```vue
|
|||
|
|
<script setup lang="ts">
|
|||
|
|
import { Rate } from "vant";
|
|||
|
|
import { ref } from "vue";
|
|||
|
|
|
|||
|
|
const value = ref(3);
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<template>
|
|||
|
|
<div class="your-widget-container">
|
|||
|
|
<!-- 组件内容 -->
|
|||
|
|
<Rate v-model="value" />
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<style lang="css" scoped>
|
|||
|
|
.your-widget-container {
|
|||
|
|
/* 组件样式 */
|
|||
|
|
}
|
|||
|
|
</style>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**注意事项:**
|
|||
|
|
|
|||
|
|
- 组件使用 Vant 作为 UI 组件库
|
|||
|
|
- 组件应支持响应式设计,适配移动端屏幕
|
|||
|
|
|
|||
|
|
### 步骤 3:创建 WidgetHelper 配置
|
|||
|
|
|
|||
|
|
在 `widget-helper/` 目录下创建 Helper 配置文件,例如 `YourWidgetHelper.ts`。
|
|||
|
|
|
|||
|
|
Helper 文件需要实现 `WidgetHelper` 接口,主要包含以下方法:
|
|||
|
|
|
|||
|
|
- `buildMaterialConfig()`: 定义组件在物料面板中的展示信息(图标、名称等)
|
|||
|
|
- `buildSettings()`: 定义组件的设置面板配置(属性、样式、事件等)
|
|||
|
|
- `buildWidget()`: 定义组件的默认 Schema 结构
|
|||
|
|
- `buildSchemaController()`: 定义组件的 Schema 控制器方法
|
|||
|
|
|
|||
|
|
参考示例:
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
import type { WidgetHelper } from "~@/types/WidgetHelper";
|
|||
|
|
import { WidgetType } from "../WidgetType";
|
|||
|
|
|
|||
|
|
const config: WidgetHelper = {
|
|||
|
|
type: WidgetType.TestWidget,
|
|||
|
|
buildMaterialConfig() {
|
|||
|
|
// 返回物料配置
|
|||
|
|
},
|
|||
|
|
buildSettings(params) {
|
|||
|
|
// 返回设置面板配置
|
|||
|
|
},
|
|||
|
|
buildWidget(params) {
|
|||
|
|
// 返回组件默认 Schema
|
|||
|
|
},
|
|||
|
|
buildSchemaController() {
|
|||
|
|
// 返回 Schema 控制器方法
|
|||
|
|
},
|
|||
|
|
// 其他可选方法...
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export default config;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**详细说明请参考:** `widget-helper/TestWidgetHelper.ts` 文件的完整实现。
|
|||
|
|
|
|||
|
|
### 步骤 4:注册组件
|
|||
|
|
|
|||
|
|
在 `RegisterWidget.ts` 文件的 `SYS_COMPS_MAP` 对象中添加组件映射。
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
import TestWidget from "./widget/TestWidget.vue";
|
|||
|
|
import { WidgetType } from "./WidgetType";
|
|||
|
|
|
|||
|
|
export const SYS_COMPS_MAP: Recordable<RegisterFormComponentOptions> = {
|
|||
|
|
[WidgetType.TestWidget]: {
|
|||
|
|
instance: TestWidget,
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 你的新组件
|
|||
|
|
// [WidgetType.YourWidget]: {
|
|||
|
|
// instance: YourWidget,
|
|||
|
|
// },
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 步骤 5:注册 Helper
|
|||
|
|
|
|||
|
|
在 `RegisterWidgetHelper.ts` 文件的 `SYS_MOBILE_WIDGET_HELPERS` 数组中添加 Helper 的导入和注册。
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
import TestWidgetHelper from "./widget-helper/TestWidgetHelper";
|
|||
|
|
|
|||
|
|
export const SYS_MOBILE_WIDGET_HELPERS = [
|
|||
|
|
TestWidgetHelper,
|
|||
|
|
|
|||
|
|
// 你的新组件
|
|||
|
|
// YourWidgetHelper,
|
|||
|
|
];
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 步骤 6:添加到物料面板
|
|||
|
|
|
|||
|
|
在 `Material.ts` 文件的 `getMaterialPanelConfig()` 函数中添加组件到物料面板配置。
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
import { MaterialCollapseTypeEnum } from "~@/types/MaterialPanelType";
|
|||
|
|
import { WidgetType } from "./WidgetType";
|
|||
|
|
|
|||
|
|
export function getMaterialPanelConfig() {
|
|||
|
|
return [
|
|||
|
|
{
|
|||
|
|
name: MaterialCollapseTypeEnum.Common, // 通用组件分类
|
|||
|
|
widgetTypeList: [
|
|||
|
|
WidgetType.TestWidget,
|
|||
|
|
// WidgetType.YourWidget, // 你的新组件
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**分类说明:**
|
|||
|
|
|
|||
|
|
- `MaterialCollapseTypeEnum.Common`: 通用组件
|
|||
|
|
- 其他分类可根据组件特性选择合适的分类
|
|||
|
|
|
|||
|
|
### 完整开发清单
|
|||
|
|
|
|||
|
|
开发新组件时,请按以下清单逐项完成:
|
|||
|
|
|
|||
|
|
- [ ] 步骤 1:在 `WidgetType.ts` 中添加枚举
|
|||
|
|
- [ ] 步骤 2:创建 `widget/YourWidget.vue` 组件文件
|
|||
|
|
- [ ] 步骤 3:创建 `widget-helper/YourWidgetHelper.ts` Helper 文件
|
|||
|
|
- [ ] 步骤 4:在 `RegisterWidget.ts` 中注册组件
|
|||
|
|
- [ ] 步骤 5:在 `RegisterWidgetHelper.ts` 中注册 Helper
|
|||
|
|
- [ ] 步骤 6:在 `Material.ts` 中添加到物料面板配置
|
|||
|
|
|
|||
|
|
### 参考示例
|
|||
|
|
|
|||
|
|
完整示例可参考 `TestWidget` 组件:
|
|||
|
|
|
|||
|
|
- 组件实现:`widget/TestWidget.vue`
|
|||
|
|
- Helper 配置:`widget-helper/TestWidgetHelper.ts`
|
|||
|
|
- 类型定义:`WidgetType.TestWidget`
|
|||
|
|
- 组件注册:`RegisterWidget.ts` 中的 `SYS_COMPS_MAP[WidgetType.TestWidget]`
|
|||
|
|
- Helper 注册:`RegisterWidgetHelper.ts` 中的 `SYS_MOBILE_WIDGET_HELPERS`
|
|||
|
|
- 物料配置:`Material.ts` 中的物料面板配置
|
|||
|
|
|
|||
|
|
## BaseField / Field 三种使用方式说明
|
|||
|
|
|
|||
|
|
移动端字段容器在实际业务中有三种典型用法,本节重点说明 **`BaseField` 与 Vant `Field` 的配置与使用模式**。
|
|||
|
|
|
|||
|
|
整体上可以分为三种模式:
|
|||
|
|
|
|||
|
|
- **模式一:基础模式(只用 BaseField,完全由 Schema 驱动)**
|
|||
|
|
- **模式二:插槽扩展模式(在 BaseField 上增加自定义插槽)**
|
|||
|
|
- **模式三:完全自定义模式(基于 useField + Vant Field 自行开发)**
|
|||
|
|
|
|||
|
|
下面分别说明三种模式的使用方式与适用场景。
|
|||
|
|
|
|||
|
|
### 模式一:基础模式(只用 BaseField,零插槽开发)
|
|||
|
|
|
|||
|
|
**示例代码:**
|
|||
|
|
|
|||
|
|
```vue
|
|||
|
|
<script setup lang="ts">
|
|||
|
|
import type { WidgetProps, WidgetSchema } from "~@/types/WidgetSchema";
|
|||
|
|
import { computed, toRefs, useAttrs } from "vue";
|
|||
|
|
import { usInitValueHook } from "~@/config/widget/utils/UseInitValueHook";
|
|||
|
|
import { useCompEvent } from "~@/config/user-script/hooks/UseComp";
|
|||
|
|
import BaseField from "../components/BaseField.vue";
|
|||
|
|
import { useField } from "./hook/useField";
|
|||
|
|
|
|||
|
|
const props = defineProps<
|
|||
|
|
WidgetProps<WidgetSchema<any>> & {
|
|||
|
|
placeholder?: string;
|
|||
|
|
}
|
|||
|
|
>();
|
|||
|
|
|
|||
|
|
const { schema, fieldPath, placeholder } = toRefs(props);
|
|||
|
|
const attrs = useAttrs();
|
|||
|
|
const { fieldProps, wrapperStyle, fieldSlots } = useField({
|
|||
|
|
schema,
|
|||
|
|
attrs,
|
|||
|
|
placeholder,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
type ModelValue = string | undefined;
|
|||
|
|
const value = defineModel<ModelValue>();
|
|||
|
|
usInitValueHook(value, { schema, fieldPath });
|
|||
|
|
|
|||
|
|
const { handleBlur, handleFocus, handleValueChange } = useCompEvent(
|
|||
|
|
schema as any,
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
function setValue(nv: ModelValue) {
|
|||
|
|
value.value = nv;
|
|||
|
|
handleValueChange(nv);
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<template>
|
|||
|
|
<BaseField
|
|||
|
|
v-bind="fieldProps"
|
|||
|
|
:style="wrapperStyle"
|
|||
|
|
:field-slots="fieldSlots"
|
|||
|
|
:model-value="value"
|
|||
|
|
@update:model-value="setValue"
|
|||
|
|
@focus="handleFocus"
|
|||
|
|
@blur="handleBlur"
|
|||
|
|
/>
|
|||
|
|
</template>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- **使用方式说明:**
|
|||
|
|
- 模板中直接使用 `BaseField`,传入由 `useField` 计算出的 `fieldProps` / `wrapperStyle` / `fieldSlots`;
|
|||
|
|
- 通过 `v-model` 和事件处理器完成双向绑定和事件派发;
|
|||
|
|
- `fieldProps` / `wrapperStyle` / `fieldSlots` 均由 `useField(schema, attrs, placeholder)` 等 Hook 计算而来。
|
|||
|
|
- **字段配置来源:**
|
|||
|
|
- 标题、占位符、字数限制、校验规则、必填标记等,全部从 Schema(低码配置)中读取;
|
|||
|
|
- 开发者只需要通过 Helper 的 `buildSettings` / `buildWidget` 暴露字段配置即可。
|
|||
|
|
- **适合场景:**
|
|||
|
|
- 只想快速用一个“通用输入组件”,只需要提供标准样式;
|
|||
|
|
|
|||
|
|
**总结:** 模式一中,`BaseField` 被看作“通用字段容器”,开发者只需要准备好 `fieldProps` / `fieldSlots`,绝大多数展示和行为由平台内置。
|
|||
|
|
|
|||
|
|
### 模式二:插槽扩展模式(BaseField + 自定义插槽)
|
|||
|
|
|
|||
|
|
**示例代码:**
|
|||
|
|
|
|||
|
|
```vue
|
|||
|
|
<script setup lang="ts">
|
|||
|
|
// ... script 部分与模式一相同,这里省略
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<template>
|
|||
|
|
<BaseField
|
|||
|
|
v-bind="fieldProps"
|
|||
|
|
:style="wrapperStyle"
|
|||
|
|
:field-slots="fieldSlots"
|
|||
|
|
:model-value="value"
|
|||
|
|
@update:model-value="setValue"
|
|||
|
|
@focus="handleFocus"
|
|||
|
|
@blur="handleBlur"
|
|||
|
|
>
|
|||
|
|
<!-- 自定义标签区域 -->
|
|||
|
|
<template #label>
|
|||
|
|
<div class="custom-label">
|
|||
|
|
<span>自定义标签</span>
|
|||
|
|
<i class="help-icon" />
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<!-- 自定义左侧图标 -->
|
|||
|
|
<template #left-icon>
|
|||
|
|
<van-icon name="search" />
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<!-- 自定义右侧图标 -->
|
|||
|
|
<template #right-icon>
|
|||
|
|
<van-icon name="clear" @click="handleClear" />
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<!-- 自定义输入区域 -->
|
|||
|
|
<template #input>
|
|||
|
|
<input
|
|||
|
|
:value="value"
|
|||
|
|
@input="setValue(($event.target as HTMLInputElement).value)"
|
|||
|
|
class="custom-input"
|
|||
|
|
/>
|
|||
|
|
</template>
|
|||
|
|
</BaseField>
|
|||
|
|
</template>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- **使用方式说明:**
|
|||
|
|
- 继续使用 `BaseField` 作为外层容器,并保留基础属性和事件绑定;
|
|||
|
|
- 同时在 `<BaseField>` 内部编写插槽:
|
|||
|
|
- `#label`:自定义标签区域(可加入图标、说明、换行布局等);
|
|||
|
|
- `#left-icon` / `#right-icon`:替换为自定义图标/按钮(如搜索图标、清空按钮等);
|
|||
|
|
- `#input`:替换内部输入区域(可以是 `<input>`,也可以是更复杂的组合,如“区号 + 手机号”)。
|
|||
|
|
- **与基础模式的关系:**
|
|||
|
|
- 仍然完全复用 BaseField 的:
|
|||
|
|
- 表单布局(label 区、内容区、错误提示等);
|
|||
|
|
- 事件派发(focus / blur / valueChange);
|
|||
|
|
- Schema 驱动的属性(`fieldProps` / `fieldSlots`)。
|
|||
|
|
- 只是把“局部渲染区域”交给业务组件来自定义。
|
|||
|
|
- **适合人群/场景:**
|
|||
|
|
- 需要在标签上插入说明/图标,或改变标签的布局;
|
|||
|
|
- 需要在左右两侧放置单位、按钮、icon 等;
|
|||
|
|
- 需要对输入 DOM 做轻量改造,但又不希望重写整个 `Field` 逻辑。
|
|||
|
|
|
|||
|
|
**总结:** 模式二是在 **“不放弃 BaseField 能力”** 的前提下,通过插槽对部分区域进行增强,兼顾 **低码配置** 与 **少量编码定制**。
|
|||
|
|
|
|||
|
|
### 模式三:完全自定义模式(useField + Vant Field)
|
|||
|
|
|
|||
|
|
**示例代码:**
|
|||
|
|
|
|||
|
|
```vue
|
|||
|
|
<script setup lang="ts">
|
|||
|
|
import type { WidgetProps, WidgetSchema } from "~@/types/WidgetSchema";
|
|||
|
|
import { Field } from "vant-cisdi";
|
|||
|
|
import { toRefs, useAttrs } from "vue";
|
|||
|
|
import { usInitValueHook } from "~@/config/widget/utils/UseInitValueHook";
|
|||
|
|
import { useCompEvent } from "~@/config/user-script/hooks/UseComp";
|
|||
|
|
import { useField } from "./hook/useField";
|
|||
|
|
|
|||
|
|
const props = defineProps<
|
|||
|
|
WidgetProps<WidgetSchema<any>> & {
|
|||
|
|
placeholder?: string;
|
|||
|
|
}
|
|||
|
|
>();
|
|||
|
|
|
|||
|
|
const { schema, fieldPath, placeholder } = toRefs(props);
|
|||
|
|
const attrs = useAttrs();
|
|||
|
|
const { fieldProps, wrapperStyle, fieldSlots } = useField({
|
|||
|
|
schema,
|
|||
|
|
attrs,
|
|||
|
|
placeholder,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
type ModelValue = string | undefined;
|
|||
|
|
const value = defineModel<ModelValue>();
|
|||
|
|
usInitValueHook(value, { schema, fieldPath });
|
|||
|
|
|
|||
|
|
const { handleBlur, handleFocus, handleValueChange } = useCompEvent(
|
|||
|
|
schema as any,
|
|||
|
|
);
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<template>
|
|||
|
|
<Field
|
|||
|
|
v-model="value"
|
|||
|
|
v-bind="fieldProps"
|
|||
|
|
:style="wrapperStyle"
|
|||
|
|
@update:model-value="handleValueChange"
|
|||
|
|
@focus="handleFocus"
|
|||
|
|
@blur="handleBlur"
|
|||
|
|
>
|
|||
|
|
<!-- 根据 fieldSlots 数据动态渲染标签 -->
|
|||
|
|
<template v-if="fieldSlots.hasLabelSlot" #label>
|
|||
|
|
<div
|
|||
|
|
v-if="fieldSlots.labelData?.value?.showLabel"
|
|||
|
|
class="van-field__label-wrapper"
|
|||
|
|
>
|
|||
|
|
<label :style="{ width: fieldSlots.labelData?.value?.labelWidth }">
|
|||
|
|
<span :style="fieldSlots.labelData?.value?.customLabelStyle">
|
|||
|
|
自定义 + {{ fieldSlots.labelData?.value?.label }}
|
|||
|
|
</span>
|
|||
|
|
<i
|
|||
|
|
v-if="fieldSlots.labelData?.value?.required"
|
|||
|
|
class="van-badge__wrapper van-icon__wrap mbicon mbicon-Required van-field__required-mark"
|
|||
|
|
/>
|
|||
|
|
</label>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<!-- 根据 fieldSlots 数据动态渲染左侧图标 -->
|
|||
|
|
<template v-if="fieldSlots.hasPrepend" #left-icon>
|
|||
|
|
{{ fieldSlots.prependContent }}
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<!-- 根据 fieldSlots 数据动态渲染右侧图标 -->
|
|||
|
|
<template v-if="fieldSlots.hasAppend" #right-icon>
|
|||
|
|
{{ fieldSlots.appendContent }}
|
|||
|
|
</template>
|
|||
|
|
</Field>
|
|||
|
|
</template>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- **使用方式说明:**
|
|||
|
|
- 不再使用 `BaseField`,直接使用 Vant 的 `Field` 组件:
|
|||
|
|
- `v-model="value"`:字段双向绑定;
|
|||
|
|
- `v-bind="fieldProps"`:透传由 `useField` 计算出的属性(如 placeholder、校验相关 props 等);
|
|||
|
|
- `:style="wrapperStyle"`:应用统一样式;
|
|||
|
|
- `@update:model-value="handleValueChange"`、`@focus="handleFocus"`、`@blur="handleBlur"`:自行接入事件。
|
|||
|
|
- 使用 `fieldSlots` 提供的数据决定是否渲染 label / icon 等:
|
|||
|
|
- `fieldSlots.hasLabelSlot` + `fieldSlots.labelData?.value` 控制标签显隐、宽度、样式、必填星号等;
|
|||
|
|
- `fieldSlots.hasPrepend` / `fieldSlots.hasAppend` + 内容字段控制左右自定义区域。
|
|||
|
|
- **与前两种模式的区别:**
|
|||
|
|
- 布局与 DOM 结构完全由业务组件掌控;
|
|||
|
|
- 只复用数据与配置层能力(`fieldProps` / `fieldSlots` / 事件 Hook),不复用 BaseField 的 UI 结构。
|
|||
|
|
- **适合人群/场景:**
|
|||
|
|
- 对 DOM 结构、动画、样式有强定制需求;
|
|||
|
|
- 由前端开发者主导开发,低码配置主要提供字段元数据与校验规则;
|
|||
|
|
|
|||
|
|
**总结:** 模式三是 **“只拿 Schema & Hook,不拿默认 UI”** 的高级用法,保留低码平台的数据/事件体系,同时对视觉与交互做彻底自定义。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**建议实践:**
|
|||
|
|
|
|||
|
|
- 通用表单场景优先使用 **模式一(基础模式)**;
|
|||
|
|
- 需要少量 UI 扩展时优先考虑 **模式二(插槽扩展模式)**;
|
|||
|
|
- 仅在前两种模式无法满足需求时,再采用 **模式三(完全自定义模式)**,以控制维护成本。
|