Code前端首页关于Code前端联系我们

vue+elem 大表单解决方案(二)——表单拆分

terry 2年前 (2023-09-08) 阅读数 125 #Vue

系列文章:

  • vue+element大规模解决方案(一)-概述

前言

从这篇文章开始,我将逐步实现评论中呈现的形状效果。通常我们在编写表单的时候,会为每个表单编写一个组件。保存时,我们使用el-form提供的validate方法进行验证。如果任何表单元素未能通过验证,则表单提交将被阻止;但如果表单元素足够多,那么表单文件显然会存在问题:第一,一个文件中会有大量代码,不利于后期维护;更严重的问题是几个人同时并行开发很难解决。如果多人写一个文件,很容易发生冲突,无法达到雇佣更多人缩短构建周期的目标。基于这些问题,我尝试了以下形式分解的解决方案。

综合理念

我的想法是把表单的内容分成几个子表单组件,最后将这些子表单组装在主表单组件中;提交时对子表单进行逐一检查,最终通过,收集主表单数据最终提交。

同时提交两份表格

首先,我想尝试一下如何通过点击保存按钮来提交两份电子表格。代码是:
模板部分

<el-button type="primary" @click="handleSave">保存</el-button>
<el-form ref="form1" :model="form1" :rules="rules1" label-width="80px" size="small">
  <el-form-item label="姓名" prop="name">
    <el-input v-model="form1.name" />
  </el-form-item>
  <el-form-item label="年龄">
    <el-input v-model="form1.age" />
  </el-form-item>
</el-form>
<el-form ref="form2" :model="form2" :rules="rules2" label-width="80px" size="small">
  <el-form-item label="公司" prop="company">
    <el-input v-model="form2.company" />
  </el-form-item>
</el-form>
 

数据部分

data() {
    return {
      form1: {
        name: '',
        age: ''
      },
      form2: {
        company: ''
      },
      rules1: {
        name: [
          { required: true, message: '请输入姓名', trigger: 'blur' }
        ]
      },
      rules2: {
        company: [
          { required: true, message: '请输入公司', trigger: 'blur' }
        ]
      }
    }
},
 

如果点击“保存”,则分别检查两个表格,只有通过后才打印数据。代码是:

handleSave() {
  let validResult1
  let validResult2
  this.$refs['form1'].validate(valid => { validResult1 = valid })
  this.$refs['form2'].validate(valid => { validResult2 = valid })
  if (validResult1 && validResult2) {
    // 校验通过,打印表单数据
    console.log(this.form1, this.form2)
  } else {
    this.$message.warning('校验未通过')
  }
}
 

目前正在测试必填项未填写时的运行效果

image.pngimage.png

如果所有检查都成功,则打印数据

image.pngimage.png

到目前为止一切都很顺利,没有什么新鲜事。它只是验证多个表单。当所有匹配时,打印详细信息并启用提交。否则,表明检查失败。

部分板材组件扩展

既然方向可行,那么我们就开始构建子表单组件并拆分代码。这是子表单之一的代码

<template>
    <el-form ref="form" :model="formData" :rules="rules" label-width="80px" size="small">
        <el-form-item label="姓名" prop="name">
          <el-input v-model="formData.name" />
        </el-form-item>
        <el-form-item label="年龄">
          <el-input v-model="formData.age" />
        </el-form-item>
    </el-form>
</template>
 

js部分代码为:

export default {
  name: 'Form1',
  data() {
    return {
      formData: {},
      rules: {
        name: [
          { required: true, message: '请输入姓名', trigger: 'blur' }
        ]
      }
    }
  },
  methods: {
    validForm() {
      let result = false
      this.$refs['form'].validate((valid) => { result = valid })
      return result
    }
  }
}
 

以同样的方式实现 form2.vue。
当前修改index.vue主表单文件

js部分

import Form1 from './form1'
import Form2 from './form2'
export default {
  name: 'TheForm',
  components: {
    Form1,
    Form2
  },
  data() {
      return {
      }
  },
  methods: {
    handleSave() {
      const validResult1 = this.$refs['form1'].validForm()
      const validResult2 = this.$refs['form2'].validForm()
      if (validResult1 && validResult2) {
        // 校验通过,打印表单数据
        console.log(this.$refs['form1'].formData)
        console.log(this.$refs['form2'].formData)
      } else {
        this.$message.warning('校验未通过')
      }
    }
  }
}
 

相比之前代码的变化是不再直接操作el-form,而是通过自定义表单组件提供的接口调用方法并获取数据(validForm方法和formData数据)。

测试修改后的效果为:

检查失败

image.pngimage.png

检查成功,数据打印

image.pngimage.png

提高可扩展性

在上面的代码中,每个表单必须定义一个validResult变量来记录表单验证结果。显然,这是不可扩展的。由于各个子窗体的接口是一致的,所以可以无差别地使用遍历。那么如何维护遍历键值呢?另外,在上面的代码中,子表单组件无法接收用于回显的初始数据。很自然,我们使用一个formDataMap对象来统一管理每个子页面的数据,并且我们可以使用formDataMap的属性作为键值。 。

修改index.vue文件的data部分,添加以下代码:

formDataMap: {
    form1: {
          // 如果有初始值则单独罗列出来,否则空着即可
    },
    form2: {}
}
 

由于子表单组件ref中分别使用了formDataMap中的属性名form1form2,因此在遍历时按顺序找到它们,并修改了保存功能。代码是:

handleSave() {
      // 解析出全部表单key值,通过key值获取组件ref
      const formKeys = Object.keys(this.formDataMap)
      const validResults = formKeys.map(formKey => this.$refs[formKey].validForm())
      // 如果所有校验通过
      if (validResults.every(r => r)) {
        // 校验通过,打印表单数据,这里不再单个输出子表单里的数据,
        // 而是组装成完正的表单数据
        const formData = {}
        formKeys.map(formKey => {
          const partFormData = this.$refs[formKey].formData
          Object.assign(formData, partFormData)
        })
        console.log(formData)
      } else {
        this.$message.warning('校验未通过')
      }
    }
  }
 

测试后结果如下:

检查失败

image.pngimage.png

验证成功,打印数据

image.pngimage.png

子表单接受初始数据

在上面的通用表单中已经定义了formDataMap,但是没有传递到子表单中,所以我们开始传递参数

 <form1 ref="form1" :data="formDataMap.form1" />
 <form2 ref="form2" :data="formDataMap.form2" />
 
formDataMap: {
    form1: {
      name: 'wyh',
      age: 30
    },
    form2: {}
}
 

进入 form1.vue 接受道具

props: {
    data: {
      type: Object,
      default: () => ({})
    }
},
 

为了在formData
中定位传入的属性,需要注意,通过使用属性immediate,可以立即定位传入的♿

image.pngimage.png

watch: {
   data: {
      handler(newValue) {
        this.formData = easyClone(newValue) || {}
      },
      immediate: true
    }
},
 

经测试一切正常。

混合提取物

由于各个分页组件的逻辑类似,所以form1.vue中的很多propswatchmethods应该在form2中。未来的形式。写起来当然要用mixin进行代码提取
提取的mixin代码如下:

import { easyClone } from '@/utils'
export default {
  props: {
    data: {
      type: Object,
      default: () => ({})
    }
  },
  data() {
    return {
      formData: {}
    }
  },
  watch: {
    data: {
      handler(newValue) {
        this.formData = easyClone(newValue) || {}
      },
      immediate: true
    }
  },
  methods: {
    validForm() {
      let result = false
      this.$refs['form'].validate((valid) => { result = valid })
      return result
    }
  }
}
 

解压后,form1.vue变得极其简单,只遵循规则

import SuperFormMixin from '@/mixins/super-form-mixin'
export default {
  name: 'Form1',
  mixins: [SuperFormMixin],
  data() {
    return {
      rules: {
        name: [
          { required: true, message: '请输入姓名', trigger: 'blur' }
        ]
      }
    }
  }
}
 

检查后一切正常。至此,表单划分的基本方向和实现已经出来了,接下来就是复杂表单逻辑的填充工作。下一个要面对的问题是如何实现大型形状的工具附着点?敬请期待。
感谢您的阅读,欢迎指正!

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

上一篇:从Vue2.x源码找到的知识点 下一篇:VueCLI

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门