diff --git a/src/views/transportCapacity/driver.vue b/src/views/transportCapacity/driver.vue index 2efbed7..d9ce637 100644 --- a/src/views/transportCapacity/driver.vue +++ b/src/views/transportCapacity/driver.vue @@ -1593,17 +1593,40 @@ export default { const fullNation = `${nation}族`; return this.nationOptions.includes(fullNation) ? fullNation : nation; }, + /** + * OCR / 手工录入日期归一化:对齐车辆侧与后端 FlexibleLocalDateDeserializer。 + * 支持 yyyyMMdd、yyyy-MM-dd、2018.3.9、2030年05月20日及任意非数字分隔;无法识别返回空串。 + */ normalizeBirthday(value = '') { const birthday = String(value || '').trim(); - const compactMatch = birthday.match(/^(\d{4})(\d{2})(\d{2})$/); - if (compactMatch) { - const [, year, month, day] = compactMatch; - return `${year}-${month}-${day}`; + if (!birthday) return ''; + if (/长期|永久/.test(birthday)) return ''; + const looseMatch = birthday.match(/(\d{4})\D+(\d{1,2})\D+(\d{1,2})/); + if (looseMatch) { + return this.toValidDateText(looseMatch[1], looseMatch[2], looseMatch[3]); } - const match = birthday.match(/^(\d{4})[-/.年](\d{1,2})[-/.月](\d{1,2})日?$/); - if (!match) return birthday; - const [, year, month, day] = match; - return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`; + const compact = birthday.replace(/\D/g, ''); + if (compact.length === 8) { + return this.toValidDateText(compact.slice(0, 4), compact.slice(4, 6), compact.slice(6, 8)); + } + return ''; + }, + toValidDateText(yearText, monthText, dayText) { + const year = Number(yearText); + const month = Number(monthText); + const day = Number(dayText); + if (!Number.isInteger(year) || !Number.isInteger(month) || !Number.isInteger(day)) { + return ''; + } + const date = new Date(year, month - 1, day); + if ( + date.getFullYear() !== year || + date.getMonth() !== month - 1 || + date.getDate() !== day + ) { + return ''; + } + return `${String(year).padStart(4, '0')}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`; }, parseIdCardInfo(idCardNo = '') { if (!/^\d{17}[\dX]$/.test(idCardNo)) { diff --git a/src/views/transportCapacity/vehicle.vue b/src/views/transportCapacity/vehicle.vue index 49802c9..9ec02ca 100644 --- a/src/views/transportCapacity/vehicle.vue +++ b/src/views/transportCapacity/vehicle.vue @@ -1174,37 +1174,59 @@ export default { }); }, /** - * OCR 日期常为区间(如 2022-08-17至2024-01-05)或含无效日(如 01-65)。 - * 提取可解析的合法日期;prefer=first 取登记类起始日,prefer=last 取有效期截止日。 + * OCR 日期常为区间(如 2022-08-17至2024-01-05)、无分隔符(如 20260317)、 + * 年月日/点分(2030年05月20日、2018.3.9)或任意非数字分隔。 + * 对齐司机侧 normalizeBirthday / 后端 OcrFieldSupport:年与月、月与日之间允许任意非数字。 + * prefer=first 取登记类起始日,prefer=last 取有效期截止日。 + * 返回值统一为 YYYY-MM-DD,供 el-date-picker 展示与后端 LocalDate 反序列化。 */ normalizeDate(value = '', prefer = 'first') { const dateValue = String(value || '').trim(); if (!dateValue) return ''; - const segments = dateValue.match(/\d{4}[-/.年]\d{1,2}[-/.月]\d{1,2}日?/g) || []; - const sourceList = segments.length ? segments : [dateValue]; - const candidates = sourceList - .map(item => { - const match = String(item) - .trim() - .match(/^(\d{4})[-/.年](\d{1,2})[-/.月](\d{1,2})日?$/); - if (!match) return ''; - const year = Number(match[1]); - const month = Number(match[2]); - const day = Number(match[3]); - const date = new Date(year, month - 1, day); - if ( - date.getFullYear() !== year || - date.getMonth() !== month - 1 || - date.getDate() !== day - ) { - return ''; + if (/长期|永久/.test(dateValue)) return ''; + const candidates = []; + const loosePattern = /(\d{4})\D+(\d{1,2})\D+(\d{1,2})/g; + let match = loosePattern.exec(dateValue); + while (match) { + const normalized = this.toValidDateText(match[1], match[2], match[3]); + if (normalized) { + candidates.push(normalized); + } + match = loosePattern.exec(dateValue); + } + if (!candidates.length) { + const compact = dateValue.replace(/\D/g, ''); + if (compact.length === 8) { + const normalized = this.toValidDateText( + compact.slice(0, 4), + compact.slice(4, 6), + compact.slice(6, 8) + ); + if (normalized) { + candidates.push(normalized); } - return `${String(year).padStart(4, '0')}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`; - }) - .filter(Boolean); + } + } if (!candidates.length) return ''; return prefer === 'last' ? candidates[candidates.length - 1] : candidates[0]; }, + toValidDateText(yearText, monthText, dayText) { + const year = Number(yearText); + const month = Number(monthText); + const day = Number(dayText); + if (!Number.isInteger(year) || !Number.isInteger(month) || !Number.isInteger(day)) { + return ''; + } + const date = new Date(year, month - 1, day); + if ( + date.getFullYear() !== year || + date.getMonth() !== month - 1 || + date.getDate() !== day + ) { + return ''; + } + return `${String(year).padStart(4, '0')}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`; + }, normalizeVehicleDateFields(form = {}) { const dateFields = [ ['compulsoryScrapDate', 'first'], @@ -1216,8 +1238,13 @@ export default { ['registrationDate', 'first'], ]; dateFields.forEach(([prop, prefer]) => { - if (form[prop] === undefined || form[prop] === null || form[prop] === '') return; - form[prop] = this.normalizeDate(form[prop], prefer); + if (form[prop] === undefined || form[prop] === null || form[prop] === '') { + form[prop] = null; + return; + } + const normalized = this.normalizeDate(form[prop], prefer); + // 无法识别或空串统一置 null,避免把 "" / "20260317" 原样提交给 LocalDate + form[prop] = normalized || null; }); return form; },