709 lines
25 KiB
HTML
709 lines
25 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>{{student_name}} - 答题结果详情</title>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
font-family: 'Microsoft YaHei', sans-serif;
|
|
}
|
|
|
|
body {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
min-height: 100vh;
|
|
}
|
|
|
|
.container {
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
background: white;
|
|
border-radius: 20px;
|
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.header {
|
|
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
|
color: white;
|
|
padding: 30px;
|
|
text-align: center;
|
|
}
|
|
|
|
.header h1 {
|
|
font-size: 32px;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.header p {
|
|
opacity: 0.9;
|
|
font-size: 16px;
|
|
}
|
|
|
|
.student-info {
|
|
background: #f8f9fa;
|
|
padding: 20px 30px;
|
|
border-bottom: 1px solid #e9ecef;
|
|
}
|
|
|
|
.info-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: 20px;
|
|
}
|
|
|
|
.info-item {
|
|
background: white;
|
|
padding: 15px;
|
|
border-radius: 10px;
|
|
border-left: 4px solid #4facfe;
|
|
}
|
|
|
|
.info-label {
|
|
font-weight: 600;
|
|
color: #666;
|
|
margin-bottom: 5px;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.info-value {
|
|
color: #333;
|
|
font-size: 16px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.results-summary {
|
|
background: linear-gradient(135deg, #ff7e5f 0%, #feb47b 50%, #ff7e5f 100%);
|
|
color: white;
|
|
padding: 30px;
|
|
text-align: center;
|
|
}
|
|
|
|
.score-display {
|
|
font-size: 48px;
|
|
font-weight: bold;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.score-label {
|
|
font-size: 18px;
|
|
opacity: 0.9;
|
|
}
|
|
|
|
.results-container {
|
|
padding: 30px;
|
|
}
|
|
|
|
.question-card {
|
|
background: white;
|
|
border: 2px solid #e9ecef;
|
|
border-radius: 15px;
|
|
padding: 25px;
|
|
margin-bottom: 20px;
|
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
|
transition: all 0.3s;
|
|
}
|
|
|
|
.question-card.correct {
|
|
border-color: #4CAF50;
|
|
box-shadow: 0 2px 15px rgba(76, 175, 80, 0.1);
|
|
}
|
|
|
|
.question-card.incorrect {
|
|
border-color: #f44336;
|
|
box-shadow: 0 2px 15px rgba(244, 67, 54, 0.1);
|
|
}
|
|
|
|
.question-header {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.question-number {
|
|
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
|
color: white;
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-weight: 600;
|
|
margin-right: 15px;
|
|
font-size: 18px;
|
|
}
|
|
|
|
.question-type {
|
|
padding: 6px 12px;
|
|
border-radius: 20px;
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
margin-left: auto;
|
|
}
|
|
|
|
.question-type.基础题 {
|
|
background: #e8f5e8;
|
|
color: #2e7d32;
|
|
border: 1px solid #2e7d32;
|
|
}
|
|
|
|
.question-type.进阶题 {
|
|
background: #fff3e0;
|
|
color: #f57c00;
|
|
border: 1px solid #f57c00;
|
|
}
|
|
|
|
.question-type.竞赛题 {
|
|
background: #fce4ec;
|
|
color: #c2185b;
|
|
border: 1px solid #c2185b;
|
|
}
|
|
|
|
.result-status {
|
|
margin-left: 10px;
|
|
padding: 6px 12px;
|
|
border-radius: 20px;
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.result-status.correct {
|
|
background: #4CAF50;
|
|
color: white;
|
|
}
|
|
|
|
.result-status.incorrect {
|
|
background: #f44336;
|
|
color: white;
|
|
}
|
|
|
|
.question-text {
|
|
font-size: 18px;
|
|
color: #333;
|
|
line-height: 1.6;
|
|
margin-bottom: 20px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.options-list {
|
|
list-style: none;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.option-item {
|
|
background: #f8f9fa;
|
|
border: 2px solid #e9ecef;
|
|
border-radius: 8px;
|
|
padding: 15px 20px;
|
|
margin-bottom: 10px;
|
|
display: flex;
|
|
align-items: center;
|
|
position: relative;
|
|
}
|
|
|
|
.option-item.selected-correct {
|
|
background: #e8f5e8;
|
|
border-color: #4CAF50;
|
|
}
|
|
|
|
.option-item.selected-incorrect {
|
|
background: #ffebee;
|
|
border-color: #f44336;
|
|
}
|
|
|
|
.option-item.correct-answer {
|
|
background: #e3f2fd;
|
|
border-color: #2196F3;
|
|
}
|
|
|
|
.option-label {
|
|
display: inline-block;
|
|
width: 25px;
|
|
height: 25px;
|
|
background: #6c757d;
|
|
color: white;
|
|
text-align: center;
|
|
line-height: 25px;
|
|
border-radius: 50%;
|
|
margin-right: 12px;
|
|
font-weight: 600;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.option-text {
|
|
flex: 1;
|
|
font-size: 16px;
|
|
color: #333;
|
|
}
|
|
|
|
.option-status {
|
|
margin-left: 10px;
|
|
padding: 4px 8px;
|
|
border-radius: 12px;
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.option-status.user-correct {
|
|
background: #4CAF50;
|
|
color: white;
|
|
}
|
|
|
|
.option-status.user-incorrect {
|
|
background: #f44336;
|
|
color: white;
|
|
}
|
|
|
|
.option-status.correct-answer {
|
|
background: #2196F3;
|
|
color: white;
|
|
}
|
|
|
|
.answer-summary {
|
|
background: #f8f9fa;
|
|
padding: 15px;
|
|
border-radius: 8px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.answer-label {
|
|
font-weight: 600;
|
|
color: #666;
|
|
}
|
|
|
|
.answer-value {
|
|
font-weight: 500;
|
|
}
|
|
|
|
.answer-value.correct {
|
|
color: #4CAF50;
|
|
}
|
|
|
|
.answer-value.incorrect {
|
|
color: #f44336;
|
|
}
|
|
|
|
.loading {
|
|
text-align: center;
|
|
padding: 50px;
|
|
color: #666;
|
|
}
|
|
|
|
.error {
|
|
background: #ffebee;
|
|
color: #f44336;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
margin: 20px;
|
|
text-align: center;
|
|
}
|
|
|
|
.actions {
|
|
padding: 30px;
|
|
text-align: center;
|
|
background: transparent;
|
|
border-top: none;
|
|
}
|
|
|
|
.btn {
|
|
display: inline-block;
|
|
padding: 12px 24px;
|
|
margin: 0 10px;
|
|
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
|
color: white;
|
|
text-decoration: none;
|
|
border-radius: 25px;
|
|
font-weight: 600;
|
|
transition: all 0.3s;
|
|
border: none;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.btn:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 5px 15px rgba(79, 172, 254, 0.4);
|
|
}
|
|
|
|
.btn-secondary {
|
|
background: linear-gradient(135deg, #6c757d, #5a6268);
|
|
}
|
|
|
|
.btn-success {
|
|
background: linear-gradient(135deg, #4CAF50, #45a049);
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.container {
|
|
border-radius: 15px;
|
|
}
|
|
|
|
.header {
|
|
padding: 20px;
|
|
}
|
|
|
|
.header h1 {
|
|
font-size: 24px;
|
|
}
|
|
|
|
.info-grid {
|
|
grid-template-columns: 1fr;
|
|
gap: 10px;
|
|
}
|
|
|
|
.question-text {
|
|
font-size: 16px;
|
|
}
|
|
|
|
.option-text {
|
|
font-size: 14px;
|
|
}
|
|
|
|
.score-display {
|
|
font-size: 36px;
|
|
}
|
|
|
|
.btn {
|
|
display: block;
|
|
margin: 10px auto;
|
|
width: 80%;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="header">
|
|
<h1>📊 答题结果详情</h1>
|
|
<p>查看每道题的答题情况和正确答案</p>
|
|
</div>
|
|
|
|
<div class="student-info">
|
|
<div class="info-grid">
|
|
<div class="info-item">
|
|
<div class="info-label">姓名</div>
|
|
<div class="info-value">{{student_name}}</div>
|
|
</div>
|
|
<div class="info-item">
|
|
<div class="info-label">学校</div>
|
|
<div class="info-value">{{school}}</div>
|
|
</div>
|
|
<div class="info-item">
|
|
<div class="info-label">年级</div>
|
|
<div class="info-value">{{grade}}</div>
|
|
</div>
|
|
<div class="info-item">
|
|
<div class="info-label">选题范围</div>
|
|
<div class="info-value" id="selectedTagDisplay">正在加载...</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="results-summary">
|
|
<div class="score-display">
|
|
<div style="font-size: 36px; font-weight: bold;" id="totalScoreDisplay">正在计算...</div>
|
|
<div style="font-size: 16px; opacity: 0.9; margin-top: 5px;">
|
|
得分 / <span id="maxScoreDisplay">...</span>分
|
|
</div>
|
|
</div>
|
|
<div class="score-label">测评成绩</div>
|
|
<div style="margin-top: 15px; font-size: 14px; opacity: 0.9;">
|
|
<span id="correctCount">0</span> 题正确 /
|
|
<span id="answeredCount">0</span> 题已答
|
|
(共 <span id="totalQuestionsCount">0</span> 题)
|
|
(正确率: <span id="accuracy">0</span>%)
|
|
</div>
|
|
<div class="actions" style="margin-top: 25px; padding: 0;">
|
|
<button id="checkReportBtn" class="btn btn-success">查看详细报告</button>
|
|
<button id="refreshBtn" class="btn btn-secondary">刷新页面</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="results-container" id="resultsContainer">
|
|
<div class="loading">正在加载答题结果...</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
class QuizResultsSystem {
|
|
constructor() {
|
|
this.sessionId = '{{session_id}}';
|
|
this.results = null;
|
|
this.init();
|
|
}
|
|
|
|
async init() {
|
|
try {
|
|
await this.loadResults();
|
|
this.displayResults();
|
|
} catch (error) {
|
|
this.showError('加载答题结果失败: ' + error.message);
|
|
}
|
|
}
|
|
|
|
async loadResults() {
|
|
const response = await fetch(`/api/quiz-results/${this.sessionId}`);
|
|
if (!response.ok) {
|
|
throw new Error('无法获取答题结果');
|
|
}
|
|
this.results = await response.json();
|
|
|
|
if (!this.results.success) {
|
|
throw new Error('答题结果数据无效');
|
|
}
|
|
}
|
|
|
|
displayResults() {
|
|
// 更新学生信息
|
|
const selectedTagDisplay = document.getElementById('selectedTagDisplay');
|
|
if (selectedTagDisplay) {
|
|
selectedTagDisplay.textContent = this.results.selectedTag || '全部题目';
|
|
}
|
|
|
|
// 计算并显示统计信息
|
|
const answeredQuestions = this.results.results.length;
|
|
const correctQuestions = this.results.results.filter(r => r.isCorrect).length;
|
|
const totalQuestions = this.results.totalQuestions || answeredQuestions;
|
|
const accuracy = answeredQuestions > 0 ? Math.round((correctQuestions / answeredQuestions) * 100) : 0;
|
|
|
|
// 显示分数
|
|
document.getElementById('totalScoreDisplay').textContent = this.results.totalScore + '分';
|
|
document.getElementById('maxScoreDisplay').textContent = this.results.maxScore || 100;
|
|
|
|
// 显示题目统计
|
|
document.getElementById('correctCount').textContent = correctQuestions;
|
|
document.getElementById('answeredCount').textContent = answeredQuestions;
|
|
document.getElementById('totalQuestionsCount').textContent = totalQuestions;
|
|
document.getElementById('accuracy').textContent = accuracy;
|
|
|
|
// 显示题目详情
|
|
this.renderQuestions();
|
|
|
|
// 根据报告状态显示按钮
|
|
this.updateReportButton();
|
|
|
|
// 如果报告已生成,开始检查报告状态
|
|
if (this.results.sessionStatus === 'report_generated' || this.results.sessionStatus === 'completed') {
|
|
this.checkReportStatus();
|
|
}
|
|
}
|
|
|
|
renderQuestions() {
|
|
const container = document.getElementById('resultsContainer');
|
|
container.innerHTML = '';
|
|
|
|
this.results.results.forEach((result, index) => {
|
|
const questionCard = document.createElement('div');
|
|
questionCard.className = `question-card ${result.isCorrect ? 'correct' : 'incorrect'}`;
|
|
|
|
questionCard.innerHTML = `
|
|
<div class="question-header">
|
|
<div class="question-number">${result.questionNumber}</div>
|
|
<div class="question-type ${result.questionType}">${result.questionType}</div>
|
|
<div class="result-status ${result.isCorrect ? 'correct' : 'incorrect'}">
|
|
${result.isCorrect ? '✓ 正确' : '✗ 错误'}
|
|
</div>
|
|
</div>
|
|
<div class="question-text">${result.questionText}</div>
|
|
<ul class="options-list">
|
|
${this.renderOptions(result)}
|
|
</ul>
|
|
<div class="answer-summary">
|
|
<div>
|
|
<span class="answer-label">你的答案:</span>
|
|
<span class="answer-value ${result.isCorrect ? 'correct' : 'incorrect'}">${result.userAnswer || '未作答'}</span>
|
|
</div>
|
|
<div>
|
|
<span class="answer-label">正确答案:</span>
|
|
<span class="answer-value correct">${result.correctAnswer}</span>
|
|
</div>
|
|
<div>
|
|
<span class="answer-label">得分:</span>
|
|
<span class="answer-value">${result.isCorrect ? result.score : 0} 分</span>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
container.appendChild(questionCard);
|
|
});
|
|
}
|
|
|
|
renderOptions(result) {
|
|
let optionsHTML = '';
|
|
const labels = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'];
|
|
|
|
// 如果选项数据不存在或为空,显示提示信息
|
|
if (!result.options || Object.keys(result.options).length === 0) {
|
|
return '<li class="option-item" style="color: #666; font-style: italic;">选项数据暂不可用</li>';
|
|
}
|
|
|
|
labels.forEach(label => {
|
|
const optionText = result.options[label];
|
|
if (optionText) {
|
|
let optionClass = 'option-item';
|
|
let statusBadge = '';
|
|
|
|
if (result.userAnswer === label && result.isCorrect) {
|
|
optionClass += ' selected-correct';
|
|
statusBadge = '<span class="option-status user-correct">你的答案 ✓</span>';
|
|
} else if (result.userAnswer === label && !result.isCorrect) {
|
|
optionClass += ' selected-incorrect';
|
|
statusBadge = '<span class="option-status user-incorrect">你的答案 ✗</span>';
|
|
} else if (result.correctAnswer === label && result.userAnswer !== label) {
|
|
optionClass += ' correct-answer';
|
|
statusBadge = '<span class="option-status correct-answer">正确答案 ✓</span>';
|
|
}
|
|
|
|
optionsHTML += `
|
|
<li class="${optionClass}">
|
|
<span class="option-label">${label}</span>
|
|
<span class="option-text">${optionText}</span>
|
|
${statusBadge}
|
|
</li>
|
|
`;
|
|
}
|
|
});
|
|
|
|
// 如果没有找到任何选项,显示提示信息
|
|
if (!optionsHTML) {
|
|
optionsHTML = '<li class="option-item" style="color: #666; font-style: italic;">选项数据格式异常</li>';
|
|
}
|
|
|
|
return optionsHTML;
|
|
}
|
|
|
|
async checkReportStatus() {
|
|
// 如果报告已经生成,直接显示按钮
|
|
if (this.results.sessionStatus === 'report_generated') {
|
|
this.showReportButton();
|
|
return;
|
|
}
|
|
|
|
// 否则,先短暂等待,然后开始检查
|
|
setTimeout(() => {
|
|
this.startReportStatusCheck();
|
|
}, 2000);
|
|
}
|
|
|
|
updateReportButton() {
|
|
const reportBtn = document.getElementById('checkReportBtn');
|
|
|
|
if (this.results.sessionStatus === 'report_generated') {
|
|
// 报告已生成,按钮可点击
|
|
reportBtn.textContent = '查看详细报告';
|
|
reportBtn.disabled = false;
|
|
reportBtn.style.opacity = '1';
|
|
reportBtn.style.cursor = 'pointer';
|
|
reportBtn.onclick = () => {
|
|
// 获取报告ID并跳转到报告页面
|
|
this.navigateToReport();
|
|
};
|
|
} else {
|
|
// 报告未生成,显示"报告生成中"且不可点击
|
|
reportBtn.textContent = '报告生成中';
|
|
reportBtn.disabled = true;
|
|
reportBtn.style.opacity = '0.6';
|
|
reportBtn.style.cursor = 'not-allowed';
|
|
reportBtn.onclick = null;
|
|
}
|
|
}
|
|
|
|
async navigateToReport() {
|
|
try {
|
|
// 首先尝试从 results 中获取报告ID
|
|
if (this.results.reportId) {
|
|
window.location.href = `/report.html?id=${this.results.reportId}`;
|
|
return;
|
|
}
|
|
|
|
// 如果没有,则通过 API 获取报告列表
|
|
const response = await fetch('/api/reports');
|
|
const data = await response.json();
|
|
|
|
const report = data.reports.find(r => r.session_id === this.sessionId);
|
|
|
|
if (report) {
|
|
window.location.href = `/report.html?id=${report.id}`;
|
|
} else {
|
|
// 如果找不到报告,显示提示信息
|
|
alert('报告正在生成中,请稍后再试');
|
|
// 刷新页面重新检查状态
|
|
setTimeout(() => {
|
|
window.location.reload();
|
|
}, 1000);
|
|
}
|
|
} catch (error) {
|
|
console.error('获取报告失败:', error);
|
|
alert('获取报告失败,请稍后再试');
|
|
}
|
|
}
|
|
|
|
showReportButton() {
|
|
const reportBtn = document.getElementById('checkReportBtn');
|
|
reportBtn.textContent = '查看详细报告';
|
|
reportBtn.disabled = false;
|
|
reportBtn.style.opacity = '1';
|
|
reportBtn.style.cursor = 'pointer';
|
|
reportBtn.onclick = () => {
|
|
// 获取报告ID并跳转到报告页面
|
|
this.navigateToReport();
|
|
};
|
|
}
|
|
|
|
startReportStatusCheck() {
|
|
let checkCount = 0;
|
|
const maxChecks = 30;
|
|
|
|
const checkInterval = setInterval(async () => {
|
|
checkCount++;
|
|
|
|
try {
|
|
const response = await fetch('/api/reports');
|
|
const data = await response.json();
|
|
|
|
const report = data.reports.find(r => r.session_id === this.sessionId);
|
|
|
|
if (report) {
|
|
clearInterval(checkInterval);
|
|
const reportBtn = document.getElementById('checkReportBtn');
|
|
reportBtn.style.display = 'inline-block';
|
|
reportBtn.textContent = '查看详细报告';
|
|
reportBtn.onclick = () => {
|
|
window.location.href = `/report.html?id=${report.id}`;
|
|
};
|
|
} else if (checkCount >= maxChecks) {
|
|
clearInterval(checkInterval);
|
|
// 超时后显示提示信息
|
|
const reportBtn = document.getElementById('checkReportBtn');
|
|
reportBtn.style.display = 'inline-block';
|
|
reportBtn.textContent = '报告生成中';
|
|
reportBtn.disabled = true;
|
|
reportBtn.style.opacity = '0.6';
|
|
}
|
|
} catch (error) {
|
|
console.error('检查报告状态失败:', error);
|
|
}
|
|
}, 2000);
|
|
}
|
|
|
|
showError(message) {
|
|
const container = document.getElementById('resultsContainer');
|
|
container.innerHTML = `<div class="error">${message}</div>`;
|
|
}
|
|
}
|
|
|
|
// 初始化系统
|
|
window.quizResults = new QuizResultsSystem();
|
|
|
|
// 刷新按钮事件
|
|
document.getElementById('refreshBtn').addEventListener('click', () => {
|
|
window.location.reload();
|
|
});
|
|
</script>
|
|
</body>
|
|
</html> |