家谱现有接口调试50%

This commit is contained in:
rain
2026-07-09 17:29:25 +08:00
commit 6050508144
262 changed files with 63354 additions and 0 deletions
+342
View File
@@ -0,0 +1,342 @@
(function (root, factory) {
// 认证页脚本同时给浏览器页面和 Node 测试使用。
if (typeof module === 'object' && module.exports) {
module.exports = factory(root);
return;
}
root.AuthPages = factory(root);
if (root.document) {
root.document.addEventListener('DOMContentLoaded', function () {
root.AuthPages.init();
});
}
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
'use strict';
var documentRef = root.document;
function hashPassword(value) {
// 接口文档要求密码传 32 位 MD5;没有加载 md5.js 时保留原值,便于测试定位。
if (root.hex_md5) return root.hex_md5(value);
if (root.md5) return root.md5(value);
return value;
}
function buildLoginBody(values, hashFn) {
// 这里只组装页面字段,grantType、tenantId、clientId 由 api-client 统一补齐。
var hash = hashFn || hashPassword;
var body = {
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 = {
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 = {
phone: values.phone,
password: hash(values.password),
nickName: values.nickName || values.phone
};
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 || ''
};
}
function getApi() {
return root.GenealogyApi && root.GenealogyApi.defaultClient;
}
function getCaptchaScene(formType) {
// PC/H5 认证场景来自后台验证码配置,客户端 Key 为 web_pc。
var map = {
login: 'WEB_H5_LOGIN',
register: 'WEB_H5_REGISTER',
'password-reset': 'WEB_H5_FORGOT_PASSWORD'
};
return map[formType] || '';
}
function getFormCaptchaScene(form, formType) {
// 页面可通过 data-captcha-scene 显式声明后台配置的验证码场景。
var sceneCode = form && form.getAttribute && form.getAttribute('data-captcha-scene');
return sceneCode || getCaptchaScene(formType);
}
async function ensureCaptcha(form, sceneCode, subject) {
// 未配置 PC/H5 验证场景时不调用验证中心,也不伪造 validToken。
if (!sceneCode) return true;
// 验证适配层不存在时不阻塞测试环境;真实页面会加载 captcha-pages.js。
if (!root.CaptchaPages || !root.CaptchaPages.ensureToken) return true;
return Boolean(await root.CaptchaPages.ensureToken(form, {
sceneCode: sceneCode,
subject: subject
}));
}
function query(selector, rootNode) {
return (rootNode || documentRef).querySelector(selector);
}
function queryAll(selector, rootNode) {
return Array.prototype.slice.call((rootNode || documentRef).querySelectorAll(selector));
}
function readForm(form) {
// 页面通过 name 属性和接口字段对齐,避免按 placeholder 文案取值。
var values = {};
queryAll('input[name], textarea[name], select[name]', form).forEach(function (field) {
values[field.name] = String(field.value || '').trim();
});
return values;
}
function setBusy(button, busy) {
// 加载状态只切 class 和 aria,不在 JS 中写样式。
if (!button) return;
button.classList.toggle('is-loading', busy);
if ('disabled' in button) button.disabled = busy;
button.setAttribute('aria-busy', busy ? 'true' : 'false');
}
function showMessage(message) {
if (root.MessageUtil && root.MessageUtil.show) {
root.MessageUtil.show(message);
return;
}
if (root.console && root.console.warn) root.console.warn(message);
}
function requireValue(value, message) {
if (value) return true;
showMessage(message);
return false;
}
function bindSubmit(selector, handler) {
// 所有认证表单统一拦截 submit,避免链接跳转绕过接口请求。
var form = query(selector);
if (!form) return;
form.addEventListener('submit', function (event) {
event.preventDefault();
handler(form, query('[type="submit"]', form));
});
}
function setLoginMode(activeMode) {
// 登录方式切换只更新状态,具体样式由 login.css 控制。
queryAll('[data-login-mode-tab]').forEach(function (button) {
var isActive = button.getAttribute('data-login-mode-tab') === activeMode;
button.classList.toggle('is-active', isActive);
button.setAttribute('aria-selected', isActive ? 'true' : 'false');
});
queryAll('[data-login-mode-panel]').forEach(function (panel) {
var isActive = panel.getAttribute('data-login-mode-panel') === activeMode;
panel.hidden = !isActive;
});
}
async function submitLogin(form, button) {
// 登录成功后跳转地址由 HTML 的 data-success-url 控制,页面可单独调整。
var api = getApi();
var values = readForm(form);
if (!api) return;
if (!requireValue(values.phone, '请填写手机号')) return;
if (!requireValue(values.password, '请填写密码')) return;
if (!await ensureCaptcha(form, getFormCaptchaScene(form, 'login'), values.phone)) return;
values = readForm(form);
setBusy(button, true);
try {
await api.login(buildLoginBody(values));
root.location.href = form.getAttribute('data-success-url') || 'profile.html';
} catch (error) {
showMessage(error.message || '登录失败');
} finally {
setBusy(button, false);
}
}
async function submitSmsLogin(form, button) {
// PC 短信登录与密码登录共用 WEB_H5_LOGIN 场景,短信码由同页按钮发送。
var api = getApi();
var values = readForm(form);
if (!api) return;
if (!requireValue(values.phone, '请填写手机号')) return;
if (!requireValue(values.smsCode, '请填写短信验证码')) return;
if (!await ensureCaptcha(form, getFormCaptchaScene(form, 'login'), values.phone)) return;
values = readForm(form);
setBusy(button, true);
try {
await api.loginBySms(buildSmsLoginBody(values));
root.location.href = form.getAttribute('data-success-url') || 'profile.html';
} catch (error) {
showMessage(error.message || '短信登录失败');
} finally {
setBusy(button, false);
}
}
async function submitRegister(form, button) {
// 注册成功后默认进入创建家谱流程。
var api = getApi();
var values = readForm(form);
if (!api) return;
if (!requireValue(values.phone, '请填写手机号')) return;
if (!requireValue(values.password, '请填写密码')) return;
if (!await ensureCaptcha(form, getFormCaptchaScene(form, 'register'), values.phone)) return;
values = readForm(form);
setBusy(button, true);
try {
await api.register(buildRegisterBody(values));
root.location.href = form.getAttribute('data-success-url') || 'create-genealogy.html';
} catch (error) {
showMessage(error.message || '注册失败');
} finally {
setBusy(button, false);
}
}
async function sendCode(button) {
// 当前只接找回密码验证码;后续注册短信可复用 data-scene-code 扩展。
var api = getApi();
var form = button.closest('form');
var values = readForm(form);
var sceneCode = button.getAttribute('data-scene-code') || getFormCaptchaScene(form, 'password-reset');
if (!api) return;
if (!requireValue(values.phone, '请填写手机号')) return;
if (!await ensureCaptcha(form, sceneCode, values.phone)) return;
values = readForm(form);
setBusy(button, true);
try {
await api.sendSmsCode({
sceneCode: sceneCode,
phone: values.phone,
validToken: values.validToken || ''
});
showMessage('验证码已发送');
} catch (error) {
showMessage(error.message || '验证码发送失败');
} finally {
setBusy(button, false);
}
}
async function submitPasswordReset(form, button) {
// 前端先校验两次密码一致,再按接口字段提交重置请求。
var api = getApi();
var values = readForm(form);
if (!api) return;
if (!requireValue(values.phone, '请填写手机号')) return;
if (!requireValue(values.smsCode, '请填写验证码')) return;
if (!requireValue(values.newPassword, '请填写新密码')) return;
if (values.newPassword !== values.confirmPassword) {
showMessage('两次输入的新密码不一致');
return;
}
if (!await ensureCaptcha(form, getFormCaptchaScene(form, 'password-reset'), values.phone)) return;
values = readForm(form);
setBusy(button, true);
try {
await api.resetPassword(buildPasswordResetBody(values));
showMessage('密码已重设,请重新登录');
root.location.href = form.getAttribute('data-success-url') || 'login.html';
} catch (error) {
showMessage(error.message || '重设密码失败');
} finally {
setBusy(button, false);
}
}
function bindCodeButtons() {
queryAll('[data-api-send-code]').forEach(function (button) {
button.addEventListener('click', function (event) {
event.preventDefault();
sendCode(button);
});
});
}
function bindLoginModeTabs() {
queryAll('[data-login-mode-tab]').forEach(function (button) {
button.addEventListener('click', function () {
setLoginMode(button.getAttribute('data-login-mode-tab'));
});
});
}
function init() {
// 每个页面只会命中自己的 data-auth-form,不需要拆多个入口文件。
if (!documentRef) return;
bindSubmit('[data-auth-form="login"]', submitLogin);
bindSubmit('[data-auth-form="sms-login"]', submitSmsLogin);
bindSubmit('[data-auth-form="register"]', submitRegister);
bindSubmit('[data-auth-form="password-reset"]', submitPasswordReset);
bindLoginModeTabs();
bindCodeButtons();
}
return {
buildLoginBody: buildLoginBody,
buildSmsLoginBody: buildSmsLoginBody,
buildRegisterBody: buildRegisterBody,
buildPasswordResetBody: buildPasswordResetBody,
getCaptchaScene: getCaptchaScene,
getFormCaptchaScene: getFormCaptchaScene,
init: init
};
});