Files
chatroom/resources/views/index.blade.php
lkddi d884853968 修复:排行榜/留言板缺失布局、退出登录跳转、WebSocket 配置与部署文档
- 修复 LeaderboardController 查询不存在的 sign 字段导致 500 错误
- 修复 leaderboard/index 和 guestbook/index 引用不存在的 layouts.app 布局
- 将排行榜和留言板改为独立 HTML 页面结构(含 Tailwind CDN)
- 修复退出登录返回 JSON 而非重定向的问题,现在会正确跳转回登录页
- 将 REDIS_CLIENT 从 phpredis 改为 predis(兼容无扩展环境)
- 新增 RoomSeeder 自动创建默认公共大厅房间
- 新增 Nginx 生产环境配置示例(含 WebSocket 反向代理)
- 重写 README.md 为完整的中文部署指南
- 修复 rooms/index 和 chat/frame 中 Alpine.js 语法错误
- 将 chat.js 加入 Vite 构建配置
- 新增验证码配置文件
2026-02-26 14:57:24 +08:00

140 lines
6.4 KiB
PHP

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ \App\Models\SysParam::where('alias', 'sys_name')->value('body') ?? '飘落的流星在线聊天' }} - 登录</title>
<!-- 使用现代 CDN 引入 Tailwind CSS (快速构建 UI) -->
<script src="https://cdn.tailwindcss.com"></script>
<meta name="csrf-token" content="{{ csrf_token() }}">
</head>
<body class="bg-gray-100 h-screen flex items-center justify-center">
<div class="bg-white p-8 rounded-lg shadow-md w-full max-w-md">
<h1 class="text-2xl font-bold text-center mb-6 text-gray-800">
{{ \App\Models\SysParam::where('alias', 'sys_name')->value('body') ?? '在线聊天室' }}
</h1>
<p class="text-sm text-gray-500 text-center mb-6">
{{ \App\Models\SysParam::where('alias', 'sys_notice')->value('body') ?? '欢迎您的加入' }}
</p>
<!-- 登录提示区 -->
<div id="alert-box" class="hidden mb-4 p-3 rounded text-sm text-center"></div>
<form id="login-form" class="space-y-4">
<div>
<label for="username" class="block text-sm font-medium text-gray-700">昵称 (第一次登录即注册)</label>
<input type="text" id="username" name="username" required
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
placeholder="允许中英文、数字、下划线">
</div>
<div>
<label for="password" class="block text-sm font-medium text-gray-700">密码</label>
<input type="password" id="password" name="password" required
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
placeholder="请输入密码">
</div>
<div>
<label for="captcha" class="block text-sm font-medium text-gray-700">验证码</label>
<div class="mt-1 flex space-x-2">
<input type="text" id="captcha" name="captcha" required
class="block w-2/3 px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
placeholder="输入右侧字符">
<div class="w-1/3 cursor-pointer" onclick="refreshCaptcha()">
<!-- 验证码图片,点击刷新 -->
<img src="{{ captcha_src() }}" alt="验证码" id="captcha-img"
class="h-full w-full rounded-md border border-gray-300 object-cover">
</div>
</div>
</div>
<button type="submit" id="submit-btn"
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50">
进入聊天室
</button>
</form>
</div>
<script>
// 刷新验证码
function refreshCaptcha() {
document.getElementById('captcha-img').src = '{{ captcha_src() }}' + Math.random();
}
// 提交登录表单
document.getElementById('login-form').addEventListener('submit', function(e) {
e.preventDefault();
const btn = document.getElementById('submit-btn');
const alertBox = document.getElementById('alert-box');
btn.disabled = true;
btn.innerText = '正在进入...';
alertBox.classList.add('hidden');
const formData = new FormData(this);
const data = Object.fromEntries(formData.entries());
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(response => response.json().then(data => ({
status: response.status,
body: data
})))
.then(result => {
if (result.status === 200 && result.body.status === 'success') {
// 登录成功,显示成功并跳转
showAlert(result.body.message, 'success');
setTimeout(() => {
// 转跳到大厅房间列表
window.location.href = '/rooms';
}, 1000);
} else {
// 验证失败或密码错误
const errorMsg = result.body.message || (result.body.errors ? Object.values(result.body
.errors)[0][0] : '登录失败');
showAlert(errorMsg, 'error');
refreshCaptcha();
document.getElementById('captcha').value = '';
btn.disabled = false;
btn.innerText = '进入聊天室';
}
})
.catch(error => {
console.error('Error:', error);
showAlert('网络或服务器错误,请稍后再试。', 'error');
refreshCaptcha();
btn.disabled = false;
btn.innerText = '进入聊天室';
});
});
function showAlert(message, type) {
const box = document.getElementById('alert-box');
box.innerText = message;
box.classList.remove('hidden', 'bg-red-100', 'text-red-700', 'bg-green-100', 'text-green-700');
if (type === 'error') {
box.classList.add('bg-red-100', 'text-red-700');
} else {
box.classList.add('bg-green-100', 'text-green-700');
}
}
</script>
</body>
</html>