jzyg123 commited on
Commit
70abfc9
·
verified ·
1 Parent(s): 430716a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +139 -51
app.py CHANGED
@@ -462,10 +462,22 @@ def admin():
462
  if not session.get('is_admin'):
463
  return render_template('admin.html', logged_in=False, admin_enabled=_admin_enabled())
464
 
465
- # 已登录:读取历史并过滤
466
  q = request.args.get('q', '').strip()
467
- history = load_history()
468
- entries = history
 
 
 
 
 
 
 
 
 
 
 
 
469
  if q:
470
  qlower = q.lower()
471
  def _match(e):
@@ -475,9 +487,11 @@ def admin():
475
  (e.get('studentName') or '').lower().find(qlower) != -1 or
476
  str(e.get('seid') or '').lower().find(qlower) != -1
477
  )
478
- entries = [e for e in history if _match(e)]
 
 
479
 
480
- return render_template('admin.html', logged_in=True, entries=entries, query=q)
481
 
482
 
483
  @app.route('/admin/logout')
@@ -940,58 +954,132 @@ def create_templates():
940
  </head>
941
  <body class="bg-gray-100 text-gray-800">
942
  <div class="container mx-auto p-4 md:p-6">
943
- <div class="flex justify-between items-center mb-6">
944
- <h1 class="text-2xl md:text-3xl font-bold text-gray-900">查询历史记录</h1>
945
- <a href="{{ url_for('admin_logout') }}" class="text-sm text-indigo-600 hover:underline">登出</a>
946
- </div>
947
-
948
- <div class="bg-white p-4 rounded-lg shadow-md mb-6">
949
- <form method="post" class="flex flex-col md:flex-row md:items-center gap-4">
950
- <div class="flex-grow">
951
- <label for="search" class="sr-only">搜索</label>
952
- <input type="text" name="search" id="search" value="{{ search_term or '' }}" placeholder="按姓名、用户名或考试名称搜索..." class="block w-full px-4 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500">
953
- </div>
954
- <button type="submit" class="w-full md:w-auto px-6 py-2 bg-indigo-600 text-white font-semibold rounded-md shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 transition-colors">搜索</button>
955
- </form>
956
- </div>
957
 
958
- {% if history %}
959
- <div class="overflow-x-auto bg-white rounded-lg shadow-md">
960
- <table class="min-w-full divide-y divide-gray-200">
961
- <thead class="bg-gray-50">
962
- <tr>
963
- <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">学生姓名</th>
964
- <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">用户名</th>
965
- <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">考试名称</th>
966
- <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">总分</th>
967
- <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">统考排名</th>
968
- <th scope="col" class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">操作</th>
969
- </tr>
970
- </thead>
971
- <tbody class="bg-white divide-y divide-gray-200">
972
- {% for item in history %}
973
- <tr>
974
- <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{{ item.studentName }}</td>
975
- <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ item.username }}</td>
976
- <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ item.examName }}</td>
977
- <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ item.totalScore }}</td>
978
- <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ item.unionOrder }}</td>
979
- <td class="px-6 py-4 whitespace-nowrap text-center text-sm font-medium">
980
- <a href="{{ url_for('show_report', item_id=item.id) }}" class="text-indigo-600 hover:text-indigo-900">查看报告</a>
981
- </td>
982
- </tr>
983
- {% endfor %}
984
- </tbody>
985
- </table>
986
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
987
  {% else %}
988
- <div class="text-center py-16 bg-white rounded-lg shadow-md">
989
- <svg class="mx-auto h-12 w-12 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path vector-effect="non-scaling-stroke" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" /></svg>
990
- <h3 class="mt-2 text-sm font-medium text-gray-900">暂无历史记录</h3>
991
- <p class="mt-1 text-sm text-gray-500">{% if search_term %}没有找到匹配 "<strong>{{ search_term }}</strong>" 的记录。{% else %}数据库中还没有任何查询记录。{% endif %}</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
992
  </div>
993
  {% endif %}
994
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
995
  </body>
996
  </html>
997
  """
 
462
  if not session.get('is_admin'):
463
  return render_template('admin.html', logged_in=False, admin_enabled=_admin_enabled())
464
 
465
+ # 已登录:读取、排序和过滤
466
  q = request.args.get('q', '').strip()
467
+ sort_by = request.args.get('sort_by', 'date_desc')
468
+
469
+ all_history = load_history()
470
+
471
+ # 排序逻辑
472
+ if sort_by == 'date_asc':
473
+ all_history.reverse()
474
+ elif sort_by == 'score_desc':
475
+ all_history.sort(key=lambda x: float(x.get('totalScore') or 0), reverse=True)
476
+ elif sort_by == 'score_asc':
477
+ all_history.sort(key=lambda x: float(x.get('totalScore') or 0))
478
+ # 'date_desc' 是默认顺序,无需处理
479
+
480
+ # 过滤逻辑
481
  if q:
482
  qlower = q.lower()
483
  def _match(e):
 
487
  (e.get('studentName') or '').lower().find(qlower) != -1 or
488
  str(e.get('seid') or '').lower().find(qlower) != -1
489
  )
490
+ entries = [e for e in all_history if _match(e)]
491
+ else:
492
+ entries = all_history
493
 
494
+ return render_template('admin.html', logged_in=True, history=entries, search_term=q, sort_by=sort_by)
495
 
496
 
497
  @app.route('/admin/logout')
 
954
  </head>
955
  <body class="bg-gray-100 text-gray-800">
956
  <div class="container mx-auto p-4 md:p-6">
957
+ {% if logged_in %}
958
+ <div class="flex justify-between items-center mb-6">
959
+ <h1 class="text-2xl md:text-3xl font-bold text-gray-900">查询历史记录</h1>
960
+ <a href="{{ url_for('admin_logout') }}" class="text-sm text-indigo-600 hover:underline">登出</a>
961
+ </div>
 
 
 
 
 
 
 
 
 
962
 
963
+ <!-- 搜索和排序表单 -->
964
+ <div class="bg-white p-4 rounded-lg shadow-md mb-6">
965
+ <form method="get" action="{{ url_for('admin') }}" class="grid grid-cols-1 md:grid-cols-3 gap-4 items-center">
966
+ <div class="md:col-span-2">
967
+ <label for="q" class="sr-only">搜索</label>
968
+ <input type="text" name="q" id="q" value="{{ search_term or '' }}" placeholder="按姓名、用户名或考试名称搜索..." class="block w-full px-4 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500">
969
+ </div>
970
+ <div class="grid grid-cols-2 gap-4">
971
+ <div>
972
+ <label for="sort_by" class="sr-only">排序方式</label>
973
+ <select name="sort_by" id="sort_by" class="block w-full px-4 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500">
974
+ <option value="date_desc" {% if sort_by == 'date_desc' %}selected{% endif %}>日期降序</option>
975
+ <option value="date_asc" {% if sort_by == 'date_asc' %}selected{% endif %}>日期升序</option>
976
+ <option value="score_desc" {% if sort_by == 'score_desc' %}selected{% endif %}>总分降序</option>
977
+ <option value="score_asc" {% if sort_by == 'score_asc' %}selected{% endif %}>总分升序</option>
978
+ </select>
979
+ </div>
980
+ <button type="submit" class="w-full px-6 py-2 bg-indigo-600 text-white font-semibold rounded-md shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 transition-colors">筛选</button>
981
+ </div>
982
+ </form>
 
 
 
 
 
 
 
 
983
  </div>
984
+
985
+ <!-- 历史记录卡片 -->
986
+ {% if history %}
987
+ <div class="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-6">
988
+ {% for item in history %}
989
+ <div class="bg-white rounded-xl shadow-lg overflow-hidden transform hover:-translate-y-1 transition-transform duration-300">
990
+ <div class="p-5">
991
+ <p class="text-sm text-gray-500">{{ item.studentName }}</p>
992
+ <h3 class="text-lg font-bold text-gray-800 truncate" title="{{ item.examName }}">{{ item.examName }}</h3>
993
+ <div class="mt-3 space-y-3 text-sm">
994
+ <div class="flex justify-between items-center">
995
+ <span class="font-medium text-gray-600">总分: <span class="font-bold text-indigo-600 text-base">{{ item.totalScore }}</span></span>
996
+ <span class="font-medium text-gray-600">统考排名: <span class="font-bold text-indigo-600 text-base">{{ item.unionOrder }}</span></span>
997
+ </div>
998
+ <div class="space-y-1 text-gray-700">
999
+ <div>
1000
+ 用户名:
1001
+ <code class="copy-code px-1.5 py-0.5 bg-gray-100 rounded font-mono cursor-pointer hover:bg-gray-200" title="点击复制">
1002
+ {{ item.username }}
1003
+ </code>
1004
+ </div>
1005
+ <div>
1006
+ 密码:
1007
+ <code class="copy-code px-1.5 py-0.5 bg-gray-100 rounded font-mono cursor-pointer hover:bg-gray-200" title="点击复制">
1008
+ {{ item.password }}
1009
+ </code>
1010
+ </div>
1011
+ <div>
1012
+ SEID:
1013
+ <code class="copy-code px-1.5 py-0.5 bg-gray-100 rounded font-mono cursor-pointer hover:bg-gray-200" title="点击复制">
1014
+ {{ item.seid }}
1015
+ </code>
1016
+ </div>
1017
+ </div>
1018
+ </div>
1019
+ </div>
1020
+ <div class="grid grid-cols-2 gap-px bg-gray-100">
1021
+ <a href="{{ url_for('show_report', item_id=item.id) }}" target="_blank" class="flex items-center justify-center px-4 py-3 bg-white text-sm font-medium text-indigo-600 hover:bg-indigo-50 transition-colors">查看报告</a>
1022
+ <a href="{{ url_for('print_report', item_id=item.id) }}" target="_blank" class="flex items-center justify-center px-4 py-3 bg-white text-sm font-medium text-green-600 hover:bg-green-50 transition-colors">生成PDF</a>
1023
+ </div>
1024
+ </div>
1025
+ {% endfor %}
1026
+ </div>
1027
+ {% else %}
1028
+ <div class="text-center py-16 bg-white rounded-lg shadow-md">
1029
+ <svg class="mx-auto h-12 w-12 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path vector-effect="non-scaling-stroke" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" /></svg>
1030
+ <h3 class="mt-2 text-sm font-medium text-gray-900">暂无历史记录</h3>
1031
+ <p class="mt-1 text-sm text-gray-500">{% if search_term %}没有找到匹配 "<strong>{{ search_term }}</strong>" 的记录。{% else %}数据库中还没有任何查询记录。{% endif %}</p>
1032
+ </div>
1033
+ {% endif %}
1034
  {% else %}
1035
+ <!-- 登录表单 -->
1036
+ <div class="max-w-md mx-auto mt-10">
1037
+ <div class="bg-white p-8 rounded-xl shadow-lg">
1038
+ <h1 class="text-2xl font-bold text-center mb-6">管理后台登录</h1>
1039
+ {% if not admin_enabled %}
1040
+ <div class="bg-yellow-100 border-l-4 border-yellow-500 text-yellow-700 p-4 rounded-md mb-4" role="alert">
1041
+ <p><strong>提示:</strong>后台功能已禁用。</p>
1042
+ <p>如需启用,请在环境变量中设置 <code class="font-mono bg-yellow-200 px-1 rounded">ADMIN_PASSWORD</code>。</p>
1043
+ </div>
1044
+ {% endif %}
1045
+ {% if error %}
1046
+ <div class="bg-red-100 border-l-4 border-red-500 text-red-700 p-4 rounded-md mb-4" role="alert">
1047
+ <p>{{ error }}</p>
1048
+ </div>
1049
+ {% endif %}
1050
+ <form method="post" action="{{ url_for('admin') }}">
1051
+ <div class="mb-4">
1052
+ <label for="password" class="block text-sm font-medium text-gray-700">密码</label>
1053
+ <input type="password" name="password" id="password" required class="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 transition">
1054
+ </div>
1055
+ <button type="submit" {% if not admin_enabled %}disabled{% endif %} class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors">登录</button>
1056
+ </form>
1057
+ </div>
1058
  </div>
1059
  {% endif %}
1060
  </div>
1061
+ <script>
1062
+ // 行内代码点击复制
1063
+ document.addEventListener('DOMContentLoaded', function () {
1064
+ const codes = document.querySelectorAll('.copy-code');
1065
+ codes.forEach(function (el) {
1066
+ el.addEventListener('click', function () {
1067
+ const text = el.innerText.trim();
1068
+ navigator.clipboard.writeText(text).then(function () {
1069
+ const originalTitle = el.getAttribute('title') || '';
1070
+ el.setAttribute('title', '已复制');
1071
+ el.classList.add('bg-green-100');
1072
+ setTimeout(function () {
1073
+ el.classList.remove('bg-green-100');
1074
+ el.setAttribute('title', originalTitle || '点击复制');
1075
+ }, 1200);
1076
+ }).catch(function (err) {
1077
+ console.error('无法复制: ', err);
1078
+ });
1079
+ });
1080
+ });
1081
+ });
1082
+ </script>
1083
  </body>
1084
  </html>
1085
  """