2026-06-17 17:25:00 +08:00

419 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const auth = require('../../../utils/auth');
const PHONE_RE = /^1[3456789]\d{9}$/;
function normalizeAuthStatus(v) {
if (v == null || v === '') return 0;
const n = Number(v);
return Number.isNaN(n) ? 0 : n;
}
/** 0 未认证 | 1 审核中 | 2 审核通过 | 3 审核失败 | 4 已取消 */
function authStatusText(status) {
const map = {
0: '未认证',
1: '审核中',
2: '审核通过',
3: '审核失败',
4: '已取消'
};
return map[normalizeAuthStatus(status)] || '未认证';
}
function canSubmitAuth(status) {
const s = normalizeAuthStatus(status);
return s === 0 || s === 3;
}
/** 仅 0 未认证 显示「提交认证」,仅 3 审核失败 显示「重新提交认证」 */
function submitAuthButtonText(status) {
return normalizeAuthStatus(status) === 0 ? '提交认证' : '重新提交认证';
}
/** authStatus 以 /api/my/profile 为准;profile 无字段时再尝试认证详情接口 */
function pickAuthStatus(profile, authDetail) {
const p = profile && typeof profile === 'object' ? profile : {};
const d = authDetail && typeof authDetail === 'object' ? authDetail : {};
const v =
p.authStatus != null
? p.authStatus
: p.AuthStatus != null
? p.AuthStatus
: d.authStatus != null
? d.authStatus
: d.AuthStatus;
return normalizeAuthStatus(v);
}
Page({
data: {
activeTab: 1, // 1=身份证 2=护照
// 上传图片 URL(远程)
idCardImageUrl: '',
idCardBackImageUrl: '',
passportImageUrl: '',
householdImageUrl: '',
// 识别字段(只读,OCR 自动填充)
name: '',
idCardNumber: '',
birthDate: '',
genderOptions: [
{ value: 0, label: '男' },
{ value: 1, label: '女' }
],
genderIndex: 0,
// 护照识别额外字段(只读)
passportNumber: '',
nationality: '',
issueDate: '',
expiryDate: '',
// 联系方式
mobile: '',
verificationCode: '',
// UI 状态
recognizing: false,
submitting: false,
codeSending: false,
countdown: 0,
ocrDone: false,
authStatus: 0,
authStatusText: '未认证',
canSubmit: true,
submitBtnText: '提交认证',
loading: true
},
_timer: null,
onLoad() {
this.loadAuth();
},
onShow() {
const accessToken = wx.getStorageSync('accessToken') || '';
if (!accessToken) {
wx.redirectTo({ url: '/subpackages/mine/login/login' });
}
},
onUnload() {
this._clearTimer();
},
onPullDownRefresh() {
this.loadAuth().finally(() => wx.stopPullDownRefresh());
},
onBack() {
if (getCurrentPages().length > 1) {
wx.navigateBack({ delta: 1 });
} else {
wx.switchTab({ url: '/pages/mine/mine' });
}
},
onSwitchTab(e) {
const tab = Number(e.currentTarget.dataset.tab);
if (tab === this.data.activeTab) return;
this.setData({
activeTab: tab,
verificationCode: '',
ocrDone: false,
name: '', idCardNumber: '', birthDate: '', genderIndex: 0,
passportNumber: '', nationality: '', issueDate: '', expiryDate: '',
idCardImageUrl: '', idCardBackImageUrl: '', passportImageUrl: '', householdImageUrl: ''
});
this.loadAuth();
},
loadAuth() {
this.setData({ loading: true });
return Promise.all([
auth.fetchMyAuth().catch(() => ({})),
auth.fetchMyProfile().catch(() => ({}))
]).then(([res, profile]) => {
const d = res && typeof res === 'object' ? res : {};
if(d.birthDate ){
d.birthDate = d.birthDate.split(' ')[0] || d.birthday || '';
}
const name = d.name || d.realName || '';
const mobile = d.mobile || d.phone || '';
const idCardNumber = d.idCardNumber || d.idNumber || '';
const idCardImageUrl = d.idCardImageUrl || d.idCardFrontImageUrl || '';
const idCardBackImageUrl = d.idCardBackImageUrl || '';
const passportImageUrl = d.passportPhotoUrl || d.passportImageUrl || '';
const householdImageUrl = d.householdPhotoUrl || d.householdImageUrl || '';
const birthDate = d.birthDate || '';
const rawGender = d.gender != null ? String(d.gender) : '';
const genderIndex = (rawGender === '1' || rawGender === 'F' || rawGender === '女') ? 1 : 0;
const ocrDone = Boolean(name || idCardNumber || passportImageUrl || householdImageUrl);
const authStatus = pickAuthStatus(profile, d);
const canSubmit = canSubmitAuth(authStatus);
const submitBtnText = submitAuthButtonText(authStatus);
const authStatusTextVal = authStatusText(authStatus);
const activeTab = Number(d.authType) && Number(d.authType) > 0 ? Number(d.authType) : this.data.activeTab;
const passportNumber = d.passportNumber || d.passportNo || '';
this.setData({
activeTab,
passportNumber,
name, mobile, idCardNumber,
idCardImageUrl, idCardBackImageUrl, passportImageUrl, householdImageUrl,
birthDate, genderIndex, ocrDone,
authStatus,
authStatusText: authStatusTextVal,
canSubmit,
submitBtnText
});
}).finally(() => {
this.setData({ loading: false });
});
},
onChooseImage(e) {
if (this.data.authStatus === 2) return;
const kind = e.currentTarget.dataset.kind;
wx.chooseMedia({
count: 1,
mediaType: ['image'],
sourceType: ['album', 'camera'],
success: (res) => {
const tempFilePath = res.tempFiles && res.tempFiles[0] ? res.tempFiles[0].tempFilePath : '';
if (!tempFilePath) return;
this._uploadAndOcr(tempFilePath, kind);
}
});
},
async _uploadAndOcr(tempFilePath, kind) {
wx.showLoading({ title: '上传识别中...', mask: true });
this.setData({ recognizing: true });
try {
const imageUrl = await auth.uploadFile(tempFilePath);
if (kind === 'idFront') this.setData({ idCardImageUrl: imageUrl });
else if (kind === 'idBack') this.setData({ idCardBackImageUrl: imageUrl });
else if (kind === 'passport') this.setData({ passportImageUrl: imageUrl });
else if (kind === 'household') this.setData({ householdImageUrl: imageUrl });
if (kind === 'idFront') {
const ocr = await auth.ocrIdCard(imageUrl);
if (ocr && ocr.success === false) {
wx.showToast({ title: ocr.message || '身份证识别失败', icon: 'none' });
return;
}
const genderIndex = (ocr && (ocr.gender === '女' || ocr.gender === 'F' || ocr.gender === '1')) ? 1 : 0;
this.setData({
name: (ocr && ocr.name) || '',
idCardNumber: (ocr && ocr.idCardNumber) || '',
birthDate: (ocr && ocr.birthDate) || '',
genderIndex,
ocrDone: true
});
wx.showToast({ title: '识别成功', icon: 'success' });
} else if (kind === 'passport') {
const ocr = await auth.ocrPassport(imageUrl);
if (ocr && ocr.success === false) {
wx.showToast({ title: ocr.message || '护照识别失败', icon: 'none' });
return;
}
const genderIndex = (ocr && (ocr.gender === '女' || ocr.gender === 'F' || ocr.gender === '1')) ? 1 : 0;
this.setData({
name: (ocr && ocr.name) || '',
passportNumber: (ocr && ocr.passportNumber) || '',
nationality: (ocr && ocr.nationality) || '',
birthDate: (ocr && (ocr.birthDateYmd || ocr.birthDate)) || '',
issueDate: (ocr && (ocr.issueDate || ocr.issueDateYmd)) || '',
expiryDate: (ocr && (ocr.expiryDate || ocr.validToDate)) || '',
genderIndex,
ocrDone: true
});
wx.showToast({ title: '识别成功', icon: 'success' });
} else if (kind === 'household') {
const ocr = await auth.ocrHousehold(imageUrl);
if (ocr && ocr.success === false) {
wx.showToast({ title: ocr.message || '户口本识别失败', icon: 'none' });
return;
}
const genderIndex = (ocr && (ocr.gender === '女' || ocr.gender === 'F' || ocr.gender === '1')) ? 1 : 0;
this.setData({
name: (ocr && ocr.name) || '',
idCardNumber: (ocr && ocr.idCardNumber) || '',
birthDate: (ocr && ocr.birthDate) || '',
genderIndex,
ocrDone: true
});
wx.showToast({ title: '识别成功', icon: 'success' });
} else {
wx.showToast({ title: '上传成功', icon: 'success' });
}
} catch (err) {
wx.showToast({ title: (err && err.message) || '识别失败', icon: 'none' });
} finally {
this.setData({ recognizing: false });
// wx.hideLoading();
}
},
onMobileInput(e) {
this.setData({ mobile: (e.detail.value || '').trim() });
},
onCodeInput(e) {
this.setData({ verificationCode: (e.detail.value || '').trim() });
},
onSendCodeTap() {
const { mobile, countdown, codeSending } = this.data;
if (codeSending || countdown > 0) return;
if (!PHONE_RE.test(mobile)) {
wx.showToast({ title: '请输入正确手机号', icon: 'none' });
return;
}
this.setData({ codeSending: true });
wx.showLoading({ title: '发送中...' });
auth
.sendVerificationCode(mobile)
.then(() => {
wx.showToast({ title: '验证码已发送', icon: 'success' });
this._startCountdown(60);
})
.catch(() => {
wx.showToast({ title: '发送失败', icon: 'none' });
})
.finally(() => {
this.setData({ codeSending: false });
// wx.hideLoading();
});
},
_startCountdown(sec) {
this._clearTimer();
this.setData({ countdown: sec });
this._timer = setInterval(() => {
const next = this.data.countdown - 1;
if (next <= 0) {
this._clearTimer();
this.setData({ countdown: 0 });
} else {
this.setData({ countdown: next });
}
}, 1000);
},
_clearTimer() {
if (this._timer) {
clearInterval(this._timer);
this._timer = null;
}
},
onSubmitTap() {
if (this.data.submitting) return;
const {
activeTab, idCardImageUrl, idCardBackImageUrl, passportImageUrl, householdImageUrl,
name, idCardNumber, birthDate,
genderOptions, genderIndex, mobile, verificationCode, passportNumber
} = this.data;
if (!name) {
wx.showToast({ title: '请先上传证件图片完成识别', icon: 'none' });
return;
}
if (!PHONE_RE.test(mobile)) {
wx.showToast({ title: '请输入正确手机号', icon: 'none' });
return;
}
if (!verificationCode) {
wx.showToast({ title: '请输入验证码', icon: 'none' });
return;
}
const genderValue = genderOptions[genderIndex] ? genderOptions[genderIndex].value : 0;
let payload;
if (activeTab === 1) {
if (!idCardImageUrl) {
wx.showToast({ title: '请上传身份证人像面', icon: 'none' });
return;
}
if (!idCardNumber) {
wx.showToast({ title: '请先完成身份证识别', icon: 'none' });
return;
}
payload = {
authType: 1,
name,
mobile,
smsCode: verificationCode,
gender: genderValue,
birthDate: birthDate || undefined,
idCardNumber,
idCardImageUrl,
idCardBackImageUrl
};
} else if (activeTab === 3) {
if (!householdImageUrl) {
wx.showToast({ title: '请上传户口本图片', icon: 'none' });
return;
}
if (!idCardNumber) {
wx.showToast({ title: '请先完成户口本识别', icon: 'none' });
return;
}
payload = {
authType: 3,
name,
mobile,
smsCode: verificationCode,
gender: genderValue,
birthDate: birthDate || undefined,
idCardNumber,
householdPhotoUrl: householdImageUrl
};
} else {
if (!passportImageUrl) {
wx.showToast({ title: '请上传护照', icon: 'none' });
return;
}
if (!birthDate) {
wx.showToast({ title: '请先完成护照识别', icon: 'none' });
return;
}
payload = {
authType: 2,
name,
mobile,
smsCode: verificationCode,
gender: genderValue,
birthDate,
passportPhotoUrl: passportImageUrl,
passportNumber: passportNumber
};
}
this.setData({ submitting: true });
wx.showLoading({ title: '提交中...' });
auth
.submitMyAuth(payload)
.then(() => {
wx.showToast({ title: '提交成功', icon: 'success' });
this.loadAuth();
setTimeout(() => wx.navigateBack({ delta: 1 }), 1500);
})
.catch((err) => {
wx.showToast({ title: (err && err.message) || '提交失败', icon: 'none' });
})
.finally(() => {
this.setData({ submitting: false });
// wx.hideLoading();
});
}
});