timeline_new.html 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. {% extends "base.html" %}
  2. {% block title %}时间线 New - 满意错题本{% endblock %}
  3. {% block page_title %}时间线 New{% endblock %}
  4. {% block extra_css %}
  5. <style>
  6. .tl-node { position: relative; width: 7.5rem; flex-shrink: 0; display: flex; flex-direction: column; align-items: center; text-decoration: none; }
  7. .tl-thumb { width: 4.5rem; height: 5rem; border-radius: 0.75rem; overflow: hidden; background: #f3f4f6; border: 1px solid #e5e7eb; margin-bottom: 0.65rem; position: relative; flex-shrink: 0; display: flex; align-items: center; justify-content: center; transition: box-shadow 0.15s, border-color 0.15s; }
  8. .tl-thumb img { width: 100%; height: 100%; object-fit: cover; }
  9. .tl-thumb-text { font-size: 10px; color: #6b7280; padding: 0.4rem 0.5rem; line-height: 1.4; text-align: center; word-break: break-word; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
  10. .tl-badge { position: absolute; top: 4px; right: 4px; min-width: 16px; height: 16px; padding: 0 4px; border-radius: 9999px; background: rgba(99,102,241,0.9); color: #fff; font-size: 9px; line-height: 16px; text-align: center; z-index: 2; }
  11. .tl-dot { width: 14px; height: 14px; border-radius: 9999px; background: #fff; border: 3px solid #d1d5db; z-index: 1; flex-shrink: 0; transition: all 0.15s; }
  12. .tl-line { position: absolute; left: 0; right: 0; top: calc(5rem + 0.65rem + 7px - 1px); height: 2px; background: #e5e7eb; pointer-events: none; }
  13. .tl-date { margin-top: 0.5rem; }
  14. .tl-node:hover .tl-dot { border-color: #6366f1; }
  15. .tl-node:hover .tl-thumb { border-color: #c7d2fe; }
  16. .tl-node.active .tl-dot { background: #6366f1; border-color: #6366f1; box-shadow: 0 0 0 4px rgba(99,102,241,0.15); }
  17. .tl-node.active .tl-thumb { border-color: #6366f1; box-shadow: 0 4px 14px rgba(99,102,241,0.25); }
  18. .tl-node.active .tl-week { color: #4f46e5; font-weight: 700; }
  19. </style>
  20. {% endblock %}
  21. {% block content %}
  22. <!-- 统计 + 跳转 -->
  23. <div class="bg-white rounded-2xl border border-gray-100 p-4 mb-4 flex flex-wrap items-center gap-3">
  24. <div class="flex flex-wrap items-center gap-2 text-xs text-gray-500">
  25. <span class="px-2.5 py-1 rounded-lg bg-gray-50 border border-gray-100"><b class="text-gray-700">{{ nodes|length }}</b> 天</span>
  26. <span class="px-2.5 py-1 rounded-lg bg-gray-50 border border-gray-100"><b class="text-gray-700">{{ total_records }}</b> 条记录</span>
  27. <span class="px-2.5 py-1 rounded-lg bg-gray-50 border border-gray-100"><b class="text-gray-700">{{ total_images }}</b> 张错题</span>
  28. <span class="px-2.5 py-1 rounded-lg bg-gray-50 border border-gray-100"><b class="text-gray-700">{{ total_texts }}</b> 条文字</span>
  29. </div>
  30. <div class="ml-auto flex items-center gap-2">
  31. <label class="text-xs text-gray-400">跳转日期</label>
  32. <input type="date" value="{{ current_date }}" onchange="location.href='/timeline-new?date='+this.value"
  33. class="border border-gray-200 rounded-xl px-3 py-1.5 text-xs focus:outline-none focus:ring-2 focus:ring-primary/30 text-gray-600">
  34. <a href="/timeline" class="text-xs text-gray-400 hover:text-primary px-2.5 py-1.5 rounded-lg hover:bg-gray-50 transition flex items-center gap-1"><i class="fa-solid fa-clock-rotate-left"></i>旧时间线</a>
  35. </div>
  36. </div>
  37. {% if nodes %}
  38. <!-- 时间轴 -->
  39. <div class="bg-white rounded-2xl border border-gray-100 overflow-hidden mb-6">
  40. <div class="px-5 pt-4 pb-2 flex items-center justify-between">
  41. <h3 class="text-sm font-semibold text-gray-700">时间轴</h3>
  42. <span class="text-xs text-gray-400">点击节点查看该日记录</span>
  43. </div>
  44. <div class="tl-scroll overflow-x-auto">
  45. <div class="tl-track relative min-w-max px-8 py-5">
  46. <div class="tl-line"></div>
  47. <div class="relative flex gap-2">
  48. {% for node in nodes|reverse %}
  49. <a href="/timeline-new?date={{ node.date }}" class="tl-node {% if node.active %}active{% endif %}">
  50. <div class="tl-thumb">
  51. {% if node.preview %}
  52. <img src="/static/uploads/{{ node.preview.filename }}" alt="{{ node.preview.original_name }}" loading="lazy">
  53. {% elif node.preview_text %}
  54. <span class="tl-thumb-text">{{ node.preview_text.question[:24] }}</span>
  55. {% endif %}
  56. {% if node.total %}
  57. <span class="tl-badge">{{ node.total }}</span>
  58. {% endif %}
  59. </div>
  60. <div class="tl-dot"></div>
  61. <div class="tl-date">
  62. <div class="text-xs font-medium text-gray-600">{{ node.date[5:] }}</div>
  63. <div class="tl-week text-[10px] text-gray-400 mt-0.5">{{ node.week }}</div>
  64. </div>
  65. </a>
  66. {% endfor %}
  67. </div>
  68. </div>
  69. </div>
  70. </div>
  71. <!-- 当前节点详情 -->
  72. {% if active_node %}
  73. <section class="mb-6">
  74. <div class="flex flex-wrap items-center gap-2 mb-4">
  75. <i class="fa-solid fa-location-dot text-primary text-sm"></i>
  76. <h3 class="text-sm font-semibold text-gray-700">{{ active_node.date }}({{ active_node.week }})记录</h3>
  77. <span class="text-xs text-gray-400">共 {{ active_node.total }} 条</span>
  78. <div class="ml-auto flex items-center gap-2 text-xs">
  79. {% if prev_date %}<a href="/timeline-new?date={{ prev_date }}" class="px-2 py-1 rounded-lg bg-gray-50 text-gray-500 hover:bg-gray-100 transition">前一天</a>{% endif %}
  80. {% if next_date %}<a href="/timeline-new?date={{ next_date }}" class="px-2 py-1 rounded-lg bg-gray-50 text-gray-500 hover:bg-gray-100 transition">后一天</a>{% endif %}
  81. </div>
  82. </div>
  83. <div class="space-y-5">
  84. {% for item in active_node['items'] %}
  85. <div class="bg-white rounded-2xl shadow-sm border border-gray-100 overflow-hidden">
  86. <div class="px-5 py-3.5 flex items-center gap-3 border-b border-gray-50">
  87. <span class="w-3 h-3 rounded-full flex-shrink-0" style="background-color:{{ item.subject.color }};box-shadow:0 0 0 3px {{ item.subject.color }}20"></span>
  88. <span class="font-bold text-gray-800 text-sm">{{ item.subject.name }}</span>
  89. <span class="text-xs text-gray-400 ml-auto">{{ item.images|length }} 张错题 · {{ item.texts|length }} 条文字</span>
  90. <a href="/subject/{{ item.subject.id }}?date={{ active_node.date }}" class="text-xs text-primary hover:underline ml-2 flex items-center gap-1 flex-shrink-0"><i class="fa-solid fa-arrow-right"></i>进入科目</a>
  91. </div>
  92. <div class="p-4">
  93. {% if item.images %}
  94. <div class="grid grid-cols-5 gap-3">
  95. {% for img in item.images %}
  96. <div class="group relative aspect-[3/4] rounded-xl overflow-hidden bg-gray-100 cursor-pointer" onclick="openLightbox('/static/uploads/{{ img.filename }}','{{ img.original_name }}',lbImgNew{{ item.subject.id }},{{ loop.index0 }})">
  97. <img src="/static/uploads/{{ img.filename }}" class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300" loading="lazy">
  98. <div class="absolute inset-0 bg-black/0 group-hover:bg-black/20 transition"></div>
  99. <div class="absolute bottom-1.5 left-1.5 right-1.5">
  100. <p class="text-[10px] text-white/80 truncate px-1 py-0.5 bg-black/30 rounded">{{ img.original_name }}</p>
  101. </div>
  102. </div>
  103. {% endfor %}
  104. </div>
  105. {% endif %}
  106. {% if item.texts %}
  107. <div class="{% if item.images %}border-t border-gray-50 pt-3 mt-3{% endif %}">
  108. <div class="text-xs text-gray-400 mb-2 flex items-center gap-1"><i class="fa-solid fa-keyboard"></i>{{ item.texts|length }} 条文字记录</div>
  109. <div class="grid grid-cols-5 gap-3">
  110. {% for txt in item.texts %}
  111. <div class="rounded-xl overflow-hidden bg-violet-50 border border-violet-100 cursor-pointer hover:shadow-sm transition" onclick="toggleTimelineAnswer(this)">
  112. <div class="p-3">
  113. <p class="text-xs font-medium text-gray-700 line-clamp-2 leading-relaxed">{{ txt.question[:40] }}</p>
  114. </div>
  115. <div class="timeline-answer hidden px-3 pb-3">
  116. <p class="text-[11px] text-gray-500 leading-relaxed">{{ txt.answer }}</p>
  117. </div>
  118. </div>
  119. {% endfor %}
  120. </div>
  121. </div>
  122. {% endif %}
  123. </div>
  124. </div>
  125. {% endfor %}
  126. </div>
  127. </section>
  128. {% else %}
  129. <div class="bg-white rounded-2xl border border-gray-100 p-12 text-center">
  130. <div class="text-5xl text-gray-200 mb-4"><i class="fa-regular fa-calendar-xmark"></i></div>
  131. <p class="text-gray-400 mb-2">该日期暂无错题记录</p>
  132. <p class="text-sm text-gray-300">点击上方时间轴节点或跳转其他日期</p>
  133. </div>
  134. {% endif %}
  135. {% else %}
  136. <div class="bg-white rounded-2xl border border-gray-100 p-16 text-center">
  137. <div class="text-6xl text-gray-200 mb-4"><i class="fa-regular fa-calendar-xmark"></i></div>
  138. <p class="text-gray-400 mb-2">暂无错题记录</p>
  139. <p class="text-sm text-gray-300">前往科目页上传错题后,时间轴将自动显示节点</p>
  140. </div>
  141. {% endif %}
  142. {% endblock %}
  143. {% block extra_js %}
  144. <script>
  145. {% if active_node %}
  146. {% for item in active_node['items'] %}
  147. var lbImgNew{{ item.subject.id }} = [
  148. {% for img in item.images %}"/static/uploads/{{ img.filename }}",{% endfor %}
  149. ];
  150. {% endfor %}
  151. {% endif %}
  152. function toggleTimelineAnswer(el) {
  153. var answer = el.querySelector('.timeline-answer');
  154. if (answer) answer.classList.toggle('hidden');
  155. }
  156. // 加载时将当前选中节点滚动到视野中央
  157. (function(){
  158. var wrap = document.querySelector('.tl-scroll');
  159. var active = document.querySelector('.tl-node.active');
  160. if (wrap && active) {
  161. var target = active.offsetLeft + active.offsetWidth / 2 - wrap.clientWidth / 2;
  162. wrap.scrollTo({ left: Math.max(0, target), behavior: 'smooth' });
  163. }
  164. })();
  165. </script>
  166. {% endblock %}