迁移首页登录脚本
This commit is contained in:
@@ -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<void>}
|
||||||
|
*/
|
||||||
|
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();
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700;900&family=Noto+Serif+SC:wght@700;900&family=Oswald:wght@400;500;600;700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700;900&family=Noto+Serif+SC:wght@700;900&family=Oswald:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||||
|
@vite('resources/js/home-login.js')
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
--bg: #0d0f0d;
|
--bg: #0d0f0d;
|
||||||
@@ -835,7 +836,10 @@
|
|||||||
style="display: {{ $flashMessage ? 'block' : 'none' }};"
|
style="display: {{ $flashMessage ? 'block' : 'none' }};"
|
||||||
>{{ $flashMessage }}</div>
|
>{{ $flashMessage }}</div>
|
||||||
|
|
||||||
<form id="login-form" class="login-form">
|
<form id="login-form" class="login-form"
|
||||||
|
data-login-url="{{ route('login.post') }}"
|
||||||
|
data-rooms-url="{{ route('rooms.index') }}"
|
||||||
|
data-room-url-template="{{ url('/room/:id') }}">
|
||||||
<div class="field-grid">
|
<div class="field-grid">
|
||||||
<div class="field-block">
|
<div class="field-block">
|
||||||
<label for="username"><span class="field-index">01</span>昵称</label>
|
<label for="username"><span class="field-index">01</span>昵称</label>
|
||||||
@@ -851,7 +855,8 @@
|
|||||||
<label for="captcha"><span class="field-index">03</span>验证码</label>
|
<label for="captcha"><span class="field-index">03</span>验证码</label>
|
||||||
<div class="captcha-row">
|
<div class="captcha-row">
|
||||||
<input class="field-input" type="text" id="captcha" name="captcha" maxlength="10" placeholder="输入验证码" required>
|
<input class="field-input" type="text" id="captcha" name="captcha" maxlength="10" placeholder="输入验证码" required>
|
||||||
<img src="/captcha/default?{{ mt_rand() }}" alt="验证码" id="captcha-img" class="captcha-image" onclick="refreshCaptcha()" title="点击刷新验证码">
|
<img src="/captcha/default?{{ mt_rand() }}" alt="验证码" id="captcha-img" class="captcha-image"
|
||||||
|
data-captcha-refresh-url="/captcha/default" title="点击刷新验证码">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -911,129 +916,6 @@
|
|||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script>
|
|
||||||
function refreshCaptcha() {
|
|
||||||
document.getElementById('captcha-img').src = '/captcha/default?' + Math.random();
|
|
||||||
}
|
|
||||||
|
|
||||||
function syncSelectedState() {
|
|
||||||
document.querySelectorAll('.gender-option').forEach(function(option) {
|
|
||||||
const input = option.querySelector('input[type="radio"]');
|
|
||||||
option.classList.toggle('selected', Boolean(input && input.checked));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.querySelectorAll('.gender-option input[type="radio"]').forEach(function(input) {
|
|
||||||
input.addEventListener('change', syncSelectedState);
|
|
||||||
});
|
|
||||||
|
|
||||||
const roomSelect = document.getElementById('room_id');
|
|
||||||
if (roomSelect) {
|
|
||||||
roomSelect.addEventListener('change', updateRoomHint);
|
|
||||||
}
|
|
||||||
|
|
||||||
syncSelectedState();
|
|
||||||
updateRoomHint();
|
|
||||||
|
|
||||||
document.getElementById('login-form').addEventListener('submit', function(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
const button = document.getElementById('submit-btn');
|
|
||||||
const formData = new FormData(this);
|
|
||||||
const data = Object.fromEntries(formData.entries());
|
|
||||||
const roomId = data.room_id || '1';
|
|
||||||
|
|
||||||
button.disabled = true;
|
|
||||||
button.innerText = '正在进入...';
|
|
||||||
showAlert('', 'error', false);
|
|
||||||
|
|
||||||
fetch('{{ route('login.post') }}', {
|
|
||||||
method: 'POST',
|
|
||||||
credentials: 'same-origin',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
|
|
||||||
'Accept': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify(data),
|
|
||||||
})
|
|
||||||
.then(function(response) {
|
|
||||||
return response.json().then(function(body) {
|
|
||||||
return {
|
|
||||||
status: response.status,
|
|
||||||
body: body,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.then(function(result) {
|
|
||||||
if (result.status === 419 || (result.body && result.body.message === 'CSRF token mismatch.')) {
|
|
||||||
showAlert('安全令牌已过期,正在刷新页面...', 'error', true);
|
|
||||||
setTimeout(function() {
|
|
||||||
window.location.reload();
|
|
||||||
}, 1500);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.status === 200 && result.body.status === 'success') {
|
|
||||||
showAlert(result.body.message, 'success', true);
|
|
||||||
const chatUrl = '/room/' + roomId;
|
|
||||||
const chatWindow = window.open(
|
|
||||||
chatUrl,
|
|
||||||
'chatroom',
|
|
||||||
'toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes'
|
|
||||||
);
|
|
||||||
|
|
||||||
if (chatWindow) {
|
|
||||||
chatWindow.moveTo(0, 0);
|
|
||||||
chatWindow.resizeTo(screen.availWidth, screen.availHeight);
|
|
||||||
chatWindow.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(function() {
|
|
||||||
window.location.href = '/rooms';
|
|
||||||
}, 1500);
|
|
||||||
} else {
|
|
||||||
const errorMessage =
|
|
||||||
result.body.message ||
|
|
||||||
(result.body.errors ? Object.values(result.body.errors)[0][0] : '登录失败,请稍后再试。');
|
|
||||||
showAlert(errorMessage, 'error', true);
|
|
||||||
refreshCaptcha();
|
|
||||||
document.getElementById('captcha').value = '';
|
|
||||||
button.disabled = false;
|
|
||||||
button.innerText = '进入和平聊吧';
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(function() {
|
|
||||||
showAlert('网络或服务器异常,请稍后再试。', 'error', true);
|
|
||||||
refreshCaptcha();
|
|
||||||
button.disabled = false;
|
|
||||||
button.innerText = '进入和平聊吧';
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function showAlert(message, type, visible) {
|
|
||||||
const alertBox = document.getElementById('alert-box');
|
|
||||||
alertBox.textContent = message;
|
|
||||||
alertBox.className = type === 'success' ? 'alert-success' : 'alert-error';
|
|
||||||
alertBox.id = 'alert-box';
|
|
||||||
alertBox.style.display = visible ? 'block' : 'none';
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export default defineConfig({
|
|||||||
"resources/js/chat.js",
|
"resources/js/chat.js",
|
||||||
"resources/js/effects.js",
|
"resources/js/effects.js",
|
||||||
"resources/js/front.js",
|
"resources/js/front.js",
|
||||||
|
"resources/js/home-login.js",
|
||||||
],
|
],
|
||||||
refresh: true,
|
refresh: true,
|
||||||
}),
|
}),
|
||||||
|
|||||||
Reference in New Issue
Block a user