vue3中的form表单层级嵌套问题
先上代码
parent.vue
<script setup>
import { ref, reactive } from "vue";
import TaskList from "./ChildForm.vue";
const formRef = ref();
const formData = reactive({
projectName: "",
manager: "",
tasks: [],
});
const rules = reactive({
projectName: [
{ required: true, message: "项目名称不能为空", trigger: "blur" },
{ min: 3, max: 50, message: "长度在3到50个字符", trigger: "blur" },
],
manager: [{ required: true, message: "负责人不能为空", trigger: "change" }],
});
const validateTasks = (rule, value, callback) => {
if (formData.tasks.length === 0) {
callback(new Error("至少需要添加一个任务"));
} else {
callback();
}
};
const submit = () => {
formRef.value.validate((valid) => {
if (valid) {
console.log("提交数据:", formData);
}
});
};
</script>
<template>
<el-form ref="formRef" :model="formData" :rules="rules" label-width="120px">
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="formData.projectName" />
</el-form-item>
<el-form-item label="负责人" prop="manager">
<el-select v-model="formData.manager">
<el-option label="张三" value="zhangsan" />
<el-option label="李四" value="lisi" />
</el-select>
</el-form-item>
<el-form-item prop="tasks" :rules="[{ validator: validateTasks }]">
<TaskList v-model="formData.tasks" />
</el-form-item>
<el-button type="primary" @click="submit">提交</el-button>
</el-form>
</template>
child.vue
<script setup>
import { ref, computed, reactive } from "vue";
const props = defineProps({
modelValue: {
type: Array,
default: () => [],
},
});
const emit = defineEmits(["update:modelValue"]);
const taskRules = reactive({
name: [{ required: true, message: "任务名称不能为空", trigger: "blur" }],
priority: [{ required: true, message: "请选择优先级", trigger: "change" }],
});
const tasks = computed({
get: () => props.modelValue,
set: (value) => emit("update:modelValue", value),
});
const addTask = () => {
tasks.value.push({ name: "", priority: "medium" });
};
const removeTask = (index) => {
tasks.value.splice(index, 1);
};
</script>
<template>
<div class="task-list">
<el-button @click="addTask">添加任务</el-button>
<el-form
v-for="(task, index) in tasks"
:key="index"
:model="task"
:rules="taskRules"
class="task-form"
>
<el-form-item prop="name">
<el-input v-model="task.name" placeholder="任务名称" />
</el-form-item>
<el-form-item prop="priority">
<el-select v-model="task.priority">
<el-option label="高" value="high" />
<el-option label="中" value="medium" />
<el-option label="低" value="low" />
</el-select>
</el-form-item>
<el-button @click="removeTask(index)">删除</el-button>
</el-form>
</div>
</template>
<style scoped>
.task-form {
display: flex;
align-items: center;
gap: 10px;
margin-top: 10px;
}
</style>
效果如下:
一、组件结构设计原理
- 数据流向设计父组件通过v-model实现数据双向绑定:
// ProjectForm.vue
const formData = reactive({
tasks: [] // 自动同步到子组件
})
// TaskList.vue
const tasks = computed({
get: () => props.modelValue,
set: (v) => emit('update:modelValue', v)
})
2. 验证责任划分
- 父组件:验证任务列表非空
- 子组件:验证单个任务字段
二、分步实现流程
步骤1:父组件基础验证
// ProjectForm.vue
const validateTasks = (_, __, callback) => {
formData.tasks.length === 0
? callback(new Error('至少需要1个任务'))
: callback()
}
步骤2:子组件独立验证
// TaskList.vue
const taskRules = {
name: {
required: true,
trigger: 'blur',
message: '任务名称必填'
}
}
步骤3:动态prop绑定
<el-form-item
:prop="`tasks[${index}].name`"
:rules="taskRules.name">
<el-input v-model="item.name"/>
</el-form-item>
三、验证联动机制
- 提交时联合验证
// ProjectForm.vue
const submit = () => {
formRef.value.validate().then(() => {
taskListRef.value.validate().then(() => {
// 全部验证通过
})
})
}
2. 实时错误反馈
// TaskList.vue
watch(() => props.modelValue, () => {
formRef.value?.validate()
}, { deep: true })
四、异常处理方案
// 统一错误捕获
try {
await Promise.all([
formRef.value.validate(),
taskListRef.value.validate()
])
} catch (errors) {
console.error('验证失败:', errors)
}