fix(表单验证): 改进表单验证失败处理和身份证输入组件验证逻辑
添加表单验证失败的调试信息和更健壮的错误处理,确保即使没有错误信息也能显示合理的提示 重构身份证输入组件,移除键盘相关代码,添加输入和变化事件处理,改进验证逻辑和调试信息
Showing
2 changed files
with
120 additions
and
59 deletions
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2022-09-14 14:44:30 | 2 | * @Date: 2022-09-14 14:44:30 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2024-11-21 10:39:09 | 4 | + * @LastEditTime: 2025-10-07 10:44:04 |
| 5 | * @FilePath: /data-table/src/components/IdentityField/index.vue | 5 | * @FilePath: /data-table/src/components/IdentityField/index.vue |
| 6 | * @Description: 身份证输入控件 | 6 | * @Description: 身份证输入控件 |
| 7 | --> | 7 | --> |
| ... | @@ -43,13 +43,15 @@ | ... | @@ -43,13 +43,15 @@ |
| 43 | :readonly="item.component_props.readonly" | 43 | :readonly="item.component_props.readonly" |
| 44 | :border="false" | 44 | :border="false" |
| 45 | @blur="onBlur(item)" | 45 | @blur="onBlur(item)" |
| 46 | + @input="onInput" | ||
| 47 | + @change="onChange" | ||
| 46 | > | 48 | > |
| 47 | <template #button> | 49 | <template #button> |
| 48 | <van-button size="small" type="primary" v-if="IdEditShow" @click="clickEdit()">编辑</van-button> | 50 | <van-button size="small" type="primary" v-if="IdEditShow" @click="clickEdit()">编辑</van-button> |
| 49 | </template> | 51 | </template> |
| 50 | </van-field> | 52 | </van-field> |
| 51 | <!-- <div v-if="gender" class="gender"><span>性别:</span>{{ gender }}</div> --> | 53 | <!-- <div v-if="gender" class="gender"><span>性别:</span>{{ gender }}</div> --> |
| 52 | - <van-number-keyboard | 54 | + <!-- <van-number-keyboard |
| 53 | v-model="item.value" | 55 | v-model="item.value" |
| 54 | :show="show" | 56 | :show="show" |
| 55 | extra-key="X" | 57 | extra-key="X" |
| ... | @@ -58,7 +60,7 @@ | ... | @@ -58,7 +60,7 @@ |
| 58 | @input="onInput" | 60 | @input="onInput" |
| 59 | @delete="onDelete" | 61 | @delete="onDelete" |
| 60 | safe-area-inset-bottom | 62 | safe-area-inset-bottom |
| 61 | - /> | 63 | + /> --> |
| 62 | </div> | 64 | </div> |
| 63 | </template> | 65 | </template> |
| 64 | 66 | ||
| ... | @@ -70,6 +72,7 @@ import { showSuccessToast, showFailToast, showConfirmDialog } from "vant"; | ... | @@ -70,6 +72,7 @@ import { showSuccessToast, showFailToast, showConfirmDialog } from "vant"; |
| 70 | // import idCard from "idcard"; | 72 | // import idCard from "idcard"; |
| 71 | import { styleColor } from "@/constant.js"; | 73 | import { styleColor } from "@/constant.js"; |
| 72 | import Cookies from 'js-cookie'; | 74 | import Cookies from 'js-cookie'; |
| 75 | +import { nextTick } from 'vue'; | ||
| 73 | 76 | ||
| 74 | const $route = useRoute(); | 77 | const $route = useRoute(); |
| 75 | const props = defineProps({ | 78 | const props = defineProps({ |
| ... | @@ -219,79 +222,120 @@ const clickRightIcon = () => { // 编辑模式 | ... | @@ -219,79 +222,120 @@ const clickRightIcon = () => { // 编辑模式 |
| 219 | fieldRef.value?.focus(); | 222 | fieldRef.value?.focus(); |
| 220 | }) | 223 | }) |
| 221 | } | 224 | } |
| 222 | -const openKeyboard = (e) => { | 225 | +// const openKeyboard = (e) => { |
| 223 | - if (readonly || e.target.className.indexOf('edit') > 0 || edit_mode.value) return false; // 如果为只读或者编辑模式,不能设置 | 226 | +// if (readonly || e.target.className.indexOf('edit') > 0 || edit_mode.value) return false; // 如果为只读或者编辑模式,不能设置 |
| 224 | - // // 键盘上移动 | 227 | +// // // 键盘上移动 |
| 225 | - // const target_to_view_height = window.innerHeight - e.target.getBoundingClientRect().y; // 元素到适口高度 | 228 | +// // const target_to_view_height = window.innerHeight - e.target.getBoundingClientRect().y; // 元素到适口高度 |
| 226 | - // const target_top = document.body.scrollHeight - $(e.target).offset().top; // 元素到正文高度 | 229 | +// // const target_top = document.body.scrollHeight - $(e.target).offset().top; // 元素到正文高度 |
| 227 | - // let scroll_height = ""; | 230 | +// // let scroll_height = ""; |
| 228 | - // console.warn(target_top); | 231 | +// // console.warn(target_top); |
| 229 | - // if (target_top < 250) { | 232 | +// // if (target_top < 250) { |
| 230 | - // document.getElementById("app").style.paddingBottom = "250px"; | 233 | +// // document.getElementById("app").style.paddingBottom = "250px"; |
| 231 | - // window.scrollTo(0, $("#app").height()); | 234 | +// // window.scrollTo(0, $("#app").height()); |
| 232 | - // } else { | 235 | +// // } else { |
| 233 | - // // 向上滚动位置 | 236 | +// // // 向上滚动位置 |
| 234 | - // document.documentElement.scrollTop = (target_top > 250 ? 0 : target_top) + 250; | 237 | +// // document.documentElement.scrollTop = (target_top > 250 ? 0 : target_top) + 250; |
| 235 | - // } | 238 | +// // } |
| 236 | - // 键盘上移动 | 239 | +// // 键盘上移动 |
| 237 | - const target_to_view_height = | 240 | +// const target_to_view_height = |
| 238 | - window.innerHeight - e.target.getBoundingClientRect().bottom; // 元素到适口高度 | 241 | +// window.innerHeight - e.target.getBoundingClientRect().bottom; // 元素到适口高度 |
| 239 | - const target_top = document.body.scrollHeight - $(e.target).offset().top; // 元素到正文高度 | 242 | +// const target_top = document.body.scrollHeight - $(e.target).offset().top; // 元素到正文高度 |
| 240 | - let scroll_height = ""; | 243 | +// let scroll_height = ""; |
| 241 | - if (target_to_view_height <= 250) { | 244 | +// if (target_to_view_height <= 250) { |
| 242 | - document.getElementById("app").style.paddingBottom = "250px"; | 245 | +// document.getElementById("app").style.paddingBottom = "250px"; |
| 243 | - // 向上滚动位置 | 246 | +// // 向上滚动位置 |
| 244 | - document.documentElement.scrollTop = $(e.target).offset().top - 244; | 247 | +// document.documentElement.scrollTop = $(e.target).offset().top - 244; |
| 245 | - } | 248 | +// } |
| 246 | - // 选中添加border颜色 | 249 | +// // 选中添加border颜色 |
| 247 | - content = $(e.target).parent(); | 250 | +// content = $(e.target).parent(); |
| 248 | - // TAG: 自定义主题颜色 | 251 | +// // TAG: 自定义主题颜色 |
| 249 | - content.css("border-color", "#c2915f"); | 252 | +// content.css("border-color", "#c2915f"); |
| 250 | - setTimeout(() => { | 253 | +// setTimeout(() => { |
| 251 | - show.value = true; | 254 | +// show.value = true; |
| 252 | - }, 300); | 255 | +// }, 300); |
| 253 | - // 记录点击field名 | 256 | +// // 记录点击field名 |
| 254 | - store.changeFieldName(props.item.name); | 257 | +// store.changeFieldName(props.item.name); |
| 255 | -}; | 258 | +// }; |
| 256 | -const blurKeyboard = () => { | 259 | +// const blurKeyboard = () => { |
| 257 | - show.value = false; | 260 | +// show.value = false; |
| 258 | - document.getElementById("app").style.paddingBottom = "0"; | 261 | +// document.getElementById("app").style.paddingBottom = "0"; |
| 259 | - // 还原border颜色 | 262 | +// // 还原border颜色 |
| 260 | - content.css("border-color", "#eaeaea"); | 263 | +// content.css("border-color", "#eaeaea"); |
| 261 | - // 键盘失焦检查输入和添加性别显示 | 264 | +// // 键盘失焦检查输入和添加性别显示 |
| 262 | - const input_val = props.item.value; | 265 | +// const input_val = props.item.value; |
| 263 | - if (required && !input_val) { | 266 | +// if (required && !input_val) { |
| 264 | - showFailToast("身份证号码不能为空"); | 267 | +// showFailToast("身份证号码不能为空"); |
| 265 | - } else if (input_val && !idCard.verify(input_val)) { | 268 | +// } else if (input_val && !idCard.verify(input_val)) { |
| 266 | - showFailToast("请输入正确身份证号码"); | 269 | +// showFailToast("请输入正确身份证号码"); |
| 267 | - } else { | 270 | +// } else { |
| 268 | - // gender.value = getGenderByIdNumber(input_val) | 271 | +// // gender.value = getGenderByIdNumber(input_val) |
| 269 | - } | 272 | +// } |
| 270 | -}; | 273 | +// }; |
| 271 | 274 | ||
| 272 | // 校验函数返回 true 表示校验通过,false 表示不通过 | 275 | // 校验函数返回 true 表示校验通过,false 表示不通过 |
| 273 | // 身份证号码为15位或者18位,15位时全为数字,18位前17位为数字,最后一位是校验位,可能为数字或字符X | 276 | // 身份证号码为15位或者18位,15位时全为数字,18位前17位为数字,最后一位是校验位,可能为数字或字符X |
| 274 | const required = props.item.component_props.required; | 277 | const required = props.item.component_props.required; |
| 275 | const validator = (val) => { | 278 | const validator = (val) => { |
| 276 | - if (required && !val) { | 279 | + // 添加调试信息,帮助排查问题 |
| 280 | + console.log('身份证验证器被调用:', { | ||
| 281 | + value: val, | ||
| 282 | + type: typeof val, | ||
| 283 | + required: required, | ||
| 284 | + isEmpty: !val, | ||
| 285 | + length: val ? val.length : 0 | ||
| 286 | + }); | ||
| 287 | + | ||
| 288 | + // 确保val是字符串类型 | ||
| 289 | + const valStr = String(val || ''); | ||
| 290 | + | ||
| 291 | + if (required && !valStr) { | ||
| 292 | + console.log('验证失败: 必填项为空'); | ||
| 277 | return false; | 293 | return false; |
| 278 | - } else if (val && !idCard.verify(val)) { | 294 | + } else if (valStr && !idCard.verify(valStr)) { |
| 295 | + console.log('验证失败: 身份证格式不正确'); | ||
| 279 | return false; | 296 | return false; |
| 280 | } else { | 297 | } else { |
| 298 | + console.log('验证通过'); | ||
| 281 | return true; | 299 | return true; |
| 282 | } | 300 | } |
| 283 | }; | 301 | }; |
| 302 | + | ||
| 284 | // 错误提示文案 | 303 | // 错误提示文案 |
| 285 | const validatorMessage = (val, rule) => { | 304 | const validatorMessage = (val, rule) => { |
| 286 | - if (required && !val) { | 305 | + const valStr = String(val || ''); |
| 306 | + if (required && !valStr) { | ||
| 287 | return "身份证号码不能为空"; | 307 | return "身份证号码不能为空"; |
| 288 | - } else if (val && !idCard.verify(val)) { | 308 | + } else if (valStr && !idCard.verify(valStr)) { |
| 289 | return "请输入正确身份证号码"; | 309 | return "请输入正确身份证号码"; |
| 290 | } | 310 | } |
| 291 | }; | 311 | }; |
| 292 | -const rules = [{ validator, message: validatorMessage }]; | ||
| 293 | 312 | ||
| 294 | -const onInput = (value) => {}; | 313 | +const rules = [{ |
| 314 | + validator, | ||
| 315 | + message: validatorMessage, | ||
| 316 | + trigger: 'onBlur' // 明确指定触发时机 | ||
| 317 | +}]; | ||
| 318 | + | ||
| 319 | +const onInput = (value) => { | ||
| 320 | + console.log('身份证输入事件触发:', value); | ||
| 321 | + // 触发验证 | ||
| 322 | + nextTick(() => { | ||
| 323 | + if (fieldRef.value) { | ||
| 324 | + fieldRef.value.validate(); | ||
| 325 | + } | ||
| 326 | + }); | ||
| 327 | +}; | ||
| 328 | + | ||
| 329 | +const onChange = (value) => { | ||
| 330 | + console.log('身份证值变化事件触发:', value); | ||
| 331 | + // 触发验证 | ||
| 332 | + nextTick(() => { | ||
| 333 | + if (fieldRef.value) { | ||
| 334 | + fieldRef.value.validate(); | ||
| 335 | + } | ||
| 336 | + }); | ||
| 337 | +}; | ||
| 338 | + | ||
| 295 | const onDelete = () => {}; | 339 | const onDelete = () => {}; |
| 296 | 340 | ||
| 297 | const gender = ref(''); | 341 | const gender = ref(''); | ... | ... |
| ... | @@ -1301,7 +1301,18 @@ const onSubmit = async (values) => { // 表单提交回调 | ... | @@ -1301,7 +1301,18 @@ const onSubmit = async (values) => { // 表单提交回调 |
| 1301 | }; | 1301 | }; |
| 1302 | 1302 | ||
| 1303 | const onFailed = (errorInfo) => { // 提交表单且验证不通过后触发 | 1303 | const onFailed = (errorInfo) => { // 提交表单且验证不通过后触发 |
| 1304 | + console.log('表单验证失败:', errorInfo); // 添加调试信息 | ||
| 1305 | + | ||
| 1306 | + // 检查是否有错误信息 | ||
| 1307 | + if (!errorInfo || !errorInfo.errors || errorInfo.errors.length === 0) { | ||
| 1308 | + console.warn('没有具体的错误信息'); | ||
| 1309 | + showToast('表单验证失败,请检查输入内容'); | ||
| 1310 | + return; | ||
| 1311 | + } | ||
| 1312 | + | ||
| 1304 | const error_item = errorInfo.errors[0]; | 1313 | const error_item = errorInfo.errors[0]; |
| 1314 | + console.log('第一个错误项:', error_item); // 添加调试信息 | ||
| 1315 | + | ||
| 1305 | // 通过name找到对应的label | 1316 | // 通过name找到对应的label |
| 1306 | let error_name = ''; | 1317 | let error_name = ''; |
| 1307 | formData.value.forEach(item => { | 1318 | formData.value.forEach(item => { |
| ... | @@ -1309,7 +1320,13 @@ const onFailed = (errorInfo) => { // 提交表单且验证不通过后触发 | ... | @@ -1309,7 +1320,13 @@ const onFailed = (errorInfo) => { // 提交表单且验证不通过后触发 |
| 1309 | error_name = item.component_props.label | 1320 | error_name = item.component_props.label |
| 1310 | } | 1321 | } |
| 1311 | }); | 1322 | }); |
| 1312 | - showToast(error_name + ': ' + error_item.message); | 1323 | + |
| 1324 | + // 确保有错误名称和消息 | ||
| 1325 | + const finalErrorName = error_name || error_item.name || '未知字段'; | ||
| 1326 | + const finalErrorMessage = error_item.message || '验证失败'; | ||
| 1327 | + | ||
| 1328 | + console.log('显示错误提示:', finalErrorName + ': ' + finalErrorMessage); | ||
| 1329 | + showToast(finalErrorName + ': ' + finalErrorMessage); | ||
| 1313 | } | 1330 | } |
| 1314 | 1331 | ||
| 1315 | // TAG: 监听formData的变化,当有新的字段添加时重新设置监听器 | 1332 | // TAG: 监听formData的变化,当有新的字段添加时重新设置监听器 | ... | ... |
-
Please register or login to post a comment