目录
一、实现背景
二、简介
三、组织架构设计
四、实现方式
五、代码示例
六、示例代码效果预览
七、项目预览地址 & 项目源码地址
目前项目还有诸多待完善的地方,大家有好的想法、建议、意见等欢迎再次评论,或于github提交Issues
一、实现背景
一切为了摸鱼而努力!!!
在现代Web应用程序中,表单组件是不可或缺的一部分。但是,手动创建每个表单项是一个非常繁琐的过程。为此,我提供了一个基于Vue3的可配置的表单组件,帮助您快速构建表单,无需手动编写HTML或JavaScript代码。这篇文章将向您展示如何使用JSON配置文件一站式生成Vue3 Form表单组件,并在项目中使用它。
二、简介
此表单组件是目前内嵌在一个基础项目中的,并没有作单独的npm包进行发布,因为目前是一个比较简单且基础的版本,需要优化的点还非常多。希望大家能多多提出宝贵的意见或建议,本文主要是针对实现思路等进行描述。
目前实现功能:栅格化布局、监听单个表单数据变化、Form 表单除upload外的所有子组件。
通过JSON配置一站式生成form表单,该组件保留了ElementPlus全部的使用习惯和使用方式,对ElementPlus原功能进行了完美的保留,支持所有属性设置(方法使用统一事件监听替代)
三、组织架构设计
在当前的表单组件中,数据层、UI层、事件层等模块相互独立,通过交互来协调和通信。
其中,数据层负责存储和管理表单组件的数据;UI层负责渲染表单组件的外观;事件层负责处理用户与表单组件互动时的事件。通过这种方式,我们实现了一个高度可扩展和可重用的表单组件。
四、实现方式
Vue3支持使用JSX/TSX语法,通过JSX/TSX(我这里使用的是TSX)进行不同表单组件的生成,枚举出Form表单的所有子组件。这里采用TSX的方式可以避免每一种组件都要去写一份Vue文件;
通过匹配组件类型,生成对应的每一个表单组件;
为保留ElementPlus组件所有属性,需要采用透传的方式,去列出每一个组件所可能用到的属性显然是不明智的;
布局排版的生成采用el-row、el-col实现栅格布局,可通过配置进行动态调整;
ElementPlus表单组件中的所有方法显然不是那么好处理的,因此使用同一个方法去监听每个表单项的变化,并提供给“用户”此时变化的key、newVal以及oldVal,这里采用Proxy进行了数据拦截;
提供一个表单校验和重置表单数据的方法。
某些ElementPlus表单组件中会提供一些插槽给用户使用,因此这些插槽也需要保留下来
组件生成示例:
case 'el-input':
return (
<ElInput
type={elItem.specificType}
{...elItem.bindObj}
v-model={formData[elItem.key]}
v-slots={itemInteriorSlotsObject}
/>
)
五、代码示例
- 配置项数据
const FormConfig = reactive({
rowConfigBind: {
gutter: 20,
colSpan: 8
// className: 'row-class'
},
formConfigBind: {
labelPosition: 'top'
},
colsList: [
{
label: '一个普通的输入框',
colSpan: 8,
type: 'el-input',
specificType: 'text',
bindObj: {
placeholder: '请输入文字',
formatter: (value: any) => `$ ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ','),
parser: (value: any) => value.replace(/\$\s?|(,*)/g, '')
},
key: 'name'
},
{
label: '一个普通的输入框',
htmlLabel:
'<span class="customize-label-style">\n' +
' 自定义tooltip效果\n' +
' <span class="tip-content-wrap">\n' +
' <span class="tip-content">这是自己定义的tooltip</span>\n' +
' <span class="triangle-style"></span>\n' +
' </span>\n' +
'\n' +
' </span>\n',
colSpan: 8,
type: 'el-input',
specificType: 'text',
bindObj: {
placeholder: '请输入文字',
formatter: (value: any) => `$ ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ','),
parser: (value: any) => value.replace(/\$\s?|(,*)/g, '')
},
key: 'name2'
},
{
label: '添加了append文字的输入框',
colSpan: 8,
type: 'el-input',
bindObj: {
placeholder: '请输入'
},
rules: [
{
required: true,
message: '请输入',
trigger: 'change'
}
],
slots: [
{
name: 'append',
content: '.com'
}
],
key: 'comAdress'
},
{
label: '添加了append JSX组件的输入框',
colSpan: 8,
type: 'el-input',
bindObj: {
placeholder: '请输入'
},
slots: [
{
name: 'append',
content: TestTSXComp
}
],
key: 'appendJSXComp'
},
{
label: '添加了append Vue组件的输入框',
colSpan: 8,
type: 'el-input',
bindObj: {
placeholder: '请输入'
},
slots: [
{
name: 'append',
content: TestVueComp
}
],
key: 'appendVueComp'
},
{
label: '数字输入框',
colSpan: 4,
type: 'el-input-number',
bindObj: {
placeholder: '请输入'
},
key: 'inputNumberVal'
},
{
label: '测试表单生成函数',
type: 'slots',
key: 'content',
colSpan: 24
},
{
label: '下拉选',
colSpan: 8,
type: 'el-select',
bindObj: {
placeholder: '请选择',
multiple: true
},
options: [
{
label: '第一个选项',
value: 'A',
bindObj: {
disabled: true
}
},
{
label: '第二个选项',
value: 'B'
},
{
label: '第三个选项',
value: 'C'
}
],
key: 'optionValue'
},
{
label: '单选框',
colSpan: 8,
type: 'el-radio',
options: [
{
label: '第一个选项',
value: 'A',
bindObj: {
disabled: true
}
},
{
label: '第二个选项',
value: 'B'
},
{
label: '第三个选项',
value: 'C'
}
],
key: 'radioValue'
},
{
label: '按钮单选框',
colSpan: 8,
type: 'el-radio',
bindObj: {
textColor: 'red'
},
options: [
{
label: '第一个选项',
value: 'A',
isButton: true
},
{
label: '第二个选项',
value: 'B',
isButton: true
},
{
label: '第三个选项',
value: 'C',
isButton: true // 如果是button样式展示,那么设置此属性为true
}
],
key: 'radioBtnValue'
},
{
label: '自动补全输入框',
colSpan: 8,
type: 'el-autocomplete',
bindObj: {},
querySearchFun: querySearchFun,
key: 'autocompleteValue'
},
{
label: '日期选择框',
colSpan: 8,
type: 'el-date-picker',
specificType: 'date',
bindObj: {},
key: 'detePickerValue'
},
{
label: '日期时间选择框',
colSpan: 8,
type: 'el-date-picker',
specificType: 'datetime',
bindObj: {},
key: 'deteTimePickerValue'
},
{
label: '评分',
colSpan: 8,
type: 'el-rate',
bindObj: {
voidIcon: 'ChatRound',
colors: ['#409eff', '#67c23a', '#FF9900'],
icons: [ChatRound, ChatLineRound, ChatDotRound]
},
key: 'rateValue'
},
{
label: '滑块',
colSpan: 8,
type: 'el-slider',
bindObj: {
showInput: true
},
key: 'elSliderValue'
},
{
label: '开关',
colSpan: 8,
type: 'el-switch',
bindObj: {
style: '--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949'
},
key: 'elSwitchValue'
},
{
label: '时间选择器',
colSpan: 4,
type: 'el-time-picker',
key: 'timePickerValue'
},
{
label: '时间选择',
colSpan: 4,
type: 'el-time-select',
key: 'timeSelectValue'
}
]
})
- 表单数据
// 数据定义
const formData = reactive({
name: '',
comAdress: '',
inputNumberVal: '',
appendJSXComp: '',
appendVueComp: '',
optionValue: [],
autocompleteValue: ''
})
- 组件应用
<GenerateElForm
ref="formInstance"
:form-config="FormConfig"
:form-data="formData"
@updateFormValue="watcherFun"
>
<template #content>
<div class="form-slot-one">这是插槽的内容</div>
</template>
</GenerateElForm>
- watcherFun
// 监听数据变化的方法
const watcherFun = (key: string, oldVal: any, newVal: any) => {
console.log(
'监听到数据变化',
`当前变化的key是: ${key}, 它的旧值是: ${oldVal}, 它的新值是: ${newVal}`
)
}
六、示例代码效果预览
七、项目预览地址 & 项目源码地址
项目预览地址:http://1.14.75.249/
项目源码地址:https://github.com/zuotiandeni/lcyBlog