From de7c79f0de8b579522bde3b6671cbef2b3ad804e Mon Sep 17 00:00:00 2001 From: lkddi Date: Sat, 25 Apr 2026 13:45:34 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=81=E7=A7=BB=E9=A6=96=E9=A1=B5=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/js/home-login.js | 220 ++++++++++++++++++++++++++++++++ resources/views/index.blade.php | 132 +------------------ vite.config.js | 1 + 3 files changed, 228 insertions(+), 125 deletions(-) create mode 100644 resources/js/home-login.js diff --git a/resources/js/home-login.js b/resources/js/home-login.js new file mode 100644 index 0000000..ebb87b5 --- /dev/null +++ b/resources/js/home-login.js @@ -0,0 +1,220 @@ +// 首页登录交互入口,负责验证码刷新、房间提示、性别选中态和 AJAX 登录。 + +let homeLoginControlsBound = false; + +/** + * 读取 CSRF 令牌,供登录请求使用。 + * + * @returns {string} + */ +function getCsrfToken() { + return document.querySelector('meta[name="csrf-token"]')?.getAttribute("content") ?? ""; +} + +/** + * 刷新验证码图片,避免浏览器缓存旧图。 + * + * @param {HTMLImageElement} captchaImage + * @returns {void} + */ +function refreshCaptcha(captchaImage) { + const refreshUrl = captchaImage.getAttribute("data-captcha-refresh-url") ?? "/captcha/default"; + captchaImage.src = `${refreshUrl}?${Math.random()}`; +} + +/** + * 同步性别单选项的选中视觉状态。 + * + * @returns {void} + */ +function syncSelectedState() { + document.querySelectorAll(".gender-option").forEach((option) => { + const input = option.querySelector('input[type="radio"]'); + option.classList.toggle("selected", Boolean(input?.checked)); + }); +} + +/** + * 根据当前房间选择更新提示文案。 + * + * @returns {void} + */ +function updateRoomHint() { + const roomSelect = document.getElementById("room_id"); + const hint = document.getElementById("room-hint"); + + if (!roomSelect || !hint || roomSelect.selectedIndex < 0) { + return; + } + + const selectedOption = roomSelect.options[roomSelect.selectedIndex]; + const roomName = selectedOption.getAttribute("data-room-name") || selectedOption.text; + const roomOwner = selectedOption.getAttribute("data-room-owner") || "未设置"; + + hint.textContent = `当前房间:${roomName} / 房主:${roomOwner}`; +} + +/** + * 展示或隐藏登录提示框。 + * + * @param {string} message + * @param {string} type + * @param {boolean} visible + * @returns {void} + */ +function showAlert(message, type, visible) { + const alertBox = document.getElementById("alert-box"); + if (!alertBox) { + return; + } + + alertBox.textContent = message; + alertBox.className = type === "success" ? "alert-success" : "alert-error"; + alertBox.id = "alert-box"; + alertBox.style.display = visible ? "block" : "none"; +} + +/** + * 打开聊天室窗口,并尽量铺满当前可用屏幕。 + * + * @param {string} url + * @returns {void} + */ +function openChatRoomWindow(url) { + const chatWindow = window.open( + url, + "chatroom", + "toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes", + ); + + if (!chatWindow) { + return; + } + + chatWindow.moveTo(0, 0); + chatWindow.resizeTo(window.screen.availWidth, window.screen.availHeight); + chatWindow.focus(); +} + +/** + * 处理登录表单提交。 + * + * @param {SubmitEvent} event + * @returns {Promise} + */ +async function submitLogin(event) { + event.preventDefault(); + + const form = event.target; + if (!(form instanceof HTMLFormElement)) { + return; + } + + const button = document.getElementById("submit-btn"); + const captchaInput = document.getElementById("captcha"); + const captchaImage = document.getElementById("captcha-img"); + const loginUrl = form.getAttribute("data-login-url") ?? form.action; + const roomsUrl = form.getAttribute("data-rooms-url") ?? "/rooms"; + const roomUrlTemplate = form.getAttribute("data-room-url-template") ?? "/room/:id"; + const formData = new FormData(form); + const data = Object.fromEntries(formData.entries()); + const roomId = data.room_id || "1"; + + if (button instanceof HTMLButtonElement) { + button.disabled = true; + button.innerText = "正在进入..."; + } + + showAlert("", "error", false); + + try { + const response = await fetch(loginUrl, { + method: "POST", + credentials: "same-origin", + headers: { + "Content-Type": "application/json", + "X-CSRF-TOKEN": getCsrfToken(), + Accept: "application/json", + }, + body: JSON.stringify(data), + }); + const body = await response.json(); + + if (response.status === 419 || body?.message === "CSRF token mismatch.") { + showAlert("安全令牌已过期,正在刷新页面...", "error", true); + window.setTimeout(() => { + window.location.reload(); + }, 1500); + return; + } + + if (response.status === 200 && body.status === "success") { + showAlert(body.message, "success", true); + openChatRoomWindow(roomUrlTemplate.replace(":id", roomId)); + window.setTimeout(() => { + window.location.href = roomsUrl; + }, 1500); + return; + } + + const errorMessage = body.message || (body.errors ? Object.values(body.errors)[0][0] : "登录失败,请稍后再试。"); + showAlert(errorMessage, "error", true); + + if (captchaImage instanceof HTMLImageElement) { + refreshCaptcha(captchaImage); + } + if (captchaInput instanceof HTMLInputElement) { + captchaInput.value = ""; + } + if (button instanceof HTMLButtonElement) { + button.disabled = false; + button.innerText = "进入和平聊吧"; + } + } catch { + showAlert("网络或服务器异常,请稍后再试。", "error", true); + if (captchaImage instanceof HTMLImageElement) { + refreshCaptcha(captchaImage); + } + if (button instanceof HTMLButtonElement) { + button.disabled = false; + button.innerText = "进入和平聊吧"; + } + } +} + +/** + * 绑定首页登录页交互。 + * + * @returns {void} + */ +function bindHomeLoginControls() { + if (homeLoginControlsBound || typeof document === "undefined") { + return; + } + + homeLoginControlsBound = true; + + document.addEventListener("click", (event) => { + if (!(event.target instanceof HTMLImageElement)) { + return; + } + + if (event.target.hasAttribute("data-captcha-refresh-url")) { + refreshCaptcha(event.target); + } + }); + + document.querySelectorAll('.gender-option input[type="radio"]').forEach((input) => { + input.addEventListener("change", syncSelectedState); + }); + + document.getElementById("room_id")?.addEventListener("change", updateRoomHint); + document.getElementById("login-form")?.addEventListener("submit", (event) => { + void submitLogin(event); + }); + + syncSelectedState(); + updateRoomHint(); +} + +bindHomeLoginControls(); diff --git a/resources/views/index.blade.php b/resources/views/index.blade.php index 88883bb..a5750ec 100644 --- a/resources/views/index.blade.php +++ b/resources/views/index.blade.php @@ -23,6 +23,7 @@ + @vite('resources/js/home-login.js')