完成10%
This commit is contained in:
+213
-35
@@ -16,9 +16,11 @@
|
||||
|
||||
var documentRef = root.document;
|
||||
var CAPTCHA_SCENES = {
|
||||
password: 'WEB_H5_CHANGE_PASSWORD',
|
||||
phone: 'WEB_H5_CHANGE_PHONE'
|
||||
phone: 'PC_PHONE_CHANGE',
|
||||
deactivate: 'PC_ACCOUNT_DEACTIVATE'
|
||||
};
|
||||
var SMS_CODE_COOLDOWN_SECONDS = 60;
|
||||
var boundPhone = '';
|
||||
|
||||
function getCaptchaScene(formType) {
|
||||
return CAPTCHA_SCENES[formType] || '';
|
||||
@@ -31,20 +33,13 @@
|
||||
return value;
|
||||
}
|
||||
|
||||
function trimOrUndefined(value) {
|
||||
var text = String(value === undefined || value === null ? '' : value).trim();
|
||||
|
||||
return text || undefined;
|
||||
}
|
||||
|
||||
function buildPasswordChangeBody(values, hashFn) {
|
||||
var source = values || {};
|
||||
var hash = hashFn || hashPassword;
|
||||
|
||||
return {
|
||||
oldPassword: hash(String(source.oldPassword || '').trim()),
|
||||
newPassword: hash(String(source.newPassword || '').trim()),
|
||||
validToken: trimOrUndefined(source.validToken)
|
||||
newPassword: hash(String(source.newPassword || '').trim())
|
||||
};
|
||||
}
|
||||
|
||||
@@ -52,19 +47,16 @@
|
||||
var source = values || {};
|
||||
|
||||
return {
|
||||
newPhone: String(source.newPhone || '').trim(),
|
||||
smsCode: String(source.smsCode || '').trim(),
|
||||
validToken: trimOrUndefined(source.validToken)
|
||||
phone: String(source.phone || '').trim(),
|
||||
smsCode: String(source.smsCode || '').trim()
|
||||
};
|
||||
}
|
||||
|
||||
function buildDeactivateBody(values, hashFn) {
|
||||
function buildDeactivateBody(values) {
|
||||
var source = values || {};
|
||||
var hash = hashFn || hashPassword;
|
||||
|
||||
return {
|
||||
password: hash(String(source.password || '').trim()),
|
||||
reason: trimOrUndefined(source.reason)
|
||||
smsCode: String(source.smsCode || '').trim()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -72,12 +64,43 @@
|
||||
return String(value || '').trim() === '确认注销';
|
||||
}
|
||||
|
||||
function isPhone(value) {
|
||||
return /^1\d{10}$/.test(String(value || '').trim());
|
||||
}
|
||||
|
||||
function isSmsCode(value) {
|
||||
return /^\d{4,6}$/.test(String(value || '').trim());
|
||||
}
|
||||
|
||||
function getApi() {
|
||||
return root.GenealogyApi && root.GenealogyApi.defaultClient;
|
||||
}
|
||||
|
||||
function shouldRedirectToLogin(api, error) {
|
||||
var status = error && (error.status || error.code);
|
||||
|
||||
return !api || !api.getToken || !api.getToken() || Number(status) === 401;
|
||||
}
|
||||
|
||||
function redirectToLogin(api) {
|
||||
if (api && api.clearToken) api.clearToken();
|
||||
if (!root.location) return;
|
||||
if (typeof root.location.replace === 'function') {
|
||||
root.location.replace('login.html');
|
||||
return;
|
||||
}
|
||||
|
||||
root.location.href = 'login.html';
|
||||
}
|
||||
|
||||
function redirectUnauthorized(api, error) {
|
||||
if (!shouldRedirectToLogin(api, error)) return false;
|
||||
redirectToLogin(api);
|
||||
return true;
|
||||
}
|
||||
|
||||
async function ensureCaptcha(form, formType, subject) {
|
||||
// 改密和换绑必须先取得与当前业务场景绑定的验证码票据。
|
||||
// 发送敏感业务短信前,先取得与当前场景绑定的验证码票据。
|
||||
var captcha = root.CaptchaPages;
|
||||
var sceneCode = getCaptchaScene(formType);
|
||||
|
||||
@@ -126,30 +149,110 @@
|
||||
if (status) status.textContent = message;
|
||||
}
|
||||
|
||||
function setSubmitting(form, isSubmitting) {
|
||||
queryAll('button[type="submit"]', form).forEach(function (button) {
|
||||
button.disabled = isSubmitting;
|
||||
});
|
||||
}
|
||||
|
||||
function clearCaptchaToken(form) {
|
||||
var field = query('input[name="validToken"]', form);
|
||||
|
||||
if (field) field.value = '';
|
||||
}
|
||||
|
||||
function startSmsCooldown(button) {
|
||||
var originalText;
|
||||
var remaining = SMS_CODE_COOLDOWN_SECONDS;
|
||||
var interval;
|
||||
|
||||
if (!button || typeof root.setInterval !== 'function') return;
|
||||
if (button.__smsCooldownTimer && typeof root.clearInterval === 'function') {
|
||||
root.clearInterval(button.__smsCooldownTimer);
|
||||
}
|
||||
|
||||
originalText = button.getAttribute('data-sms-code-label') || button.textContent.trim();
|
||||
button.setAttribute('data-sms-code-label', originalText);
|
||||
button.disabled = true;
|
||||
button.textContent = remaining + ' 秒后重试';
|
||||
interval = root.setInterval(function () {
|
||||
remaining -= 1;
|
||||
if (remaining > 0) {
|
||||
button.textContent = remaining + ' 秒后重试';
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof root.clearInterval === 'function') root.clearInterval(interval);
|
||||
button.__smsCooldownTimer = null;
|
||||
button.disabled = false;
|
||||
button.textContent = originalText;
|
||||
}, 1000);
|
||||
button.__smsCooldownTimer = interval;
|
||||
}
|
||||
|
||||
function maskPhone(phone) {
|
||||
var value = String(phone || '').trim();
|
||||
|
||||
return isPhone(value) ? value.slice(0, 3) + '****' + value.slice(-4) : '';
|
||||
}
|
||||
|
||||
async function loadBoundPhone() {
|
||||
var api = getApi();
|
||||
var profile;
|
||||
var message;
|
||||
|
||||
if (!api || !documentRef) return;
|
||||
if (redirectUnauthorized(api)) return;
|
||||
|
||||
try {
|
||||
profile = await api.currentProfile();
|
||||
boundPhone = String(profile && profile.phone || '').trim();
|
||||
message = isPhone(boundPhone)
|
||||
? '验证码将发送至 ' + maskPhone(boundPhone)
|
||||
: '未获取到已绑定手机号';
|
||||
queryAll('[data-security-bound-phone]').forEach(function (node) {
|
||||
node.textContent = message;
|
||||
});
|
||||
} catch (error) {
|
||||
if (redirectUnauthorized(api, error)) return;
|
||||
queryAll('[data-security-bound-phone]').forEach(function (node) {
|
||||
node.textContent = '读取已绑定手机号失败';
|
||||
});
|
||||
showMessage(error.message || '账号资料加载失败');
|
||||
}
|
||||
}
|
||||
|
||||
async function submitPassword(form) {
|
||||
var api = getApi();
|
||||
var values = getFormValues(form);
|
||||
|
||||
if (!api) return;
|
||||
if (redirectUnauthorized(api)) return;
|
||||
if (!values.oldPassword || !values.newPassword) {
|
||||
showMessage('请填写原密码和新密码');
|
||||
return;
|
||||
}
|
||||
if (String(values.newPassword).length < 6) {
|
||||
showMessage('新密码至少需要 6 位');
|
||||
return;
|
||||
}
|
||||
if (values.newPassword !== values.confirmPassword) {
|
||||
showMessage('两次新密码不一致');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!await ensureCaptcha(form, 'password')) return;
|
||||
values = getFormValues(form);
|
||||
|
||||
try {
|
||||
setSubmitting(form, true);
|
||||
setStatus(form, '提交中...');
|
||||
await api.changePassword(buildPasswordChangeBody(values));
|
||||
setStatus(form, '密码已修改');
|
||||
showMessage('密码已修改');
|
||||
form.reset();
|
||||
} catch (error) {
|
||||
if (redirectUnauthorized(api, error)) return;
|
||||
setStatus(form, '修改失败');
|
||||
showMessage(error.message || '密码修改失败');
|
||||
} finally {
|
||||
setSubmitting(form, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,22 +260,37 @@
|
||||
var api = getApi();
|
||||
var body = buildPhoneChangeBody(getFormValues(form));
|
||||
|
||||
if (!api) return;
|
||||
if (!body.newPhone || !body.smsCode) {
|
||||
if (redirectUnauthorized(api)) return;
|
||||
if (!body.phone || !body.smsCode) {
|
||||
showMessage('请填写新手机号和验证码');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!await ensureCaptcha(form, 'phone', body.newPhone)) return;
|
||||
body = buildPhoneChangeBody(getFormValues(form));
|
||||
if (!isPhone(body.phone)) {
|
||||
showMessage('请输入正确的新手机号');
|
||||
return;
|
||||
}
|
||||
if (!isSmsCode(body.smsCode)) {
|
||||
showMessage('请输入正确的短信验证码');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setSubmitting(form, true);
|
||||
setStatus(form, '提交中...');
|
||||
await api.changePhone(body);
|
||||
boundPhone = body.phone;
|
||||
queryAll('[data-security-bound-phone]').forEach(function (node) {
|
||||
node.textContent = '验证码将发送至 ' + maskPhone(boundPhone);
|
||||
});
|
||||
setStatus(form, '手机号已换绑');
|
||||
showMessage('手机号已换绑');
|
||||
form.reset();
|
||||
} catch (error) {
|
||||
if (redirectUnauthorized(api, error)) return;
|
||||
setStatus(form, '换绑失败');
|
||||
showMessage(error.message || '手机号换绑失败');
|
||||
} finally {
|
||||
setSubmitting(form, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,26 +300,60 @@
|
||||
var form = query('[data-security-form="phone"]');
|
||||
var values;
|
||||
|
||||
if (!api || !form) return;
|
||||
if (!form || redirectUnauthorized(api)) return;
|
||||
values = getFormValues(form);
|
||||
if (!/^1[3-9]\d{9}$/.test(String(values.newPhone || '').trim())) {
|
||||
if (!isPhone(values.phone)) {
|
||||
showMessage('请输入正确的新手机号');
|
||||
return;
|
||||
}
|
||||
if (!await ensureCaptcha(form, 'phone', values.newPhone)) return;
|
||||
if (!await ensureCaptcha(form, 'phone', values.phone)) return;
|
||||
values = getFormValues(form);
|
||||
button.disabled = true;
|
||||
|
||||
try {
|
||||
await api.sendSmsCode({
|
||||
phone: values.newPhone,
|
||||
phone: values.phone,
|
||||
sceneCode: getCaptchaScene('phone'),
|
||||
validToken: values.validToken || ''
|
||||
});
|
||||
clearCaptchaToken(form);
|
||||
showMessage('验证码已发送');
|
||||
startSmsCooldown(button);
|
||||
} catch (error) {
|
||||
if (redirectUnauthorized(api, error)) return;
|
||||
clearCaptchaToken(form);
|
||||
showMessage(error.message || '验证码发送失败');
|
||||
button.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function sendDeactivateCode(button) {
|
||||
var api = getApi();
|
||||
var form = query('[data-security-form="deactivate"]');
|
||||
var values;
|
||||
|
||||
if (!form || redirectUnauthorized(api)) return;
|
||||
if (!isPhone(boundPhone)) {
|
||||
showMessage('未获取到已绑定手机号,请刷新后重试');
|
||||
return;
|
||||
}
|
||||
if (!await ensureCaptcha(form, 'deactivate', boundPhone)) return;
|
||||
values = getFormValues(form);
|
||||
button.disabled = true;
|
||||
|
||||
try {
|
||||
await api.sendSmsCode({
|
||||
phone: boundPhone,
|
||||
sceneCode: getCaptchaScene('deactivate'),
|
||||
validToken: values.validToken || ''
|
||||
});
|
||||
clearCaptchaToken(form);
|
||||
showMessage('验证码已发送');
|
||||
startSmsCooldown(button);
|
||||
} catch (error) {
|
||||
if (redirectUnauthorized(api, error)) return;
|
||||
clearCaptchaToken(form);
|
||||
showMessage(error.message || '验证码发送失败');
|
||||
} finally {
|
||||
button.disabled = false;
|
||||
}
|
||||
}
|
||||
@@ -210,9 +362,9 @@
|
||||
var api = getApi();
|
||||
var values = getFormValues(form);
|
||||
|
||||
if (!api) return;
|
||||
if (!values.password) {
|
||||
showMessage('请填写当前密码');
|
||||
if (redirectUnauthorized(api)) return;
|
||||
if (!isSmsCode(values.smsCode)) {
|
||||
showMessage('请输入正确的短信验证码');
|
||||
return;
|
||||
}
|
||||
if (!isDangerConfirmed(values.confirmText)) {
|
||||
@@ -222,11 +374,17 @@
|
||||
if (root.confirm && !root.confirm('账号注销后将退出登录,确认继续?')) return;
|
||||
|
||||
try {
|
||||
setSubmitting(form, true);
|
||||
setStatus(form, '提交中...');
|
||||
await api.deactivateAccount(buildDeactivateBody(values));
|
||||
showMessage('账号已提交注销');
|
||||
root.location.href = 'index.html';
|
||||
} catch (error) {
|
||||
if (redirectUnauthorized(api, error)) return;
|
||||
setStatus(form, '注销失败');
|
||||
showMessage(error.message || '账号注销失败');
|
||||
} finally {
|
||||
setSubmitting(form, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,6 +420,7 @@
|
||||
|
||||
documentRef.addEventListener('click', function (event) {
|
||||
var codeButton = event.target.closest('[data-security-send-code]');
|
||||
var deactivateCodeButton = event.target.closest('[data-security-send-deactivate-code]');
|
||||
var button = event.target.closest('[data-security-logout]');
|
||||
|
||||
if (codeButton) {
|
||||
@@ -269,16 +428,32 @@
|
||||
sendPhoneCode(codeButton);
|
||||
return;
|
||||
}
|
||||
if (deactivateCodeButton) {
|
||||
event.preventDefault();
|
||||
sendDeactivateCode(deactivateCodeButton);
|
||||
return;
|
||||
}
|
||||
if (!button) return;
|
||||
event.preventDefault();
|
||||
logout();
|
||||
});
|
||||
}
|
||||
|
||||
function bindCaptchaTokenInvalidation() {
|
||||
queryAll('[data-security-form] input[name="phone"]').forEach(function (field) {
|
||||
field.addEventListener('input', function () {
|
||||
clearCaptchaToken(field.closest('[data-security-form]'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function init() {
|
||||
if (!documentRef || !query('[data-security-page]')) return;
|
||||
if (redirectUnauthorized(getApi())) return;
|
||||
|
||||
bindActions();
|
||||
bindCaptchaTokenInvalidation();
|
||||
loadBoundPhone();
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -287,6 +462,9 @@
|
||||
buildDeactivateBody: buildDeactivateBody,
|
||||
getCaptchaScene: getCaptchaScene,
|
||||
isDangerConfirmed: isDangerConfirmed,
|
||||
isPhone: isPhone,
|
||||
isSmsCode: isSmsCode,
|
||||
shouldRedirectToLogin: shouldRedirectToLogin,
|
||||
init: init
|
||||
};
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user