Files
chatroom/resources/views/index.blade.php
lkddi e21f049643 修复:勤务日榜在线时长统计虚高(142小时)+ UI文字调整
Bug修复:
- closeDutyLog 增加 whereDate 限制,只关闭今日日志,历史遗留记录置0,避免跨天时长被计入榜单
- tickDutyLog(ChatController/AutoSaveExp)找不到今日开放日志时不再盲目新建,避免同一 login_at 产生几十条重复记录后 SUM 叠加导致虚假142小时
- AppointmentService 撤职时 closeDutyLog 同步增加今日/历史遗留区分处理

UI调整:
- 登录页版权文字「飘落的流星」→「流星」
- 后台布局标题「飘落流星 控制台」→「控制台」
- 后台侧边栏移除非超管查看各模块时的「(只读)」标注
2026-03-01 22:55:55 +08:00

498 lines
16 KiB
PHP
Raw 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 === 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>