🐛 司机/车辆档案日期兼容 OCR 宽松格式

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-12 13:02:09 +08:00
parent 2377a2e8d4
commit 959c1821c1
2 changed files with 83 additions and 33 deletions
+31 -8
View File
@@ -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)) {
+52 -25
View File
@@ -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;
},