描述
项目中经常会遇到使用动态表单的情况,动态表单最核心的问题莫过于表单数据、数据校验。基于目前响应式数据的前端技术,通过elementUI记录下动态表单的其中一种处理方案。
效果要求
数据结构
默认有【报名签到】,仅一条,可以动态创建/删除【考勤签到】、【临时签到】。拟定数据结构如下:
const baseForm = {
signin:[],
attendance:[],
temp:[]
}
const baseField = {
title: null,
name: null,
geo: null,
radius: null,
start_time: null,
end_time: null
restrict_location: 0,
require_sign: 0
}
export default{
date(){
return {
formData: Object.assign({}, baseForm)
}
}
}
为了方便formData字段的统一维护,以及过程中的重置等,所以就拎出来定义了。
校验规则
elementUI 提供了结合form的表单校验规则,而此次动态添加的表单字段均相同,所以将表单验证规则按如下定义,缩减代码,在dom中绑定使用。
data(){
return {
.
.
.
rules: {
name: {required: true,message: '请输入活动标题',trigger: 'blur'},
geo: {required: true,message: '未检测到经纬度地址',trigger: 'blur'},
...
}
}
}
DOM结构
数据结构、验证规则都已经配置好了,就可以开始进行DOM结构的处理了。
<el-form ref="dataForm" :model="formData" :rules="rules" :inline="false" label-position="top">
<template v-if="formData.signin.length > 0">
<el-form-item
label="签到-开始时间"
prop="signin[0].start_time"
:rules="rules.start_time"
>
<el-date-picker
v-model="formData.signin[0].start_time"
type="datetime"
align="right"
format="yyyy-MM-dd HH:mm:ss"
value-format="yyyy-MM-dd HH:mm:ss"
>
</el-date-picker>
</el-form-item>
<el-form-item
label="签到-截止开时间"
prop="signin[0].end_time"
:rules="rules.end_time"
>
<el-date-picker
v-model="formData.signin[0].end_time"
type="datetime"
align="right"
format="yyyy-MM-dd HH:mm:ss"
value-format="yyyy-MM-dd HH:mm:ss"
>
</el-date-picker>
</el-form-item>
<el-form-item
label="签到地址"
class="address"
prop ="signin[0].name"
:rules="rules.name"
>
<el-input
type="text"
v-model="formData.signin[0].name"
placeholder="请选择活动地址"
:readonly="true"
/>
</el-form-item>
<el-form-item
label="签到范围"
prop="signin[0].radius"
>
<el-input
v-model="formData.signin[0].radius"
type="text" placeholder="请选择签到范围"
:readonly="true"
style="width: 70%;"
/>米
</el-form-item>
<el-form-item>
手写签到:
<el-switch
v-model="formData.signin[0].require_sign"
active-color="#13ce66"
inactive-color="#ff4949"
:active-value="1"
:inactive-value="0"
>
</el-switch>
</el-form-item>
<el-form-item>
地理范围限制:
<el-switch
v-model="formData.signin[0].restrict_location"
active-color="#13ce66"
inactive-color="#ff4949"
:active-value="1"
:inactive-value="0">
</el-switch>
</el-form-item>
</template>
<template v-if="formData.attendance.length > 0">
<div class="box" v-for="(item,index) in formData.attendance" >
<el-form-item
label="签到标题"
:prop="`attendance.${index}.title`"
:rules="rules.title">
<el-input type="text" v-model="item.title" placeholder="请输入签到标题"/>
</el-form-item>
<el-form-item
label="签到-开始时间"
:prop="`attendance.${index}.start_time`"
:rules="rules.start_time"
>
<el-date-picker
v-model="item.start_time"
type="datetime"
align="right"
format="yyyy-MM-dd HH:mm:ss"
value-format="yyyy-MM-dd HH:mm:ss"
>
</el-date-picker>
</el-form-item>
<el-form-item
label="签到-截止开时间"
:prop="`attendance.${index}.end_time`"
:rules="rules.end_time"
>
<el-date-picker
v-model="item.end_time"
type="datetime"
align="right"
format="yyyy-MM-dd HH:mm:ss"
value-format="yyyy-MM-dd HH:mm:ss"
>
</el-date-picker>
</el-form-item>
</div>
</template>
...后续代码都差不多,就省略了。
</el-form>
<div class="fixed-box">
<div class="item-box">
<div class="item" @click="handelAddAttendance">添加考勤签到</div>
<div class="item" @click="handelAddTemp">添加临时签到</div>
</div>
</div>
相关Method
methods(){
async getInfo(id) {
const loading = this.$loading({
lock: true,
text: 'Loading',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
try {
const res = await 后端请求返回
const { data } = res
if(data.sign_in_event && Object.keys(data.sign_in_event).length > 0) {
this.formData.signin = []
this.formData.signin.push({...data.sign_in_event})
} else {
const _baseObj = deepClone(baseObj)
this.formData.signin.push({
name: _baseObj.name,
geo: _baseObj.geo,
radius: _baseObj.radius,
start_time: _baseObj.start_time,
time: _baseObj.end_time,
restrict_location: 1,
require_sign: 1
})
}
if(data.attendance_event && data.attendance_event.length > 0) {
this.formData.attendance = []
this.formData.attendance = data.attendance_event
}
if(data.temp_event && data.temp_event.length > 0) {
this.formData.temp = []
this.formData.temp = data.temp_event
}
} catch (error) {
console.log(error)
} finally {
loading.close()
}
},
handelAddAttendance(){
this.formData.attendance.push(Object.assign({}, baseObj))
},
handleRemoveAttendance(index) {
this.formData.attendance.splice(index,1)
},
handelAddTemp(){
this.formData.temp.push(Object.assign({}, baseObj))
},
handleRemoveTemp(index){
this.formData.temp.splice(index,1)
},
}
总结
在动态渲染表单时,最主要的就是3个地方。
1、el-form-item的prop:要与动态生成到formData.【signin、attendance、temp】中的位置保持一致,所以这里根据循环的index来进行取值。例如:attendance.${index}.start_time
2、el-form-item的rules: 我们单独为各表单项绑定对应的校验规则;
3、form表单项的v-model:根据对【attendance、temp】的循环,分别进行各项的值绑定,以保证数据项的修改;