evaluations.html 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. {% extends "base.html" %}
  2. {% block title %}知识评价 - 满意错题本{% endblock %}
  3. {% block page_title %}知识评价{% endblock %}
  4. {% block content %}
  5. <div class="flex items-center justify-between mb-6">
  6. <div class="flex items-center gap-4">
  7. <div class="w-10 h-10 rounded-xl bg-amber-100 flex items-center justify-center text-amber-600">
  8. <i class="fa-solid fa-clipboard-check text-lg"></i>
  9. </div>
  10. <div>
  11. <p class="text-sm font-semibold text-gray-700">知识评价</p>
  12. <p class="text-xs text-gray-400 mt-0.5">记录老师定期评价结果</p>
  13. </div>
  14. </div>
  15. <button id="btnAddEval" class="inline-flex items-center gap-2 bg-amber-500 text-white px-5 py-2.5 rounded-xl text-sm font-medium hover:bg-amber-600 transition shadow-sm">
  16. <i class="fa-solid fa-plus"></i>新增评价
  17. </button>
  18. </div>
  19. <!-- Subject filter -->
  20. <div class="bg-white rounded-2xl border border-gray-100 p-3 mb-6 flex items-center gap-2 overflow-x-auto">
  21. <a href="/evaluations" class="flex-shrink-0 px-4 py-1.5 rounded-xl text-xs font-medium transition {% if not current_subject %}bg-amber-500 text-white shadow-sm{% else %}bg-gray-50 text-gray-500 hover:bg-gray-100{% endif %}">全部</a>
  22. {% for s in subjects %}
  23. <a href="/evaluations?subject_id={{ s.id }}" class="flex-shrink-0 px-4 py-1.5 rounded-xl text-xs font-medium transition {% if current_subject == s.id %}bg-amber-500 text-white shadow-sm{% else %}bg-gray-50 text-gray-500 hover:bg-gray-100{% endif %}">
  24. <span class="w-2 h-2 rounded-full inline-block mr-1.5" style="background-color:{{ s.color }}"></span>{{ s.name }}
  25. </a>
  26. {% endfor %}
  27. </div>
  28. {% if evaluations %}
  29. <div class="space-y-3">
  30. {% for ev in evaluations %}
  31. {% set subject = subjects | selectattr('id','equalto',ev.subject_id) | first %}
  32. <div class="bg-white rounded-2xl border border-gray-100 p-5 hover:shadow-sm transition">
  33. <div class="flex items-start gap-4">
  34. {% if subject %}
  35. <span class="w-3 h-3 rounded-full mt-1.5 flex-shrink-0" style="background-color:{{ subject.color }};box-shadow:0 0 0 3px {{ subject.color }}20"></span>
  36. {% endif %}
  37. <div class="flex-1 min-w-0">
  38. <div class="flex items-center gap-3 mb-1 flex-wrap">
  39. <h4 class="font-bold text-gray-800 text-sm">{{ ev.title }}</h4>
  40. <span class="text-xs text-gray-400 bg-gray-100 px-2.5 py-0.5 rounded-full">{{ ev.date }}</span>
  41. {% if subject %}<span class="text-xs text-gray-400">· {{ subject.name }}</span>{% endif %}
  42. </div>
  43. {% if ev.content %}
  44. <p class="text-sm text-gray-500 leading-relaxed mt-2">{{ ev.content }}</p>
  45. {% endif %}
  46. {% if ev.filename %}
  47. {% set ext = ev.filename.split('.') | last | lower %}
  48. {% if ext in ['jpg','jpeg','png','webp','gif','bmp'] %}
  49. <div class="mt-3">
  50. <div class="relative rounded-xl overflow-hidden bg-gray-100 cursor-pointer group max-w-xs" onclick="openLightbox('/static/uploads/{{ ev.filename }}','{{ ev.title }}',evalLb{{ ev.id }},0)">
  51. <img src="/static/uploads/{{ ev.filename }}" class="w-full h-auto object-cover group-hover:scale-105 transition-transform duration-300" loading="lazy">
  52. <div class="absolute inset-0 bg-black/0 group-hover:bg-black/20 transition flex items-center justify-center">
  53. <i class="fa-solid fa-magnifying-glass-plus text-white opacity-0 group-hover:opacity-100 text-xl transition"></i>
  54. </div>
  55. </div>
  56. </div>
  57. {% else %}
  58. <div class="mt-3">
  59. <a href="/static/uploads/{{ ev.filename }}" target="_blank" class="inline-flex items-center gap-2 text-xs text-amber-600 hover:text-amber-700 bg-amber-50 px-3 py-1.5 rounded-lg transition">
  60. <i class="fa-solid fa-paperclip"></i>{{ ev.filename }}
  61. </a>
  62. </div>
  63. {% endif %}
  64. {% endif %}
  65. </div>
  66. <div class="flex-shrink-0 flex items-center gap-3">
  67. <span class="text-xs text-gray-300 hidden sm:block">{{ ev.upload_time.strftime('%m-%d %H:%M') }}</span>
  68. <form method="POST" action="/evaluation/{{ ev.id }}/delete" onsubmit="return confirm('确定删除此评价?')">
  69. <button type="submit" class="text-gray-300 hover:text-red-400 transition" title="删除"><i class="fa-solid fa-trash-can"></i></button>
  70. </form>
  71. </div>
  72. </div>
  73. </div>
  74. {% endfor %}
  75. </div>
  76. {% else %}
  77. <div class="bg-white rounded-2xl border border-gray-100 p-16 text-center">
  78. <div class="text-6xl text-gray-200 mb-4"><i class="fa-solid fa-clipboard"></i></div>
  79. <p class="text-gray-400 mb-6">暂无评价记录</p>
  80. <button id="btnAddEvalEmpty" class="inline-flex items-center gap-2 bg-amber-500 text-white px-6 py-3 rounded-xl text-sm font-medium hover:bg-amber-600 transition">
  81. <i class="fa-solid fa-plus"></i>新增第一条评价
  82. </button>
  83. </div>
  84. {% endif %}
  85. <!-- Modal -->
  86. <div id="evalModal" class="fixed inset-0 z-50 hidden items-center justify-center modal-overlay bg-black/40">
  87. <div class="bg-white rounded-2xl w-full max-w-lg mx-4 shadow-2xl max-h-[90vh] overflow-y-auto">
  88. <div class="px-6 py-5 border-b border-gray-100 flex items-center justify-between sticky top-0 bg-white z-10 rounded-t-2xl">
  89. <h3 class="font-bold text-gray-800">新增知识评价</h3>
  90. <button id="btnCloseEvalModal" class="text-gray-400 hover:text-gray-600"><i class="fa-solid fa-xmark text-lg"></i></button>
  91. </div>
  92. <form method="POST" action="/evaluations/add" enctype="multipart/form-data" class="p-6 space-y-5">
  93. <div>
  94. <label class="block text-sm font-medium text-gray-600 mb-2">科目 <span class="text-red-400">*</span></label>
  95. <select name="subject_id" required class="w-full border border-gray-200 rounded-xl px-4 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-amber-300 focus:border-amber-400 transition bg-white">
  96. <option value="">请选择科目</option>
  97. {% for s in subjects %}
  98. <option value="{{ s.id }}">{{ s.name }}</option>
  99. {% endfor %}
  100. </select>
  101. </div>
  102. <div class="grid grid-cols-2 gap-4">
  103. <div>
  104. <label class="block text-sm font-medium text-gray-600 mb-2">评价日期</label>
  105. <input type="date" name="date" required value="{{ today }}"
  106. class="w-full border border-gray-200 rounded-xl px-4 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-amber-300 transition">
  107. </div>
  108. <div>
  109. <label class="block text-sm font-medium text-gray-600 mb-2">标题</label>
  110. <input type="text" name="title" required placeholder="简要描述评价内容"
  111. class="w-full border border-gray-200 rounded-xl px-4 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-amber-300 transition">
  112. </div>
  113. </div>
  114. <div>
  115. <label class="block text-sm font-medium text-gray-600 mb-2">详细内容</label>
  116. <textarea name="content" rows="4" placeholder="描述老师的评价内容、知识点掌握情况、改进建议等..."
  117. class="w-full border border-gray-200 rounded-xl px-4 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-amber-300 transition resize-none"></textarea>
  118. </div>
  119. <div>
  120. <label class="block text-sm font-medium text-gray-600 mb-2">附件(可选)</label>
  121. <input type="file" name="file" accept=".pdf,.jpg,.jpeg,.png,.webp"
  122. class="w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-xl file:border-0 file:text-sm file:font-medium file:bg-amber-50 file:text-amber-600 hover:file:bg-amber-100">
  123. </div>
  124. <div class="flex gap-3 pt-2">
  125. <button type="button" id="btnCancelEval" class="flex-1 border border-gray-200 text-gray-600 py-2.5 rounded-xl text-sm font-medium hover:bg-gray-50 transition">取消</button>
  126. <button type="submit" class="flex-1 bg-amber-500 text-white py-2.5 rounded-xl text-sm font-medium hover:bg-amber-600 transition">保存评价</button>
  127. </div>
  128. </form>
  129. </div>
  130. </div>
  131. {% endblock %}
  132. {% block extra_js %}
  133. <script>
  134. (function(){
  135. var modal = document.getElementById('evalModal');
  136. function openModal(){ if(modal) modal.classList.remove('hidden'); }
  137. function closeModal(){ if(modal) modal.classList.add('hidden'); }
  138. var btns = document.querySelectorAll('#btnAddEval, #btnAddEvalEmpty');
  139. var closeBtns = document.querySelectorAll('#btnCloseEvalModal, #btnCancelEval');
  140. btns.forEach(function(b){ if(b) b.addEventListener('click', openModal); });
  141. closeBtns.forEach(function(b){ if(b) b.addEventListener('click', closeModal); });
  142. if(modal){
  143. modal.addEventListener('click', function(e){
  144. if(e.target === modal) closeModal();
  145. });
  146. }
  147. var today = new Date().toISOString().slice(0,10);
  148. document.querySelectorAll('input[name="date"]').forEach(function(el){
  149. if(!el.value) el.value = today;
  150. });
  151. })();
  152. </script>
  153. {% endblock %}