Files
chatroom/resources/views/index.blade.php

506 lines
16 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{--
文件功能:聊天室登录/注册首页(复刻原版 DEFAULT.asp 风格)
包含房间选择、性别选择,登录后弹出独立窗口直接进入聊天
--}}
<!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>
<meta name="csrf-token" content="{{ csrf_token() }}">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "宋体", SimSun, "Microsoft YaHei", sans-serif;
font-size: 12px;
background: #c0d8ef;
color: #333;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
/* 居中卡片 */
.login-card {
width: 720px;
border: 1px solid #8aaccf;
border-radius: 5px;
overflow: hidden;
box-shadow: 2px 3px 12px rgba(0, 0, 0, 0.18);
}
/* 标题栏 */
.card-header {
background: linear-gradient(180deg, #3a6fa0, #2a5580);
color: #fff;
text-align: center;
padding: 12px 0 10px;
font-size: 18px;
font-weight: bold;
letter-spacing: 3px;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3);
}
/* 公告条 */
.notice-bar {
background: #fffde0;
border-bottom: 1px solid #e8d88e;
padding: 4px 10px;
font-size: 12px;
color: #996600;
text-align: center;
}
/* 内容区:左右分栏 */
.card-body {
display: flex;
}
/* 左侧:登录表单 */
.login-left {
flex: 1;
background: #eef5ff;
padding: 24px 30px;
display: flex;
flex-direction: column;
justify-content: center;
}
.section-title {
font-size: 13px;
font-weight: bold;
color: #2a5580;
border-bottom: 1px solid #b8d0e8;
padding-bottom: 6px;
margin-bottom: 14px;
}
.form-row {
display: flex;
align-items: center;
margin-bottom: 12px;
}
.form-label {
width: 50px;
text-align: right;
font-size: 12px;
color: #336699;
font-weight: bold;
padding-right: 8px;
flex-shrink: 0;
}
.form-input {
flex: 1;
padding: 5px 8px;
height: 32px;
border: 1px solid #aaa;
border-radius: 3px;
font-size: 12px;
color: #333;
background: #fff;
max-width: 200px;
}
.form-input:focus {
border-color: #336699;
outline: none;
box-shadow: 0 0 3px rgba(51, 102, 153, 0.3);
}
.gender-row {
display: flex;
align-items: center;
gap: 20px;
margin: 6px 0 14px;
padding-left: 58px;
font-size: 12px;
}
.gender-row label {
cursor: pointer;
color: #336699;
display: flex;
align-items: center;
gap: 3px;
}
.captcha-wrap {
display: flex;
align-items: center;
flex: 1;
gap: 6px;
}
.captcha-wrap input {
width: 80px;
flex: none;
height: 32px;
}
.captcha-wrap img {
height: 32px;
cursor: pointer;
border: 1px solid #aaa;
border-radius: 3px;
}
.btn-row {
display: flex;
justify-content: center;
gap: 10px;
margin-top: 16px;
}
.btn {
padding: 6px 24px;
border: 1px solid #999;
border-radius: 3px;
background: linear-gradient(180deg, #f8f8f8, #e0e0e0);
color: #333;
font-size: 12px;
cursor: pointer;
}
.btn:hover {
background: linear-gradient(180deg, #e8e8e8, #d0d0d0);
}
.btn-primary {
background: linear-gradient(180deg, #4a8cc5, #336699);
color: #fff;
border-color: #2a5580;
font-weight: bold;
}
.btn-primary:hover {
background: linear-gradient(180deg, #3a7cb5, #2a5a88);
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.link-row {
text-align: center;
margin-top: 10px;
font-size: 11px;
color: #999;
}
.alert {
display: none;
padding: 6px 10px;
margin-bottom: 10px;
border-radius: 3px;
font-size: 12px;
text-align: center;
}
.alert-error {
background: #fde8e8;
color: #cc0000;
border: 1px solid #f5c0c0;
}
.alert-success {
background: #e8fde8;
color: #006600;
border: 1px solid #c0f5c0;
}
/* 右侧:房间列表 */
.login-right {
width: 250px;
background: #f0f6ff;
border-left: 1px solid #b8d0e8;
display: flex;
flex-direction: column;
}
.room-header {
background: #d8e8f5;
color: #336699;
font-size: 12px;
font-weight: bold;
padding: 7px 10px;
text-align: center;
border-bottom: 1px solid #b8d0e8;
}
.room-list {
flex: 1;
overflow-y: auto;
}
.room-item {
display: flex;
align-items: center;
padding: 6px 10px;
cursor: pointer;
border-bottom: 1px solid #e0ecf7;
font-size: 12px;
transition: background 0.15s;
}
.room-item:hover {
background: #dde8ff;
}
.room-item.selected {
background: #d0e4f5;
color: #1a4a70;
}
.room-item input[type="radio"] {
margin-right: 8px;
flex-shrink: 0;
}
.room-info {
flex: 1;
min-width: 0;
}
.room-name {
font-weight: bold;
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.room-owner {
font-size: 11px;
color: #8aaccf;
display: block;
}
.room-item.selected .room-owner {
color: #b8d0e8;
}
.room-footer {
background: #e0ecf7;
padding: 5px 10px;
font-size: 11px;
color: #666;
text-align: center;
border-top: 1px solid #b8d0e8;
}
/* 底部版权 */
.card-footer {
text-align: center;
padding: 6px;
font-size: 11px;
color: #7a9fc0;
background: #e0ecf7;
border-top: 1px solid #b8d0e8;
}
</style>
</head>
<body>
<div class="login-card">
{{-- 标题 --}}
<div class="card-header">
{{ \App\Models\SysParam::where('alias', 'sys_name')->value('body') ?? '飘落的流星在线聊天' }}
</div>
{{-- 公告 --}}
<div class="notice-bar">
{{ \App\Models\SysParam::where('alias', 'sys_notice')->value('body') ?? '欢迎来到聊天室!第一次登录即为注册,请记住您的密码。' }}
</div>
{{-- 内容区 --}}
<form id="login-form">
<div class="card-body">
{{-- 左侧:登录表单 --}}
<div class="login-left">
<div class="section-title">🔑 用户登录</div>
<div id="alert-box" class="alert"></div>
<div class="form-row">
<span class="form-label">昵称:</span>
<input type="text" id="username" name="username" class="form-input" maxlength="10"
placeholder="中英文、数字、下划线" required>
</div>
<div class="form-row">
<span class="form-label">密码:</span>
<input type="password" id="password" name="password" class="form-input" maxlength="20"
placeholder="请输入密码" required>
</div>
<div class="form-row">
<span class="form-label">验证:</span>
<div class="captcha-wrap">
<input type="text" id="captcha" name="captcha" class="form-input" maxlength="10"
placeholder="输入验证码" required>
<img src="/captcha/default?{{ mt_rand() }}" alt="验证码" id="captcha-img"
onclick="refreshCaptcha()" title="点击刷新">
</div>
</div>
<div class="gender-row">
<label><input type="radio" name="bSex" value="1" checked> </label>
<label><input type="radio" name="bSex" value="2"> </label>
</div>
<div class="btn-row">
<button type="submit" id="submit-btn" class="btn btn-primary">进入聊天</button>
<button type="reset" class="btn">重填</button>
</div>
<div class="link-row">
第一次登录即为注册,请记住您的密码
@if (\App\Models\SysParam::where('alias', 'smtp_enabled')->value('body') === '1')
<div style="margin-top: 8px;">
<a href="javascript:alert('邮箱找回密码业务即将上线...');"
style="color: #336699; text-decoration: none;">忘了密码?点击通过邮箱找回</a>
</div>
@endif
</div>
{{-- 推荐浏览器提示 --}}
<div style="margin-top: 10px; font-size: 11px; color: #888; text-align: center;">
🌐 推荐
<a href="https://www.microsoft.com/zh-cn/edge/download" target="_blank"
style="color: #1565c0; text-decoration: none;">Edge</a>
/
<a href="https://www.google.com/chrome/" target="_blank"
style="color: #c0392b; text-decoration: none;">Chrome</a>
浏览器IE 等旧版不支持聊天室
</div>
</div>
{{-- 右侧:房间列表 --}}
<div class="login-right">
<div class="room-header">📋 选择房间</div>
<div class="room-list">
@forelse ($rooms as $room)
<label class="room-item {{ $loop->first ? 'selected' : '' }}">
<input type="radio" name="room_id" value="{{ $room->id }}"
{{ $loop->first ? 'checked' : '' }}>
<div class="room-info">
<span class="room-name">{{ $room->name }}</span>
@if ($room->master)
<span class="room-owner">房主:{{ $room->master }}</span>
@endif
</div>
</label>
@empty
<div style="padding: 20px; text-align: center; color: #999;">暂无房间</div>
@endforelse
</div>
<div class="room-footer"> {{ count($rooms) }} 个房间</div>
</div>
</div>
</form>
{{-- 底部 --}}
<div class="card-footer">Powered by 流星 &copy; {{ date('Y') }}</div>
</div>
<script>
function refreshCaptcha() {
document.getElementById('captcha-img').src = '/captcha/default?' + Math.random();
}
document.querySelectorAll('.room-item').forEach(item => {
item.addEventListener('click', function() {
document.querySelectorAll('.room-item').forEach(i => i.classList.remove('selected'));
this.classList.add('selected');
});
});
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.style.display = 'none';
const formData = new FormData(this);
const data = Object.fromEntries(formData.entries());
const roomId = data.room_id || '1';
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(body => ({
status: response.status,
body
})))
.then(result => {
if (result.status === 419 || (result.body && result.body.message === 'CSRF token mismatch.')) {
showAlert('安全令牌已过期,正在自动刷新...', 'error');
setTimeout(() => {
window.location.reload();
}, 1500);
return;
}
if (result.status === 200 && result.body.status === 'success') {
showAlert(result.body.message, 'success');
const chatUrl = '/room/' + roomId;
const chatWin = window.open(chatUrl, 'chatroom',
'toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes'
);
if (chatWin) {
chatWin.moveTo(0, 0);
chatWin.resizeTo(screen.availWidth, screen.availHeight);
chatWin.focus();
}
setTimeout(() => {
window.location.href = '/rooms';
}, 1500);
} 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.className = 'alert ' + (type === 'error' ? 'alert-error' : 'alert-success');
box.style.display = 'block';
}
</script>
</body>
</html>