|
|
@@ -49,43 +49,80 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
-<!-- Upload form -->
|
|
|
-<div class="bg-white rounded-2xl border border-dashed border-gray-200 p-5 mb-6">
|
|
|
- <h4 class="font-semibold text-gray-700 text-sm mb-3"><i class="fa-solid fa-cloud-arrow-up mr-2 text-primary"></i>上传错题图片</h4>
|
|
|
- <form method="POST" action="/subject/{{ subject.id }}/upload" enctype="multipart/form-data" id="uploadForm">
|
|
|
- <input type="hidden" name="date" value="{{ current_date }}">
|
|
|
- <div class="flex items-start gap-4">
|
|
|
- <label class="flex-1 cursor-pointer">
|
|
|
- <div class="border-2 border-dashed border-gray-200 rounded-xl p-6 text-center hover:border-primary/50 hover:bg-indigo-50/30 transition" id="dropZone">
|
|
|
- <i class="fa-solid fa-images text-2xl text-gray-300 mb-2 block"></i>
|
|
|
- <p class="text-sm text-gray-500">点击选择或拖拽图片到此</p>
|
|
|
- <p class="text-xs text-gray-300 mt-1">支持 JPG、PNG、WEBP,可多选</p>
|
|
|
- <input type="file" name="files" multiple accept="image/*" class="hidden" id="fileInput" onchange="updateFileLabel()">
|
|
|
+<!-- Input tabs -->
|
|
|
+<div class="bg-white rounded-2xl border border-gray-100 p-5 mb-6">
|
|
|
+ <div class="flex gap-2 mb-5">
|
|
|
+ <button id="tabImage" onclick="switchTab('image')" class="tab-btn flex-1 py-2.5 rounded-xl text-sm font-medium transition bg-primary text-white shadow-sm">
|
|
|
+ <i class="fa-solid fa-image mr-1.5"></i>上传错题图片
|
|
|
+ </button>
|
|
|
+ <button id="tabText" onclick="switchTab('text')" class="tab-btn flex-1 py-2.5 rounded-xl text-sm font-medium transition bg-gray-100 text-gray-500 hover:bg-gray-50">
|
|
|
+ <i class="fa-solid fa-keyboard mr-1.5"></i>文字录���错题
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Image upload panel -->
|
|
|
+ <div id="panelImage">
|
|
|
+ <form method="POST" action="/subject/{{ subject.id }}/upload" enctype="multipart/form-data" id="uploadForm">
|
|
|
+ <input type="hidden" name="date" value="{{ current_date }}">
|
|
|
+ <div class="flex items-start gap-4">
|
|
|
+ <label class="flex-1 cursor-pointer">
|
|
|
+ <div class="border-2 border-dashed border-gray-200 rounded-xl p-6 text-center hover:border-primary/50 hover:bg-indigo-50/30 transition" id="dropZone">
|
|
|
+ <i class="fa-solid fa-images text-2xl text-gray-300 mb-2 block"></i>
|
|
|
+ <p class="text-sm text-gray-500">点击选择或拖拽图片到此</p>
|
|
|
+ <p class="text-xs text-gray-300 mt-1">支持 JPG、PNG、WEBP,可多选</p>
|
|
|
+ <input type="file" name="files" multiple accept="image/*" class="hidden" id="fileInput" onchange="updateFileLabel()">
|
|
|
+ </div>
|
|
|
+ </label>
|
|
|
+ <button type="submit" class="flex-shrink-0 bg-primary text-white px-5 py-2.5 rounded-xl text-sm font-medium hover:bg-indigo-600 transition shadow-sm">
|
|
|
+ <i class="fa-solid fa-upload mr-1.5"></i>上传
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ <p id="fileLabel" class="text-xs text-primary mt-2 min-h-[18px]"></p>
|
|
|
+ </form>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Text input panel -->
|
|
|
+ <div id="panelText" class="hidden">
|
|
|
+ <form method="POST" action="/subject/{{ subject.id }}/text">
|
|
|
+ <input type="hidden" name="date" value="{{ current_date }}">
|
|
|
+ <div class="space-y-4">
|
|
|
+ <div>
|
|
|
+ <label class="block text-sm font-medium text-gray-600 mb-1.5">错题题目 <span class="text-red-400">*</span></label>
|
|
|
+ <textarea name="question" required rows="3" placeholder="请输入错题的题目内容..."
|
|
|
+ class="w-full border border-gray-200 rounded-xl px-4 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-primary/30 focus:border-primary transition resize-none"></textarea>
|
|
|
</div>
|
|
|
- </label>
|
|
|
- <button type="submit" class="flex-shrink-0 bg-primary text-white px-5 py-2.5 rounded-xl text-sm font-medium hover:bg-indigo-600 transition shadow-sm">
|
|
|
- <i class="fa-solid fa-upload mr-1.5"></i>上传
|
|
|
- </button>
|
|
|
- </div>
|
|
|
- <p id="fileLabel" class="text-xs text-primary mt-2 min-h-[18px]"></p>
|
|
|
- </form>
|
|
|
+ <div>
|
|
|
+ <label class="block text-sm font-medium text-gray-600 mb-1.5">正确答案 / 解析</label>
|
|
|
+ <textarea name="answer" rows="2" placeholder="请输入正确答案或解题思路..."
|
|
|
+ class="w-full border border-gray-200 rounded-xl px-4 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-primary/30 focus:border-primary transition resize-none"></textarea>
|
|
|
+ </div>
|
|
|
+ <div class="flex justify-end">
|
|
|
+ <button type="submit" class="bg-primary text-white px-6 py-2.5 rounded-xl text-sm font-medium hover:bg-indigo-600 transition shadow-sm">
|
|
|
+ <i class="fa-solid fa-check mr-1.5"></i>保存错题
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </form>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
|
|
|
-<!-- Image grid -->
|
|
|
-{% if images %}
|
|
|
+<!-- Content grid -->
|
|
|
+{% set text_errors = images | selectattr('filename', 'equalto', '') | list %}
|
|
|
+{% set img_errors = images | selectattr('filename', 'notequalto', '') | list %}
|
|
|
+{% if img_errors or text_errors %}
|
|
|
<div class="mb-4 flex items-center justify-between">
|
|
|
- <h3 class="font-semibold text-gray-700 text-sm">{{ current_date }} 的错题({{ images|length }} 张)</h3>
|
|
|
- <span class="text-xs text-gray-400">点击可放大查看</span>
|
|
|
+ <h3 class="font-semibold text-gray-700 text-sm">{{ current_date }} 的错题({{ images|length }} 条)</h3>
|
|
|
+ <span class="text-xs text-gray-400">{% if img_errors %}{{ img_errors|length }} 张图片{% if text_errors %} · {{ text_errors|length }} 条文字{% endif %}{% else %}{{ text_errors|length }} 条文字记录{% endif %}</span>
|
|
|
</div>
|
|
|
<div class="grid grid-cols-4 gap-4">
|
|
|
- {% for img in images %}
|
|
|
+ {% for img in img_errors %}
|
|
|
<div class="image-card group relative aspect-[3/4] rounded-2xl overflow-hidden bg-gray-100 shadow-sm cursor-pointer" onclick="openLightbox('/static/uploads/{{ img.filename }}','{{ img.original_name }}',lbAllImages,{{ loop.index0 }})">
|
|
|
<img src="/static/uploads/{{ img.filename }}" class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300" loading="lazy">
|
|
|
<div class="image-actions absolute inset-0 bg-black/30 opacity-0 transition flex items-center justify-center gap-2">
|
|
|
<button onclick="event.stopPropagation();openLightbox('/static/uploads/{{ img.filename }}','{{ img.original_name }}',lbAllImages,{{ loop.index0 }})" class="w-9 h-9 rounded-full bg-white/90 flex items-center justify-center text-gray-700 hover:bg-white transition" title="查看">
|
|
|
<i class="fa-solid fa-magnifying-glass-plus text-sm"></i>
|
|
|
</button>
|
|
|
- <form method="POST" action="/image/{{ img.id }}/delete" onsubmit="return confirm('确定删除这张错题图片?')" onclick="event.stopPropagation()">
|
|
|
+ <form method="POST" action="/image/{{ img.id }}/delete" onsubmit="return confirm('确定删除这条错题记录?')" onclick="event.stopPropagation()">
|
|
|
<button type="submit" class="w-9 h-9 rounded-full bg-white/90 flex items-center justify-center text-red-500 hover:bg-red-50 transition" title="删除">
|
|
|
<i class="fa-solid fa-trash-can text-sm"></i>
|
|
|
</button>
|
|
|
@@ -96,6 +133,35 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
{% endfor %}
|
|
|
+ {% for txt in text_errors %}
|
|
|
+ <div class="image-card group relative rounded-2xl overflow-hidden bg-white shadow-sm border border-gray-100 cursor-pointer hover:shadow-md transition" onclick="toggleAnswer({{ txt.id }})">
|
|
|
+ <div class="p-4 flex flex-col h-full">
|
|
|
+ <div class="flex items-center gap-2 mb-2">
|
|
|
+ <span class="w-2 h-2 rounded-full bg-violet-400 flex-shrink-0"></span>
|
|
|
+ <span class="text-[10px] text-gray-400 flex-shrink-0">文字记录</span>
|
|
|
+ <div class="ml-auto flex gap-1 image-actions opacity-0 transition" onclick="event.stopPropagation()">
|
|
|
+ <form method="POST" action="/image/{{ txt.id }}/delete" onsubmit="return confirm('确定删除这条错题记录?')">
|
|
|
+ <button type="submit" class="w-7 h-7 rounded-full bg-gray-100 hover:bg-red-50 flex items-center justify-center text-gray-400 hover:text-red-500 transition" title="删除">
|
|
|
+ <i class="fa-solid fa-trash-can text-xs"></i>
|
|
|
+ </button>
|
|
|
+ </form>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <p class="text-sm text-gray-800 font-medium leading-relaxed flex-1 {{ 'line-clamp-4' if not txt.answer else 'line-clamp-3' }}">{{ txt.question }}</p>
|
|
|
+ {% if txt.answer %}
|
|
|
+ <div id="answer-{{ txt.id }}" class="hidden mt-3 pt-3 border-t border-gray-100">
|
|
|
+ <p class="text-xs text-gray-500 leading-relaxed">{{ txt.answer }}</p>
|
|
|
+ </div>
|
|
|
+ {% endif %}
|
|
|
+ </div>
|
|
|
+ <div class="px-4 py-2.5 bg-gray-50 border-t border-gray-100 flex items-center justify-between">
|
|
|
+ <span class="text-[10px] text-gray-400">点击{%'展开解析{% if txt.answer %} / 收起解析{% endif %}'%}</span>
|
|
|
+ {% if txt.answer %}
|
|
|
+ <span class="text-[10px] text-primary flex items-center gap-1"><i class="fa-solid fa-lightbulb"></i>有解析</span>
|
|
|
+ {% endif %}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ {% endfor %}
|
|
|
</div>
|
|
|
{% else %}
|
|
|
<div class="bg-white rounded-2xl border border-gray-100 p-12 text-center">
|
|
|
@@ -107,6 +173,31 @@
|
|
|
|
|
|
{% block extra_js %}
|
|
|
<script>
|
|
|
+function switchTab(tab) {
|
|
|
+ var pi = document.getElementById('panelImage');
|
|
|
+ var pt = document.getElementById('panelText');
|
|
|
+ var bi = document.getElementById('tabImage');
|
|
|
+ var bt = document.getElementById('tabText');
|
|
|
+ if (tab === 'text') {
|
|
|
+ pi.classList.add('hidden');
|
|
|
+ pt.classList.remove('hidden');
|
|
|
+ bi.classList.remove('bg-primary','text-white','shadow-sm');
|
|
|
+ bi.classList.add('bg-gray-100','text-gray-500','hover:bg-gray-50');
|
|
|
+ bt.classList.remove('bg-gray-100','text-gray-500','hover:bg-gray-50');
|
|
|
+ bt.classList.add('bg-primary','text-white','shadow-sm');
|
|
|
+ } else {
|
|
|
+ pt.classList.add('hidden');
|
|
|
+ pi.classList.remove('hidden');
|
|
|
+ bt.classList.remove('bg-primary','text-white','shadow-sm');
|
|
|
+ bt.classList.add('bg-gray-100','text-gray-500','hover:bg-gray-50');
|
|
|
+ bi.classList.remove('bg-gray-100','text-gray-500','hover:bg-gray-50');
|
|
|
+ bi.classList.add('bg-primary','text-white','shadow-sm');
|
|
|
+ }
|
|
|
+}
|
|
|
+function toggleAnswer(id) {
|
|
|
+ var el = document.getElementById('answer-' + id);
|
|
|
+ if (el) el.classList.toggle('hidden');
|
|
|
+}
|
|
|
const lbAllImages = [
|
|
|
{% for img in images %}
|
|
|
"/static/uploads/{{ img.filename }}",
|