373 lines
11 KiB
JavaScript
Raw Normal View History

2026-04-10 01:49:07 +08:00
const auth = require('../../../utils/auth');
const PHONE_RE = /^1[3456789]\d{9}$/;
2026-04-10 16:10:33 +08:00
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=护照
2026-04-10 01:49:07 +08:00
// 上传图片 URL(远程)
idCardImageUrl: '',
idCardBackImageUrl: '',
passportImageUrl: '',
2026-04-10 01:49:07 +08:00
// 识别字段(只读,OCR 自动填充)
name: '',
idCardNumber: '',
birthDate: '',
genderOptions: [
2026-04-10 01:49:07 +08:00
{ value: 0, label: '男' },
{ value: 1, label: '女' }
],
genderIndex: 0,
2026-04-10 01:49:07 +08:00
// 护照识别额外字段(只读)
passportNumber: '',
nationality: '',
issueDate: '',
expiryDate: '',
// 联系方式
mobile: '',
verificationCode: '',
// UI 状态
2026-04-10 01:49:07 +08:00
recognizing: false,
submitting: false,
codeSending: false,
countdown: 0,
2026-04-10 16:10:33 +08:00
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,
2026-04-10 01:49:07 +08:00
verificationCode: '',
ocrDone: false,
name: '', idCardNumber: '', birthDate: '', genderIndex: 0,
passportNumber: '', nationality: '', issueDate: '', expiryDate: '',
idCardImageUrl: '', idCardBackImageUrl: '', passportImageUrl: ''
});
2026-04-10 01:49:07 +08:00
this.loadAuth();
},
loadAuth() {
2026-04-10 16:10:33 +08:00
return Promise.all([
auth.fetchMyAuth().catch(() => ({})),
auth.fetchMyProfile().catch(() => ({}))
]).then(([res, profile]) => {
const d = res && typeof res === 'object' ? res : {};
2026-04-13 18:10:43 +08:00
if(d.birthDate ){
d.birthDate = d.birthDate.split(' ')[0] || d.birthday || '';
}
2026-04-10 16:10:33 +08:00
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 || '';
2026-04-13 18:10:43 +08:00
const birthDate = d.birthDate || d.birthday || '';
2026-04-10 16:10:33 +08:00
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;
2026-04-10 01:49:07 +08:00
this._uploadAndOcr(tempFilePath, kind);
}
});
},
2026-04-10 01:49:07 +08:00
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.birthDate || ocr.birthDateYmd)) || '',
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 });
2026-04-13 23:56:07 +08:00
// wx.hideLoading();
2026-04-10 01:49:07 +08:00
}
},
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 });
2026-04-13 23:56:07 +08:00
// 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 {
2026-04-10 01:49:07 +08:00
activeTab, idCardImageUrl, passportImageUrl,
name, idCardNumber, birthDate,
2026-04-13 18:10:43 +08:00
genderOptions, genderIndex, mobile, verificationCode,idCardBackImageUrl
} = this.data;
if (!name) {
2026-04-10 01:49:07 +08:00
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;
}
2026-04-10 01:49:07 +08:00
const genderValue = genderOptions[genderIndex] ? genderOptions[genderIndex].value : 0;
let payload;
if (activeTab === 1) {
2026-04-10 01:49:07 +08:00
if (!idCardImageUrl) {
wx.showToast({ title: '请上传身份证人像面', icon: 'none' });
return;
}
if (!idCardNumber) {
2026-04-10 01:49:07 +08:00
wx.showToast({ title: '请先完成身份证识别', icon: 'none' });
return;
}
2026-04-10 01:49:07 +08:00
payload = {
2026-04-13 15:28:42 +08:00
authType: 1,
2026-04-10 01:49:07 +08:00
name,
mobile,
smsCode: verificationCode,
gender: genderValue,
birthDate: birthDate || undefined,
idCardNumber,
2026-04-13 18:10:43 +08:00
idCardImageUrl,
idCardBackImageUrl
2026-04-10 01:49:07 +08:00
};
} else {
if (!passportImageUrl) {
wx.showToast({ title: '请上传护照', icon: 'none' });
return;
}
if (!birthDate) {
2026-04-10 01:49:07 +08:00
wx.showToast({ title: '请先完成护照识别', icon: 'none' });
return;
}
2026-04-10 01:49:07 +08:00
payload = {
2026-04-13 15:28:42 +08:00
authType: 2,
2026-04-10 01:49:07 +08:00
name,
mobile,
smsCode: verificationCode,
gender: genderValue,
birthDate,
passportPhotoUrl: passportImageUrl
};
}
this.setData({ submitting: true });
wx.showLoading({ title: '提交中...' });
auth
.submitMyAuth(payload)
.then(() => {
wx.showToast({ title: '提交成功', icon: 'success' });
2026-04-13 18:10:43 +08:00
this.loadAuth();
setTimeout(() => wx.navigateBack({ delta: 1 }), 1500);
})
.catch((err) => {
wx.showToast({ title: (err && err.message) || '提交失败', icon: 'none' });
})
.finally(() => {
this.setData({ submitting: false });
2026-04-13 23:56:07 +08:00
// wx.hideLoading();
});
}
});