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: '', // 识别字段(只读,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: '提交认证' }, _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: '' }); this.loadAuth(); }, loadAuth() { 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 birthDate = d.birthDate || d.birthday || ''; const rawGender = d.gender != null ? String(d.gender) : ''; const genderIndex = (rawGender === '1' || rawGender === 'F' || rawGender === '女') ? 1 : 0; const ocrDone = Boolean(name || idCardNumber || passportImageUrl); const authStatus = pickAuthStatus(profile, d); const canSubmit = canSubmitAuth(authStatus); const submitBtnText = submitAuthButtonText(authStatus); const authStatusTextVal = authStatusText(authStatus); this.setData({ name, mobile, idCardNumber, idCardImageUrl, idCardBackImageUrl, passportImageUrl, birthDate, genderIndex, ocrDone, authStatus, authStatusText: authStatusTextVal, canSubmit, submitBtnText }); }); }, onChooseImage(e) { 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 }); 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 { 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, passportImageUrl, name, idCardNumber, birthDate, genderOptions, genderIndex, mobile, verificationCode,idCardBackImageUrl,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 (!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, idCardNumber: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(); }); } });