Files
chatroom/resources/views/layouts/app.blade.php

286 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.
{{--
文件功能:前台统一布局模板
提供公共 <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"
x-data="{ mobileMenuOpen: false }">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative">
<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">
{{-- 移动端菜单按钮 --}}
<button @click="mobileMenuOpen = !mobileMenuOpen"
class="sm:hidden text-indigo-200 hover:text-white focus:outline-none p-1 transition ml-2"
aria-label="Toggle menu">
<svg x-show="!mobileMenuOpen" class="w-6 h-6" fill="none" stroke="currentColor"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 6h16M4 12h16M4 18h16"></path>
</svg>
<svg x-show="mobileMenuOpen" class="w-6 h-6" fill="none" stroke="currentColor"
viewBox="0 0 24 24" style="display: none;">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
{{-- 公共导航链接 --}}
<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('invite.leaderboard') }}"
class="text-rose-400 hover:text-rose-300 font-bold flex items-center transition hidden sm:flex {{ request()->routeIs('invite.leaderboard') ? 'text-rose-200 underline underline-offset-4' : '' }}">
邀请排行
</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="{{ Auth::user()->headface_url ?? '/images/headface/1.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 x-show="mobileMenuOpen" x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 -translate-y-2" x-transition:enter-end="opacity-100 translate-y-0"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100 translate-y-0" x-transition:leave-end="opacity-0 -translate-y-2"
@click.away="mobileMenuOpen = false"
class="sm:hidden absolute top-14 left-0 w-full bg-indigo-800 border-b border-indigo-700 shadow-xl z-50 flex flex-col pt-1 pb-3"
style="display: none;">
<a href="{{ route('rooms.index') }}"
class="px-4 py-2.5 text-indigo-100 hover:bg-indigo-700 hover:text-white font-medium border-l-4 {{ request()->routeIs('rooms.index') ? 'border-indigo-400 bg-indigo-700/50' : 'border-transparent' }}">
大厅
</a>
<a href="{{ route('invite.leaderboard') }}"
class="px-4 py-2.5 text-rose-300 hover:bg-indigo-700 hover:text-rose-200 font-medium border-l-4 {{ request()->routeIs('invite.leaderboard') ? 'border-rose-400 bg-indigo-700/50' : 'border-transparent' }}">
邀请排行
</a>
<a href="{{ route('leaderboard.index') }}"
class="px-4 py-2.5 text-yellow-300 hover:bg-indigo-700 hover:text-yellow-200 font-medium border-l-4 {{ request()->routeIs('leaderboard.index') ? 'border-yellow-400 bg-indigo-700/50' : 'border-transparent' }}">
风云榜
</a>
<a href="{{ route('leaderboard.today') }}"
class="px-4 py-2.5 text-green-300 hover:bg-indigo-700 hover:text-green-200 font-medium border-l-4 {{ request()->routeIs('leaderboard.today') ? 'border-green-400 bg-indigo-700/50' : 'border-transparent' }}">
今日榜
</a>
<a href="{{ route('duty-hall.index') }}"
class="px-4 py-2.5 text-purple-200 hover:bg-indigo-700 hover:text-white font-medium border-l-4 {{ request()->routeIs('duty-hall.*') ? 'border-purple-400 bg-indigo-700/50' : 'border-transparent' }}">
勤务台
</a>
<a href="{{ route('guestbook.index') }}"
class="px-4 py-2.5 text-indigo-100 hover:bg-indigo-700 hover:text-white font-medium border-l-4 {{ request()->routeIs('guestbook.*') ? 'border-indigo-400 bg-indigo-700/50' : 'border-transparent' }}">
留言板
</a>
<a href="{{ route('changelog.index') }}"
class="px-4 py-2.5 text-purple-200 hover:bg-indigo-700 hover:text-white font-medium border-l-4 {{ request()->routeIs('changelog.*') ? 'border-purple-400 bg-indigo-700/50' : 'border-transparent' }}">
更新日志
</a>
<a href="{{ route('feedback.index') }}"
class="px-4 py-2.5 text-sky-200 hover:bg-indigo-700 hover:text-white font-medium border-l-4 {{ request()->routeIs('feedback.*') ? 'border-sky-400 bg-indigo-700/50' : 'border-transparent' }}">
用户反馈
</a>
@auth
<div class="my-1 border-t border-indigo-700/50"></div>
<a href="{{ route('guide') }}"
class="px-4 py-2.5 text-indigo-100 hover:bg-indigo-700 hover:text-white font-medium border-l-4 border-transparent">
说明
</a>
@if (Auth::user()->user_level >= 15)
<a href="{{ route('admin.dashboard') }}"
class="px-4 py-2.5 text-amber-300 hover:bg-indigo-700 hover:text-amber-200 font-medium border-l-4 border-transparent">
后台管理
</a>
@endif
@endauth
</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>