base.html 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>{% block title %}错题本{% endblock %}</title>
  7. <script src="https://cdn.tailwindcss.com"></script>
  8. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
  9. <script>
  10. tailwind.config = {
  11. theme: {
  12. extend: {
  13. colors: {
  14. primary: '#6366f1',
  15. secondary: '#8b5cf6',
  16. accent: '#f43f5e',
  17. }
  18. }
  19. }
  20. }
  21. </script>
  22. <style>
  23. body { font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif; }
  24. .sidebar-link.active { background: rgba(99,102,241,0.10); color: #6366f1; border-right: 3px solid #6366f1; }
  25. .image-card:hover .image-actions { opacity: 1; }
  26. .image-actions { opacity: 0; transition: opacity 0.2s; }
  27. ::-webkit-scrollbar { width: 6px; height: 6px; }
  28. ::-webkit-scrollbar-track { background: #f1f5f9; }
  29. ::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 3px; }
  30. .modal-overlay { backdrop-filter: blur(6px); }
  31. /* Lightbox */
  32. #lightbox { cursor: zoom-out; }
  33. #lightbox img { max-height: 85vh; max-width: 90vw; object-fit: contain; }
  34. .date-chip { transition: all 0.15s; }
  35. .date-chip:hover { transform: translateY(-1px); }
  36. </style>
  37. {% block extra_css %}{% endblock %}
  38. </head>
  39. <body class="bg-gray-50 min-h-screen">
  40. <div class="flex min-h-screen">
  41. <!-- Sidebar -->
  42. <aside class="w-56 bg-white shadow-lg fixed h-full z-20 flex flex-col">
  43. <div class="p-5 border-b border-gray-100">
  44. <div class="flex items-center gap-3">
  45. <div class="w-9 h-9 rounded-xl bg-primary flex items-center justify-center text-white text-base">
  46. <i class="fa-solid fa-book-open"></i>
  47. </div>
  48. <div>
  49. <h1 class="font-bold text-gray-800 text-sm leading-tight">满意错题本</h1>
  50. <p class="text-[10px] text-gray-400 mt-0.5">在线复习助手</p>
  51. </div>
  52. </div>
  53. </div>
  54. <nav class="flex-1 py-3">
  55. <a href="/" class="sidebar-link flex items-center gap-3 px-5 py-2.5 text-gray-500 hover:bg-gray-50 transition {% if request.url.path == '/' %}active{% endif %}">
  56. <i class="fa-solid fa-house w-5 text-center text-base"></i><span class="text-sm font-medium">首页</span>
  57. </a>
  58. <a href="/subjects" class="sidebar-link flex items-center gap-3 px-5 py-2.5 text-gray-500 hover:bg-gray-50 transition {% if '/subjects' in request.url.path %}active{% endif %}">
  59. <i class="fa-solid fa-folder w-5 text-center text-base"></i><span class="text-sm font-medium">科目管理</span>
  60. </a>
  61. <a href="/timeline" class="sidebar-link flex items-center gap-3 px-5 py-2.5 text-gray-500 hover:bg-gray-50 transition {% if '/timeline' in request.url.path %}active{% endif %}">
  62. <i class="fa-solid fa-clock-rotate-left w-5 text-center text-base"></i><span class="text-sm font-medium">时间线</span>
  63. </a>
  64. <a href="/review" class="sidebar-link flex items-center gap-3 px-5 py-2.5 text-gray-500 hover:bg-gray-50 transition {% if '/review' in request.url.path %}active{% endif %}">
  65. <i class="fa-solid fa-graduation-cap w-5 text-center text-base"></i><span class="text-sm font-medium">复习汇总</span>
  66. </a>
  67. <a href="/evaluations" class="sidebar-link flex items-center gap-3 px-5 py-2.5 text-gray-500 hover:bg-gray-50 transition {% if '/evaluations' in request.url.path %}active{% endif %}">
  68. <i class="fa-solid fa-clipboard-check w-5 text-center text-base"></i><span class="text-sm font-medium">知识评价</span>
  69. </a>
  70. </nav>
  71. <div class="p-4 border-t border-gray-100">
  72. <p class="text-[10px] text-gray-300 text-center">© 2025 满意错题本</p>
  73. </div>
  74. </aside>
  75. <!-- Main content -->
  76. <main class="flex-1 ml-56">
  77. <header class="bg-white/80 backdrop-blur border-b border-gray-100 px-8 py-3.5 flex items-center justify-between sticky top-0 z-10">
  78. <h2 class="text-base font-semibold text-gray-700">{% block page_title %}{% endblock %}</h2>
  79. <div class="text-xs text-gray-400" id="current-date"></div>
  80. </header>
  81. <div class="p-8">
  82. {% block content %}{% endblock %}
  83. </div>
  84. </main>
  85. </div>
  86. <!-- Lightbox -->
  87. <div id="lightbox" class="fixed inset-0 z-50 hidden items-center justify-center modal-overlay bg-black/70" onclick="closeLightbox(event)">
  88. <button class="absolute top-4 right-4 text-white/80 hover:text-white text-2xl z-10" onclick="closeLightbox(event)">
  89. <i class="fa-solid fa-xmark"></i>
  90. </button>
  91. <button id="lb-prev" class="absolute left-4 text-white/80 hover:text-white text-2xl hidden" onclick="navigateLightbox(-1,event)">
  92. <i class="fa-solid fa-chevron-left"></i>
  93. </button>
  94. <button id="lb-next" class="absolute right-4 text-white/80 hover:text-white text-2xl hidden" onclick="navigateLightbox(1,event)">
  95. <i class="fa-solid fa-chevron-right"></i>
  96. </button>
  97. <img id="lb-img" src="" alt="" class="rounded-lg shadow-2xl max-h-[85vh] max-w-[90vw] object-contain">
  98. <p id="lb-caption" class="absolute bottom-4 left-1/2 -translate-x-1/2 text-white/80 text-sm bg-black/40 px-4 py-1.5 rounded-full"></p>
  99. </div>
  100. <script>
  101. document.getElementById('current-date').textContent = new Date().toLocaleDateString('zh-CN', {year:'numeric',month:'long',day:'numeric',weekday:'long'});
  102. // Lightbox
  103. let lbImages = [];
  104. let lbIndex = 0;
  105. function openLightbox(src, caption, allImages, index) {
  106. lbImages = allImages || [src];
  107. lbIndex = index || 0;
  108. updateLightbox();
  109. const lb = document.getElementById('lightbox');
  110. lb.classList.remove('hidden');
  111. lb.classList.add('flex');
  112. document.body.style.overflow = 'hidden';
  113. }
  114. function closeLightbox(e) {
  115. if (e && e.target.tagName === 'IMG') return;
  116. const lb = document.getElementById('lightbox');
  117. lb.classList.add('hidden');
  118. lb.classList.remove('flex');
  119. document.body.style.overflow = '';
  120. }
  121. function navigateLightbox(dir, e) {
  122. if (e) e.stopPropagation();
  123. if (lbImages.length <= 1) return;
  124. lbIndex = (lbIndex + dir + lbImages.length) % lbImages.length;
  125. updateLightbox();
  126. }
  127. function updateLightbox() {
  128. document.getElementById('lb-img').src = lbImages[lbIndex];
  129. document.getElementById('lb-caption').textContent = lbImages[lbIndex + 1] ? `${lbIndex+1}/${lbImages.length} · ${document.getElementById('lb-img').dataset.caption || ''}` : '';
  130. document.getElementById('lb-prev').classList.toggle('hidden', lbImages.length <= 1);
  131. document.getElementById('lb-next').classList.toggle('hidden', lbImages.length <= 1);
  132. }
  133. document.addEventListener('keydown', e => {
  134. if (document.getElementById('lightbox').classList.contains('hidden')) return;
  135. if (e.key === 'ArrowLeft') navigateLightbox(-1);
  136. else if (e.key === 'ArrowRight') navigateLightbox(1);
  137. else if (e.key === 'Escape') closeLightbox(e);
  138. });
  139. </script>
  140. {% block extra_js %}{% endblock %}
  141. </body>
  142. </html>