记录element验证踩坑:async-validator: [‘region is required‘]

在使用ElementUI进行下拉框表单验证时遇到问题,错误源于v-model的region与el-form-item的prop不匹配。解决方案是确保<el-form>的v-model(这里是addForm)和prop(如region)一致,同时注意数据模型的嵌套结构。文章提供了一个修正后的代码示例。

记录一下。在写element下拉框表单验证的时候出现的报错

这个报错中:‘region’指的是名字,错误原因也就是v-model和验证的prop验证名不一致造成的

一定要记得<el-form> 的el-form-item的prop与v-model名字保持一致

注意最外层:model数据嵌套

  <el-form :model="addForm"  :rules="rules" ref="numberValidateForm"   style="position: absolute;bottom: 7%;left: 10%;"  label-position="right"  label-width="100px" class="form">
       <el-form-item  label='单位角色' prop='region'>
          <el-select 
                  placeholder="请选择单位角色"
                  v-model="addForm.region"
                   >
                  <el-option
                    v-for="item in seldata"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value">
                  </el-option>
                </el-select>
       </el-form-item>
       
      </el-form>
 

   rules: {
        region: [
          { required: true, message: "请选择单位角色",  trigger: ['blur','change'] }
        ]
      },
      seldata:[
        {
          value:'建设单位',
          lable:'建设单位'
        },
        {
          value:'施工单位',
          lable:'施工单位'
        },{
          value:'监理单位',
          lable:'监理单位'
        },{
          value:'设计单位',
          lable:'设计单位'
        }
      ],
      addForm:{
         region:''
      }

<script setup> import { reactive, ref, onMounted, computed, nextTick } from "vue"; import { ElMessage } from "element-plus"; import { queryPageApi, addApi, updateApi, queryDeptApi } from "@/api/emp"; import { Plus } from "@element-plus/icons-vue"; const formRef = ref(); const formInline = reactive({ user: "", region: "", date: null, // 新增初始化 begin: "", end: "", }); const GENDER_MAP = { 1: "男", 2: "女" }; const JOB_MAP = { 1: "班主任", 2: "讲师", 3: "学工主管", 4: "教研主管", 5: "咨询师", }; // 日期范围验证器(确保开始/结束时间成对出现) const validateDates = (rule, value, callback) => { const { begin, end } = formInline; // 检查成对存在性 if (!!begin !== !!end) { callback(new Error("开始时间和结束时间必须同时填写或同时为空")); return; } // 当两者都存在时检查时间顺序 if (begin && end) { if (new Date(end) < new Date(begin)) { callback(new Error("结束日期不能早于开始日期")); return; } } callback(); }; // 表单验证规则配置(Element Plus 校验规则) const rules = reactive({ begin: [ { validator: (rule, value, callback) => { validateDates(rule, value, callback); formRef.value?.validateField("end"); // 触发结束日期重新校验 }, trigger: ["change", "blur"], // 增加 blur 触发时机 }, ], end: [ { validator: (rule, value, callback) => { validateDates(rule, value, callback); formRef.value?.validateField("begin"); // 触发开始日期重新校验 }, trigger: ["change", "blur"], // 增加 blur 触发时机 }, ], username: [ { required: true, message: "请输入用户名", trigger: "blur" }, { min: 2, max: 20, message: "用户名长度应在2到20个字符之间", trigger: "blur" }, ], name: [ { required: true, message: "请输入姓名", trigger: "blur" }, { min: 2, max: 10, message: "姓名长度应在2到10个字符之间", trigger: "blur" }, ], gender: [{ required: true, message: "请选择性别", trigger: "change" }], phone: [ { required: true, message: "请输入手机号", trigger: "blur" }, { pattern: /^1\d{10}$/, message: "请输入有效的手机号", trigger: "blur" }, ], job: [{ required: true, message: "请选择职位", trigger: "change" }], deptId: [{ required: true, message: "请选择部门", trigger: "change" }], entryDate: [{ required: true, message: "请选择入职日期", trigger: "change" }], }); // 表单提交处理(查询操作) const onSubmit = () => { search(); console.log("提交查询参数:", { name: formInline.user, gender: formInline.region, startDate: formInline.begin, end: formInline.end, }); }; // 日期字段清除处理(联动清空两个日期) const handleDateClear = () => { formInline.date = null; // 新增 formInline.begin = ""; formInline.end = ""; }; // 表单重置方法(清空所有字段) const clearForm = () => { formInline.user = ""; formInline.region = ""; handleDateClear(); // 复用日期清除方法 search(); }; // 示例数据 const empList = ref([]); // 分页配置 const currentPage = ref(1); const pageSize = ref(10); const total = ref(0); // 分页处理 const handleSizeChange = (val) => { pageSize.value = val; // 更新每页条数 currentPage.value = 1; // 重置到第一页 search(); }; const handleCurrentChange = (val) => { currentPage.value = val; // 更新当前页码 search(); }; //查询员工 const search = async () => { console.log("Search:", formInline); try { const result = await queryPageApi( formInline.user, formInline.region ? parseInt(formInline.region) : "", formInline.begin, formInline.end, currentPage.value, pageSize.value ); console.log("API Response:", result); // 添加响应日志 if (result.code === 1) { empList.value = result.data?.rows || []; total.value = result.data?.total || 0; } else { ElMessage.error(result.msg || "查询失败"); } } catch (e) { console.error("搜索失败:", e); // 添加错误日志 ElMessage.error("请求异常"); empList.value = []; } }; // --------------------新增员工-------------------- const mode = ref("add"); // 新增模式标识 const dialogVisible = ref(false); const dialogTitle = computed(() => (mode.value === "add" ? "新增员工" : "编辑员工")); const formData = reactive({ id: "", username: "", name: "", gender: "", phone: "", job: "", salary: "", deptId: "", entryDate: "", image: "", exprList: [ // 修改字段名与数据模型对齐 { dateRange: [], company: "", job: "", // 原position改为job }, ], }); //钩子函数 onMounted(async () => { try { const res = await queryDeptApi(); if (res.code === 1) { deptOptions.value = res.data.map((item) => ({ label: item.name, value: String(item.id), })); } } catch (e) { ElMessage.error("部门加载失败"); } // 确保无论如何都执行查询 search(); }); // 部门数据获取 const deptOptions = ref([]); // 打开对话框方法 const openDialog = (rowData) => { dialogVisible.value = true; // 必须先设置对话框可见 mode.value = rowData ? "edit" : "add"; if (rowData) { nextTick(() => { Object.assign(formData, { ...rowData, gender: String(rowData.gender), job: String(rowData.job), deptId: String(rowData.deptId), image: rowData.image || "", }); dialogForm.value?.clearValidate?.(); }); } else { resetFormData(); } }; // 新增清空表单方法 const resetFormData = () => { Object.assign(formData, { id: "", username: "", name: "", gender: "", phone: "", job: "", salary: "", deptId: "", entryDate: "", image: "", exprList: [ { dateRange: [], company: "", job: "", }, ], }); }; // 新增提交方法 const submitForm = async () => { try { const payload = { ...formData, gender: Number(formData.gender), job: Number(formData.job), deptId: Number(formData.deptId), salary: Number(formData.salary), exprList: formData.exprList.map((exp) => ({ company: exp.company, job: exp.job, begin: exp.dateRange?.[0] || "", end: exp.dateRange?.[1] || "", })), }; const result = mode.value === "add" ? await addApi(payload) : await updateApi(payload); if (result.code === 1) { ElMessage.success(mode.value === "add" ? "新增成功" : "修改成功"); dialogVisible.value = false; search(); } } catch (error) { ElMessage.error("提交失败"); } }; </script> <template> <!-- 页面主标题 --> <h2 class="page-title">员工管理</h2> <!-- 搜索筛选表单 --> <el-form ref="formRef" :inline="true" :model="formInline" :rules="rules" class="demo-form-inline" > <el-row :gutter="16" class="full-width-row"> <!-- 姓名查询模块 --> <el-col :span="5"> <el-form-item label="姓名" prop="user"> <el-input v-model="formInline.user" placeholder="员工姓名" clearable /> </el-form-item> </el-col> <!-- 性别筛选模块 --> <el-col :span="5"> <el-form-item label="性别" prop="region"> <el-select v-model="formInline.region" placeholder="性别" clearable> <el-option label="男" value="1" /> <el-option label="女" value="2" /> </el-select> </el-form-item> </el-col> <!-- 入职时间范围选择 --> <el-col :span="8"> <el-form-item label="入职时间" prop="dateRange"> <el-date-picker v-model="formInline.date" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD" style="width: 100%" /> </el-form-item> </el-col> <!-- 操作按钮组 --> <el-col :span="null" class="flex-auto"> <el-form-item> <el-button type="primary" @click="onSubmit">查询</el-button> <el-button @click="clearForm">清除</el-button> </el-form-item> </el-col> </el-row> </el-form> <!-- 操作按钮组 --> <el-col :span="20" class="button-group"> <el-form-item> <el-button type="primary" class="ml-2" @click="openDialog">+ 新增</el-button> <el-button type="danger">- 删除</el-button> </el-form-item> </el-col> <!-- --------------------表格-------------------- --> <el-table :data="empList" border style="width: 100%"> <el-table-column type="selection" width="55" align="center"></el-table-column> <el-table-column prop="name" label="姓名" width="120" align="center" ></el-table-column> <el-table-column label="性别" width="100" align="center"> <template #default="scope"> {{ scope.row.gender == 1 ? "男" : "女" }} </template> </el-table-column> <el-table-column label="头像" width="170" align="center"> <template #default="scope"> <img :src="scope.row.image" alt="Avatar" class="avatar" /> </template> </el-table-column> <el-table-column prop="deptName" label="部门名称" width="170" align="center" ></el-table-column> <el-table-column label="职位" width="120" align="center"> <template #default="scope"> <span v-if="scope.row.job == 1">班主任</span> <span v-else-if="scope.row.job == 2">讲师</span> <span v-else-if="scope.row.job == 3">学工主管</span> <span v-else-if="scope.row.job == 4">教研主管</span> <span v-else-if="scope.row.job == 5">咨询师</span> <span v-else>其他</span> </template> </el-table-column> <el-table-column prop="entryDate" label="入职日期" width="180" align="center" ></el-table-column> <el-table-column prop="updateTime" label="最后操作时间" width="210" align="center" ></el-table-column> <el-table-column label="操作" fixed="right" align="center"> <template #default="scope"> <el-button size="small" type="primary" @click="openDialog(scope.row)" >编辑</el-button > <el-button size="small" type="danger" @click="">删除</el-button> </template> </el-table-column> </el-table> <!-- 分页 --> <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" v-model:current-page="currentPage" v-model:page-size="pageSize" :page-sizes="[10, 20, 30, 40]" layout="total, sizes, prev, pager, next, jumper" :total="total" > </el-pagination> <!-- ---------------新增----------------- --> <el-dialog v-model="dialogVisible" :title="dialogTitle" width="50%"> <el-form :model="formData" :rules="rules" label-width="120px" ref="dialogForm"> <!-- 基本信息 --> <el-divider content-position="left">基本信息</el-divider> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="用户名" prop="username"> <el-input v-model="formData.username" placeholder="请输入员工用户名,2-20个字" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="姓名" prop="name"> <el-input v-model="formData.name" placeholder="请输入员工姓名,2-10个字" /> </el-form-item> </el-col> </el-row> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="性别" prop="gender"> <el-select v-model="formData.gender" placeholder="请选择"> <el-option label="男" value="1" /> <el-option label="女" value="2" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="手机号" prop="phone"> <el-input v-model="formData.phone" placeholder="请输入员工手机号" /> </el-form-item> </el-col> </el-row> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="职位" prop="job"> <el-select v-model="formData.job" placeholder="请选择"> <el-option label="班主任" value="1" /> <el-option label="讲师" value="2" /> <el-option label="学工主管" value="3" /> <el-option label="教研主管" value="4" /> <el-option label="咨询师" value="5" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="薪资"> <el-input v-model="formData.salary" placeholder="请输入员工薪资" /> </el-form-item> </el-col> </el-row> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="所属部门" prop="deptId"> <el-select v-model="formData.deptId" placeholder="请选择"> <el-option v-for="item in deptOptions" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="入职日期" prop="entryDate"> <el-date-picker v-model="formData.entryDate" type="date" value-format="YYYY-MM-DD" placeholder="选择日期" /> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="24"> <el-form-item label="头像"> <el-upload action="#" :auto-upload="false" :show-file-list="false" :on-change=" (file) => { const reader = new FileReader(); reader.onload = (e) => (formData.image = e.target.result); reader.readAsDataURL(file.raw); } " > <img v-if="formData.image" :src="formData.image" class="avatar" /> <el-icon v-else class="avatar-uploader-icon"> <Plus /> </el-icon> </el-upload> </el-form-item> </el-col> </el-row> <!-- 工作经历 --> <el-divider content-position="left"> 工作经历 <el-button type="primary" size="small" @click="formData.exprList.push({ dateRange: [], company: '', job: '' })" > 添加工作经历 </el-button> </el-divider> <div v-for="(exp, index) in formData.exprList" :key="index"> <el-row :gutter="3"> <el-col :span="10"> <el-form-item size="small" label="时间" label-width="80px"> <el-date-picker v-model="exp.dateRange" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD" style="width: 100%" /> </el-form-item> </el-col> <el-col :span="6"> <el-form-item size="small" label="公司" label-width="60px"> <el-input placeholder="请输入公司名称" v-model="exp.company" /> </el-form-item> </el-col> <el-col :span="6"> <el-form-item size="small" label="职位" label-width="60px"> <el-input placeholder="请输入职位" v-model="exp.job" /> </el-form-item> </el-col> <el-col :span="1"> <el-button type="danger" @click="formData.exprList.splice(index, 1)" size="small" >删除</el-button > </el-col> </el-row> </div> </el-form> <el-form-item class="dialog-footer"> <el-button type="primary" @click="submitForm">保存</el-button> <el-button @click="dialogVisible = false">取消</el-button> </el-form-item> </el-dialog> </template> <style scoped> .avatar { width: 40px; height: 40px; object-fit: cover; border-radius: 50%; } /* 页面标题样式 */ .page-title { margin-bottom: 24px; color: #303133; } /* 表单元素统一宽度 */ .demo-form-inline :deep(.el-input), .demo-form-inline :deep(.el-select) { --el-input-width: 220px; --el-select-width: 220px; } /* 日期范围选择布局 */ .date-range { width: 100%; display: flex; align-items: center; } .separator { text-align: center; } /* 操作按钮间距 */ .action-buttons { margin-left: 16px; } </style> 前端304显示空白
05-15
<template> <el-form ref="ruleFormRef" style="max-width: 600px" :model="ruleForm" :rules="rules" label-width="auto" > <el-form-item label="产品名称" prop="name"> <el-input v-model="ruleForm.name" /> </el-form-item> <el-form-item label="Activity zone" prop="region"> <el-select v-model="ruleForm.region" placeholder="Activity zone"> <el-option label="Zone one" value="shanghai" /> <el-option label="Zone two" value="beijing" /> </el-select> </el-form-item> <el-form-item label="Activity count" prop="count"> <el-select-v2 v-model="ruleForm.count" placeholder="Activity count" :options="options" /> </el-form-item> <el-form-item label="Activity time" required> <el-col :span="11"> <el-form-item prop="date1"> <el-date-picker v-model="ruleForm.date1" type="date" aria-label="Pick a date" placeholder="Pick a date" style="width: 100%" /> </el-form-item> </el-col> <el-col class="text-center" :span="2"> <span class="text-gray-500">-</span> </el-col> <el-col :span="11"> <el-form-item prop="date2"> <el-time-picker v-model="ruleForm.date2" aria-label="Pick a time" placeholder="Pick a time" style="width: 100%" /> </el-form-item> </el-col> </el-form-item> <el-form-item label="Instant delivery" prop="delivery"> <el-switch v-model="ruleForm.delivery" /> </el-form-item> <el-form-item label="Activity location" prop="location"> <el-segmented v-model="ruleForm.location" :options="locationOptions" /> </el-form-item> <el-form-item label="Activity type" prop="type"> <el-checkbox-group v-model="ruleForm.type"> <el-checkbox value="Online activities" name="type"> Online activities </el-checkbox> <el-checkbox value="Promotion activities" name="type"> Promotion activities </el-checkbox> <el-checkbox value="Offline activities" name="type"> Offline activities </el-checkbox> <el-checkbox value="Simple brand exposure" name="type"> Simple brand exposure </el-checkbox> </el-checkbox-group> </el-form-item> <el-form-item label="Resources" prop="resource"> <el-radio-group v-model="ruleForm.resource"> <el-radio value="Sponsorship">Sponsorship</el-radio> <el-radio value="Venue">Venue</el-radio> </el-radio-group> </el-form-item> <el-form-item label="Activity form" prop="desc"> <el-input v-model="ruleForm.desc" type="textarea" /> </el-form-item> <el-form-item> <el-button type="primary" @click="submitForm(ruleFormRef)"> Create </el-button> <el-button @click="resetForm(ruleFormRef)">Reset</el-button> </el-form-item> </el-form> </template> <script lang="ts" setup> import { reactive, ref } from 'vue' import type { FormInstance, FormRules } from 'element-plus' interface RuleForm { name: string region: string count: string date1: string date2: string delivery: boolean location: string type: string[] resource: string desc: string } const ruleFormRef = ref<FormInstance>() const ruleForm = reactive<RuleForm>({ name: 'Hello', region: '', count: '', date1: '', date2: '', delivery: false, location: '', type: [], resource: '', desc: '', }) const locationOptions = ['Home', 'Company', 'School'] const rules = reactive<FormRules<RuleForm>>({ name: [ { required: true, message: 'Please input Activity name', trigger: 'blur' }, { min: 3, max: 5, message: 'Length should be 3 to 5', trigger: 'blur' }, ], region: [ { required: true, message: 'Please select Activity zone', trigger: 'change', }, ], count: [ { required: true, message: 'Please select Activity count', trigger: 'change', }, ], date1: [ { type: 'date', required: true, message: 'Please pick a date', trigger: 'change', }, ], date2: [ { type: 'date', required: true, message: 'Please pick a time', trigger: 'change', }, ], location: [ { required: true, message: 'Please select a location', trigger: 'change', }, ], type: [ { type: 'array', required: true, message: 'Please select at least one activity type', trigger: 'change', }, ], resource: [ { required: true, message: 'Please select activity resource', trigger: 'change', }, ], desc: [ { required: true, message: 'Please input activity form', trigger: 'blur' }, ], }) const submitForm = async (formEl: FormInstance | undefined) => { if (!formEl) return await formEl.validate((valid, fields) => { if (valid) { console.log('submit!') } else { console.log('error submit!', fields) } }) } const resetForm = (formEl: FormInstance | undefined) => { if (!formEl) return formEl.resetFields() } const options = Array.from({ length: 10000 }).map((_, idx) => ({ value: `${idx + 1}`, label: `${idx + 1}`, })) </script> 产品名称列不能超过10个汉字
最新发布
07-11
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值