// 忘记密码页面交互逻辑:智能账号检测与分流引导 let passwordForgotControlsBound = false; let currentUsername = ""; /** * 读取 CSRF 令牌,供找回密码请求使用。 * * @returns {string} */ function getCsrfToken() { return document.querySelector('meta[name="csrf-token"]')?.getAttribute("content") ?? ""; } /** * 获取当前应当使用的局部消息框元素。 * * @returns {HTMLDivElement|null} */ function getActiveAlertBox() { const stepDetect = document.getElementById("step-detect"); if (stepDetect && stepDetect.style.display !== "none") { return document.getElementById("alert-detect"); } return document.getElementById("alert-email"); } /** * 展示找回密码页面提示。 * * @param {string} message * @param {string} type * @returns {void} */ function showAlert(message, type) { const alertBox = getActiveAlertBox(); if (!alertBox) { return; } alertBox.textContent = message; alertBox.className = type === "success" ? "alert alert-success" : "alert alert-error"; alertBox.style.display = "block"; // 平滑滚动提示框到可视区域 alertBox.scrollIntoView({ behavior: "smooth", block: "nearest" }); } /** * 隐藏找回密码页面提示。 * * @returns {void} */ function hideAlert() { const alertDetect = document.getElementById("alert-detect"); const alertEmail = document.getElementById("alert-email"); if (alertDetect) { alertDetect.style.display = "none"; } if (alertEmail) { alertEmail.style.display = "none"; } } /** * 绑定双绑定状态下的选项卡切换事件。 * * @returns {void} */ function bindTabEvents() { const tabsContainer = document.getElementById("channel-tabs"); if (!tabsContainer) { return; } tabsContainer.addEventListener("click", (event) => { const btn = event.target.closest(".tab-btn"); if (!btn) { return; } // 清除所有高亮 tabsContainer.querySelectorAll(".tab-btn").forEach((b) => b.classList.remove("active")); btn.classList.add("active"); // 切换显示面板 const targetId = btn.getAttribute("data-target"); document.querySelectorAll(".channel-pane").forEach((pane) => { if (pane.id === targetId) { pane.style.display = "block"; } else { pane.style.display = "none"; } }); }); } /** * 第一步:提交昵称检测账号绑定状态。 * * @param {SubmitEvent} event * @returns {Promise} */ async function submitAccountDetect(event) { event.preventDefault(); const form = event.target; const detectBtn = document.getElementById("detect-btn"); if (!(form instanceof HTMLFormElement)) { return; } if (detectBtn instanceof HTMLButtonElement) { detectBtn.disabled = true; detectBtn.innerText = "账号检索中..."; } hideAlert(); try { const response = await fetch(form.action, { method: "POST", headers: { "Content-Type": "application/json", "X-CSRF-TOKEN": getCsrfToken(), Accept: "application/json", }, body: JSON.stringify(Object.fromEntries(new FormData(form).entries())), }); const body = await response.json(); if (response.status === 429) { showAlert(body.message || "请求过于频繁,请稍后再试。", "error"); return; } if (body.status === "not_found") { showAlert(body.message, "error"); return; } if (body.status === "success") { currentUsername = body.username; document.getElementById("step-detect").style.display = "none"; document.getElementById("step-result").style.display = "block"; document.getElementById("recovery-title").innerText = "密码找回建议"; document.getElementById("forgot-footer").style.display = "none"; const summary = document.getElementById("detect-summary"); const tabs = document.getElementById("channel-tabs"); const panelWechat = document.getElementById("panel-wechat"); const panelEmail = document.getElementById("panel-email"); const panelNone = document.getElementById("panel-none"); // 默认隐藏所有特定面板 tabs.style.display = "none"; panelWechat.style.display = "none"; panelEmail.style.display = "none"; panelNone.style.display = "none"; const hasEmail = body.has_email; const hasWechat = body.has_wechat; const hintLabel = document.getElementById("masked-email-hint"); const emailInput = document.getElementById("email"); if (hasEmail && hasWechat) { summary.innerHTML = `检测到账号 ${currentUsername} 已同时绑定了微信和邮箱。您可以选择以下任意一种方式找回密码:`; tabs.style.display = "grid"; // 默认选中微信面板 tabs.querySelectorAll(".tab-btn").forEach((b) => b.classList.remove("active")); const wechatTabBtn = tabs.querySelector('[data-target="panel-wechat"]'); if (wechatTabBtn) { wechatTabBtn.classList.add("active"); } panelWechat.style.display = "block"; if (hintLabel) { hintLabel.innerHTML = `📬 绑定的邮箱提示:${body.masked_email}`; } if (emailInput instanceof HTMLInputElement) { emailInput.value = ""; } } else if (hasWechat) { summary.innerHTML = `检测到账号 ${currentUsername} 仅绑定了微信。系统不支持邮箱找回,建议您使用微信助手进行重置:`; panelWechat.style.display = "block"; } else if (hasEmail) { summary.innerHTML = `检测到账号 ${currentUsername} 仅绑定了邮箱。建议您使用邮箱发送重置链接找回:`; panelEmail.style.display = "block"; if (hintLabel) { hintLabel.innerHTML = `📬 绑定的邮箱提示:${body.masked_email}`; } if (emailInput instanceof HTMLInputElement) { emailInput.value = ""; } } else { summary.innerHTML = `检测到账号 ${currentUsername} 尚未绑定微信或邮箱找回途径:`; panelNone.style.display = "block"; } } } catch { showAlert("网络或服务器连接异常,请稍后再试。", "error"); } finally { if (detectBtn instanceof HTMLButtonElement) { detectBtn.disabled = false; detectBtn.innerText = "下一步 (检测账号)"; } } } /** * 第二步:提交发送重置链接邮件请求。 * * @param {SubmitEvent} event * @returns {Promise} */ async function submitPasswordRecovery(event) { event.preventDefault(); const form = event.target; const submitButton = document.getElementById("submit-btn"); if (!(form instanceof HTMLFormElement)) { return; } if (submitButton instanceof HTMLButtonElement) { submitButton.disabled = true; submitButton.innerText = "发送重置链接中..."; } hideAlert(); let isSentSuccess = false; try { const emailInput = document.getElementById("email"); const emailVal = emailInput instanceof HTMLInputElement ? emailInput.value.trim() : ""; const response = await fetch(form.getAttribute("data-password-email-url") ?? "", { method: "POST", headers: { "Content-Type": "application/json", "X-CSRF-TOKEN": getCsrfToken(), Accept: "application/json", }, body: JSON.stringify({ username: currentUsername, email: emailVal, }), }); const body = await response.json(); if (response.status === 429) { showAlert(body.message || "请求发送过于频繁,请稍后再试。", "error"); return; } if (body.status === "success") { showAlert(body.message, "success"); isSentSuccess = true; return; } const errorMessage = body.message || (body.errors ? Object.values(body.errors)[0][0] : "重置邮件发送失败,请稍后重试。"); showAlert(errorMessage, "error"); } catch { showAlert("网络发送异常,请稍后再试。", "error"); } finally { if (submitButton instanceof HTMLButtonElement) { if (isSentSuccess) { // 成功时启动 30 秒置灰倒计时锁定 let seconds = 30; submitButton.disabled = true; submitButton.innerText = `${seconds}秒内不能重复发送`; const timer = setInterval(() => { seconds--; if (seconds <= 0) { clearInterval(timer); submitButton.disabled = false; submitButton.innerText = "二次验证并发送重置邮件"; } else { submitButton.innerText = `${seconds}秒内不能重复发送`; } }, 1000); } else { // 校验失败,立刻解除禁用供重新编辑 submitButton.disabled = false; submitButton.innerText = "二次验证并发送重置邮件"; } } } } /** * 绑定智能找回向导控制事件。 * * @returns {void} */ function bindPasswordForgotControls() { if (passwordForgotControlsBound || typeof document === "undefined") { return; } passwordForgotControlsBound = true; // 第一步表单提交 document.getElementById("account-detect-form")?.addEventListener("submit", (event) => { void submitAccountDetect(event); }); // 第二步发信表单提交 document.getElementById("password-recovery-form")?.addEventListener("submit", (event) => { void submitPasswordRecovery(event); }); // 返回按钮事件 document.getElementById("back-detect-btn")?.addEventListener("click", () => { hideAlert(); document.getElementById("step-detect").style.display = "block"; document.getElementById("step-result").style.display = "none"; document.getElementById("recovery-title").innerText = "忘记密码"; document.getElementById("forgot-footer").style.display = "block"; }); bindTabEvents(); } bindPasswordForgotControls();