hookehuyr

fix(表单验证): 改进表单验证失败处理和身份证输入组件验证逻辑

添加表单验证失败的调试信息和更健壮的错误处理,确保即使没有错误信息也能显示合理的提示
重构身份证输入组件,移除键盘相关代码,添加输入和变化事件处理,改进验证逻辑和调试信息
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的变化,当有新的字段添加时重新设置监听器
......