【手撸低代码工具】二次封装UI库(三)表单与表单内各组件的那点事
表单
表单里面有很多 dom,比如 input、textarea、select、CheckBox 等,但是他们并没有一个统称,看标题“表单里的各种组件”,我只好用这种称呼来描述。
elementPlus 的划分是:Form 表单组件的下面有各种组件,包括 el-form 在内。帮助文档的菜单可以这么做,因为看一下就知道了,但是。。。
比如我说,我要封装一下“表单组件”,你想到的一定是 el-form 这类的,不会想到 input 吧。
我要是说,我要封装一下表单子组件,你可能想到的是 el-form-item,那么有没有可能想到的是 input 呢?
是的,在接口命名上出现了冲突。
之前给 input 这类的起个统称:IFromItem,还自我感觉良好。 但是当封装到 el-form-item 的时候就懵了!咋办?只在组件内部定义 Interface?这不是糊弄人吗?改个名字就叫“FromSon”?似乎不好吧。
三个层次
其实表单可以分为三个层次:
- el-form:对应整体 的 table,一个大表单,需要自己的 props 和 meta。
- el-form-item:对应一个 tr 或者 td,表单里的一行,也需要自己的 props,对应自己的 meta 和 input 的meta,还有表单的验证。
- input 等:目前没有一个统称。。。需要 props 和 meta。
看着是不是有点晕,对的,很晕。名字搞错了,所以要改名,要改一些代码。
所以,接口想改名就要趁早,越早影响越小,以后就没法改了。
接口名称
想了一个统称:FormChild 。我也想不到更好的了,先凑合用吧,至少不会混淆。
- el-form:一个大表格
- props IFromProps :el-form 需要的各种属性,比如labelSuffix、labelWidth等
- formMeta IFromMeta:表单级别的 meta,实现各种辅助功能需要的属性
- moduleId 等:模块、表单的标识
- colOrder:数组,字段的显示顺序
- columnsNumber:列数
- subMeta:对象,分栏等
- ruleMeta:对象,验证需要的
- linkageMeta:对象,联动用的
- customerControl:扩展用的
- childPropsList IFormChildPropsList:这是一个综合,照顾 json 里面存放的数据。
- model T:表单的对象数据。
- formMeta IFromMeta:表单级别的 meta,实现各种辅助功能需要的属性
- props IFromProps :el-form 需要的各种属性,比如labelSuffix、labelWidth等
- el-form-item:一行,或者一个字段
- props IFormItemMeta:综合的,表单里的验证,child 的 meta等
- colOrder:字段排序依据,也是显示依据。
- childProps:input 的 props 集合, 含 meta
- childMeta:input 等的纯 meta 集合
- ruleMeta:验证规则
- showCol:联动的时候,是否显示的依据。
- formColSpan:el-col 的 span 属性,设置列数的
- props IFormItemMeta:综合的,表单里的验证,child 的 meta等
- form-child:input 这样的 props 的集合。
先确定总体范围,然后再细化调整。这样至少名称可以先弄好了。
枚举和魔数
上次遇到判断组件类型的时候,我直接用了魔数,后来发现可以用枚举(enum)实现,效果更好。于是改进了一下。可读性更好了,只是使用的时候代码有点长。
/**
* 控件类型的枚举
*/
export const enum EControlType {
// 文本
text = 101,
textarea = 100,
password = 102,
email = 103,
tel = 104,
url = 105,
search = 106,
autocomplete = 107,
color = 108,
// 数字
number = 110,
range = 111,
rate = 112,
// 日期
date = 120,
datetime = 121,
month = 122,
week = 123,
year = 124,
daterange = 125,
datetimerange = 126,
monthrange = 127,
dates = 128,
// 时间
timepicker = 130,
timepickerrange = 131,
timeselect = 132,
timeselectrange = 133,
// 上传
file = 140,
picture = 141,
video = 142,
// 选择等
checkbox = 150,
switch = 151,
checkboxs = 152,
radios = 153,
// 下拉
select = 160, // 单选下拉
selects = 161, // 多选下拉
selectGroup = 162, // 分组下拉单选
selectGroups = 163, // 分组下拉多选
selectCascader = 164, // 联动下拉
selectTree = 165, // 树状下拉
selectTrees = 166 // 树状多选
}
应该可以扩展,我打算把200+的数字都留个扩展用,比如你可以定义 200 号是一个 你喜欢的组件,然后可以把这个组件加入到表单里面。
这样就可以支持各种各样的需求了。而且也不用去修改表单组件内部的代码。
合并组件
以前分的比较细致,导致出现了一大堆 xxxx.vue 文件,改起来就会比较费劲,比如这次修改 Interface 的名称。
所以干脆,我把若干比较相近的组件合并为一个,虽然内部代码有点繁琐,但是应该算是一个折中的方案吧。
比如 把 switch 和 el-checkbox 合并在一起:
<script setup lang="ts" generic="T extends object">
// 引入组件需要的属性 引入表单子控件的管理类
import { itemController, EControlType } from '../map'
import type { IFormChildProps } from '../map'
defineOptions({
name: 'nf-el-from-item-checkbox',
inheritAttrs: false,
})
// 定义 props
const props = withDefaults(defineProps<IFormChildProps<T> & {
title?: string
}>(), {
clearable: true,
title: ''
})
const { value } = itemController(props)
</script>
引入 Interface 定义 props 设置默认值,然后调用 controller 创建 value,最后到 template 上面去绑定即可,抽象之后,代码可以变得更简洁。
<template>
<!--开关 -->
<el-switch
v-if="meta.controlType === EControlType.switch"
v-model="value"
v-bind="$attrs"
:clearable="clearable"
>
</el-switch>
<!--勾选框-->
<el-checkbox
v-else
v-model="value"
v-bind="$attrs"
:clearable="clearable"
>
{{title}}
</el-checkbox>
</template>
用 v-if
依据 meta.controlType
判断显示哪个组件,然后按照需求绑定属性即可。
这样合并之后,组件数量由三十多个缩减到 十六个。不能再少了,内部代码会乱。
转载自:https://juejin.cn/post/7242209076361101371