接到新的项目需求,需要把一个json文件动态渲染成表单,并添加各种校验规则等。经过各种查资料,最终完成了此功能开发,对这块的知识点进行梳理如下。
我们需要实现的Json文件格式如下:
// An highlighted block
"configs": [
{
"name": "school1",
"cnName": "学校一",
"childNodes": [
{
"name": "grade1",
"cnName": "一年级",
"props": [
{
"name": "class1",
"cnName": "一班",
"dataType": "int",
"desc": "班级人数",
"validator": {
"min": 20,
"max": 50,
"required": true
}
},
{
"name": "class2",
"cnName": "二班",
"dataType": "int",
"desc": "班级人数",
"validator": {
"min": 20,
"max": 50,
"required": true
}
},
{
"name": "class3",
"cnName": "三班",
"dataType": "int",
"desc": "班级人数",
"validator": {
"min": 20,
"max": 50,
"required": true
}
},
]
}
]
},
{
"name": "school2",
"cnName": "学校二",
"childNodes": [
{
"name": "grade1",
"cnName": "一年级",
"props": [
{
"name": "class1",
"cnName": "一班",
"dataType": "int",
"desc": "班级人数",
"validator": {
"min": 20,
"max": 50,
"required": true
}
}
]
},
{
"name": "grade2",
"cnName": "二年级",
"props": [
{
"name": "class1",
"cnName": "一班",
"dataType": "int",
"desc": "班级人数",
"validator": {
"min": 20,
"max": 50,
"required": true
}
},
{
"name": "class2",
"cnName": "二班",
"dataType": "int",
"desc": "班级人数",
"validator": {
"min": 20,
"max": 50,
"required": true
}
},
]
}
]
}
]
简单列举一下,由于我们需求的JSON可能会嵌套很多层,所以必须实现递归渲染。
index.vue
index.vue 本身可作为递归组件使用。 configs 为Json配置文件
<div class="form-group">
<div v-for="(item, index) in configs" :key="index">
<div class="titleMain">
<span class="verticalLine"></span>{{ item.cnName }}
</div>
<!-- 判断json文件是否有childNodes,有的话,继续循环childNodes内的对象 -->
<template v-if="item.childNodes">
<div
v-for="(itemChild, indexChild) in item.childNodes"
:key="indexChild"
>
<!-- :configs="itemChild" childNodes内部实现递归循环 -->
<form-group
v-if="itemChild.childNodes"
:configs="itemChild"
></form-group>
<!-- 展示动态表单组件 -->
<div>
<el-form
ref="formgroup"
class="dynamic-form"
:model="getModel(itemChild.props, 'form' + indexChild)"
>
<dynamic-form
ref="dynamicForm"
v-for="(itemInput, indexInput) in itemChild.props"
:key="itemInput.name + index + indexInput"
:item="itemInput"
:childItem="itemChild"
:configs="configs"
:numberValidator="numberValidator"
:value="itemInput.value"
@input="handleInput($event, itemInput.name, itemInput)"
></dynamic-form>
</el-form>
</div>
</div>
</template>
<!-- 判断json文件是否有childNodes,没有的话,直接展示动态表单组件 -->
<template v-else>
<div>
<div>
<el-form
ref="formgroup"
class="dynamic-form"
:model="getModel(itemChild.props, 'form2' + indexChild)"
>
<dynamic-form
ref="dynamicForm"
v-for="(itemInput, indexInput) in itemChild.props"
:key="itemInput.name + index + '_' + indexInput"
:item="itemInput"
:childItem="itemChild"
:configs="configs"
:numberValidator="numberValidator"
:value="itemInput.value"
@input="handleInput($event, itemInput.name, itemInput)"
></dynamic-form>
</el-form>
</div>
</div>
</template>
</div>
</div>
dynamicForm.vue
项目中目前使用的组件都是输入框和下拉框,因此动态表单中只使用了el-input 和 el-select。此处逻辑是参照网上看到的帖子内容,并非完全自创。
<el-form-item
v-if="!item.hidden"
:rules="rules"
:label="item.cnName"
:prop="item.name"
class="formItem"
>
<el-input
v-if="item.dataType === 'int'"
v-bind="$attrs"
v-on="$listeners"
:placeholder="item.placeholder"
@change="
val => {
item.valueChange
? item.valueChange(val, formData, clone, numberValidator)
: valueChange()
}
"
:disabled="item.disable"
:readonly="item.readonly"
:autosize="item.autosize"
v-model="item.value"
></el-input>
<el-input
v-else-if="item.dataType === 'string'"
v-bind="$attrs"
v-on="$listeners"
:placeholder="item.placeholder"
:disabled="item.disable"
:readonly="item.readonly"
v-model="item.value"
></el-input>
<el-select
v-else-if="item.dataType === 'select'"
v-bind="$attrs"
v-on="$listeners"
:multiple="item.multiple"
:disabled="item.disabled"
@change="
val => {
item.valueChange(val, childItem)
}
"
>
<el-option
v-for="o in item.optionals"
:key="o.value"
:label="o.cnName"
:value="o.value"
:disabled="o.disabled"
>
</el-option>
</el-select>
</el-form-item>
json中props数组中的每一个对象对应了一个输入框,通过父子组件传参,将props中的对象遍历,分别传到dynamicForm组件,组件拿到props中每个对象的属性,对应展示即可。
表单动态渲染的部分大概就是这样。
后面针对此json文件的校验部分进行梳理。