| <!DOCTYPE html> |
| <html lang="zh"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>{% block title %}个人博客{% endblock %}</title> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"> |
| <style> |
| :root { |
| --primary-blue: #6391C5; |
| --light-blue: #B3CFEF; |
| --warm-cream: #FEEEDA; |
| --soft-purple: #C5CDFD; |
| --text-dark: #2C3E50; |
| --bg-light: #F8FAFC; |
| --sidebar-width: 280px; |
| --header-height: 70px; |
| } |
| |
| * { |
| margin: 0; |
| padding: 0; |
| box-sizing: border-box; |
| } |
| |
| body { |
| font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; |
| line-height: 1.6; |
| color: var(--text-dark); |
| background-color: var(--bg-light); |
| min-height: 100vh; |
| } |
| |
| |
| .app-container { |
| display: flex; |
| min-height: 100vh; |
| } |
| |
| |
| .sidebar { |
| width: var(--sidebar-width); |
| height: 100vh; |
| position: fixed; |
| left: 0; |
| top: 0; |
| background: white; |
| border-right: 2px solid rgba(99, 145, 197, 0.1); |
| display: flex; |
| flex-direction: column; |
| transition: transform 0.3s ease; |
| z-index: 100; |
| } |
| |
| .sidebar-header { |
| padding: 2rem; |
| border-bottom: 2px solid rgba(99, 145, 197, 0.1); |
| } |
| |
| .logo { |
| display: flex; |
| align-items: center; |
| gap: 1rem; |
| text-decoration: none; |
| color: var(--primary-blue); |
| font-size: 1.5rem; |
| font-weight: 600; |
| } |
| |
| .logo i { |
| font-size: 2rem; |
| background: linear-gradient(135deg, var(--primary-blue), var(--light-blue)); |
| -webkit-background-clip: text; |
| -webkit-text-fill-color: transparent; |
| transition: transform 0.3s ease; |
| } |
| |
| .logo:hover i { |
| transform: rotate(-10deg); |
| } |
| |
| .sidebar-content { |
| flex: 1; |
| padding: 2rem 1rem; |
| overflow-y: auto; |
| } |
| |
| .nav-group { |
| margin-bottom: 2rem; |
| } |
| |
| .nav-group-title { |
| font-size: 0.875rem; |
| font-weight: 600; |
| color: #64748B; |
| text-transform: uppercase; |
| letter-spacing: 0.05em; |
| padding: 0 1rem; |
| margin-bottom: 1rem; |
| } |
| |
| .nav-links { |
| display: flex; |
| flex-direction: column; |
| gap: 0.5rem; |
| } |
| |
| .nav-link { |
| display: flex; |
| align-items: center; |
| gap: 1rem; |
| padding: 0.875rem 1rem; |
| color: var(--text-dark); |
| text-decoration: none; |
| border-radius: 12px; |
| transition: all 0.3s ease; |
| font-weight: 500; |
| position: relative; |
| overflow: hidden; |
| } |
| |
| .nav-link::before { |
| content: ''; |
| position: absolute; |
| left: 0; |
| top: 0; |
| width: 4px; |
| height: 100%; |
| background: linear-gradient(135deg, var(--primary-blue), var(--light-blue)); |
| opacity: 0; |
| transition: opacity 0.3s ease; |
| } |
| |
| .nav-link:hover { |
| background: linear-gradient(to right, rgba(99, 145, 197, 0.1), transparent); |
| } |
| |
| .nav-link:hover::before { |
| opacity: 1; |
| } |
| |
| .nav-link i { |
| font-size: 1.25rem; |
| color: var(--primary-blue); |
| transition: transform 0.3s ease; |
| } |
| |
| .nav-link:hover i { |
| transform: translateX(4px); |
| } |
| |
| .nav-link span { |
| font-size: 1.0625rem; |
| } |
| |
| .sidebar-footer { |
| padding: 1.5rem; |
| border-top: 2px solid rgba(99, 145, 197, 0.1); |
| display: flex; |
| align-items: center; |
| gap: 1rem; |
| } |
| |
| |
| .top-header { |
| display: none; |
| position: fixed; |
| top: 0; |
| left: 0; |
| right: 0; |
| height: var(--header-height); |
| background: white; |
| border-bottom: 2px solid rgba(99, 145, 197, 0.1); |
| padding: 0 1.5rem; |
| z-index: 99; |
| } |
| |
| .header-content { |
| height: 100%; |
| display: flex; |
| align-items: center; |
| justify-content: space-between; |
| } |
| |
| .header-nav { |
| display: flex; |
| align-items: center; |
| gap: 1.5rem; |
| } |
| |
| .menu-toggle { |
| display: none; |
| background: none; |
| border: none; |
| color: var(--primary-blue); |
| font-size: 1.5rem; |
| cursor: pointer; |
| padding: 0.5rem; |
| border-radius: 8px; |
| transition: all 0.3s ease; |
| } |
| |
| .menu-toggle:hover { |
| background: rgba(99, 145, 197, 0.1); |
| } |
| |
| |
| .main-content { |
| flex: 1; |
| margin-left: var(--sidebar-width); |
| padding: 2rem; |
| position: relative; |
| min-height: 100vh; |
| } |
| |
| |
| @media (max-width: 768px) { |
| .sidebar { |
| transform: translateX(-100%); |
| } |
| |
| .sidebar.active { |
| transform: translateX(0); |
| } |
| |
| .top-header { |
| display: block; |
| } |
| |
| .menu-toggle { |
| display: block; |
| } |
| |
| .main-content { |
| margin-left: 0; |
| margin-top: var(--header-height); |
| padding: 1.5rem; |
| max-width: 100vh; |
| } |
| } |
| |
| |
| .overlay { |
| display: none; |
| position: fixed; |
| top: 0; |
| left: 0; |
| right: 0; |
| bottom: 0; |
| background: rgba(0, 0, 0, 0.5); |
| backdrop-filter: blur(4px); |
| z-index: 98; |
| opacity: 0; |
| transition: opacity 0.3s ease; |
| } |
| |
| .overlay.active { |
| display: block; |
| opacity: 1; |
| } |
| |
| |
| ::-webkit-scrollbar { |
| width: 8px; |
| } |
| |
| ::-webkit-scrollbar-track { |
| background: transparent; |
| } |
| |
| ::-webkit-scrollbar-thumb { |
| background: var(--light-blue); |
| border-radius: 4px; |
| } |
| |
| ::-webkit-scrollbar-thumb:hover { |
| background: var(--primary-blue); |
| } |
| </style> |
| {% block extra_css %}{% endblock %} |
| </head> |
| <body> |
| <div class="app-container"> |
| |
| <header class="top-header"> |
| <div class="header-content"> |
| <button class="menu-toggle" id="menuToggle"> |
| <i class="fas fa-bars"></i> |
| </button> |
| <a href="{{ url_for('main.index') }}" class="logo"> |
| <i class="fas fa-feather"></i> |
| <span>博客</span> |
| </a> |
| <nav class="header-nav"> |
| <a href="{{ url_for('main.index') }}" class="nav-link"> |
| <i class="fas fa-home"></i> |
| <span>首页</span> |
| </a> |
| {% if session.get('logged_in') %} |
| <a href="{{ url_for('admin.dashboard') }}" class="nav-link"> |
| <i class="fas fa-cog"></i> |
| <span>管理</span> |
| </a> |
| {% endif %} |
| </nav> |
| </div> |
| </header> |
|
|
| |
| <div class="overlay" id="overlay"></div> |
|
|
| |
| <aside class="sidebar" id="sidebar"> |
| <div class="sidebar-header"> |
| <a href="{{ url_for('main.index') }}" class="logo"> |
| <i class="fas fa-feather"></i> |
| <span>Wisdom Hub</span> |
| </a> |
| </div> |
|
|
| <div class="sidebar-content"> |
| <nav class="nav-group"> |
| <h3 class="nav-group-title">导航</h3> |
| <div class="nav-links"> |
| <a href="{{ url_for('main.index') }}" class="nav-link"> |
| <i class="fas fa-home"></i> |
| <span>首页</span> |
| </a> |
| {% if session.get('logged_in') %} |
| <a href="{{ url_for('admin.dashboard') }}" class="nav-link"> |
| <i class="fas fa-cog"></i> |
| <span>管理中心</span> |
| </a> |
| {% endif %} |
| </div> |
| </nav> |
| |
| {% if session.get('logged_in') %} |
| <nav class="nav-group"> |
| <h3 class="nav-group-title">内容管理</h3> |
| <div class="nav-links"> |
| <a href="{{ url_for('admin.editor') }}" class="nav-link"> |
| <i class="fas fa-edit"></i> |
| <span>写文章</span> |
| </a> |
| </div> |
| </nav> |
| {% endif %} |
| </div> |
|
|
| <div class="sidebar-footer"> |
| {% if session.get('logged_in') %} |
| <form action="{{ url_for('admin.login') }}" method="POST" style="width: 100%;"> |
| <button type="submit" class="nav-link" style="width: 100%; text-align: left; background: none; border: none; cursor: pointer;"> |
| <i class="fas fa-sign-out-alt"></i> |
| <span>退出登录</span> |
| </button> |
| </form> |
| {% else %} |
| <a href="{{ url_for('admin.login') }}" class="nav-link"> |
| <i class="fas fa-sign-in-alt"></i> |
| <span>登录</span> |
| </a> |
| {% endif %} |
| </div> |
| </aside> |
|
|
| |
| <main class="main-content"> |
| {% block content %}{% endblock %} |
| </main> |
| </div> |
|
|
| <script> |
| |
| const menuToggle = document.getElementById('menuToggle'); |
| const sidebar = document.getElementById('sidebar'); |
| const overlay = document.getElementById('overlay'); |
| |
| function toggleMenu() { |
| sidebar.classList.toggle('active'); |
| overlay.classList.toggle('active'); |
| document.body.style.overflow = sidebar.classList.contains('active') ? 'hidden' : ''; |
| } |
| |
| menuToggle.addEventListener('click', toggleMenu); |
| overlay.addEventListener('click', toggleMenu); |
| |
| |
| window.addEventListener('resize', () => { |
| if (window.innerWidth > 768 && sidebar.classList.contains('active')) { |
| sidebar.classList.remove('active'); |
| overlay.classList.remove('active'); |
| document.body.style.overflow = ''; |
| } |
| }); |
| </script> |
|
|
| {% block extra_js %}{% endblock %} |
| </body> |
| </html> |