解决view-ui-plus 中表单验证不通过问题,select 组件开启multiple模式 总是提示错误,即使不验证也提示,有值也验证失败

😉 你好呀,我是爱编程的Sherry,很高兴在这里遇见你!我是一名拥有十多年开发经验的前端工程师。这一路走来,面对困难时也曾感到迷茫,凭借不懈的努力和坚持,重新找到了前进的方向。我的人生格言是——认准方向,坚持不懈,你终将迎来辉煌!欢迎关注我😁,我将在这里分享工作中积累的经验和心得,希望我的分享能够给你带来帮助。

错误场景一

报错描述

在使用view-ui-plus 的 Select组件开启多选后验证不通过,明明有选项报错,即使去掉验证也报错。
在这里插入图片描述

错误代码展示

const ruleValidate = reactive({
  targetSys: [{ required: false, message: '请选择', trigger: 'change' }]
});

错误原因分析

1. 确保 v-model 绑定的是一个数组
2. 设置正确的验证规则
3. 检查触发验证的方式是否正确

检查触发验证的方式是否正确,对于 Select 组件来说,通常是 change 事件

4. 检查 handleSelect 方法不会干扰验证

检查你的 handleSelect 方法是否有可能改变 formValidate.targetSys 或者其他可能影响验证状态的行为。确保它只做必要的事情,并且不会意外地清除或修改绑定的数据。

5. 确认 targetSysList 数据源无误

确保 targetSysList 提供了正确的选项数据,而且每个 Optionvaluelabel 都是有效的。

解决方案

经过分析后发现是验证规则问题,做一下修改:

rules: {
  targetSys: [
    { required: true, type: 'array', message: '请选择至少一项', trigger: 'change' },
    { type: 'array', min: 1, message: '最少选择一项', trigger: 'change' }
  ]
}

设置 type: 'array' 来指定这是对数组类型的验证,并设置 min 属性来规定最小长度。

错误场景二

错误描述

TreeSelect组件明明选择了,但是校验还是不通过
在这里插入图片描述
错误代码展示:

<FormItem label="文件夹" prop="pid">
  <TreeSelect v-model="formValidate.pid" :data="folderList" />
</FormItem>
formValidate: {
  name: '',
  pid: null,
  description: ''
},
ruleValidate: {
  name: [
    { required: true, message: `请输入`, trigger: 'blur' }
  ],
  pid: [
    { required: true, message: `请选择`, trigger: 'change' }
  ]
}

错误原因分析

TreeSelect是按照字符串格式进行验证的,这里数据中的value是数字格式,报错是因为格式不匹配。

解决方案

声明type: 'number'

formValidate: {
  name: '',
  pid: null,
  description: ''
},
ruleValidate: {
  name: [
    { required: true, message: `请输入`, trigger: 'blur' }
  ],
  pid: [
    { required: true, type: 'number', message: `请选择`, trigger: 'change' }
  ]
}

这样也有一个弊端,取消弹框this.$refs.formValidateRef.resetFields()后,再次打开弹框默认会验证
在这里插入图片描述

最好的方式是将value转换成字符串格式。如果是下拉框的值最好将值全部转换为字符串格式,不然后面关闭弹框是没办法(暂时也没有更好的办法)取消提示错误的。

总结

表单验证会报错,除了没有值会通不过,格式错误,验证规则错误也会通不过,以后遇到这种明明有值,但还一直提示不通过,首先要去考虑是否是数据格式不匹配,或则验证规则不匹配,这个方法通用,不限制于任何框架。

<!-- 补课 --> <template> <div class="page-content" v-loading="loading"> <el-card class="search-card"> <el-form-item label="上课日期:"> <el-radio-group class="custom-radio-group" @change="getData" v-model="query.timeType"> <el-radio-button label="限" :value="-1" /> <el-radio-button label="本周" :value="1" /> <el-radio-button label="本月" :value="2" /> <el-radio-button label="上月" :value="3" /> <el-radio-button label="自定义" :value="4" /> </el-radio-group> </el-form-item> <el-divider border-style="dashed" class="query-split-line" /> <el-form-item label="补课状态:"> <el-radio-group class="custom-radio-group" @change="getData" v-model="query.makeupStatus"> <el-radio-button label="限" :value="-1" /> <el-radio-button label="已补课" :value="1" /> <el-radio-button label="未补课" :value="0" /> </el-radio-group> </el-form-item> <el-divider border-style="dashed" v-if="showMore" class="query-split-line" /> <el-form-item label="更多筛选:" class="more-filter" v-show="showMore"> <el-select placeholder="授课模式" clearable v-model="query.teachType" @change="getData"> <el-option label="班课" :value="1" /> <el-option label="1对1" :value="2" /> </el-select> <CampusSelect placeholder="校区" multiple @change="getData" clearable v-model="query.campusIds" /> <GradeSelect placeholder="年级" multiple @change="getData" clearable v-model="query.grades" /> <SubjectSelect placeholder="科目" multiple @change="getData" clearable v-model="query.subjectIds" /> <CourseTypeSelect placeholder="课程类型" v-model="query.courseTypeIds" clearable multiple @change="getData" /> <TeacherSelect placeholder="请选择老师" @change="getData" clearable v-model="query.teacherId" filterable /> <TeacherSelect placeholder="请选择教务" @change="getData" clearable v-model="query.assistantId" filterable /> <DatePicker transfer type="daterange" placeholder="补课日期" :value="query.makeupDateRange" style="width: 200px" @on-change="onMakeupDateRangeChange" /> <el-select placeholder="上课时间段" /> <el-select placeholder="上课教室" @change="getData" /> </el-form-item> <el-divider border-style="dashed" class="cursor-pointer query-split-line" @click="showMore = !showMore"> {{ showMore ? "收起" : "更多筛选" }} <el-icon> <ArrowUp v-if="showMore" /> <ArrowDown v-else /> </el-icon> </el-divider> </el-card> <el-card> <div class="flex justify-between text-sm mb4"> <div class="flex gap-1"> 共 <strong>{{ total }}</strong> 条班级记录 </div> <!--查询--> <div class="flex gap-3"> <el-input placeholder="输入学生关键字查询" v-model="input" class="input-with-select"> <template #prepend> <el-select v-model="selectType" style="width: 80px"> <el-option label="课程" value="course" /> <el-option label="班级" value="class" /> <el-option label="学生" value="student" /> </el-select> </template> </el-input> <!--批量操作--> <el-dropdown @command="(command: string) => handleMakeupAction(selectedRows, command, true)"> <el-button class="batch-action-btn"> 批量操作 <el-icon><arrow-down /></el-icon> </el-button> <template #dropdown> <el-dropdown-menu> <el-dropdown-item @click="handleBatchMakeup">直接补课</el-dropdown-item> <!-- 插班补课按钮屏蔽 --> <!-- <el-dropdown-item command="in-class">插班补课</el-dropdown-item> --> <el-dropdown-item command="cancel">取消补课</el-dropdown-item> </el-dropdown-menu> </template> </el-dropdown> </div> </div> <SelectionTable ref="tableRef" :data="tableData" header-cell-class-name="table-header" border v-model="selectedRows"> <el-table-column label="上课日期" show-overflow-tooltip min-width="180"> <template #default="scope">{{ moment(scope.row.date).format("YYYY-MM-DD ") }}</template> </el-table-column> <el-table-column label="上课时间" show-overflow-tooltip min-width="180" prop="classItem.classTime" /> <el-table-column label="ID" show-overflow-tooltip min-width="100" prop="student.id" /> <el-table-column label="班级" show-overflow-tooltip min-width="300" prop="classGroup.name" /> <el-table-column label="学员" show-overflow-tooltip min-width="100" prop="student.name" /> <el-table-column label="老师" show-overflow-tooltip min-width="100"> <template #default="scope"> {{ getNames(scope.row.classItem.teachers).join(",") }} </template> </el-table-column> <el-table-column label="教务" show-overflow-tooltip min-width="100"> <template #default="scope"> {{ getNames(scope.row.classGroup.assistants).join(",") }} </template> </el-table-column> <el-table-column label="考勤状态" width="100"> <template #default="scope"> <el-tag v-if="scope.row.classItem.status == 1" type="success">出勤</el-tag> <el-tag v-else-if="scope.row.classItem.status == 2" type="warning">请假</el-tag> <el-tag v-else-if="scope.row.classItem.status == 3" type="danger">缺勤</el-tag> <el-tag v-else type="info">未知</el-tag> </template> </el-table-column> <el-table-column label="补课状态" width="100"> <template #default="scope"> <el-tag v-if="scope.row.makeupStatus == 0" type="info">未补课</el-tag> <el-tag v-else-if="scope.row.makeupStatus == 1" type="success">已补课</el-tag> </template> </el-table-column> <el-table-column label="补课类型" width="100"> <template #default="scope"> <el-tag v-if="scope.row.makeupType == MakeupType.Direct" type="success">直接补课</el-tag> <el-tag v-else-if="scope.row.makeupType == MakeupType.MakeupInOtherClass" type="success">插班补课</el-tag> </template> </el-table-column> <el-table-column label="补课老师" width="100"> <template #default="scope"> {{ getNames([scope.row.teacherId]).join(",") }} </template> </el-table-column> <el-table-column label="补课日期" width="100" prop="makeupDate"> <template #default="scope"> {{ scope.row.makeupDate ? moment(scope.row.makeupDate).format("YYYY-MM-DD") : "" }} </template> </el-table-column> <el-table-column label="备注" show-overflow-tooltip width="120" prop="remark" /> <el-table-column label="操作" width="80" fixed="right"> <template #default="{ row }"> <el-dropdown trigger="click"> <span class="more-action"> 更多 <el-icon class="arrow-icon"><ArrowDown /></el-icon> </span> <template #dropdown> <el-dropdown-menu> <el-dropdown-item v-if="row.makeupStatus === 0" @click="handleMakeup">直接补课</el-dropdown-item> <!-- 插班补课按钮屏蔽 --> <!-- <el-dropdown-item v-if="row.makeupStatus === 0" command="in-class">插班补课</el-dropdown-item> --> <el-dropdown-item v-if="row.makeupStatus === 1" @click="cancelMakeup(row)">取消补课</el-dropdown-item> </el-dropdown-menu> </template> </el-dropdown> </template> </el-table-column> </SelectionTable> </el-card> <!--直接补课对话框--> <el-dialog v-model="directMakeup" title="直接补课" width="600px"> <el-form :model="form" label-position="top" ref="formRef" :rules="rules"> <el-form-item label="补课老师" prop="teacherId"> <TeacherSelect v-model="form.teacherId" filterable clearable placeholder="请选择老师" /> </el-form-item> <el-form-item label="补课日期" prop="makeupDate"> <el-date-picker v-model="form.makeupDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择补课日期" style="width: 40%" /> </el-form-item> <el-form-item label="补课时间段" prop="makeUpTimeRange"> <TimePicker confirm type="timerange" :value="form.makeUpTimeRange" @on-change="onTimeRangeChange" placeholder="请选择上课时段" :steps="[1, 15]" :format="timeFormat" /> </el-form-item> <el-form-item label="备注" prop="remark"> <el-input v-model="form.remark" placeholder="请填写备注" type="textarea" /> </el-form-item> </el-form> <template #footer> <div class="dialog-footer"> <el-button type="primary" :disabled="loading" @click="handleConfirmMakeup">确定</el-button> <el-button @click="handleDialogClose">取消</el-button> </div> </template> </el-dialog> <!--插班补课对话框--> <el-dialog v-model="inclassMakeup" title="插班补课" width="1000px"> <el-alert title="只支持同课程下的班级安排补课,每个月3号过后,能插入上个月及以前月份的班级" type="warning" class="mb-4" /> <div class="filters mb-4 flex gap-4"> <!-- 校区下拉选择 --> <CampusSelect placeholder="请选择" v-model="classItemQuery.campusIds" multiple @change="getClassItemList" style="width: 90px" /> <!-- 班级输入建议 --> <el-autocomplete placeholder="请选择班级" style="width: 180px" clearable /> <!-- 日期选择 --> <el-date-picker type="daterange" value-format="YYYY-MM-DD" start-placeholder="开始日期" end-placeholder="结束日期" class="w-64" /> </div> <!--插班补课表格--> <el-table :data="classItemList" border header-cell-class-name="table-header" @selection-change="handleClassItemSelectionChange"> <el-table-column type="selection" width="55" align="center" :selectable="() => true" :reserve-selection="false"> <template #header> <!-- 空 header,隐藏全选框 --> </template> </el-table-column> <el-table-column label="班级" show-overflow-tooltip min-width="180" prop="classGroup.name" /> <el-table-column label="上课时间" show-overflow-tooltip min-width="180"> <template #default="scope">{{ moment(scope.row.date).format("YYYY-MM-DD ") }}{{ scope.row.startTime }} - {{ scope.row.endTime }}</template> </el-table-column> <el-table-column label="老师" show-overflow-tooltip min-width="100"> <template #default="scope"> {{ getNames(scope.row.teachers).join(",") }} </template> </el-table-column> <el-table-column label="教室" show-overflow-tooltip min-width="100" prop="classRoomName" /> </el-table> <template #footer> <div class="dialog-footer"> <el-button type="primary" :disabled="loading" @click="submitMakeupInclass">确定</el-button> <el-button @click="inclassMakeup = false">取消</el-button> </div> </template> </el-dialog> </div> </template> <script setup lang="ts"> import type { ElTable, FormInstance } from "element-plus"; import moment from "moment"; import { useUserStore } from "@/store"; import { Makeup, MakeupQuery, MakeupStatus, MakeupType, rules } from "@/models/enroll/Makeup"; import { useRouter } from "vue-router"; import { MakeupAPI } from "@/api/MakeupApi"; import { DatePicker } from "view-ui-plus"; import { ClassItem } from "@/models/routine/ClassGroup"; import { RoutineAPI } from "@/api/RoutineAPI"; import { ClassItemQuery } from "@/models/routine/scheduleTable/Query"; import { TimePicker } from "view-ui-plus"; const classItemList = ref<ClassItem[]>([]); const userStore = useUserStore(); const directMakeup = ref(false); const inclassMakeup = ref(false); const timeFormat: any = "HH:mm"; // 状态管理 const loading = ref(false); const showMore = ref(true); const tableData = ref<Makeup[]>([]); const total = ref(0); const input = ref(""); const selectType = ref("student"); const tableRef = ref(); const selectedRows = ref<Makeup[]>([]); const query = ref<MakeupQuery>(new MakeupQuery()); const classItemQuery = ref<ClassItemQuery>(new ClassItemQuery()); //插班补课表格复选框 const selectedClassItems = ref<ClassItem[]>([]); function handleClassItemSelectionChange(rows: ClassItem[]) { selectedClassItems.value = rows; } //直接补课表 const formRef = ref<FormInstance>(); const form = ref(new Makeup()); //重置直接补课表,清除验证状态 function resetForm() { form.value = new Makeup(); nextTick(() => { if (formRef.value) { formRef.value.clearValidate(); // 清除验证状态 } }); } //统一对话框关闭处理 function handleDialogClose() { resetForm(); directMakeup.value = false; } function onTimeRangeChange(range: string[]) { form.value.makeUpTimeRange = range[0] ? range : []; } //提交补课 async function handleConfirmMakeup() { formRef.value?.validate(async (pass) => { if (pass) { if (form.value.id) { loading.value = true; form.value.makeupStatus = MakeupStatus.Completed; const res = await MakeupAPI.updateMakeup(form.value); loading.value = false; if (res) { handleDialogClose(); getData(); } } else { const datas = selectedRows.value.map((item) => { item.makeupStatus = MakeupStatus.Completed; const data = new Makeup(); Object.assign(data, item); data.makeupType = form.value.makeupType; data.makeUpTimeRange = form.value.makeUpTimeRange; data.makeupDate = item.makeupDate; data.teacherId = form.value.teacherId; data.makeupDate = form.value.makeupDate; data.makeupStatus = item.makeupStatus; data.remark = form.value.remark; return data; }); loading.value = true; const res = await MakeupAPI.updateMakeupBatch(datas); loading.value = false; if (res) { handleDialogClose(); getData(); } } } }); } /** 提交插班补课 */ function submitMakeupInclass() {} function handleMakeup(row: Makeup) { form.value = JSON.parse(JSON.stringify(row)); form.value.makeupType = MakeupType.Direct; directMakeup.value = true; } function handleBatchMakeup() { if (selectedRows.value.length === 0) { ElMessage.warning("请先选择需要补课的数据"); return; } if (selectedRows.value.some((item) => item.makeupStatus !== 0)) { ElMessage.warning("请选择未补课的数据"); return; } form.value = new Makeup(); form.value.makeupType = MakeupType.Direct; directMakeup.value = true; } async function cancelMakeup(row: Makeup) { ElMessageBox.confirm("确定要取消补课吗?", "提示", { confirmButtonText: "确定", cancelButtonText: "取消", }) .then(async () => { loading.value = true; const res = await MakeupAPI.cancelMakeup([row.id]); loading.value = false; if (res) { getData(); } }) .catch(() => { ElMessage.info("操作取消"); }); } //补课类型选择 const handleMakeupAction = async (rows: Makeup[], action: string, isBatch = false) => { try { if (!isBatch) { // 条操作,直接执行,无需校验 const row = rows[0]; switch (action) { case "in-class": selectedRows.value = rows; (async () => { await getClassItemList(); inclassMakeup.value = true; })(); return; case "cancel": //cancelMakeup接口 try { await ElMessageBox.confirm("确定要取消这条补课记录吗?", "取消确认", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning", }); } catch (cancel) { return; // 用户取消操作 } const cancelSuccess = await MakeupAPI.cancelMakeup([row.id]); if (cancelSuccess) { ElMessage.success("取消成功"); getData(); // 刷新数据 } return; } return; } // 批量操作校验 const makeStatuses = new Set(rows.map((r) => r.makeupStatus)); if (makeStatuses.size > 1 && action === "cancel") { ElMessage.warning("请选择相同补课状态的记录进行批量操作"); return; } const makeStatuse = rows[0].makeupStatus; if (action === "cancel" && makeStatuse !== 1) { ElMessage.warning("只有已补课状态的记录才允许取消补课"); return; } // if ((action === "direct" || action === "in-class") && makeStatuse !== 0) { // ElMessage.warning("只有未补课状态的记录才允许进行补课操作"); // return; //} // 批量操作取消补课 if (action === "cancel") { // 添加用户确认 try { await ElMessageBox.confirm(`确定要取消选中的${rows.length}条补课记录吗?`, "批量取消确认", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning", }); } catch (cancel) { return; // 用户取消操作 } // 调用取消补课API const ids = rows.map((r) => r.id); const cancelSuccess = await MakeupAPI.cancelMakeup(ids); if (cancelSuccess) { ElMessage.success("批量取消成功"); getData(); // 刷新数据 // 清空选择 selectedRows.value = []; tableRef.value?.clearSelection(); } return; } else { if (action === "direct") { selectedRows.value = rows; directMakeup.value = true; return; } else if (action === "in-class") { rows.forEach((r) => { selectedRows.value = rows; inclassMakeup.value = true; }); getData(); } } } finally { loading.value = false; } }; async function getData() { loading.value = true; const res = await MakeupAPI.queryMakeup(query.value); loading.value = false; tableData.value = Array.isArray(res?.records) ? res.records : []; total.value = res?.total || 0; nextTick(() => { tableRef.value.toggleSelection(selectedRows.value); }); } async function getClassItemList() { loading.value = true; const res = (await RoutineAPI.queryClassItem(classItemQuery.value)) as PageResult<ClassItem>; loading.value = false; classItemList.value = (res?.records ?? []) as ClassItem[]; total.value = (res?.total ?? 0) as number; } function getNames(row: string[]) { return row?.map((r) => userStore.userNameMap.get(parseInt(r))) ?? []; } function handleSelectionChange(rows: Makeup[]) { selectedRows.value = rows; } function onMakeupDateRangeChange(range: string[]) { query.value.makeupDateRange = range[0] ? [range[0] + " 00:00:00", range[1] + " 23:59:59"] : []; getData(); } onMounted(() => { getData(); getClassItemList(); }); </script> <style lang="scss" scoped></style> 你帮我检查一下,为什么我现在直接补课直接用了了,请求内容[] No properties 回复{ "success": false, "code": 500, "message": "补课失败", "data": null }
最新发布
08-10
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sherry Tian

打赏1元鼓励作者

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值