Vue3子组件向父组件传值及Vuetify步骤器按钮禁用问题
解决方案:Vuetify步骤器表单验证与组件数据传递
一、先解决子组件向父组件传递状态与数据问题
你的核心卡点是子组件没有实时向父组件同步表单的验证状态,导致父组件无法控制步骤器按钮。以下是修正后的组件代码:
1. 修正PersonalDataForm.vue
添加事件定义,实时同步表单完成状态,验证通过后提交数据:
<template> <v-container> <v-card class="rounded-lg py-4 px-8" elevation="8"> <form @submit.prevent="handleSubmit"> <!-- 表单字段部分保持不变 --> <v-row> <v-col cols="12" md="6"> <v-text-field v-model="state.firstName" :error-messages="v$.firstName.$errors.map((e) => e.$message)" label="Nome" required variant="underlined" color="deep-purple-lighten-1" @blur="v$.firstName.$touch" @input="v$.firstName.$touch" ></v-text-field> </v-col> <!-- 其余字段省略 --> </v-row> <v-btn class="me-4" @click="handleSubmit"> submit </v-btn> </form> </v-card> </v-container> </template> <script setup> import { ref, computed } from 'vue'; import { useVuelidate } from "@vuelidate/core"; import { required } from "@vuelidate/validators"; // 定义需要向父组件触发的事件 const emit = defineEmits(['update:isFormIncomplete', 'formDataSubmitted']); const state = ref({ firstName: "", lastName: "", birthDate: "", birthPlace: "", fiscalCode: "", }); const rules = { firstName: { required }, lastName: { required }, birthDate: { required }, birthPlace: { required }, fiscalCode: { required } }; const v$ = useVuelidate(rules, state.value); // 实时计算表单是否未完成,并同步给父组件 const isFormIncomplete = computed(() => { const incomplete = Object.values(state.value).some(value => !value) || v$.value.$invalid; emit('update:isFormIncomplete', incomplete); return incomplete; }); function handleSubmit() { v$.value.$touch(); if (!v$.value.$invalid) { const formData = { ...state.value }; emit('formDataSubmitted', formData); // 向父组件传递表单数据 } else { console.log("表单验证失败,无法提交。"); } } </script>
2. 修正ShippingForm.vue
同样修正事件逻辑,同步表单状态:
<template> <v-container> <v-card class="rounded-lg py-4 px-8" elevation="8"> <form @submit.prevent="handleSubmit"> <!-- 表单字段部分保持不变 --> <v-row> <v-col cols="12" md="6"> <v-text-field v-model="state.firstName" :error-messages="v$.firstName.$errors.map((e) => e.$message)" label="Nome" required variant="underlined" color="deep-purple-lighten-1" @blur="v$.firstName.$touch" @input="v$.firstName.$touch" ></v-text-field> </v-col> <!-- 其余字段省略 --> </v-row> <v-btn class="me-4" type="submit" @click="handleSubmit"> submit </v-btn> </form> </v-card> </v-container> </template> <script setup> import { reactive, computed, defineEmits } from 'vue'; import { useVuelidate } from "@vuelidate/core"; import { email, required, numeric, minLength, maxLength } from "@vuelidate/validators"; // 定义事件 const emit = defineEmits(['update:isFormIncomplete', 'formDataSubmitted']); const initialState = { firstName: "", lastName: "", region: "", province: "", city: "", houseNumber: "", email: "", phoneNumber: "", }; const state = reactive({ ...initialState }); const regions = ["Emilia", "Lombardia", "Calabria", "Piemonte"]; const provinces = ["piacenza", "Milano", "Cosenza", "Torino"]; const cities = ["Caorso", "Assago", "Lamezia", "Settimo Torinese"]; const rules = { firstName: { required }, lastName: { required }, region: { required }, province: { required }, city: { required }, houseNumber: { required, numeric }, email: { required, email }, phoneNumber: { required, numeric, minLength: minLength(12), maxLength: maxLength(12) }, }; const v$ = useVuelidate(rules, state); // 实时同步表单状态到父组件 const isFormIncomplete = computed(() => { const incomplete = Object.values(state).some(value => !value) || v$.value.$invalid; emit('update:isFormIncomplete', incomplete); return incomplete; }); function handleSubmit() { v$.value.$touch(); if (!v$.value.$invalid) { const formData = { ...state }; emit('formDataSubmitted', formData); } else { console.log("表单验证失败,无法提交。"); } } </script>
二、配置Vuetify步骤器控制下一步按钮禁用
改用v-stepper-item结构,自定义控制按钮,根据子组件传递的状态禁用下一步:
<template> <v-stepper color="deep-purple-darken-1" v-model="currentStep" class="my-5" > <!-- 第一步:个人信息 --> <v-stepper-item :step="1" :complete="!personalDataFormIsIncomplete"> <template v-slot:label>Dati Anagrafici</template> <v-card> <PersonalDataForm @update:isFormIncomplete="val => personalDataFormIsIncomplete = val" @formDataSubmitted="handlePersonalDataForm" /> </v-card> </v-stepper-item> <!-- 第二步:配送地址 --> <v-stepper-item :step="2" :complete="!shippingFormIsIncomplete"> <template v-slot:label>Indirizzo di spedizione</template> <v-card> <ShippingForm @update:isFormIncomplete="val => shippingFormIsIncomplete = val" @formDataSubmitted="handleShippingForm" /> </v-card> </v-stepper-item> <!-- 第三步:确认页 --> <v-stepper-item :step="3"> <template v-slot:label>Riepilogo</template> <v-card> <Recap @completed="submitData" :disabled="isFormIncomplete"/> </v-card> </v-stepper-item> <!-- 自定义步骤控制按钮 --> <template v-slot:controls> <v-btn @click="currentStep--" :disabled="currentStep === 1"> Indietro </v-btn> <v-btn color="deep-purple-darken-1" @click="currentStep++" :disabled="isCurrentStepIncomplete" > {{ currentStep === 3 ? 'Conferma' : 'Avanti' }} </v-btn> </template> </v-stepper> </template> <script setup> import { ref, computed } from 'vue'; import PersonalDataForm from '@/components/forms/PersonalDataForm.vue'; import ShippingForm from '@/components/forms/ShippingForm.vue'; import Recap from '../components/Recap.vue'; const currentStep = ref(1); const personalDataFormIsIncomplete = ref(true); const shippingFormIsIncomplete = ref(true); // 判断当前步骤是否未完成,用于禁用下一步按钮 const isCurrentStepIncomplete = computed(() => { switch(currentStep.value) { case 1: return personalDataFormIsIncomplete.value; case 2: return shippingFormIsIncomplete.value; case 3: return false; default: return true; } }); const isFormIncomplete = computed(() => { return personalDataFormIsIncomplete.value || shippingFormIsIncomplete.value; }); const handlePersonalDataForm = (formData) => { console.log("收到个人数据:", formData); }; const handleShippingForm = (formData) => { console.log("收到配送数据:", formData); }; const submitData = () => { console.log('提交数据...'); }; </script>
核心逻辑说明
- 组件通信:子组件通过
defineEmits定义事件,实时向父组件同步表单的完成状态(update:isFormIncomplete),验证通过后传递表单数据(formDataSubmitted)。 - 步骤器控制:通过
v-model="currentStep"管理当前步骤,自定义controls插槽实现按钮逻辑,根据isCurrentStepIncomplete计算属性判断是否禁用下一步。 - 验证同步:子组件的表单完成状态结合了字段非空检查和Vuelidate的
$invalid状态,确保只有完全验证通过的表单才能进入下一步。
内容的提问来源于stack exchange,提问作者Scheggia
相关产品推荐
相关产品推荐

