mobile . themes
This commit is contained in:
@@ -51,9 +51,11 @@
|
||||
|
||||
<!-- 主体内容区 -->
|
||||
<main class="flex-1 min-w-0">
|
||||
<div class="bg-white/80 backdrop-blur-xl rounded-3xl shadow-sm border border-slate-100 p-6 sm:p-8 relative">
|
||||
<div class="bg-white/80 backdrop-blur-xl rounded-3xl shadow-sm border border-slate-100 p-6 sm:p-8 relative overflow-hidden">
|
||||
<div class="absolute top-0 right-0 w-64 h-64 bg-indigo-50/30 rounded-bl-full -z-10"></div>
|
||||
{% block admin_content %}{% endblock %}
|
||||
<div class="admin-content-wrapper">
|
||||
{% block admin_content %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@@ -7,16 +7,16 @@
|
||||
<h1 class="text-3xl font-extrabold bg-gradient-to-r from-blue-400 via-purple-400 to-pink-400 bg-clip-text text-transparent">杯赛管理</h1>
|
||||
|
||||
<div class="futuristic-card-dark overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table-futuristic">
|
||||
<div class="table-responsive">
|
||||
<table class="table-futuristic" style="min-width: 900px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>名称</th>
|
||||
<th>主办方</th>
|
||||
<th>状态</th>
|
||||
<th>开始日期</th>
|
||||
<th>操作</th>
|
||||
<th style="min-width: 60px;">ID</th>
|
||||
<th style="min-width: 200px;">名称</th>
|
||||
<th style="min-width: 150px;">主办方</th>
|
||||
<th style="min-width: 120px;">状态</th>
|
||||
<th style="min-width: 150px;">开始日期</th>
|
||||
<th style="min-width: 180px;">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="contests-tbody">
|
||||
@@ -56,8 +56,8 @@ async function loadContests() {
|
||||
: '<span class="text-xs text-red-500">已废止</span>';
|
||||
html += `<tr>
|
||||
<td>${c.id}</td>
|
||||
<td class="font-medium text-slate-200">${c.name}</td>
|
||||
<td class="text-slate-400">${c.organizer || '-'}</td>
|
||||
<td class="font-medium text-slate-200" style="max-width: 200px; white-space: normal; word-wrap: break-word;">${c.name}</td>
|
||||
<td class="text-slate-400" style="max-width: 150px; white-space: normal; word-wrap: break-word;">${c.organizer || '-'}</td>
|
||||
<td><span class="badge-futuristic ${statusClass}">${statusText}</span></td>
|
||||
<td class="text-slate-400">${c.start_date || '-'}</td>
|
||||
<td class="space-x-2">
|
||||
|
||||
@@ -15,16 +15,16 @@
|
||||
</div>
|
||||
|
||||
<!-- 统计卡片 -->
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 md:gap-6">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4 md:gap-6">
|
||||
<div class="futuristic-card-dark relative overflow-hidden group hover:-translate-y-1 transition-transform">
|
||||
<div class="absolute -right-4 -bottom-4 w-24 h-24 bg-cyan-500/20 rounded-full blur-2xl group-hover:bg-cyan-500/30 transition-colors"></div>
|
||||
<div class="flex items-center gap-3 mb-3 relative z-10">
|
||||
<div class="w-10 h-10 rounded-xl bg-cyan-500/20 text-cyan-400 flex items-center justify-center shadow-lg shadow-cyan-500/20 border border-cyan-500/30">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"/></svg>
|
||||
</div>
|
||||
<div class="text-sm font-bold text-slate-300">总用户数</div>
|
||||
<div class="text-xs sm:text-sm font-bold text-slate-300">总用户数</div>
|
||||
</div>
|
||||
<div id="stat-users" class="text-3xl font-black text-white relative z-10">-</div>
|
||||
<div id="stat-users" class="text-2xl sm:text-3xl font-black text-white relative z-10">-</div>
|
||||
</div>
|
||||
|
||||
<div class="futuristic-card-dark relative overflow-hidden group hover:-translate-y-1 transition-transform">
|
||||
@@ -33,9 +33,9 @@
|
||||
<div class="w-10 h-10 rounded-xl bg-purple-500/20 text-purple-400 flex items-center justify-center shadow-lg shadow-purple-500/20 border border-purple-500/30">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"/></svg>
|
||||
</div>
|
||||
<div class="text-sm font-bold text-slate-300">赛事总数</div>
|
||||
<div class="text-xs sm:text-sm font-bold text-slate-300">赛事总数</div>
|
||||
</div>
|
||||
<div id="stat-contests" class="text-3xl font-black text-white relative z-10">-</div>
|
||||
<div id="stat-contests" class="text-2xl sm:text-3xl font-black text-white relative z-10">-</div>
|
||||
</div>
|
||||
|
||||
<div class="futuristic-card-dark relative overflow-hidden group hover:-translate-y-1 transition-transform">
|
||||
@@ -44,9 +44,9 @@
|
||||
<div class="w-10 h-10 rounded-xl bg-emerald-500/20 text-emerald-400 flex items-center justify-center shadow-lg shadow-emerald-500/20 border border-emerald-500/30">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path 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>
|
||||
</div>
|
||||
<div class="text-sm font-bold text-slate-300">考试总数</div>
|
||||
<div class="text-xs sm:text-sm font-bold text-slate-300">考试总数</div>
|
||||
</div>
|
||||
<div id="stat-exams" class="text-3xl font-black text-white relative z-10">-</div>
|
||||
<div id="stat-exams" class="text-2xl sm:text-3xl font-black text-white relative z-10">-</div>
|
||||
</div>
|
||||
|
||||
<div class="futuristic-card-dark relative overflow-hidden group hover:-translate-y-1 transition-transform">
|
||||
@@ -55,9 +55,9 @@
|
||||
<div class="w-10 h-10 rounded-xl bg-pink-500/20 text-pink-400 flex items-center justify-center shadow-lg shadow-pink-500/20 border border-pink-500/30">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8h2a2 2 0 012 2v6a2 2 0 01-2 2h-2v4l-4-4H9a1.994 1.994 0 01-1.414-.586m0 0L11 14h4a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2v4l.586-.586z"/></svg>
|
||||
</div>
|
||||
<div class="text-sm font-bold text-slate-300">社区帖子</div>
|
||||
<div class="text-xs sm:text-sm font-bold text-slate-300">社区帖子</div>
|
||||
</div>
|
||||
<div id="stat-posts" class="text-3xl font-black text-white relative z-10">-</div>
|
||||
<div id="stat-posts" class="text-2xl sm:text-3xl font-black text-white relative z-10">-</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -105,34 +105,34 @@
|
||||
</div>
|
||||
<h2 class="text-lg font-bold bg-gradient-to-r from-teal-400 to-emerald-400 bg-clip-text text-transparent">快捷操作</h2>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-7 gap-4">
|
||||
<a href="/admin/contests" class="futuristic-card-dark hover:border-indigo-500/50 hover:shadow-lg hover:shadow-indigo-500/20 hover:-translate-y-1 transition-all text-center group flex flex-col items-center p-5">
|
||||
<div class="w-12 h-12 rounded-xl bg-indigo-500/20 flex items-center justify-center text-2xl mb-3 group-hover:scale-110 transition-transform border border-indigo-500/30">🏆</div>
|
||||
<div class="text-sm font-bold text-slate-300">杯赛管理</div>
|
||||
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-7 gap-3 sm:gap-4">
|
||||
<a href="/admin/contests" class="futuristic-card-dark hover:border-indigo-500/50 hover:shadow-lg hover:shadow-indigo-500/20 hover:-translate-y-1 transition-all text-center group flex flex-col items-center p-4 sm:p-5">
|
||||
<div class="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-indigo-500/20 flex items-center justify-center text-xl sm:text-2xl mb-2 sm:mb-3 group-hover:scale-110 transition-transform border border-indigo-500/30">🏆</div>
|
||||
<div class="text-xs sm:text-sm font-bold text-slate-300">杯赛管理</div>
|
||||
</a>
|
||||
<a href="/admin/contest-applications" class="futuristic-card-dark hover:border-orange-500/50 hover:shadow-lg hover:shadow-orange-500/20 hover:-translate-y-1 transition-all text-center group flex flex-col items-center p-5">
|
||||
<div class="w-12 h-12 rounded-xl bg-orange-500/20 flex items-center justify-center text-2xl mb-3 group-hover:scale-110 transition-transform border border-orange-500/30">📋</div>
|
||||
<div class="text-sm font-bold text-slate-300">杯赛申请</div>
|
||||
<a href="/admin/contest-applications" class="futuristic-card-dark hover:border-orange-500/50 hover:shadow-lg hover:shadow-orange-500/20 hover:-translate-y-1 transition-all text-center group flex flex-col items-center p-4 sm:p-5">
|
||||
<div class="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-orange-500/20 flex items-center justify-center text-xl sm:text-2xl mb-2 sm:mb-3 group-hover:scale-110 transition-transform border border-orange-500/30">📋</div>
|
||||
<div class="text-xs sm:text-sm font-bold text-slate-300">杯赛申请</div>
|
||||
</a>
|
||||
<a href="/admin/teacher-applications" class="futuristic-card-dark hover:border-purple-500/50 hover:shadow-lg hover:shadow-purple-500/20 hover:-translate-y-1 transition-all text-center group flex flex-col items-center p-5">
|
||||
<div class="w-12 h-12 rounded-xl bg-purple-500/20 flex items-center justify-center text-2xl mb-3 group-hover:scale-110 transition-transform border border-purple-500/30">👨🏫</div>
|
||||
<div class="text-sm font-bold text-slate-300">教师申请</div>
|
||||
<a href="/admin/teacher-applications" class="futuristic-card-dark hover:border-purple-500/50 hover:shadow-lg hover:shadow-purple-500/20 hover:-translate-y-1 transition-all text-center group flex flex-col items-center p-4 sm:p-5">
|
||||
<div class="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-purple-500/20 flex items-center justify-center text-xl sm:text-2xl mb-2 sm:mb-3 group-hover:scale-110 transition-transform border border-purple-500/30">👨🏫</div>
|
||||
<div class="text-xs sm:text-sm font-bold text-slate-300">教师申请</div>
|
||||
</a>
|
||||
<a href="/admin/exams" class="futuristic-card-dark hover:border-emerald-500/50 hover:shadow-lg hover:shadow-emerald-500/20 hover:-translate-y-1 transition-all text-center group flex flex-col items-center p-5">
|
||||
<div class="w-12 h-12 rounded-xl bg-emerald-500/20 flex items-center justify-center text-2xl mb-3 group-hover:scale-110 transition-transform border border-emerald-500/30">📝</div>
|
||||
<div class="text-sm font-bold text-slate-300">考试管理</div>
|
||||
<a href="/admin/exams" class="futuristic-card-dark hover:border-emerald-500/50 hover:shadow-lg hover:shadow-emerald-500/20 hover:-translate-y-1 transition-all text-center group flex flex-col items-center p-4 sm:p-5">
|
||||
<div class="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-emerald-500/20 flex items-center justify-center text-xl sm:text-2xl mb-2 sm:mb-3 group-hover:scale-110 transition-transform border border-emerald-500/30">📝</div>
|
||||
<div class="text-xs sm:text-sm font-bold text-slate-300">考试管理</div>
|
||||
</a>
|
||||
<a href="/admin/users" class="futuristic-card-dark hover:border-blue-500/50 hover:shadow-lg hover:shadow-blue-500/20 hover:-translate-y-1 transition-all text-center group flex flex-col items-center p-5">
|
||||
<div class="w-12 h-12 rounded-xl bg-blue-500/20 flex items-center justify-center text-2xl mb-3 group-hover:scale-110 transition-transform border border-blue-500/30">👥</div>
|
||||
<div class="text-sm font-bold text-slate-300">用户管理</div>
|
||||
<a href="/admin/users" class="futuristic-card-dark hover:border-blue-500/50 hover:shadow-lg hover:shadow-blue-500/20 hover:-translate-y-1 transition-all text-center group flex flex-col items-center p-4 sm:p-5">
|
||||
<div class="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-blue-500/20 flex items-center justify-center text-xl sm:text-2xl mb-2 sm:mb-3 group-hover:scale-110 transition-transform border border-blue-500/30">👥</div>
|
||||
<div class="text-xs sm:text-sm font-bold text-slate-300">用户管理</div>
|
||||
</a>
|
||||
<a href="/admin/posts" class="futuristic-card-dark hover:border-amber-500/50 hover:shadow-lg hover:shadow-amber-500/20 hover:-translate-y-1 transition-all text-center group flex flex-col items-center p-5">
|
||||
<div class="w-12 h-12 rounded-xl bg-amber-500/20 flex items-center justify-center text-2xl mb-3 group-hover:scale-110 transition-transform border border-amber-500/30">💬</div>
|
||||
<div class="text-sm font-bold text-slate-300">帖子管理</div>
|
||||
<a href="/admin/posts" class="futuristic-card-dark hover:border-amber-500/50 hover:shadow-lg hover:shadow-amber-500/20 hover:-translate-y-1 transition-all text-center group flex flex-col items-center p-4 sm:p-5">
|
||||
<div class="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-amber-500/20 flex items-center justify-center text-xl sm:text-2xl mb-2 sm:mb-3 group-hover:scale-110 transition-transform border border-amber-500/30">💬</div>
|
||||
<div class="text-xs sm:text-sm font-bold text-slate-300">帖子管理</div>
|
||||
</a>
|
||||
<a href="/admin/notifications" class="futuristic-card-dark hover:border-rose-500/50 hover:shadow-lg hover:shadow-rose-500/20 hover:-translate-y-1 transition-all text-center group flex flex-col items-center p-5">
|
||||
<div class="w-12 h-12 rounded-xl bg-rose-500/20 flex items-center justify-center text-2xl mb-3 group-hover:scale-110 transition-transform border border-rose-500/30">📢</div>
|
||||
<div class="text-sm font-bold text-slate-300">通知管理</div>
|
||||
<a href="/admin/notifications" class="futuristic-card-dark hover:border-rose-500/50 hover:shadow-lg hover:shadow-rose-500/20 hover:-translate-y-1 transition-all text-center group flex flex-col items-center p-4 sm:p-5">
|
||||
<div class="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-rose-500/20 flex items-center justify-center text-xl sm:text-2xl mb-2 sm:mb-3 group-hover:scale-110 transition-transform border border-rose-500/30">📢</div>
|
||||
<div class="text-xs sm:text-sm font-bold text-slate-300">通知管理</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,17 +7,17 @@
|
||||
<h1 class="text-3xl font-extrabold bg-gradient-to-r from-blue-400 via-purple-400 to-pink-400 bg-clip-text text-transparent">考试管理</h1>
|
||||
|
||||
<div class="futuristic-card-dark overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table-futuristic">
|
||||
<div class="table-responsive">
|
||||
<table class="table-futuristic" style="min-width: 1000px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>标题</th>
|
||||
<th>科目</th>
|
||||
<th>出题人</th>
|
||||
<th>状态</th>
|
||||
<th>创建时间</th>
|
||||
<th>操作</th>
|
||||
<th style="min-width: 60px;">ID</th>
|
||||
<th style="min-width: 200px;">标题</th>
|
||||
<th style="min-width: 100px;">科目</th>
|
||||
<th style="min-width: 120px;">出题人</th>
|
||||
<th style="min-width: 100px;">状态</th>
|
||||
<th style="min-width: 150px;">创建时间</th>
|
||||
<th style="min-width: 180px;">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="exams-tbody"></tbody>
|
||||
@@ -68,7 +68,7 @@ async function loadExams() {
|
||||
const isAvailable = e.status === 'available';
|
||||
html += `<tr>
|
||||
<td>${e.id}</td>
|
||||
<td class="font-medium text-slate-200 max-w-xs truncate">${e.title}</td>
|
||||
<td class="font-medium text-slate-200" style="max-width: 200px; white-space: normal; word-wrap: break-word;">${e.title}</td>
|
||||
<td class="text-slate-400">${e.subject || '-'}</td>
|
||||
<td class="text-slate-400">${e.creator_name || '-'}</td>
|
||||
<td><span class="badge-futuristic ${statusClass}">${statusText}</span></td>
|
||||
|
||||
@@ -19,17 +19,17 @@
|
||||
</div>
|
||||
|
||||
<div class="futuristic-card-dark overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table-futuristic">
|
||||
<div class="table-responsive">
|
||||
<table class="table-futuristic" style="min-width: 1000px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>标题</th>
|
||||
<th>作者</th>
|
||||
<th>标签</th>
|
||||
<th>置顶</th>
|
||||
<th>发布时间</th>
|
||||
<th>操作</th>
|
||||
<th style="min-width: 60px;">ID</th>
|
||||
<th style="min-width: 250px;">标题</th>
|
||||
<th style="min-width: 120px;">作者</th>
|
||||
<th style="min-width: 100px;">标签</th>
|
||||
<th style="min-width: 100px;">置顶</th>
|
||||
<th style="min-width: 150px;">发布时间</th>
|
||||
<th style="min-width: 200px;">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="posts-tbody"></tbody>
|
||||
@@ -84,7 +84,7 @@ async function loadPosts() {
|
||||
const tagClass = tagClassMap[p.tag] || 'bg-slate-500/20 text-slate-400 border-slate-500/30';
|
||||
html += `<tr>
|
||||
<td>${p.id}</td>
|
||||
<td class="font-medium text-slate-200 max-w-xs truncate">${p.title}</td>
|
||||
<td class="font-medium text-slate-200" style="max-width: 250px; white-space: normal; word-wrap: break-word;">${p.title}</td>
|
||||
<td class="text-slate-400">${p.author}</td>
|
||||
<td><span class="badge-futuristic ${tagClass}">${p.tag || '-'}</span></td>
|
||||
<td>
|
||||
|
||||
@@ -31,18 +31,18 @@
|
||||
|
||||
<!-- 桌面端表格视图 -->
|
||||
<div class="hidden md:block bg-white shadow-sm rounded-lg border border-slate-200 overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-slate-200">
|
||||
<div class="table-responsive">
|
||||
<table class="min-w-full divide-y divide-slate-200" style="min-width: 1100px;">
|
||||
<thead class="bg-slate-50">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">ID</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">申请人</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">杯赛</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">姓名</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">邮箱</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">申请理由</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">申请时间</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">操作</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider" style="min-width: 60px;">ID</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider" style="min-width: 100px;">申请人</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider" style="min-width: 150px;">杯赛</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider" style="min-width: 100px;">姓名</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider" style="min-width: 150px;">邮箱</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider" style="min-width: 200px;">申请理由</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider" style="min-width: 150px;">申请时间</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider" style="min-width: 150px;">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="applications-table-body" class="bg-white divide-y divide-slate-200">
|
||||
@@ -50,10 +50,10 @@
|
||||
<tr data-id="{{ app.id }}">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-900">{{ app.id }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-900">{{ app.user.name }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-500">{{ app.contest.name }}</td>
|
||||
<td class="px-6 py-4 text-sm text-slate-500" style="max-width: 150px; white-space: normal; word-wrap: break-word;">{{ app.contest.name }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-500">{{ app.name }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-500">{{ app.email }}</td>
|
||||
<td class="px-6 py-4 text-sm text-slate-500 max-w-xs truncate">{{ app.reason }}</td>
|
||||
<td class="px-6 py-4 text-sm text-slate-500" style="max-width: 150px; word-wrap: break-word;">{{ app.email }}</td>
|
||||
<td class="px-6 py-4 text-sm text-slate-500" style="max-width: 200px; white-space: normal; word-wrap: break-word;">{{ app.reason }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-500">{{ app.applied_at.strftime('%Y-%m-%d %H:%M') }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
||||
<form action="{{ url_for('approve_teacher_application', app_id=app.id) }}" method="post" style="display:inline;">
|
||||
|
||||
@@ -18,17 +18,17 @@
|
||||
</div>
|
||||
|
||||
<div class="futuristic-card-dark overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table-futuristic">
|
||||
<div class="table-responsive">
|
||||
<table class="table-futuristic" style="min-width: 900px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>用户名</th>
|
||||
<th>邮箱</th>
|
||||
<th>角色</th>
|
||||
<th>状态</th>
|
||||
<th>注册时间</th>
|
||||
<th>操作</th>
|
||||
<th style="min-width: 60px;">ID</th>
|
||||
<th style="min-width: 120px;">用户名</th>
|
||||
<th style="min-width: 180px;">邮箱</th>
|
||||
<th style="min-width: 100px;">角色</th>
|
||||
<th style="min-width: 100px;">状态</th>
|
||||
<th style="min-width: 150px;">注册时间</th>
|
||||
<th style="min-width: 180px;">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="users-tbody"></tbody>
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- 聊天视图 -->
|
||||
<div id="chatView" class="flex-1 flex-col hidden z-10 relative">
|
||||
<div id="chatView" class="flex-1 hidden z-10 relative h-full">
|
||||
<!-- 顶栏 -->
|
||||
<div id="chatHeader" class="px-6 py-4 border-b border-white/10 flex items-center justify-between bg-slate-800/60 backdrop-blur-xl sticky top-0 z-20">
|
||||
<div id="chatHeader" class="px-6 py-4 border-b border-white/10 flex items-center justify-between bg-slate-800/60 backdrop-blur-xl flex-shrink-0 z-20">
|
||||
<div class="flex items-center gap-3">
|
||||
<button onclick="mobileBack()" class="sm:hidden w-8 h-8 rounded-lg bg-slate-700/50 text-slate-300 flex items-center justify-center mr-1 border border-white/10" aria-label="返回">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/></svg>
|
||||
@@ -114,9 +114,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- 消息区域 -->
|
||||
<div id="messageArea" class="flex-1 overflow-y-auto hide-scrollbar px-6 py-4 space-y-5" onscroll="handleScroll()"></div>
|
||||
<div id="messageArea" class="flex-1 overflow-y-auto px-6 py-4 space-y-5" style="overflow-anchor: none;" onscroll="handleScroll()"></div>
|
||||
<!-- 引用回复预览 -->
|
||||
<div id="replyPreview" class="px-4 py-3 bg-cyan-500/10 backdrop-blur-md border-t border-cyan-500/30 hidden flex items-center justify-between shadow-inner">
|
||||
<div id="replyPreview" class="px-4 py-3 bg-cyan-500/10 backdrop-blur-md border-t border-cyan-500/30 hidden flex items-center justify-between shadow-inner flex-shrink-0">
|
||||
<div class="flex items-center gap-2 overflow-hidden">
|
||||
<div class="w-1 h-4 bg-cyan-400 rounded-full"></div>
|
||||
<div class="text-sm font-medium text-cyan-300 truncate"><span id="replyToText"></span></div>
|
||||
@@ -126,7 +126,7 @@
|
||||
</button>
|
||||
</div>
|
||||
<!-- 输入区域 -->
|
||||
<div class="p-4 bg-slate-800/80 backdrop-blur-xl border-t border-white/10 shadow-[0_-4px_20px_-15px_rgba(0,0,0,0.3)]">
|
||||
<div class="p-4 bg-slate-800/80 backdrop-blur-xl border-t border-white/10 shadow-[0_-4px_20px_-15px_rgba(0,0,0,0.3)] flex-shrink-0 relative">
|
||||
<!-- Emoji 面板 -->
|
||||
<div id="emojiPanel" class="hidden absolute bottom-[calc(100%+10px)] left-4 p-3 futuristic-card-dark backdrop-blur-xl rounded-2xl shadow-xl max-h-48 overflow-y-auto w-64 z-50">
|
||||
<div class="flex flex-wrap gap-2 justify-center" id="emojiGrid"></div>
|
||||
@@ -162,7 +162,7 @@
|
||||
</div>
|
||||
|
||||
<div class="flex-1 futuristic-card-dark rounded-2xl shadow-sm focus-within:ring-2 focus-within:ring-cyan-500/30 focus-within:border-cyan-400/50 transition-all overflow-hidden">
|
||||
<textarea id="msgInput" rows="1" placeholder="输入消息... (Enter发送, Shift+Enter换行)" class="flex-1 w-full px-4 py-3.5 text-sm bg-transparent text-white placeholder-slate-500 border-none focus:outline-none focus:ring-0 resize-none max-h-32 min-h-[48px] hide-scrollbar" oninput="handleTyping(); this.style.height = 'auto'; this.style.height = (this.scrollHeight) + 'px';"></textarea>
|
||||
<textarea id="msgInput" rows="1" placeholder="输入消息... (Enter发送, Shift+Enter换行)" class="flex-1 w-full px-4 py-3.5 text-sm bg-transparent text-white placeholder-slate-500 border-none focus:outline-none focus:ring-0 resize-none max-h-32 min-h-[48px] hide-scrollbar" oninput="handleTyping(); this.style.height = 'auto'; this.style.height = (this.scrollHeight) + 'px';" onkeydown="handleInputKey(event)"></textarea>
|
||||
</div>
|
||||
|
||||
<button onclick="sendMessage()" class="btn-futuristic w-12 h-12 rounded-2xl flex items-center justify-center hover:shadow-lg hover:shadow-cyan-500/30 transform hover:-translate-y-0.5 transition-all flex-shrink-0 group">
|
||||
@@ -703,7 +703,10 @@ async function loadMessages(roomId, beforeId) {
|
||||
if (!beforeId) {
|
||||
area.innerHTML = '';
|
||||
data.messages.forEach(m => area.appendChild(createMsgEl(m)));
|
||||
area.scrollTop = area.scrollHeight;
|
||||
// 确保滚动到底部
|
||||
setTimeout(() => {
|
||||
area.scrollTop = area.scrollHeight;
|
||||
}, 100);
|
||||
} else {
|
||||
const oldH = area.scrollHeight;
|
||||
data.messages.forEach((m, i) => area.insertBefore(createMsgEl(m), area.children[i]));
|
||||
@@ -813,6 +816,7 @@ function sendMessage() {
|
||||
reply_to_id: replyToId, mentions: mentionsStr
|
||||
});
|
||||
input.value = '';
|
||||
input.style.height = 'auto';
|
||||
mentionMembers = [];
|
||||
cancelReply();
|
||||
document.getElementById('emojiPanel').classList.add('hidden');
|
||||
@@ -1337,8 +1341,14 @@ async function stopRecording() {
|
||||
socket.on('new_message', (msg) => {
|
||||
if (msg.room_id === currentRoomId) {
|
||||
const area = document.getElementById('messageArea');
|
||||
const wasAtBottom = area.scrollHeight - area.scrollTop - area.clientHeight < 100;
|
||||
area.appendChild(createMsgEl(msg));
|
||||
area.scrollTop = area.scrollHeight;
|
||||
// 如果用户在底部或者是自己发的消息,自动滚动到底部
|
||||
if (wasAtBottom || msg.sender_id === currentUser.id) {
|
||||
setTimeout(() => {
|
||||
area.scrollTop = area.scrollHeight;
|
||||
}, 50);
|
||||
}
|
||||
markRead(currentRoomId);
|
||||
}
|
||||
// 更新会话列表
|
||||
|
||||
@@ -15,35 +15,35 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="futuristic-card p-6 p-6">
|
||||
<div class="flex justify-between items-start">
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold bg-gradient-to-r from-cyan-400 to-blue-400 bg-clip-text text-transparent mb-2">
|
||||
<div class="flex flex-col lg:flex-row justify-between items-start gap-4">
|
||||
<div class="flex-1 min-w-0">
|
||||
<h1 class="text-xl sm:text-2xl font-bold bg-gradient-to-r from-cyan-400 to-blue-400 bg-clip-text text-transparent mb-2 break-words">
|
||||
{{ contest.name }}
|
||||
{% if contest.status == 'abolished' %}
|
||||
<span class="ml-2 badge-futuristic text-xs rounded-full">已废止</span>
|
||||
{% endif %}
|
||||
</h1>
|
||||
<div class="flex items-center space-x-4 text-sm text-slate-400">
|
||||
<span class="flex items-center">
|
||||
<div class="flex flex-wrap items-center gap-3 sm:gap-4 text-xs sm:text-sm text-slate-400">
|
||||
<span class="flex items-center whitespace-nowrap">
|
||||
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/></svg>
|
||||
{{ contest.start_date }}
|
||||
</span>
|
||||
<span class="flex items-center" id="participants-count">
|
||||
<span class="flex items-center whitespace-nowrap" id="participants-count">
|
||||
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"/></svg>
|
||||
<span id="participants-value">{{ contest.participants }}</span>人已报名
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex space-x-3">
|
||||
<div class="flex flex-wrap gap-3 w-full lg:w-auto">
|
||||
{% if contest.status != 'abolished' %}
|
||||
{% if user %}
|
||||
<button id="register-btn"
|
||||
onclick="toggleRegistration({{ contest.id }})"
|
||||
class="{% if registered %}btn-outline-futuristic{% else %}btn-futuristic{% endif %} px-6 py-2 font-medium">
|
||||
class="{% if registered %}btn-outline-futuristic{% else %}btn-futuristic{% endif %} px-4 sm:px-6 py-2 font-medium text-sm whitespace-nowrap">
|
||||
{% if registered %}已报名{% else %}立即报名{% endif %}
|
||||
</button>
|
||||
{% if not is_member %}
|
||||
<a href="{{ url_for('apply_teacher', contest_id=contest.id) }}" class="btn-outline-futuristic px-6 py-2 font-medium">
|
||||
<a href="{{ url_for('apply_teacher', contest_id=contest.id) }}" class="btn-outline-futuristic px-4 sm:px-6 py-2 font-medium text-sm whitespace-nowrap">
|
||||
申请成为本杯赛老师
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
@@ -149,7 +149,7 @@ async function loadQuestions() {
|
||||
<p class="text-sm text-slate-800 mb-2">${escapeHtml(q.content)}</p>`;
|
||||
if (q.type === 'choice' && q.options && q.options.length) {
|
||||
html += '<div class="text-sm text-slate-600 space-y-1 ml-4">';
|
||||
q.options.forEach(opt => { html += `<div>${escapeHtml(opt)}</div>`; });
|
||||
q.options.forEach(opt => { html += `<div class="option-text">${opt}</div>`; });
|
||||
html += '</div>';
|
||||
}
|
||||
if (q.answer) {
|
||||
@@ -162,6 +162,13 @@ async function loadQuestions() {
|
||||
html += `</div></div>`;
|
||||
});
|
||||
container.innerHTML = html;
|
||||
// 渲染数学公式
|
||||
if (typeof renderMathInElement === 'function') {
|
||||
renderMathInElement(container, {
|
||||
delimiters: [{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false}],
|
||||
throwOnError: false
|
||||
});
|
||||
}
|
||||
} catch(e) {
|
||||
document.getElementById('question-list').innerHTML = '<div class="text-center py-8 text-red-500">加载失败</div>';
|
||||
}
|
||||
|
||||
@@ -86,26 +86,31 @@
|
||||
</div>
|
||||
{% else %}
|
||||
<!-- 顶部信息栏 -->
|
||||
<div class="futuristic-card p-4 sticky top-0 z-20">
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
<h1 class="text-lg font-bold bg-gradient-to-r from-purple-600 to-blue-600 bg-clip-text text-transparent">{{ exam.title }}</h1>
|
||||
<div class="mt-1 text-sm text-slate-500">
|
||||
{{ exam.subject }} · {{ exam.duration }}分钟 · 满分{{ exam.total_score }}分
|
||||
<div class="futuristic-card p-4 sticky top-0 z-20 shadow-lg">
|
||||
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3">
|
||||
<div class="flex-1 min-w-0">
|
||||
<h1 class="text-lg sm:text-xl font-bold bg-gradient-to-r from-purple-600 to-blue-600 bg-clip-text text-transparent truncate">{{ exam.title }}</h1>
|
||||
<div class="mt-1 text-xs sm:text-sm text-slate-500 flex flex-wrap gap-2">
|
||||
<span>{{ exam.subject }}</span>
|
||||
<span>·</span>
|
||||
<span>{{ exam.duration }}分钟</span>
|
||||
<span>·</span>
|
||||
<span>满分{{ exam.total_score }}分</span>
|
||||
{% if exam.scheduled_end %}
|
||||
· 截止:{{ exam.scheduled_end.strftime('%m-%d %H:%M') }}
|
||||
<span>·</span>
|
||||
<span>截止:{{ exam.scheduled_end.strftime('%m-%d %H:%M') }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="text-sm text-slate-500">
|
||||
<div class="flex items-center gap-3 sm:gap-4 flex-wrap">
|
||||
<div class="text-xs sm:text-sm text-slate-500 whitespace-nowrap">
|
||||
<span id="progress-text">0</span>/{{ questions|length }} 已答
|
||||
</div>
|
||||
<div class="flex items-center text-red-600 font-medium">
|
||||
<svg class="w-5 h-5 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
<div class="flex items-center text-red-600 font-medium text-sm sm:text-base whitespace-nowrap">
|
||||
<svg class="w-4 h-4 sm:w-5 sm:h-5 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
<span id="timer">--:--:--</span>
|
||||
</div>
|
||||
<div id="tab-warning" class="hidden text-xs text-orange-600 bg-orange-50 px-2 py-1 rounded">
|
||||
<div id="tab-warning" class="hidden text-xs text-orange-600 bg-orange-50 px-2 py-1 rounded whitespace-nowrap">
|
||||
切屏 <span id="tab-count">0</span> 次
|
||||
</div>
|
||||
</div>
|
||||
@@ -180,7 +185,7 @@
|
||||
{% for opt in q.options %}
|
||||
<label class="flex items-center space-x-3 p-3 rounded-lg border border-slate-200 hover:bg-slate-50 cursor-pointer transition-colors option-label" data-qid="{{ q.id }}">
|
||||
<input type="radio" name="q-{{ q.id }}" value="{{ ['A','B','C','D'][loop.index0] }}" class="h-4 w-4 text-primary border-slate-300 focus:ring-primary answer-input" data-qid="{{ q.id }}" onchange="onAnswerChange({{ q.id }})">
|
||||
<span class="text-slate-700">{{ ['A','B','C','D'][loop.index0] }}. {{ opt }}</span>
|
||||
<span class="text-slate-700 option-text">{{ ['A','B','C','D'][loop.index0] }}. {{ opt|safe }}</span>
|
||||
</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
{% if is_answer %}border-green-300 bg-green-50
|
||||
{% elif is_selected and not is_answer %}border-red-300 bg-red-50
|
||||
{% else %}border-slate-100{% endif %}">
|
||||
<span class="text-sm text-slate-700">{{ letter }}. {{ opt }}</span>
|
||||
<span class="text-sm text-slate-700 option-text">{{ letter }}. {{ opt|safe }}</span>
|
||||
{% if is_selected %}<span class="text-xs {% if is_answer %}text-green-600{% else %}text-red-500{% endif %} font-medium">← 考生选择</span>{% endif %}
|
||||
{% if is_answer %}<span class="text-xs text-green-600 font-medium">✓ 正确</span>{% endif %}
|
||||
</div>
|
||||
|
||||
@@ -26,6 +26,11 @@
|
||||
.print-btn { position: fixed; top: 20px; right: 20px; padding: 10px 24px; background: #3b82f6; color: #fff; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; z-index: 100; }
|
||||
.print-btn:hover { background: #2563eb; }
|
||||
</style>
|
||||
<!-- KaTeX 数学公式渲染 -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js"></script>
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js"
|
||||
onload="renderMathInElement(document.body,{delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false}],throwOnError:false});"></script>
|
||||
</head>
|
||||
<body>
|
||||
<button class="print-btn no-print" onclick="window.print()">打印 / 导出PDF</button>
|
||||
@@ -64,7 +69,7 @@
|
||||
{% if q.options %}
|
||||
<div class="options">
|
||||
{% for opt in q.options %}
|
||||
<div class="opt">{{ ['A','B','C','D'][loop.index0] }}. {{ opt }}</div>
|
||||
<div class="opt">{{ ['A','B','C','D'][loop.index0] }}. {{ opt|safe }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
@@ -2,43 +2,43 @@
|
||||
{% block title %}考试结果 - 智联青云{% endblock %}
|
||||
{% block content %}
|
||||
<div class="max-w-4xl mx-auto space-y-6">
|
||||
<div class="flex justify-between items-center">
|
||||
<h1 class="text-2xl font-bold bg-gradient-to-r from-purple-600 to-blue-600 bg-clip-text text-transparent">考试结果</h1>
|
||||
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3">
|
||||
<h1 class="text-xl sm:text-2xl font-bold bg-gradient-to-r from-purple-600 to-blue-600 bg-clip-text text-transparent">考试结果</h1>
|
||||
<a href="/exams" class="text-sm text-slate-500 hover:text-slate-700">← 返回列表</a>
|
||||
</div>
|
||||
<div class="futuristic-card p-6">
|
||||
<h2 class="text-xl font-bold bg-gradient-to-r from-purple-600 to-blue-600 bg-clip-text text-transparent">{{ exam.title }}</h2>
|
||||
<div class="mt-2 flex items-center text-sm text-slate-500 space-x-4">
|
||||
<div class="futuristic-card p-4 sm:p-6">
|
||||
<h2 class="text-lg sm:text-xl font-bold bg-gradient-to-r from-purple-600 to-blue-600 bg-clip-text text-transparent break-words">{{ exam.title }}</h2>
|
||||
<div class="mt-2 flex flex-wrap items-center text-xs sm:text-sm text-slate-500 gap-2 sm:gap-4">
|
||||
<span>{{ exam.subject }}</span>
|
||||
<span>满分{{ exam.total_score }}分</span>
|
||||
<span>提交时间:{{ submission.submitted_at }}</span>
|
||||
<span class="break-all">提交时间:{{ submission.submitted_at }}</span>
|
||||
</div>
|
||||
<div class="mt-4 p-4 rounded-lg {% if score_hidden %}bg-blue-50 border border-blue-200{% elif submission.graded %}bg-green-50 border border-green-200{% else %}bg-yellow-50 border border-yellow-200{% endif %}">
|
||||
{% if score_hidden %}
|
||||
<div class="text-lg font-medium text-blue-700">成绩尚未公布</div>
|
||||
<div class="text-sm text-blue-600 mt-1">成绩将于 {{ exam.score_release_time.strftime('%Y年%m月%d日 %H:%M') }} 公布</div>
|
||||
<div class="text-base sm:text-lg font-medium text-blue-700">成绩尚未公布</div>
|
||||
<div class="text-xs sm:text-sm text-blue-600 mt-1 break-words">成绩将于 {{ exam.score_release_time.strftime('%Y年%m月%d日 %H:%M') }} 公布</div>
|
||||
{% elif submission.graded %}
|
||||
<div class="text-3xl font-bold text-green-700">{{ submission.score }} <span class="text-lg font-normal text-green-600">/ {{ exam.total_score }}</span></div>
|
||||
<div class="text-sm text-green-600 mt-1">批改人:{{ submission.graded_by }}</div>
|
||||
<div class="text-2xl sm:text-3xl font-bold text-green-700">{{ submission.score }} <span class="text-base sm:text-lg font-normal text-green-600">/ {{ exam.total_score }}</span></div>
|
||||
<div class="text-xs sm:text-sm text-green-600 mt-1">批改人:{{ submission.graded_by }}</div>
|
||||
{% else %}
|
||||
<div class="text-lg font-medium text-yellow-700">待批改</div>
|
||||
<div class="text-sm text-yellow-600">选择题已自动批改得分:{{ submission.score }}分,主观题等待老师批改</div>
|
||||
<div class="text-base sm:text-lg font-medium text-yellow-700">待批改</div>
|
||||
<div class="text-xs sm:text-sm text-yellow-600 break-words">选择题已自动批改得分:{{ submission.score }}分,主观题等待老师批改</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% for q in questions %}
|
||||
<div class="futuristic-card p-6">
|
||||
<div class="flex items-start space-x-4">
|
||||
<span class="flex-shrink-0 w-8 h-8 bg-gradient-to-br from-purple-500 to-blue-500 rounded-full flex items-center justify-center text-white font-medium">{{ loop.index }}</span>
|
||||
<div class="flex-1 space-y-3">
|
||||
<div class="flex justify-between">
|
||||
<p class="text-lg text-slate-900">{{ q.content }}</p>
|
||||
<span class="text-sm text-slate-400 whitespace-nowrap ml-4">({{ q.score }}分)</span>
|
||||
<div class="futuristic-card p-4 sm:p-6">
|
||||
<div class="flex items-start space-x-3 sm:space-x-4">
|
||||
<span class="flex-shrink-0 w-7 h-7 sm:w-8 sm:h-8 bg-gradient-to-br from-purple-500 to-blue-500 rounded-full flex items-center justify-center text-white font-medium text-sm">{{ loop.index }}</span>
|
||||
<div class="flex-1 space-y-3 min-w-0">
|
||||
<div class="flex flex-col sm:flex-row sm:justify-between gap-2">
|
||||
<p class="text-base sm:text-lg text-slate-900 break-words">{{ q.content }}</p>
|
||||
<span class="text-xs sm:text-sm text-slate-400 whitespace-nowrap">({{ q.score }}分)</span>
|
||||
</div>
|
||||
{% if q.get('images') %}
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{% for img in q.images %}
|
||||
<img src="{{ img }}" class="max-h-48 rounded border border-slate-200 cursor-pointer" onclick="window.open(this.src)" alt="题目图片">
|
||||
<img src="{{ img }}" class="max-h-32 sm:max-h-48 rounded border border-slate-200 cursor-pointer max-w-full" onclick="window.open(this.src)" alt="题目图片">
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
@@ -57,7 +57,7 @@
|
||||
{% else %}
|
||||
<div class="w-5 h-5"></div>
|
||||
{% endif %}
|
||||
<span class="text-slate-700">{{ letter }}. {{ opt }}</span>
|
||||
<span class="text-slate-700 option-text">{{ letter }}. {{ opt|safe }}</span>
|
||||
{% if is_answer %}<span class="badge-futuristic text-xs ml-2">✓ 正确答案</span>{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
@@ -85,4 +85,15 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script>
|
||||
// 渲染页面中的数学公式
|
||||
if (typeof renderMathInElement === 'function') {
|
||||
renderMathInElement(document.body, {
|
||||
delimiters: [{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false}],
|
||||
throwOnError: false
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -459,8 +459,11 @@ function renderPosts(posts) {
|
||||
|
||||
<div class="hidden sm:flex flex-col items-center gap-3 flex-shrink-0" onclick="event.stopPropagation()">
|
||||
<!-- 游戏化头像与等级 -->
|
||||
<div class="relative w-14 h-14 rounded-2xl bg-gradient-to-br from-indigo-100 to-purple-100 flex items-center justify-center text-indigo-600 font-bold border-2 border-white shadow-md transform group-hover:rotate-6 transition-transform z-10">
|
||||
<span class="text-xl">${esc(p.author.charAt(0))}</span>
|
||||
<div class="relative w-14 h-14 rounded-2xl ${p.author_avatar ? '' : 'bg-gradient-to-br from-indigo-100 to-purple-100'} flex items-center justify-center text-indigo-600 font-bold border-2 border-white shadow-md transform group-hover:rotate-6 transition-transform z-10 overflow-hidden">
|
||||
${p.author_avatar ?
|
||||
`<img src="${p.author_avatar}" alt="${esc(p.author)}" class="w-full h-full object-cover">` :
|
||||
`<span class="text-xl">${esc(p.author.charAt(0))}</span>`
|
||||
}
|
||||
${p.is_official ?
|
||||
'<div class="absolute -bottom-2 -right-2 bg-gradient-to-r from-red-500 to-rose-600 text-white text-[9px] font-black px-1.5 py-0.5 rounded-md shadow-sm border border-white">官方</div>' :
|
||||
'<div class="absolute -bottom-1.5 -right-1.5 bg-slate-800 text-white text-[9px] font-bold px-1.5 py-0.5 rounded-full shadow-sm border border-white">Lv.' + authorLevel + '</div>'}
|
||||
@@ -612,7 +615,13 @@ async function openPost(pid) {
|
||||
</div>
|
||||
<h2 class="text-xl font-bold text-slate-900">${esc(p.title)}</h2>
|
||||
<div class="mt-2 flex items-center text-sm text-slate-400 gap-3">
|
||||
<span class="cursor-pointer hover:text-primary" onclick="showProfile('${p.author_id}')">${esc(p.author)}</span>
|
||||
<div class="flex items-center gap-2">
|
||||
${p.author_avatar ?
|
||||
`<img src="${p.author_avatar}" alt="${esc(p.author)}" class="w-6 h-6 rounded-full object-cover border border-slate-200">` :
|
||||
`<div class="w-6 h-6 rounded-full bg-gradient-to-br from-indigo-100 to-purple-100 flex items-center justify-center text-indigo-600 text-xs font-bold border border-slate-200">${esc(p.author.charAt(0))}</div>`
|
||||
}
|
||||
<span class="cursor-pointer hover:text-primary" onclick="showProfile('${p.author_id}')">${esc(p.author)}</span>
|
||||
</div>
|
||||
<span>${p.created_at}</span><span>👁 ${p.views||0}</span><span>❤️ ${p.likes}</span><span>💬 ${replies.length}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -635,7 +644,12 @@ async function openPost(pid) {
|
||||
<h3 class="text-sm font-bold text-slate-700 mb-4">💬 回复 (${replies.length})</h3>`;
|
||||
|
||||
if (CU) {
|
||||
h += `<div class="flex gap-3 mb-6"><div class="w-8 h-8 bg-primary rounded-full flex items-center justify-center text-white text-xs font-bold flex-shrink-0">${esc(CU.name.charAt(0))}</div>
|
||||
const currentUserAvatar = CU.avatar || '';
|
||||
h += `<div class="flex gap-3 mb-6">
|
||||
${currentUserAvatar ?
|
||||
`<img src="${currentUserAvatar}" alt="${esc(CU.name)}" class="w-8 h-8 rounded-full object-cover border border-slate-200 flex-shrink-0">` :
|
||||
`<div class="w-8 h-8 bg-primary rounded-full flex items-center justify-center text-white text-xs font-bold flex-shrink-0">${esc(CU.name.charAt(0))}</div>`
|
||||
}
|
||||
<div class="flex-1"><textarea id="reply-input" rows="2" class="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary/30" placeholder="写下你的回复..."></textarea>
|
||||
<div id="reply-preview" class="flex flex-wrap gap-2 mt-1 hidden"></div>
|
||||
<div class="flex justify-between items-center mt-2"><div class="flex gap-1"><button onclick="triggerUpload('reply-input')" class="p-1 rounded hover:bg-slate-200 text-sm" title="上传图片">📷</button><button onclick="triggerCamera('reply-input')" class="p-1 rounded hover:bg-slate-200 text-sm" title="拍照上传">📸</button></div><button onclick="submitReply(${p.id})" class="px-4 py-1.5 bg-primary text-white rounded-lg text-sm hover:bg-blue-700">回复</button></div></div></div>`;
|
||||
@@ -648,7 +662,10 @@ async function openPost(pid) {
|
||||
const canDel = CU && (CU.id === r.author_id || CU.role === 'teacher');
|
||||
const canEdit = CU && CU.id === r.author_id;
|
||||
h += `<div class="flex gap-3 py-3 ${i>0?'border-t border-slate-100':''}">
|
||||
<div class="w-8 h-8 bg-slate-200 rounded-full flex items-center justify-center text-slate-600 text-xs font-bold flex-shrink-0">${esc(r.author.charAt(0))}</div>
|
||||
${r.author_avatar ?
|
||||
`<img src="${r.author_avatar}" alt="${esc(r.author)}" class="w-8 h-8 rounded-full object-cover border border-slate-200 flex-shrink-0">` :
|
||||
`<div class="w-8 h-8 bg-slate-200 rounded-full flex items-center justify-center text-slate-600 text-xs font-bold flex-shrink-0">${esc(r.author.charAt(0))}</div>`
|
||||
}
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2 flex-wrap">
|
||||
|
||||
Reference in New Issue
Block a user