修复:排行榜/留言板缺失布局、退出登录跳转、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 构建配置 - 新增验证码配置文件
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
@@ -8,13 +9,14 @@
|
||||
<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>
|
||||
@@ -26,105 +28,112 @@
|
||||
<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="允许中英文、数字、下划线">
|
||||
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="请输入密码">
|
||||
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="输入右侧字符">
|
||||
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">
|
||||
<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">
|
||||
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',
|
||||
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(() => {
|
||||
// TODO: 之后重定向到真实的聊天室页面 /chat
|
||||
window.location.href = '/';
|
||||
}, 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>
|
||||
// 刷新验证码
|
||||
function refreshCaptcha() {
|
||||
document.getElementById('captcha-img').src = '{{ captcha_src() }}' + Math.random();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
// 提交登录表单
|
||||
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>
|
||||
|
||||
Reference in New Issue
Block a user