Vue动态表单

460 阅读2分钟

描述

项目中经常会遇到使用动态表单的情况,动态表单最核心的问题莫过于表单数据、数据校验。基于目前响应式数据的前端技术,通过elementUI记录下动态表单的其中一种处理方案。

效果要求

image.png

数据结构

默认有【报名签到】,仅一条,可以动态创建/删除【考勤签到】、【临时签到】。拟定数据结构如下:

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】的循环,分别进行各项的值绑定,以保证数据项的修改;