Files
resume/templates/admin/user-quiz.html
T

395 lines
23 KiB
HTML

<div class="bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden">
<div class="border-b border-slate-200 px-6 py-4 bg-slate-50">
<div class="flex items-center justify-between">
<div>
<h3 class="font-semibold text-slate-800 flex items-center gap-2">
<i class="fas fa-question-circle text-orange-500"></i> {{ with .User }}{{.Name}}{{ else }}未知用户{{ end }} 的面试题库
</h3>
<p class="text-sm text-slate-500 mt-1">管理用户的面试题目和答题记录</p>
</div>
<div class="flex items-center gap-3">
<a href="{{.AdminPath}}/user/{{.User.ID}}/quiz/generate" class="btn btn-primary">
<i class="fas fa-robot"></i> AI生成题目
</a>
<a href="{{.AdminPath}}/quiz" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> 返回列表
</a>
</div>
</div>
</div>
<div class="grid grid-cols-4 gap-4 p-6 border-b border-slate-200 bg-gradient-to-r from-slate-50 to-white">
<div class="bg-white rounded-xl p-4 border border-slate-200 shadow-sm">
<div class="flex items-center gap-3">
<div class="w-12 h-12 rounded-xl bg-blue-100 flex items-center justify-center text-blue-600">
<i class="fas fa-list-alt text-xl"></i>
</div>
<div>
<p class="text-2xl font-bold text-slate-800">{{.TotalRecords}}</p>
<p class="text-sm text-slate-500">答题记录</p>
</div>
</div>
</div>
<div class="bg-white rounded-xl p-4 border border-slate-200 shadow-sm">
<div class="flex items-center gap-3">
<div class="w-12 h-12 rounded-xl bg-green-100 flex items-center justify-center text-green-600">
<i class="fas fa-check-circle text-xl"></i>
</div>
<div>
<p class="text-2xl font-bold text-slate-800">{{.FavoriteCount}}</p>
<p class="text-sm text-slate-500">收藏题目</p>
</div>
</div>
</div>
<div class="bg-white rounded-xl p-4 border border-slate-200 shadow-sm">
<div class="flex items-center gap-3">
<div class="w-12 h-12 rounded-xl bg-red-100 flex items-center justify-center text-red-600">
<i class="fas fa-times-circle text-xl"></i>
</div>
<div>
<p class="text-2xl font-bold text-slate-800">{{.WrongCount}}</p>
<p class="text-sm text-slate-500">错题数量</p>
</div>
</div>
</div>
<div class="bg-white rounded-xl p-4 border border-slate-200 shadow-sm">
<div class="flex items-center gap-3">
<div class="w-12 h-12 rounded-xl bg-purple-100 flex items-center justify-center text-purple-600">
<i class="fas fa-percentage text-xl"></i>
</div>
<div>
<p class="text-2xl font-bold text-slate-800">{{.AvgScore}}%</p>
<p class="text-sm text-slate-500">平均正确率</p>
</div>
</div>
</div>
</div>
<div class="border-b border-slate-200">
<div class="flex">
<button onclick="switchTab('records')" id="tab-records" class="tab-btn active px-6 py-4 text-sm font-medium border-b-2 border-transparent transition-colors">
<i class="fas fa-list-alt mr-2"></i>答题记录
</button>
<button onclick="switchTab('favorites')" id="tab-favorites" class="tab-btn px-6 py-4 text-sm font-medium border-b-2 border-transparent transition-colors">
<i class="fas fa-star mr-2"></i>收藏题目
</button>
<button onclick="switchTab('wrong')" id="tab-wrong" class="tab-btn px-6 py-4 text-sm font-medium border-b-2 border-transparent transition-colors">
<i class="fas fa-times-circle mr-2"></i>错题本
</button>
</div>
</div>
<div class="p-6">
<div id="panel-records" class="tab-panel">
<div class="space-y-3">
{{ range .QuizRecords }}
<a href="{{$.AdminPath}}/user/{{$.User.ID}}/quiz/record/{{.ID}}" class="block bg-slate-50 p-4 rounded-lg border border-slate-200 hover:border-primary-300 hover:shadow-sm transition-all group">
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-lg bg-white border border-slate-200 flex items-center justify-center text-slate-500 group-hover:text-primary-500 transition-colors">
<i class="fas fa-file-alt"></i>
</div>
<div>
<h4 class="font-medium text-slate-800">{{ if .Title }}{{.Title}}{{ else }}答题记录{{ end }}</h4>
<p class="text-xs text-slate-500 mt-0.5">{{.CreatedAt}} · {{.TotalCount}} 道题</p>
</div>
</div>
<div class="flex items-center gap-4">
<div class="text-right">
<div class="flex items-center gap-2">
<span class="text-2xl font-bold {{ if ge .Score 60.0 }}text-green-600{{ else }}text-red-600{{ end }}">{{.Score}}</span>
<span class="text-sm text-slate-400"></span>
</div>
<p class="text-xs text-slate-500">{{.CorrectCount}}/{{.TotalCount}} 正确</p>
</div>
<div class="text-slate-400 group-hover:text-primary-500 transition-colors">
<i class="fas fa-chevron-right"></i>
</div>
</div>
</div>
</a>
{{ else }}
<div class="empty-state">
<div class="empty-state-icon">
<i class="fas fa-file-alt"></i>
</div>
<p>暂无答题记录</p>
</div>
{{ end }}
</div>
{{ if gt .TotalPages 1 }}
<div class="border-t border-slate-200 mt-6 pt-4 flex items-center justify-between">
<div class="text-sm text-slate-500">共 {{ .TotalRecords }} 条记录,第 {{ .CurrentPage }} / {{ .TotalPages }} 页</div>
<div class="flex items-center gap-2">
<a href="?page=1&tab=records" class="btn btn-sm btn-slate {{ if eq .CurrentPage 1 }}opacity-50 cursor-not-allowed{{ end }}" {{ if eq .CurrentPage 1 }}onclick="return false"{{ end }}>
<i class="fas fa-chevron-left"></i><i class="fas fa-chevron-left"></i>
</a>
<a href="?page={{ if eq .CurrentPage 1 }}1{{ else }}{{ subtract .CurrentPage 1 }}{{ end }}&tab=records" class="btn btn-sm btn-slate {{ if eq .CurrentPage 1 }}opacity-50 cursor-not-allowed{{ end }}" {{ if eq .CurrentPage 1 }}onclick="return false"{{ end }}>
<i class="fas fa-chevron-left"></i>
</a>
{{ range $i := to 1 $.TotalPages }}
{{ if or (eq $i 1) (eq $i $.TotalPages) (and (ge $i (subtract $.CurrentPage 2)) (le $i (add $.CurrentPage 2))) }}
<a href="?page={{ $i }}&tab=records" class="btn btn-sm {{ if eq $i $.CurrentPage }}btn-primary{{ else }}btn-slate{{ end }}">{{ $i }}</a>
{{ else if and (eq $i 2) (gt (subtract $.CurrentPage 3) 1) }}
<span class="text-slate-400 px-2">...</span>
{{ else if and (eq $i (subtract $.TotalPages 1)) (lt (add $.CurrentPage 3) $.TotalPages) }}
<span class="text-slate-400 px-2">...</span>
{{ end }}
{{ end }}
<a href="?page={{ if eq .CurrentPage .TotalPages }}{{ .TotalPages }}{{ else }}{{ add .CurrentPage 1 }}{{ end }}&tab=records" class="btn btn-sm btn-slate {{ if eq .CurrentPage .TotalPages }}opacity-50 cursor-not-allowed{{ end }}" {{ if eq .CurrentPage .TotalPages }}onclick="return false"{{ end }}>
<i class="fas fa-chevron-right"></i>
</a>
<a href="?page={{ .TotalPages }}&tab=records" class="btn btn-sm btn-slate {{ if eq .CurrentPage .TotalPages }}opacity-50 cursor-not-allowed{{ end }}" {{ if eq .CurrentPage .TotalPages }}onclick="return false"{{ end }}>
<i class="fas fa-chevron-right"></i><i class="fas fa-chevron-right"></i>
</a>
</div>
</div>
{{ end }}
</div>
<div id="panel-favorites" class="tab-panel hidden">
<div class="space-y-3">
{{ range .FavoriteQuestions }}
{{ if .IsFavorite }}
<div class="bg-yellow-50 p-4 rounded-lg border border-yellow-200">
<div class="flex items-start justify-between mb-2">
<div class="flex flex-wrap items-center gap-2">
<button onclick="toggleFavorite('{{.ID}}')" class="text-yellow-500 hover:text-yellow-600 text-lg" title="取消收藏">
<i class="fas fa-star"></i>
</button>
{{ if not .IsCorrect }}
<span class="px-2 py-0.5 bg-red-100 text-red-600 rounded text-xs">错题</span>
{{ end }}
{{ if .Question.Type }}
<span class="px-2 py-0.5 rounded text-xs"
{{ if eq .Question.Type "mcq" }}style="background-color: #dbeafe; color: #1e40af;"{{ end }}
{{ if eq .Question.Type "fill" }}style="background-color: #ede9fe; color: #5b21b6;"{{ end }}
{{ if eq .Question.Type "sa" }}style="background-color: #dcfce7; color: #166534;"{{ end }}
{{ if eq .Question.Type "algo" }}style="background-color: #ffedd5; color: #c2410c;"{{ end }}>
{{ if eq .Question.Type "mcq" }}选择题{{ else if eq .Question.Type "fill" }}填空题{{ else if eq .Question.Type "sa" }}简答题{{ else if eq .Question.Type "algo" }}算法题{{ else }}{{.Question.Type}}{{ end }}
</span>
{{ end }}
{{ if .Question.Difficulty }}
<span class="px-2 py-0.5 rounded text-xs"
{{ if eq .Question.Difficulty "入门" }}style="background-color: #dcfce7; color: #166534;"{{ end }}
{{ if eq .Question.Difficulty "初级" }}style="background-color: #dbeafe; color: #1e40af;"{{ end }}
{{ if eq .Question.Difficulty "中级" }}style="background-color: #fef3c7; color: #92400e;"{{ end }}
{{ if eq .Question.Difficulty "进阶" }}style="background-color: #ffedd5; color: #c2410c;"{{ end }}
{{ if eq .Question.Difficulty "高级" }}style="background-color: #fee2e2; color: #991b1b;"{{ end }}>
{{.Question.Difficulty}}
</span>
{{ end }}
{{ if .Question.Category }}
<span class="px-2 py-0.5 bg-indigo-100 text-indigo-600 rounded text-xs">{{.Question.Category}}</span>
{{ end }}
<span class="px-2 py-0.5 bg-slate-100 text-slate-600 rounded text-xs">{{.Question.Score}}分</span>
</div>
<button onclick="deleteFavorite('{{.ID}}')" class="btn btn-danger btn-xs" title="删除">
<i class="fas fa-trash"></i>
</button>
</div>
<p class="text-sm text-slate-800 mb-3 font-medium leading-relaxed">{{.Question.Text}}</p>
{{ if .Question.Options }}
<div class="bg-white p-3 rounded-lg mb-3 space-y-2">
{{ range $i, $opt := .Question.Options }}
<div class="flex items-start gap-2 text-sm">
<span class="w-5 h-5 rounded-full bg-slate-100 flex items-center justify-center text-xs text-slate-600 font-medium flex-shrink-0">{{index "ABCDE" $i}}</span>
<span class="text-slate-600">{{$opt}}</span>
</div>
{{ end }}
</div>
{{ end }}
<div class="flex flex-wrap items-center gap-4 mb-3">
<div class="flex items-center gap-1">
<span class="text-xs text-slate-500">正确答案:</span>
<span class="text-sm text-green-600 font-medium">{{.Question.Answer}}</span>
</div>
{{ if .UserAnswer }}
<div class="flex items-center gap-1">
<span class="text-xs text-slate-500">你的答案:</span>
<span class="text-sm {{ if .IsCorrect }}text-green-600{{ else }}text-red-600{{ end }}">{{.UserAnswer}}</span>
</div>
{{ end }}
</div>
{{ if .Question.Analysis }}
<div class="p-3 bg-white rounded-lg text-sm text-slate-600 border border-yellow-100">
<div class="font-medium text-slate-700 mb-1 flex items-center gap-1">
<i class="fas fa-lightbulb text-yellow-500"></i> 答案解析
</div>
<div class="markdown-content">{{.Question.Analysis}}</div>
</div>
{{ end }}
</div>
{{ end }}
{{ else }}
<div class="empty-state">
<div class="empty-state-icon">
<i class="fas fa-star"></i>
</div>
<p>暂无收藏题目</p>
</div>
{{ end }}
</div>
</div>
<div id="panel-wrong" class="tab-panel hidden">
<div class="space-y-3">
{{ range .FavoriteQuestions }}
{{ if not .IsCorrect }}
<div class="bg-red-50 p-4 rounded-lg border border-red-200">
<div class="flex items-start justify-between mb-2">
<div class="flex flex-wrap items-center gap-2">
<button onclick="toggleFavorite('{{.ID}}')" class="{{ if .IsFavorite }}text-yellow-500{{ else }}text-slate-300{{ end }} hover:text-yellow-600 text-lg" title="{{ if .IsFavorite }}取消收藏{{ else }}收藏{{ end }}">
<i class="{{ if .IsFavorite }}fas fa-star{{ else }}far fa-star{{ end }}"></i>
</button>
<span class="px-2 py-0.5 bg-red-100 text-red-600 rounded text-xs">错题</span>
{{ if .IsFavorite }}
<span class="px-2 py-0.5 bg-yellow-100 text-yellow-600 rounded text-xs">收藏</span>
{{ end }}
{{ if .Question.Type }}
<span class="px-2 py-0.5 rounded text-xs"
{{ if eq .Question.Type "mcq" }}style="background-color: #dbeafe; color: #1e40af;"{{ end }}
{{ if eq .Question.Type "fill" }}style="background-color: #ede9fe; color: #5b21b6;"{{ end }}
{{ if eq .Question.Type "sa" }}style="background-color: #dcfce7; color: #166534;"{{ end }}
{{ if eq .Question.Type "algo" }}style="background-color: #ffedd5; color: #c2410c;"{{ end }}>
{{ if eq .Question.Type "mcq" }}选择题{{ else if eq .Question.Type "fill" }}填空题{{ else if eq .Question.Type "sa" }}简答题{{ else if eq .Question.Type "algo" }}算法题{{ else }}{{.Question.Type}}{{ end }}
</span>
{{ end }}
{{ if .Question.Difficulty }}
<span class="px-2 py-0.5 rounded text-xs"
{{ if eq .Question.Difficulty "入门" }}style="background-color: #dcfce7; color: #166534;"{{ end }}
{{ if eq .Question.Difficulty "初级" }}style="background-color: #dbeafe; color: #1e40af;"{{ end }}
{{ if eq .Question.Difficulty "中级" }}style="background-color: #fef3c7; color: #92400e;"{{ end }}
{{ if eq .Question.Difficulty "进阶" }}style="background-color: #ffedd5; color: #c2410c;"{{ end }}
{{ if eq .Question.Difficulty "高级" }}style="background-color: #fee2e2; color: #991b1b;"{{ end }}>
{{.Question.Difficulty}}
</span>
{{ end }}
{{ if .Question.Category }}
<span class="px-2 py-0.5 bg-indigo-100 text-indigo-600 rounded text-xs">{{.Question.Category}}</span>
{{ end }}
<span class="px-2 py-0.5 bg-slate-100 text-slate-600 rounded text-xs">{{.Question.Score}}分</span>
</div>
<button onclick="deleteFavorite('{{.ID}}')" class="btn btn-danger btn-xs" title="删除">
<i class="fas fa-trash"></i>
</button>
</div>
<p class="text-sm text-slate-800 mb-3 font-medium leading-relaxed">{{.Question.Text}}</p>
{{ if .Question.Options }}
<div class="bg-white p-3 rounded-lg mb-3 space-y-2">
{{ range $i, $opt := .Question.Options }}
<div class="flex items-start gap-2 text-sm">
<span class="w-5 h-5 rounded-full bg-slate-100 flex items-center justify-center text-xs text-slate-600 font-medium flex-shrink-0">{{index "ABCDE" $i}}</span>
<span class="text-slate-600">{{$opt}}</span>
</div>
{{ end }}
</div>
{{ end }}
<div class="flex flex-wrap items-center gap-4 mb-3">
<div class="flex items-center gap-1">
<span class="text-xs text-slate-500">正确答案:</span>
<span class="text-sm text-green-600 font-medium">{{.Question.Answer}}</span>
</div>
{{ if .UserAnswer }}
<div class="flex items-center gap-1">
<span class="text-xs text-slate-500">你的答案:</span>
<span class="text-sm text-red-600">{{.UserAnswer}}</span>
</div>
{{ end }}
</div>
{{ if .Question.Analysis }}
<div class="p-3 bg-white rounded-lg text-sm text-slate-600 border border-red-100">
<div class="font-medium text-slate-700 mb-1 flex items-center gap-1">
<i class="fas fa-lightbulb text-yellow-500"></i> 答案解析
</div>
<div class="markdown-content">{{.Question.Analysis}}</div>
</div>
{{ end }}
</div>
{{ end }}
{{ else }}
<div class="empty-state">
<div class="empty-state-icon">
<i class="fas fa-times-circle"></i>
</div>
<p>暂无错题记录</p>
</div>
{{ end }}
</div>
</div>
</div>
</div>
<style>
.tab-btn {
color: #64748b;
border-bottom-color: transparent;
cursor: pointer;
background: none;
outline: none;
}
.tab-btn:hover {
color: #334155;
}
.tab-btn.active {
color: var(--primary, #3b82f6);
border-bottom-color: var(--primary, #3b82f6);
}
.tab-panel {
animation: fadeIn 0.2s ease;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(0.25rem); }
to { opacity: 1; transform: translateY(0); }
}
</style>
<script>
function switchTab(tabName) {
document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
document.querySelectorAll('.tab-panel').forEach(panel => panel.classList.add('hidden'));
document.getElementById('tab-' + tabName).classList.add('active');
document.getElementById('panel-' + tabName).classList.remove('hidden');
const url = new URL(window.location);
url.searchParams.set('tab', tabName);
window.history.replaceState({}, '', url);
}
document.addEventListener('DOMContentLoaded', function() {
const urlParams = new URLSearchParams(window.location.search);
const tab = urlParams.get('tab') || 'records';
switchTab(tab);
});
function toggleFavorite(id) {
fetch('/dashboard/api/quiz/favorite/' + id + '/toggle', { method: 'POST' })
.then(response => response.json())
.then(result => {
if (result.success) {
location.reload();
} else {
showCustomAlert('error', '错误', '操作失败');
}
})
.catch(() => showCustomAlert('error', '错误', '操作失败'));
}
function deleteFavorite(id) {
showCustomConfirm('确认删除', '确定要删除这条记录吗?', function(confirmed) {
if (!confirmed) return;
fetch('/dashboard/api/quiz/favorite/' + id, { method: 'DELETE' })
.then(response => response.json())
.then(result => {
if (result.success) {
location.reload();
} else {
showCustomAlert('error', '错误', '删除失败');
}
})
.catch(() => showCustomAlert('error', '错误', '删除失败'));
});
}
</script>