✨ 司机身份证OCR支持正反面识别并回填有效期

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-30 11:47:21 +08:00
parent fe4255c59b
commit 8d85311bb3
+112 -18
View File
@@ -303,6 +303,42 @@
/> />
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="18">
<el-col :span="6">
<el-form-item label="身份证有效期" prop="idCardValidEnd">
<div class="date-range">
<el-date-picker
v-model="driverForm.idCardValidStart"
type="date"
value-format="YYYY-MM-DD"
placeholder="起"
:disabled="driverForm.idCardLongTerm === 1"
@change="validateFormField('idCardValidEnd')"
/>
<el-date-picker
v-model="driverForm.idCardValidEnd"
type="date"
value-format="YYYY-MM-DD"
placeholder="止"
:disabled="driverForm.idCardLongTerm === 1"
@change="validateFormField('idCardValidEnd')"
/>
</div>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="长期有效" prop="idCardLongTerm">
<el-checkbox
v-model="driverForm.idCardLongTerm"
:true-label="1"
:false-label="0"
@change="handleIdCardLongTermChange"
>
是
</el-checkbox>
</el-form-item>
</el-col>
</el-row>
</section-card> </section-card>
<section-card title="驾驶证信息"> <section-card title="驾驶证信息">
@@ -701,6 +737,9 @@ const emptyForm = () => ({
posts: '', posts: '',
idCardFront: '', idCardFront: '',
idCardBack: '', idCardBack: '',
idCardValidStart: '',
idCardValidEnd: '',
idCardLongTerm: 0,
headPhoto: '', headPhoto: '',
drivingType: '', drivingType: '',
drivingLicenseNo: '', drivingLicenseNo: '',
@@ -1260,12 +1299,13 @@ export default {
}, },
handleIdCardUploadSuccess(prop, url) { handleIdCardUploadSuccess(prop, url) {
this.setImage(prop, url); this.setImage(prop, url);
if (prop !== 'idCardFront') { this.recognizeIdCard(url, prop);
return; },
handleIdCardLongTermChange() {
if (this.driverForm.idCardLongTerm === 1) {
this.driverForm.idCardValidEnd = '';
} }
this.recognizeBaiduOcr(url, 'id_card', 'front', '身份证', data => { this.validateFormField('idCardValidEnd');
this.applyIdCardRecognition(data);
});
}, },
handleDrivingLicenseUploadSuccess(prop, url) { handleDrivingLicenseUploadSuccess(prop, url) {
this.setImage(prop, url); this.setImage(prop, url);
@@ -1308,7 +1348,7 @@ export default {
loading.close(); loading.close();
}); });
}, },
recognizeIdCard(url = '') { recognizeIdCard(url = '', prop = 'idCardFront') {
if (!url) { if (!url) {
this.$message.warning('身份证图片上传成功,未获取到图片地址,无法自动识别'); this.$message.warning('身份证图片上传成功,未获取到图片地址,无法自动识别');
return; return;
@@ -1321,7 +1361,7 @@ export default {
recognitionIDCard(url) recognitionIDCard(url)
.then(res => { .then(res => {
const data = res.data.data || {}; const data = res.data.data || {};
this.applyIdCardRecognition(data); this.applyIdCardRecognition(data, prop);
this.$message.success('身份证识别完成'); this.$message.success('身份证识别完成');
}) })
.catch(() => { .catch(() => {
@@ -1444,45 +1484,62 @@ export default {
this.driverForm.drivingLicenseLongTerm = 0; this.driverForm.drivingLicenseLongTerm = 0;
} }
}, },
applyIdCardRecognition(data = {}) { applyIdCardRecognition(data = {}, prop = 'idCardFront') {
// 正反面都走同一 ShenFenZheng 接口;反面结果可能也带姓名/民族等字段,
// 反面只补空、不覆盖正面已填值,避免国徽页误识别冲掉正面信息。
const fromBack = prop === 'idCardBack';
const canWrite = (current, next) => !!next && (!fromBack || !current);
const driverName = data.name || data.driverName || this.getOcrValue(data, ['name', '姓名']); const driverName = data.name || data.driverName || this.getOcrValue(data, ['name', '姓名']);
const idCardNo = String( const idCardNo = String(
data.idNo || data.idNo ||
data.idCardNo || data.idCardNo ||
data.idCard || data.idCard ||
this.getOcrValue(data, ['idNo', 'idCardNo', 'number', '公民身份号码', '身份证号']) this.getOcrValue(data, ['idNo', 'idCardNo', 'number', '公民身份号码', '身份证号']) ||
).toUpperCase(); ''
)
.trim()
.toUpperCase();
const ocrGender = data.gender || this.getOcrValue(data, ['gender', '性别']); const ocrGender = data.gender || this.getOcrValue(data, ['gender', '性别']);
const ocrNation = const ocrNation =
data.nation || data.nation ||
data.ethnicGroup || data.ethnicGroup ||
this.getOcrValue(data, ['ethnic_group', 'nation', '民族']); this.getOcrValue(data, ['ethnic_group', 'nation', '民族']);
const ocrAddress = data.address || this.getOcrValue(data, ['address', '住址', '地址']); const ocrAddress = data.address || this.getOcrValue(data, ['address', '住址', '地址']);
if (driverName) { const ocrBirthday =
data.birthday || this.getOcrValue(data, ['birthday', '出生', '出生年月', '出生日期']);
if (canWrite(this.driverForm.driverName, driverName)) {
this.driverForm.driverName = driverName; this.driverForm.driverName = driverName;
} }
if (ocrGender) { if (canWrite(this.driverForm.gender, ocrGender)) {
this.driverForm.gender = ocrGender; this.driverForm.gender = ocrGender;
} }
if (ocrNation) { if (canWrite(this.driverForm.nation, ocrNation)) {
this.driverForm.nation = this.normalizeNation(ocrNation); this.driverForm.nation = this.normalizeNation(ocrNation);
} }
if (ocrAddress) { if (canWrite(this.driverForm.address, ocrAddress)) {
this.driverForm.address = String(ocrAddress).trim(); this.driverForm.address = String(ocrAddress).trim();
} }
if (idCardNo) { if (ocrBirthday) {
const birthday = this.normalizeBirthday(ocrBirthday);
if (canWrite(this.driverForm.birthday, birthday)) {
this.driverForm.birthday = birthday;
}
}
if (idCardNo && canWrite(this.driverForm.idCardNo, idCardNo)) {
this.driverForm.idCardNo = idCardNo; this.driverForm.idCardNo = idCardNo;
if (!this.driverForm.qualificationNo) { if (!this.driverForm.qualificationNo) {
this.driverForm.qualificationNo = idCardNo; this.driverForm.qualificationNo = idCardNo;
} }
const idCardInfo = this.parseIdCardInfo(idCardNo); const idCardInfo = this.parseIdCardInfo(idCardNo);
if (idCardInfo.birthday) { if (!this.driverForm.birthday && idCardInfo.birthday) {
this.driverForm.birthday = idCardInfo.birthday; this.driverForm.birthday = idCardInfo.birthday;
} }
if (!this.driverForm.gender && idCardInfo.gender) { if (!this.driverForm.gender && idCardInfo.gender) {
this.driverForm.gender = idCardInfo.gender; this.driverForm.gender = idCardInfo.gender;
} }
} }
// 有效期:正反面都可回填;反面通常才有,识别到就覆盖
this.applyIdCardValidity(data);
this.$nextTick(() => { this.$nextTick(() => {
[ [
'driverName', 'driverName',
@@ -1492,11 +1549,46 @@ export default {
'nation', 'nation',
'qualificationNo', 'qualificationNo',
'address', 'address',
].forEach(prop => { 'idCardValidEnd',
this.$refs.driverForm?.validateField(prop); ].forEach(field => {
this.$refs.driverForm?.validateField(field);
}); });
}); });
}, },
applyIdCardValidity(data = {}) {
const validStart =
data.validDateStart ||
data.idCardValidStart ||
this.getOcrValue(data, ['签发日期', '有效起始日期', '有效期限起', '身份证_签发日期']);
const validEndRaw =
data.validDateEnd ||
data.idCardValidEnd ||
this.getOcrValue(data, [
'失效日期',
'有效期限',
'有效期限止',
'身份证_有效期限',
'身份证_失效日期',
]);
const longTerm = data.longTerm ?? data.idCardLongTerm;
const validEndText = String(validEndRaw || '').trim();
if (longTerm === 1 || longTerm === '1' || /长期|永久/.test(validEndText)) {
this.driverForm.idCardLongTerm = 1;
this.driverForm.idCardValidEnd = '';
} else if (validEndText) {
const endDate = this.normalizeBirthday(validEndText);
if (endDate) {
this.driverForm.idCardValidEnd = endDate;
this.driverForm.idCardLongTerm = 0;
}
}
if (validStart) {
const startDate = this.normalizeBirthday(validStart);
if (startDate) {
this.driverForm.idCardValidStart = startDate;
}
}
},
getOcrValue(data = {}, keys = []) { getOcrValue(data = {}, keys = []) {
const keyInfos = data.keyInfos || data.keyInfo || data.ocrKeyInfos || []; const keyInfos = data.keyInfos || data.keyInfo || data.ocrKeyInfos || [];
const list = Array.isArray(keyInfos) ? keyInfos : []; const list = Array.isArray(keyInfos) ? keyInfos : [];
@@ -1675,6 +1767,8 @@ export default {
const row = { const row = {
...this.driverForm, ...this.driverForm,
birthday: this.normalizeBirthday(this.driverForm.birthday), birthday: this.normalizeBirthday(this.driverForm.birthday),
idCardValidStart: this.normalizeBirthday(this.driverForm.idCardValidStart),
idCardValidEnd: this.normalizeBirthday(this.driverForm.idCardValidEnd),
drivingLicenseStartDate: this.normalizeBirthday(this.driverForm.drivingLicenseStartDate), drivingLicenseStartDate: this.normalizeBirthday(this.driverForm.drivingLicenseStartDate),
drivingLicenseEndDate: this.normalizeBirthday(this.driverForm.drivingLicenseEndDate), drivingLicenseEndDate: this.normalizeBirthday(this.driverForm.drivingLicenseEndDate),
qualificationStartDate: this.normalizeBirthday(this.driverForm.qualificationStartDate), qualificationStartDate: this.normalizeBirthday(this.driverForm.qualificationStartDate),