完成10%

This commit is contained in:
rain
2026-07-24 18:11:16 +08:00
parent 8716a68fdb
commit 8870136d1b
33 changed files with 3853 additions and 336 deletions
+96 -47
View File
@@ -18,25 +18,26 @@
var FORM_CONFIG = {
login: {
selector: '#login-password-form',
captchaScene: 'WEB_H5_LOGIN',
captchaScene: 'PC_SMS_LOGIN',
successUrl: 'profile.html'
},
'sms-login': {
selector: '#login-sms-form',
captchaScene: 'WEB_H5_LOGIN',
captchaScene: 'PC_SMS_LOGIN',
successUrl: 'profile.html'
},
register: {
selector: '#register-form',
captchaScene: 'WEB_H5_REGISTER',
captchaScene: 'PC_REGISTER',
successUrl: 'login.html'
},
'password-reset': {
selector: '#password-reset-form',
captchaScene: 'WEB_H5_FORGOT_PASSWORD',
captchaScene: 'PC_FORGOT_PASSWORD',
successUrl: 'login.html'
}
};
var SMS_CODE_COOLDOWN_SECONDS = 60;
function hashPassword(value) {
// 接口文档要求密码传 32 位 MD5;没有加载 md5.js 时保留原值,便于测试定位。
@@ -46,50 +47,40 @@
}
function buildLoginBody(values, hashFn) {
// 这里只组装页面字段,grantType、tenantId、clientId 由 api-client 统一补齐。
// 这里只组装登录 DTO 字段,grantType、tenantId、clientId 由 api-client 统一补齐。
var hash = hashFn || hashPassword;
var body = {
return {
phone: values.phone,
password: hash(values.password)
};
if (values.validToken) body.validToken = values.validToken;
return body;
}
function buildSmsLoginBody(values) {
// 短信登录接口只需要页面字段,grantType、tenantId、clientId 由 api-client 统一补齐。
var body = {
return {
phone: values.phone,
smsCode: values.smsCode
};
if (values.validToken) body.validToken = values.validToken;
return body;
}
function buildRegisterBody(values, hashFn) {
// 注册验证码当前对应 validToken,可选字段只在有值时提交
// 确认密码只用于页面校验;注册接口需要手机号短信验证码
var hash = hashFn || hashPassword;
var body = {
return {
phone: values.phone,
password: hash(values.password),
nickName: values.nickName || values.phone
nickName: values.nickName || values.phone,
smsCode: values.smsCode
};
if (values.validToken) body.validToken = values.validToken;
return body;
}
function buildPasswordResetBody(values, hashFn) {
// 找回密码接口使用 newPassword 字段,页面确认密码只在提交前校验,不传给后端。
var hash = hashFn || hashPassword;
return {
phone: values.phone,
smsCode: values.smsCode,
newPassword: hash(values.newPassword),
validToken: values.validToken || ''
newPassword: hash(values.newPassword)
};
}
@@ -121,8 +112,10 @@
// 未配置 PC/H5 验证场景时不调用验证中心,也不伪造 validToken。
if (!sceneCode) return true;
// 验证适配层不存在时不阻塞测试环境;真实页面会加载 captcha-pages.js。
if (!root.CaptchaPages || !root.CaptchaPages.ensureToken) return true;
if (!root.CaptchaPages || !root.CaptchaPages.ensureToken) {
showMessage('验证码组件未加载,请刷新后重试');
return false;
}
return Boolean(await root.CaptchaPages.ensureToken(form, {
sceneCode: sceneCode,
@@ -149,6 +142,41 @@
return values;
}
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 setBusy(button, busy) {
// 加载状态只切 class 和 aria,不在 JS 中写样式。
if (!button) return;
@@ -197,7 +225,14 @@
}
if (formType === 'register') {
return validatePassword(data.password, '请填写密码');
if (!data.smsCode) return '请填写短信验证码';
if (!isSmsCode(data.smsCode)) return '请输入正确的短信验证码';
var registerPasswordMessage = validatePassword(data.password, '请填写密码');
if (registerPasswordMessage) return registerPasswordMessage;
if (!data.confirmPassword) return '请再次输入密码';
if (data.password !== data.confirmPassword) return '两次输入的密码不一致';
return '';
}
if (formType === 'password-reset') {
@@ -261,6 +296,7 @@
var isActive = panel.id === activePanelId;
panel.hidden = !isActive;
if (!isActive) clearCaptchaToken(panel);
});
}
@@ -269,12 +305,11 @@
var api = getApi();
var values = readForm(form);
if (!api) return;
if (!api) {
showMessage('接口初始化失败,请刷新后重试');
return;
}
if (!validateBeforeSubmit('login', values)) return;
if (!await ensureCaptcha(form, getFormCaptchaScene(form, 'login'), values.phone)) return;
values = readForm(form);
setBusy(button, true);
try {
await api.login(buildLoginBody(values));
@@ -291,12 +326,11 @@
var api = getApi();
var values = readForm(form);
if (!api) return;
if (!api) {
showMessage('接口初始化失败,请刷新后重试');
return;
}
if (!validateBeforeSubmit('sms-login', values)) return;
if (!await ensureCaptcha(form, getFormCaptchaScene(form, 'sms-login'), values.phone)) return;
values = readForm(form);
setBusy(button, true);
try {
await api.loginBySms(buildSmsLoginBody(values));
@@ -313,12 +347,11 @@
var api = getApi();
var values = readForm(form);
if (!api) return;
if (!api) {
showMessage('接口初始化失败,请刷新后重试');
return;
}
if (!validateBeforeSubmit('register', values)) return;
if (!await ensureCaptcha(form, getFormCaptchaScene(form, 'register'), values.phone)) return;
values = readForm(form);
setBusy(button, true);
try {
await api.register(buildRegisterBody(values));
@@ -338,7 +371,10 @@
var formType = getFormType(form);
var sceneCode = getFormCaptchaScene(form, formType);
if (!api) return;
if (!api) {
showMessage('接口初始化失败,请刷新后重试');
return;
}
if (!validateBeforeSendCode(values)) return;
if (!await ensureCaptcha(form, sceneCode, values.phone)) return;
@@ -351,10 +387,13 @@
phone: values.phone,
validToken: values.validToken || ''
});
clearCaptchaToken(form);
showMessage('验证码已发送');
setBusy(button, false);
startSmsCooldown(button);
} catch (error) {
clearCaptchaToken(form);
showMessage(error.message || '验证码发送失败');
} finally {
setBusy(button, false);
}
}
@@ -364,12 +403,11 @@
var api = getApi();
var values = readForm(form);
if (!api) return;
if (!api) {
showMessage('接口初始化失败,请刷新后重试');
return;
}
if (!validateBeforeSubmit('password-reset', values)) return;
if (!await ensureCaptcha(form, getFormCaptchaScene(form, 'password-reset'), values.phone)) return;
values = readForm(form);
setBusy(button, true);
try {
await api.resetPassword(buildPasswordResetBody(values));
@@ -399,6 +437,14 @@
});
}
function bindCaptchaTokenInvalidation() {
queryAll('form input[name="phone"]').forEach(function (field) {
field.addEventListener('input', function () {
clearCaptchaToken(field.closest('form'));
});
});
}
function getFormType(form) {
if (!form) return '';
@@ -417,6 +463,7 @@
bindSubmit(FORM_CONFIG['password-reset'].selector, submitPasswordReset);
bindLoginModeTabs();
bindCodeButtons();
bindCaptchaTokenInvalidation();
}
return {
@@ -424,6 +471,8 @@
buildSmsLoginBody: buildSmsLoginBody,
buildRegisterBody: buildRegisterBody,
buildPasswordResetBody: buildPasswordResetBody,
clearCaptchaToken: clearCaptchaToken,
startSmsCooldown: startSmsCooldown,
getCaptchaScene: getCaptchaScene,
getFormCaptchaScene: getFormCaptchaScene,
getSuccessUrl: getSuccessUrl,