You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue3 + Vee-Validate4:handleSubmit函数无法触发问题求助

问题

Vue新手,正在学习使用Composition API开发。使用VeeValidate 4进行表单验证和提交时,handleSubmit函数完全无法触发,已按照官方文档创建表单,但不清楚哪里出错。

代码示例
<template>
    <div class="modal-dialog modal-dialog modal-lg modal-dialog-centered">
        <div class="modal-content">
            <div class="modal-header">
                <h4 class="modal-title">Add Layout </h4>
                <a class="modal-close" @click="isModalOpen = !isModalOpen"><i class="fa fa-times"></i></a>
            </div>
            <form autocomplete="off" @submit="onSubmit">
                <div class="modal-body">
                    <div class="row">
                        <div class="col-md-12 col-lg-12">
                            <div class="mb-3">
                                <label class="form-label">Layout Name:<span class="text-danger">*</span></label>
                                <div class="form-group">
                                    <input type="text" name="layoutName" v-model="layoutName" class="form-control"
                                        maxlength="50" placeholder="Enter Layout Name"
                                        :class="{ 'is-invalid': layoutNameError }" />
                                    <div class="invalid-feedback">{{ layoutNameError }}</div>

                                </div>
                            </div>
                        </div>
                        <div class="col-md-6 col-lg-6 col-sm-12 col-xs-12">
                            <div class="mb-3">
                                <label class="form-label">Module:<span class="text-danger">*</span></label>
                                <div class="form-group">
                                    <Dropdown name="moduleId" v-model="moduleId" :options="moduleList"
                                        optionLabel="text" placeholder="Select Module" class="widthAll"
                                        @change="onChangeModule($event.value)" :editable="true" :filter="true"
                                        :showClear="true" />
                                </div>
                            </div>
                        </div>
                        <div class="col-md-6 col-lg-6 col-sm-12 col-xs-12">
                            <div class="mb-3">
                                <label class="form-label">Sub Module:<span class="text-danger">*</span></label>
                                <div class="form-group">
                                    <Dropdown name="subModuleId" v-model="subModuleId" :options="subModuleList"
                                        optionLabel="text" placeholder="Select Sub Module" class="widthAll"
                                        :editable="true" :filter="true" :showClear="true" />
                                </div>
                            </div>
                        </div>
                        <div class="col-md-6 col-lg-6 col-sm-12 col-xs-12">
                            <div class="mb-3">
                                <label class="form-label">Device Type:<span class="text-danger">*</span></label>
                                <div class="form-group">
                                    <Dropdown name="deviceTypeId" v-model="deviceTypeId" :options="deviceTypeList"
                                        optionLabel="text" placeholder="Select Device Type" class="widthAll"
                                        :editable="true" :filter="true" />
                                </div>
                            </div>
                        </div>
                        <div class="col-md-6 col-lg-6 col-sm-12 col-xs-12">
                            <div class="mb-3">
                                <label class="form-label">Layout Type:<span class="text-danger">*</span></label>
                                <div class="form-group">
                                    <Dropdown name="layoutTypeId" v-model="layoutTypeId" :options="layoutTypeList"
                                        optionLabel="text" placeholder="Select Layout Type" class="widthAll"
                                        :editable="true" :filter="true" />
                                </div>
                            </div>
                        </div>
                        <div class="col-md-12 col-lg-12">
                            <div class="mb-3">
                                <label class="form-label">Description:</label>
                                <div class="form-group">
                                    <input type="text" name="description" v-model="description" class="form-control"
                                        maxlength="50" placeholder="Enter description"
                                        :class="{ 'is-invalid': descriptionError }" />
                                    <div class="invalid-feedback">{{ descriptionError }}</div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="modal-footer">
                    <div class="col-12 mt-3">
                        <button type="submit" class="btn btn-success me-2">Submit</button>
                        <a href="javascript:;" class="btn btn-danger form-btns" @click="isModalOpen = false">Cancel</a>
                    </div>
                </div>
            </form>
        </div>
    </div>
</template>
<script>
import { ref, onMounted } from 'vue';
import * as Yup from 'yup';
import Dropdown from 'primevue/dropdown';
import InputMask from '@/views/shared/inputmask/inputmasktemplate.vue';
//Services
import CommonService from '@/service/crmservice/commonService';
import ManageLayoutService from '@/service/crmservice/manageLayoutService';
import { useForm, useField } from 'vee-validate';

export default {
    name: 'ManageLayoutAddView',
    components: {
        InputMask,
        Dropdown
    },
    props: {
        isModalOpen: Yup.boolean
    },
    setup(props) {
        console.log(props)
        const commonService = ref(new CommonService());
        const manageLayoutService = ref(new ManageLayoutService());
        const layoutTypeKey = ref('layoutType');
        const deviceTypeKey = ref('deviceType');
        const moduleList = ref([]);
        const subModuleList = ref([]);
        const layoutTypeList = ref([]);
        const deviceTypeList = ref([]);
        const companyId = ref(null);
        const userId = ref(null);
        const layoutObj = ref({
            layoutName: null,
            moduleId: null,
            subModuleId: null,
            deviceTypeId: null,
            layoutTypeId: null,
            description: null,
        });
        const schema = Yup.object().shape({
            layoutName: Yup.string().required('Layout Name is required'),
            moduleId: Yup.string().required('Module is required'),
            subModuleId: Yup.string().required('Sub Module is required'),
            deviceTypeId: Yup.string().required('Device Type is required'),
            layoutTypeId: Yup.string().required('Layout Type is required'),
            description: Yup.string()
        });
        const { handleSubmit, errors, meta } = useForm({
            validationSchema: schema,

        });
        const { value: layoutName, errorMessage: layoutNameError } = useField("layoutName");
        const { value: moduleId, errorMessage: moduleIdError } = useField("moduleId");
        const { value: subModuleId, errorMessage: subModuleIdError } = useField("subModuleId");
        const { value: deviceTypeId, errorMessage: deviceTypeIdError } = useField("deviceTypeId");
        const { value: layoutTypeId, errorMessage: layoutTypeIdError } = useField("layoutTypeId");
        const { value: description, errorMessage: descriptionError } = useField("description");
        const getMasterItemList = (masterKey) => {
            // $loader.show();
            commonService.value.getMasterItemList(masterKey, "asd", userId.value, companyId.value)
                .then(response => {
                    masterKey == "layoutType" ? layoutTypeList.value = response.data : deviceTypeList.value = response.data;
                    // $loader.hide();
                });
        }
        const getModuleList = () => {
            // $loader.show();
            commonService.value.getModuleList(userId.value, companyId.value)
                .then(response => {
                    moduleList.value = response.data;
                    // $loader.hide();
                })
        }
        const getSubModuleList = (moduleId) => {
            // $loader.show();
            commonService.value.getSubModuleList(moduleId, userId.value, companyId.value)
                .then(response => {
                    subModuleList.value = response.data;
                    // $loader.hide();
                })
        }
        const onChangeModule = (event) => {
            getSubModuleList(event.value);
        }
        const getUserInfo = () => {
            userId.value = localStorage.getItem("userId");
            companyId.value = localStorage.getItem("companyId");
        }
        const onSubmit = handleSubmit((values) => {
            debugger;
            console.log(JSON.stringify(values, null, 2));
        });
        onMounted(() => {
            getUserInfo();
            getModuleList();
            getMasterItemList(layoutTypeKey.value);
            getMasterItemList(deviceTypeKey.value);
        })
        return {
            layoutObj,
            deviceTypeList,
            layoutTypeList,
            moduleList,
            subModuleList,
            errors,
            meta,
            layoutName, layoutNameError,
            moduleId, moduleIdError,
            subModuleId, subModuleIdError,
            deviceTypeId, deviceTypeIdError,
            layoutTypeId, layoutTypeIdError,
            description, descriptionError,
            getMasterItemList,
            getModuleList,
            onChangeModule,
            onSubmit
        }
    }
}
</script>
问题分析与解决

1. 修正Prop类型定义错误

你的props中用Yup.boolean定义isModalOpen的类型是错误的,Vue的Prop类型需要使用Vue内置的类型(如Boolean),而非Yup的验证方法。这个错误会导致组件初始化异常,进而影响handleSubmit的绑定。

修改后的props代码:

props: {
  isModalOpen: {
    type: Boolean,
    required: true // 根据需求设置是否必填
  }
}

2. 确保VeeValidate全局注册正确

检查项目的入口文件(如main.js)是否正确注册了VeeValidate核心组件与规则:

import { createApp } from 'vue';
import App from './App.vue';
import { Field, Form, ErrorMessage, defineRule, configure } from 'vee-validate';
import { required } from '@vee-validate/rules';

// 注册必填规则
defineRule('required', required);

// 配置实时验证
configure({
  validateOnInput: true,
});

const app = createApp(App);

// 全局注册VeeValidate组件
app.component('Field', Field);
app.component('Form', Form);
app.component('ErrorMessage', ErrorMessage);

app.mount('#app');

3. 确认表单提交事件未被阻止

可以在onSubmit中添加调试日志,同时手动控制提交状态,确认事件是否触发:

const onSubmit = handleSubmit((values, actions) => {
  actions.setSubmitting(false); // 手动结束提交状态
  console.log('表单提交触发,数据:', values);
  // 后续业务逻辑
});

4. 验证表单预验证状态

如果表单存在未通过验证的字段,handleSubmit会自动阻止进入回调函数。可以在模板中添加全局错误提示,确认是否是验证导致的提交失败:

<div v-if="meta.invalid" class="text-danger mb-3">表单存在错误,请检查后提交</div>

5. 检查PrimeVue组件的字段绑定一致性

确保PrimeVue的Dropdown组件的name属性与useField声明的字段名完全一致,同时确认组件的v-model正确绑定了useField返回的value值。

内容的提问来源于stack exchange,提问作者Fahad Ejaz Butt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 00:31:03