一、 先贴实现效果图
该表单有如下验证规则:
- 昵称只能输入中文,必填
- 年龄 18-50 必填
- 性别默认女 从字典选择
- 手机号格式验证 选填
- 简介选填,有输入限制
二、 实现方案
1. 声明 @FormField()
装饰器, 用户表单配置和一些通用验证
声明装饰器
export function FormField(fieldConfig: IFormFieldConfig = {}): Function {
return (target: any, key: string) => {
fieldConfig.key = key
return AirDecorator.setFieldConfig(target, key, FIELD_CONFIG_KEY, fieldConfig, FIELD_LIST_KEY)
}
}
装饰器的配置 IFormFieldConfig
接口(也就是上篇文章提到的,我们为数不多允许使用 interface
定义结构体的地方之一)
export interface IFormFieldConfig extends IFieldConfig {
placeholder?: string;
isTextarea?: boolean;
isPassword?: boolean;
dictionary?: AirDictionaryArray<IDictionary>;
isSwitch?: boolean;
isRadioButton?: boolean;
defaultValue?: boolean | string | number;
isChinese?: boolean | string
isMobilePhone?: boolean | string
isTelPhone?: boolean | string
isEmail?: boolean | string
isPhone?: boolean | string
isRequiredString?: boolean | string
isRequiredNumber?: boolean | string
regExp?: RegExp
}
当然,其实上面还有很多配置项,也有很多的注释,为了篇幅我删掉了。
2. 声明实体用于数据结构声明和配置负载
export class UserEntity extends BaseEntity {
@FormField({
isRequiredString: true,
isChinese: true,
placeholder: '起个牛逼的昵称...',
})
@FieldName('昵称') nickname!: string
@Dictionary(SexDictionary)
@FormField({
defaultValue: Sex.FEMALE,
})
@FieldName('性别') sex!: Sex
@FormField({
isRequiredNumber: true,
min: 18,
max: 50,
isNumber: true,
})
@FieldName('年龄') age!: number
@FormField({
isMobilePhone: true,
})
@FieldName('手机') phone!: string
@FormField({
isTextarea: true,
maxLength: 250,
})
@FieldName('简介') desc!: string
}
3. 封装输入组件 AInput
AInput
组件封装文件的篇幅太长, 简单截个图吧
大概的实现逻辑是:)
- 读取传入的
entity
类上的所有@FormField()
配置信息, 渲染页面 - 将输入结果
emits
给父组件
4. View层调用
<template>
<ADialog
:title="title"
:form-ref="formRef"
:loading="isLoading"
confirm-text="保存"
:fullable="false"
@on-confirm="onSubmit()"
@on-cancel="onCancel()"
>
<el-form
ref="formRef"
:model="formData"
label-width="120px"
:rules="rules"
@submit.prevent
>
<el-form-item
:label="UserEntity.getFormFieldLabel('email')"
prop="email"
>
<AInput
v-model.email="formData.email"
:entity="UserEntity"
/>
</el-form-item>
<el-form-item
:label="UserEntity.getFormFieldLabel('nickname')"
prop="nickname"
>
<AInput
v-model.nickname="formData.nickname"
:entity="UserEntity"
/>
</el-form-item>
<el-form-item
:label="UserEntity.getFormFieldLabel('remark')"
prop="remark"
>
<AInput
v-model.remark="formData.remark"
:entity="UserEntity"
/>
</el-form-item>
</el-form>
</ADialog>
</template>
<script lang="ts" setup>
import {
ADialog, AInput,
} from '@/airpower/component'
import { airPropsParam } from '@/airpower/config/AirProps'
import { UserService } from '@/model/user/UserService'
import { UserEntity } from '@/model/user/UserEntity'
import { useAirEditor } from '@/airpower/hook/useAirEditor'
const props = defineProps(airPropsParam(new UserEntity()))
const {
isLoading, formData, formRef, title, rules,
onSubmit,
} = useAirEditor(props, UserEntity, UserService, {
})
</script>
视图层的部分代码已经删除,不影响查看实现逻辑。
三、 That's all
今天就这么多吧, 实现方式是不是跟你的不太一样呢?
是的,我还是那个 Java仔
/运维仔
/前端仔
是,也不仅仅。
2023-07-26 17:31:14 更新
项目已经在Github开源: github.com/HammCn/AirP…