- 修复 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 构建配置 - 新增验证码配置文件
140 lines
6.4 KiB
PHP
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>
|