【实操】vue+element UI tab页多表单合并校验提交

本文介绍如何在Vue中,使用el-tabs切换的多个Form组件配合一个提交按钮,通过$refs获取子组件数据并合并校验,确保所有字段完整后统一提交。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

场景

一个页面中tab栏切换多个form表单组件,只有一个保存按钮。需要把各组件下的表达数据合并校验提交。

在这里插入图片描述

思路

  1. 父组件通过两次调用$refs获取子组件的dom元素以及组件内部form的dom元素。
  2. 通过循环表单项获取validate值获取校验结果,通过Promise.all合并表单。
  3. 子组件内部getData方法返回表单数据,父调用子组件的getData方法获取子组件的表单值

代码

父页面:

<template>
  <div>
    <el-tabs v-model="activeName" type="card" @tab-click="handleClick">
      <el-tab-pane label="form1" name="first">
        <form-comp-one ref="formComp1" />
      </el-tab-pane>
      <el-tab-pane label="form2" name="second">
        <form-comp-two ref="formComp2" />
      </el-tab-pane>
    </el-tabs>
    <el-button class="submit-btn" size="small" @click="submit">提交</el-button>
  </div>
</template>
<script>
import FormCompOne from '@/components/FormCompOne.vue';
import FormCompTwo from '@/components/FormCompTwo.vue';
export default {
  components: {
    FormCompOne,
    FormCompTwo
  },
  data() {
    return {
      activeName: 'first'
    };
  },
  methods: {
    handleClick(tab, event) {
      console.log(tab, event);
    },
    submit() {
      const _this = this
      const formData1 = _this.$refs.formComp1.$refs.formData
      const formData2 = _this.$refs.formComp2.$refs.formData
       Promise.all([formData1,formData2].map(_this.getFormPromise)).then(res => {
         console.log(res)
           const validateResult = res.every(item => !!item);
           if(validateResult) {
             const params = {
              ..._this.$refs.formComp1.getData(),
              ..._this.$refs.formComp2.getData(),
             }
             console.log(params)
              alert('提交成功')
           } else {
             alert('必填内容未填写')
           }
       })
    },
    getFormPromise(form) {
      return new Promise(resolve => {
        form.validate(res => {
          resolve(res);
        })
      })
    },
  }
};
</script>
<style>
.submit-btn {
  position: absolute;
  right: 10px;
  top: 60px
}
</style>

子组件1

<template>
  <el-form 
  ref="formData"  
  :model="formData"
  :rules="formRules"
  label-width="80px" 
  style="width: 450px;"
  >
    <el-form-item label="名称" prop="name">
      <el-input class="f-input" v-model="formData.name"></el-input>
    </el-form-item>
    <el-form-item label="活动区域" prop="region">
      <el-input class="f-input" v-model="formData.region"></el-input>
    </el-form-item>
    <el-form-item label="活动形式" prop="type">
      <el-input class="f-input" v-model="formData.type"></el-input>
    </el-form-item>
  </el-form>
</template>
<script>
  class formData {
    constructor() {
      this.name='';
      this.region = ''; 
      this.type = ''; 
    }
    static getRule() {
      return {
        name:  [{ required: true, message: '请填写名称', trigger: 'blur' }],
        region: [{ required: true, message: '请填写活动区域', trigger: 'blur' }],
        type: [{ required: true, message: '请填写活动形式', trigger: 'blur' }],
      }
    }
  }
  export default {
    data() {
      return {
        formData: new formData,
        formRules: formData.getRule(),
      };
    },
    methods: {
      getData() {
        // 返回子组件的form
        return this.formData;
      },
    }
  }
</script>
<style scoped>

</style>

子组件2

<template>
  <el-form ref="formData" label-width="80px" :model="formData" :rules="formRules" style="width: 450px;">
    <el-form-item label="地址" prop="address">
      <el-input class="f-input" v-model="formData.address"></el-input>
    </el-form-item>
    <el-form-item label="年龄" prop="age">
      <el-input class="f-input" v-model="formData.age"></el-input>
    </el-form-item>
    <el-form-item label="性别" prop="sex">
      <el-input class="f-input" v-model="formData.sex"></el-input>
    </el-form-item>
</el-form>
</template>
<script>
  class formData {
    constructor() {
      this.address='';
      this.age = ''; 
      this.sex = ''; 
    }
    static getRule() {
      return {
        address:  [{ required: true, message: '请填写名称', trigger: 'blur' }],
        age: [{ required: true, message: '请填写活动区域', trigger: 'blur' }],
        sex: [{ required: true, message: '请填写活动形式', trigger: 'blur' }],
      }
    }
  }
  export default {
    data() {
      return {
        formData: new formData,
        formRules: formData.getRule(),
      };
    },
    methods: {
      getData() {
        // 返回子组件的form
        return this.formData;
      },
    }
  }
</script>
<style scoped>

</style>

源码

github代码地址:https://github.com/qi-Ruofan/formsMerge

Vue项目中使用Element UI动态生成多个Tab校验其中的表单数据是一项常见的需求。以下是实现这一功能的主要思路: ### 实现步骤 1. **创建动态Tabs组件** 使用`el-tabs`结合`v-for`渲染出对应的Tab项,并为每个Tab绑定唯一的key值。 2. **维护表单单例模型** 每个Tab中的表单需要有自己的独立model实例,可以将所有Tab的数据存储在一个数组里,例如 `formDataList = [{}, {}, ...]`。 3. **设置规则验证(rules)** 针对每组表单字段设定相应的校验规则,在`data()`函数返回的对象内定义好rule集合。 4. **触发校验逻辑** 当用户点击“提交”按钮时,遍历当前所有的form refs (通过ref注册),逐一调用其validate()方法完成逐条检验工作;如果全部合格,则继续下一步流程;若存在错误信息则提示给前端显示出来即可。 5. **示例代码** ```vue <template> <div class="dynamic-tab"> <!-- Tabs --> <el-tabs v-model="activeName" type="card" closable @tab-remove="removeTab"> <el-tab-pane :key="item.name" v-for="(item, index) in editableTabs" :label="'标签 ' + item.title" :name="item.name"> <el-form ref="forms" status-icon label-width="80px" :model="editableTabs[index]" :rules="rules"> <el-form-item prop="inputValue" label="输入框"> <el-input v-model="editableTabs[index].inputValue"></el-input> </el-form-item> </el-form> </el-tab-pane> </el-tabs> <el-button style="margin-top: 20px;" size="small" @click="addTab">新增</el-button> <el-button style="margin-top: 20px;" size="small" @click="handleSubmit">提交</el-button> </div> </template> <script> export default { data() { return { activeName: "1", // 默认激活第一个选项卡 editableTabs: [{ name: "1", title: "Tab 1", inputValue: "" }], rules: { // 校验规则 inputValue: [ { required: true, message: '请输入内容', trigger: ['blur'] } ] }, }; }, methods: { addTab() { const newTabIndex = String(this.editableTabs.length + 1); this.editableTabs.push({ name: newTabIndex, title: `新 Tab ${newTabIndex}`, inputValue: '' }); this.activeName = newTabIndex; }, removeTab(targetName) { let tabs = this.editableTabs; let activeName = this.activeName; if (activeName === targetName) { tabs.forEach((tab, index) => { if (tab.name === targetName && index > 0) { activeName = tabs[index - 1].name; // 切换到前一个Tab } else if (!index) { activeName = tabs[1]?.name || ''; // 如果删除的是第一项且只有一个元素就清空activename 或者跳转到最后一项 } }); this.activeName = activeName; } this.editableTabs = tabs.filter(tab => tab.name !== targetName); // 删除对应tab节点 }, handleSubmit() { console.log('开始校验...'); for(let i=0;i<this.$refs.forms.length;i++){ this.$refs.forms[i].validate(valid=>{ if(!valid){ alert("第"+(i+1)+"个表格有误"); } }) } } } }; </script> ``` #### 注意事项: - 确保每个表单都分配了独一无二的ref名称以便单独操作。 - 提交时需注意异步回调机制可能导致多次弹窗问题处理不当的情况发生。
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值