Files
chatroom/resources/views/layouts/app.blade.php
lkddi 5f30220609 feat: 任命/撤销通知系统 + 用户名片UI优化
- 任命/撤销事件增加 type 字段区分类型
- 任命:全屏礼花 + 紫色弹窗 + 紫色系统消息
- 撤销:灰色弹窗 + 灰色系统消息,无礼花
- 消息分发:操作者/被操作者显示在私聊面板,其他人显示在公屏
- 系统消息加随机鼓励语(各5条轮换)
- ChatStateService 修复 Redis key 前缀扫描问题(getAllActiveRoomIds)
- 用户名片折叠优化:管理员视野、职务履历均可折叠
- 管理操作 + 职务操作合并为「🔧 管理操作」折叠区
- 悄悄话改为「🎁 送礼物」按钮,礼物面板内联展开
2026-02-28 23:44:38 +08:00

208 lines
10 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.
{{--
文件功能:前台统一布局模板
提供公共 <head>、顶部导航栏、消息提示、内容区域和底部
所有前台页面(除 chat/frame index 登录页外)统一使用此模板
可用 section
@section('title') - 页面标题
@section('head') - 额外 <head> 内容JS/CSS
@section('body-class') - <body> 额外 class
@section('body-data') - <body> x-data 等属性
@section('nav-icon') - 导航栏图标 emoji 🌟)
@section('nav-title') - 导航栏标题文字
@section('nav-left') - 导航栏左侧额外内容(如返回按钮)
@section('nav-right') - 导航栏右侧操作区
@section('content') - 页面主体内容
@section('scripts') - 页面底部脚本
@author ChatRoom Laravel
--}}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>@yield('title', '飘落流星聊天室')</title>
<script src="https://cdn.tailwindcss.com"></script>
{{-- Alpine.js Intersect 插件(懒加载 x-intersect 需要,必须在主包之前加载) --}}
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/intersect@3.x.x/dist/cdn.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
<style>
/* 通用滚动条美化 */
.custom-scrollbar::-webkit-scrollbar {
width: 8px;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background-color: #cbd5e1;
border-radius: 4px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background-color: transparent;
}
</style>
@yield('head')
</head>
<body class="bg-gray-100 min-h-screen text-gray-800 text-sm flex flex-col @yield('body-class')" @yield('body-data')>
{{-- ═══════════ 顶部导航栏(统一风格) ═══════════ --}}
<header class="bg-indigo-900 border-b border-indigo-800 text-white shadow-md sticky top-0 z-50 shrink-0">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex items-center justify-between h-14">
{{-- 左侧:返回 + 标题 --}}
<div class="flex items-center space-x-4">
@hasSection('nav-left')
@yield('nav-left')
<div class="h-6 w-px bg-indigo-700"></div>
@endif
<div class="flex items-center space-x-2">
<span class="text-xl">@yield('nav-icon', '🌟')</span>
<h1 class="text-lg font-extrabold tracking-wider">@yield('nav-title', '飘落流星')</h1>
</div>
</div>
{{-- 右侧:操作区 --}}
<div class="flex items-center space-x-3 text-sm">
{{-- 公共导航链接 --}}
<a href="{{ route('rooms.index') }}"
class="text-indigo-200 hover:text-white font-bold flex items-center transition hidden sm:flex">
大厅
</a>
<a href="{{ route('leaderboard.index') }}"
class="text-yellow-400 hover:text-yellow-300 font-bold flex items-center transition hidden sm:flex">
风云榜
</a>
<a href="{{ route('leaderboard.today') }}"
class="text-green-400 hover:text-green-300 font-bold flex items-center transition hidden sm:flex">
今日榜
</a>
<a href="{{ route('duty-hall.index') }}"
class="text-purple-300 hover:text-purple-100 font-bold flex items-center transition hidden sm:flex {{ request()->routeIs('duty-hall.*') ? 'text-purple-100 underline underline-offset-4' : '' }}">
勤务台
</a>
<a href="{{ route('guestbook.index') }}"
class="text-indigo-200 hover:text-white font-bold flex items-center transition hidden sm:flex">
留言板
</a>
<a href="{{ route('changelog.index') }}"
class="text-purple-300 hover:text-purple-100 font-bold flex items-center transition hidden sm:flex {{ request()->routeIs('changelog.*') ? 'text-purple-100 underline underline-offset-4' : '' }}">
更新日志
</a>
<a href="{{ route('feedback.index') }}"
class="text-sky-300 hover:text-sky-100 font-bold flex items-center transition hidden sm:flex {{ request()->routeIs('feedback.*') ? 'text-sky-100 underline underline-offset-4' : '' }}">
用户反馈
</a>
{{-- 通用快捷操作区 --}}
@auth
<a href="{{ route('guide') }}"
class="text-indigo-200 hover:text-white font-bold transition hidden sm:flex items-center">
说明
</a>
@if (Auth::user()->user_level >= 15)
<a href="{{ route('admin.dashboard') }}"
class="text-indigo-200 hover:text-white font-bold transition hidden sm:flex items-center">
后台
</a>
@endif
@endauth
@yield('nav-right')
{{-- 用户信息(通用)及退出 --}}
@auth
<div class="flex items-center space-x-3 ml-2 pl-2 border-l border-indigo-700">
<div class="flex items-center space-x-2">
<img src="/images/headface/{{ Auth::user()->headface ?? '01.gif' }}"
class="w-7 h-7 rounded border border-indigo-500 object-cover bg-white">
<span class="font-bold hidden sm:inline">{{ Auth::user()->username }}</span>
<span
class="bg-white/20 px-1.5 py-0.5 rounded text-xs border border-white/10">LV.{{ Auth::user()->user_level }}</span>
</div>
<form action="{{ route('logout') }}" method="POST" class="inline">
@csrf
<button type="submit"
class="text-xs border border-white/30 hover:bg-white/10 px-2 py-1.5 rounded transition font-medium text-white/80 hover:text-white">
退出登录
</button>
</form>
</div>
@endauth
</div>
</div>
</div>
</header>
{{-- ═══════════ 全局提示消息3秒自动消失 ═══════════ --}}
@if (session('success'))
<div x-data="{ show: true }" x-show="show" x-init="setTimeout(() => show = false, 3000)"
x-transition:leave="transition ease-in duration-500" x-transition:leave-start="opacity-100 translate-y-0"
x-transition:leave-end="opacity-0 -translate-y-2" class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 mt-4">
<div
class="bg-green-100 border-l-4 border-green-500 text-green-700 p-4 rounded shadow-sm flex justify-between items-start">
<div>
<p class="font-bold">操作成功</p>
<p>{{ session('success') }}</p>
</div>
<button @click="show = false"
class="ml-4 text-green-500 hover:text-green-700 text-lg leading-none">&times;</button>
</div>
</div>
@endif
@if (session('error'))
<div x-data="{ show: true }" x-show="show" x-init="setTimeout(() => show = false, 4000)"
x-transition:leave="transition ease-in duration-500" x-transition:leave-start="opacity-100 translate-y-0"
x-transition:leave-end="opacity-0 -translate-y-2" class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 mt-4">
<div
class="bg-red-100 border-l-4 border-red-500 text-red-700 p-4 rounded shadow-sm flex justify-between items-start">
<div>
<p class="font-bold">操作失败</p>
<p>{{ session('error') }}</p>
</div>
<button @click="show = false"
class="ml-4 text-red-400 hover:text-red-600 text-lg leading-none">&times;</button>
</div>
</div>
@endif
{{-- ═══════════ 页面主体内容 ═══════════ --}}
<div class="flex-1">
@yield('content')
</div>
{{-- ═══════════ 底部页脚(版权信息) ═══════════ --}}
@php
/* 取第一个房间的创建年份作为网站创建时间 */
$siteStartYear = \App\Models\Room::min('created_at')
? \Carbon\Carbon::parse(\App\Models\Room::min('created_at'))->year
: date('Y');
$copyrightYear = $siteStartYear == date('Y') ? $siteStartYear : $siteStartYear . ' ' . date('Y');
@endphp
<footer class="border-t border-gray-200 bg-white mt-auto">
<div
class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6 flex flex-col sm:flex-row items-center justify-between gap-2 text-xs text-gray-400">
<span>© {{ $copyrightYear }} <span class="font-semibold text-gray-500">飘落流星聊天室</span>. All rights
reserved.</span>
<span class="flex items-center gap-3">
<span>Powered by <span class="font-medium text-indigo-500">Laravel 12</span> · <span
class="font-medium text-indigo-500">Reverb</span></span>
</span>
</div>
</footer>
{{-- ═══════════ 底部脚本 ═══════════ --}}
@yield('scripts')
</body>
</html>