feat: 注册准则阅读控制 + Footer 合规信息

准则阅读(三层控制):
- registration.show_guidelines — 弹窗/复选框模式
- registration.force_guidelines — 是否强制倒计时+滚动
- registration.guidelines_timer — 倒计时秒数

Footer 合规(8 字段,空值不显示,链接纯手写):
- site.operator_name / site.icp_number / site.icp_license
- site.police_number / site.wenwangwen / site.algorithm_filing
- site.privacy_url / site.terms_url
- 新增 safeHTML 模板函数输出不转义 HTML
This commit is contained in:
2026-06-21 23:50:52 +08:00
parent 0014bdb9ff
commit 1933ea86b4
9 changed files with 271 additions and 28 deletions

View File

@ -25,6 +25,7 @@
var passwordInput = document.getElementById('password');
var confirmPasswordInput = document.getElementById('confirmPassword');
var rememberMeCheckbox = document.getElementById('rememberMe');
var guidelinesAgreedCheckbox = document.getElementById('guidelinesAgreed');
var emailError = document.getElementById('emailError');
var passwordError = document.getElementById('passwordError');
var confirmPasswordError = document.getElementById('confirmPasswordError');
@ -32,6 +33,11 @@
// Guard: exit if not on auth page
if (!registerForm) return;
// 准则配置(从模板 data 属性读取)
var forceGuidelines = modalOverlay ? modalOverlay.getAttribute('data-force-guidelines') === 'true' : true;
var guidelinesTimer = modalOverlay ? parseInt(modalOverlay.getAttribute('data-guidelines-timer')) || 60 : 60;
var showModal = !!modalOverlay;
// 读取 redirect 参数(注册成功后跳转目标)
function getRedirect() {
var m = window.location.search.match(/[?&]redirect=([^&]+)/);
@ -39,7 +45,7 @@
return '/';
}
var countdown = 60;
var countdown = guidelinesTimer;
var timerInterval = null;
var reachedBottom = false;
var agreed = false;
@ -50,7 +56,7 @@
function openModal(onAgreed) {
isRegistrationMode = typeof onAgreed === 'function';
clearInterval(timerInterval);
countdown = 60;
countdown = guidelinesTimer;
reachedBottom = false;
agreed = false;
modalBody.scrollTop = 0;
@ -61,18 +67,25 @@
modalEnforce.style.display = '';
confirmBtn.style.display = '';
previewCloseBtn.style.display = 'none';
timerCount.textContent = '60';
timerLabel.style.display = '';
timerCount.style.display = '';
timerUnit.style.display = '';
timerDone.style.display = 'none';
timer.classList.remove('done');
countdownPaused = false;
confirmBtn.disabled = true;
scrollHint.style.display = 'flex';
scrollHint.textContent = '▼ 请滚动至底部完成阅读';
scrollHint.style.color = 'var(--color-danger)';
startCountdown();
if (forceGuidelines) {
timerCount.textContent = guidelinesTimer;
timerLabel.style.display = '';
timerCount.style.display = '';
timerUnit.style.display = '';
timerDone.style.display = 'none';
if (timer) timer.classList.remove('done');
countdownPaused = false;
confirmBtn.disabled = true;
scrollHint.style.display = 'flex';
scrollHint.textContent = '▼ 请滚动至底部完成阅读';
scrollHint.style.color = 'var(--color-danger)';
startCountdown();
} else {
// 非强制:隐藏倒计时和滚动提示,按钮直接可用
modalEnforce.style.display = 'none';
confirmBtn.disabled = false;
}
} else {
modalEnforce.style.display = 'none';
confirmBtn.style.display = 'none';
@ -239,37 +252,46 @@
return;
}
// 复选框模式:检查是否勾选准则
if (guidelinesAgreedCheckbox && !guidelinesAgreedCheckbox.checked) {
window.MetaLab.toast(toast, '请先阅读并同意社区准则', true);
return;
}
// Disable submit to prevent double-click
submitBtn.disabled = true;
submitBtn.textContent = '检查中...';
// Step 1: Check if email already registered (before showing 60s guidelines)
// Step 1: Check if email already registered
var checkXhr = new XMLHttpRequest();
checkXhr.open('POST', '/api/auth/check-email', true);
checkXhr.setRequestHeader('Content-Type', 'application/json');
checkXhr.onreadystatechange = function () {
if (checkXhr.readyState !== 4) return;
submitBtn.disabled = true; // will be re-enabled by checkFormValidity if needed
submitBtn.disabled = true;
var resp;
try { resp = JSON.parse(checkXhr.responseText); } catch (err) { resp = null; }
if (checkXhr.status === 200 && resp && resp.success) {
if (resp.data && resp.data.exists) {
// Already registered → notify user immediately, skip guidelines
window.MetaLab.toast(toast, '该邮箱已注册,请直接登录', true);
submitBtn.textContent = '注 册';
submitBtn.disabled = false;
} else {
// Not registered → proceed to guidelines modal
submitBtn.textContent = '注 册';
openModal(function () {
if (showModal) {
// 弹窗模式:弹出准则阅读
openModal(function () {
doRegister(email, password, confirm);
});
} else {
// 复选框模式:直接注册
doRegister(email, password, confirm);
});
}
}
} else {
// API error → show message, don't block
var msg = (resp && resp.message) ? resp.message : '网络异常,请稍后重试';
window.MetaLab.toast(toast, msg, true);
submitBtn.textContent = '注 册';