Files
dsProject/dsLightRag/static/YunXiao/physics_quiz.js
2025-08-28 08:20:31 +08:00

505 lines
20 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 题目数据数组
const questions = [
// 简单难度题目 (5道)
{
difficulty: 'easy',
text: '下列哪个物理量是矢量?',
options: ['A. 质量', 'B. 时间', 'C. 位移', 'D. 路程'],
answer: 'C',
explanation: '矢量具有大小和方向,位移是矢量,其他选项均为标量。'
},
{
difficulty: 'easy',
text: '下列哪个物理量是矢量?',
options: ['A. 质量', 'B. 时间', 'C. 位移', 'D. 路程'],
answer: 'C',
explanation: '矢量具有大小和方向,位移是矢量,其他选项均为标量。'
},
{
difficulty: 'easy',
text: '下列哪个物理量是矢量?',
options: ['A. 质量', 'B. 时间', 'C. 位移', 'D. 路程'],
answer: 'C',
explanation: '矢量具有大小和方向,位移是矢量,其他选项均为标量。'
},
{
difficulty: 'easy',
text: '下列哪个物理量是矢量?',
options: ['A. 质量', 'B. 时间', 'C. 位移', 'D. 路程'],
answer: 'C',
explanation: '矢量具有大小和方向,位移是矢量,其他选项均为标量。'
},
{
difficulty: 'easy',
text: '下列哪个物理量是矢量?',
options: ['A. 质量', 'B. 时间', 'C. 位移', 'D. 路程'],
answer: 'C',
explanation: '矢量具有大小和方向,位移是矢量,其他选项均为标量。'
},
// 中等难度题目 (5道)
{
difficulty: 'medium',
text: '关于万有引力定律,下列说法正确的是?',
options: [
'A. 万有引力只存在于天体之间',
'B. 万有引力与物体质量成正比,与距离成反比',
'C. 万有引力的发现者是牛顿',
'D. 地球对苹果的引力大于苹果对地球的引力'
],
answer: 'C',
explanation: '万有引力存在于任何有质量的物体之间A错误万有引力与距离的平方成反比B错误牛顿发现了万有引力定律C正确物体间的引力是相互的大小相等D错误。'
},
{
difficulty: 'medium',
text: '关于万有引力定律,下列说法正确的是?',
options: [
'A. 万有引力只存在于天体之间',
'B. 万有引力与物体质量成正比,与距离成反比',
'C. 万有引力的发现者是牛顿',
'D. 地球对苹果的引力大于苹果对地球的引力'
],
answer: 'C',
explanation: '万有引力存在于任何有质量的物体之间A错误万有引力与距离的平方成反比B错误牛顿发现了万有引力定律C正确物体间的引力是相互的大小相等D错误。'
},
{
difficulty: 'medium',
text: '关于万有引力定律,下列说法正确的是?',
options: [
'A. 万有引力只存在于天体之间',
'B. 万有引力与物体质量成正比,与距离成反比',
'C. 万有引力的发现者是牛顿',
'D. 地球对苹果的引力大于苹果对地球的引力'
],
answer: 'C',
explanation: '万有引力存在于任何有质量的物体之间A错误万有引力与距离的平方成反比B错误牛顿发现了万有引力定律C正确物体间的引力是相互的大小相等D错误。'
},
{
difficulty: 'medium',
text: '关于万有引力定律,下列说法正确的是?',
options: [
'A. 万有引力只存在于天体之间',
'B. 万有引力与物体质量成正比,与距离成反比',
'C. 万有引力的发现者是牛顿',
'D. 地球对苹果的引力大于苹果对地球的引力'
],
answer: 'C',
explanation: '万有引力存在于任何有质量的物体之间A错误万有引力与距离的平方成反比B错误牛顿发现了万有引力定律C正确物体间的引力是相互的大小相等D错误。'
},
{
difficulty: 'medium',
text: '关于万有引力定律,下列说法正确的是?',
options: [
'A. 万有引力只存在于天体之间',
'B. 万有引力与物体质量成正比,与距离成反比',
'C. 万有引力的发现者是牛顿',
'D. 地球对苹果的引力大于苹果对地球的引力'
],
answer: 'C',
explanation: '万有引力存在于任何有质量的物体之间A错误万有引力与距离的平方成反比B错误牛顿发现了万有引力定律C正确物体间的引力是相互的大小相等D错误。'
},
// 困难难度题目 (5道)
{
difficulty: 'hard',
text: '相对论中质量与能量的关系表达式是?',
options: ['A. E=mc²', 'B. F=ma', 'C. E=hv', 'D. P=mv'],
answer: 'A',
explanation: '爱因斯坦的质能方程E=mc²表明质量和能量可以相互转换。'
},
{
difficulty: 'hard',
text: '相对论中质量与能量的关系表达式是?',
options: ['A. E=mc²', 'B. F=ma', 'C. E=hv', 'D. P=mv'],
answer: 'A',
explanation: '爱因斯坦的质能方程E=mc²表明质量和能量可以相互转换。'
},
{
difficulty: 'hard',
text: '相对论中质量与能量的关系表达式是?',
options: ['A. E=mc²', 'B. F=ma', 'C. E=hv', 'D. P=mv'],
answer: 'A',
explanation: '爱因斯坦的质能方程E=mc²表明质量和能量可以相互转换。'
},
{
difficulty: 'hard',
text: '相对论中质量与能量的关系表达式是?',
options: ['A. E=mc²', 'B. F=ma', 'C. E=hv', 'D. P=mv'],
answer: 'A',
explanation: '爱因斯坦的质能方程E=mc²表明质量和能量可以相互转换。'
},
{
difficulty: 'hard',
text: '相对论中质量与能量的关系表达式是?',
options: ['A. E=mc²', 'B. F=ma', 'C. E=hv', 'D. P=mv'],
answer: 'A',
explanation: '爱因斯坦的质能方程E=mc²表明质量和能量可以相互转换。'
},
];
// 当前难度级别,初始为中等
let currentDifficulty = 'medium';
// 已完成的难度
const completedDifficulties = [];
// 各难度得分情况
const scores = {
easy: { score: 0, correct: 0, incorrect: 0 },
medium: { score: 0, correct: 0, incorrect: 0 },
hard: { score: 0, correct: 0, incorrect: 0 }
};
// 学伴功能相关变量
let mediaRecorder; let audioChunks = []; let isRecording = false;
let audioElement = null; let isPlaying = false;
let currentQuestion = null;
// DOM元素
const questionContainer = document.getElementById('question-container');
const submitBtn = document.getElementById('submit-btn');
const resultSection = document.getElementById('result-section');
const totalScoreElement = document.getElementById('total-score');
const correctCountElement = document.getElementById('correct-count');
const incorrectCountElement = document.getElementById('incorrect-count');
const navigationSection = document.getElementById('navigation-section');
const navigationMessage = document.getElementById('navigation-message');
const nextBtn = document.getElementById('next-btn');
const difficultyIndicator = document.querySelector('.difficulty-indicator');
// 学伴功能DOM元素
const recordingIndicator = document.getElementById('recordingIndicator');
const thinkingIndicator = document.getElementById('thinkingIndicator');
const resultContainer = document.getElementById('resultContainer');
const asrResultText = document.getElementById('asrResultText');
const feedbackResultText = document.getElementById('feedbackResultText');
const playAudioBtn = document.getElementById('playAudioBtn');
const audioProgress = document.getElementById('audioProgress');
const progressBar = document.getElementById('progressBar');
const audioTime = document.getElementById('audioTime');
// 初始化页面
document.addEventListener('DOMContentLoaded', () => {
renderQuestions(currentDifficulty);
// 绑定学伴答疑按钮事件
document.addEventListener('click', function(e) {
if (e.target && e.target.classList.contains('ask-xueban-btn')) {
currentQuestion = {
number: e.target.dataset.question,
difficulty: e.target.dataset.difficulty
};
startRecording();
}
});
});
// 渲染指定难度的题目
function renderQuestions(difficulty) {
questionContainer.innerHTML = '';
// 筛选当前难度的题目
const filteredQuestions = questions.filter(q => q.difficulty === difficulty);
// 生成题目HTML
filteredQuestions.forEach((q, index) => {
const questionNumber = index + 1;
const questionElement = document.createElement('div');
questionElement.className = 'question';
questionElement.dataset.difficulty = q.difficulty;
questionElement.dataset.question = questionNumber;
questionElement.innerHTML = `
<div class="question-header">
<div class="question-number">${getDifficultyName(q.difficulty)}难度 - 问题 ${questionNumber}</div>
<div class="question-points">20分</div>
</div>
<p class="question-text">${q.text}</p>
<div class="options">
${q.options.map((option, optIndex) => {
const optionLetter = String.fromCharCode(65 + optIndex);
return `
<div class="option">
<input type="radio" name="q${q.difficulty}-${questionNumber}" id="q${q.difficulty}-${questionNumber}-${optionLetter.toLowerCase()}" value="${optionLetter}">
<label for="q${q.difficulty}-${questionNumber}-${optionLetter.toLowerCase()}">${option}</label>
</div>
`;
}).join('')}
</div>
<div class="explanation" id="explanation-${q.difficulty}-${questionNumber}">${q.explanation}</div>
<button class="ask-xueban-btn hidden" data-question="${questionNumber}" data-difficulty="${q.difficulty}">学伴答疑</button>
`;
questionContainer.appendChild(questionElement);
});
}
// 获取难度名称
function getDifficultyName(difficulty) {
const names = {
easy: '简单',
medium: '中等',
hard: '高级'
};
return names[difficulty] || difficulty;
}
// 提交答案
submitBtn.addEventListener('click', () => {
checkAnswers();
submitBtn.disabled = true;
submitBtn.textContent = '已提交';
});
// 检查答案
function checkAnswers() {
let correctCount = 0;
let incorrectCount = 0;
let totalScore = 0;
// 获取当前难度的题目
const currentQuestions = questions.filter(q => q.difficulty === currentDifficulty);
// 检查每道题的答案
currentQuestions.forEach((q, index) => {
const questionNumber = index + 1;
const selectedOption = document.querySelector(`input[name="q${q.difficulty}-${questionNumber}"]:checked`);
const explanationElement = document.getElementById(`explanation-${q.difficulty}-${questionNumber}`);
const optionsContainer = document.querySelector(`div[data-difficulty="${q.difficulty}"][data-question="${questionNumber}"] .options`);
const allOptions = optionsContainer.querySelectorAll('.option');
// 显示解析
explanationElement.classList.add('show');
if (!selectedOption) {
// 未答题
optionsContainer.style.border = '2px solid #f39c12';
return;
}
// 标记正确或错误
const selectedValue = selectedOption.value;
if (selectedValue === q.answer) {
// 正确
selectedOption.closest('.option').classList.add('correct');
correctCount++;
totalScore += 20;
} else {
// 错误
selectedOption.closest('.option').classList.add('incorrect');
// 标记正确答案
allOptions.forEach(option => {
if (option.querySelector(`input[value="${q.answer}"]`)) {
option.classList.add('correct');
}
});
incorrectCount++;
}
});
// 更新当前难度得分
scores[currentDifficulty] = {
score: totalScore,
correct: correctCount,
incorrect: incorrectCount
};
// 添加到已完成难度
if (!completedDifficulties.includes(currentDifficulty)) {
completedDifficulties.push(currentDifficulty);
}
// 显示结果
showResults();
// 显示下一步建议
showNextRecommendation();
}
// 显示结果
function showResults() {
resultSection.style.display = 'block';
// 计算总得分
let totalScore = 0;
let totalCorrect = 0;
let totalIncorrect = 0;
completedDifficulties.forEach(difficulty => {
totalScore += scores[difficulty].score;
totalCorrect += scores[difficulty].correct;
totalIncorrect += scores[difficulty].incorrect;
});
// 更新结果显示
totalScoreElement.textContent = totalScore;
correctCountElement.textContent = totalCorrect;
incorrectCountElement.textContent = totalIncorrect;
// 滚动到结果区域
resultSection.scrollIntoView({ behavior: 'smooth' });
}
// 显示下一步建议
function showNextRecommendation() {
navigationSection.style.display = 'block';
const currentScore = scores[currentDifficulty];
if (currentDifficulty === 'medium') {
if (currentScore.correct >= 3) {
navigationMessage.textContent = `恭喜!您在${getDifficultyName(currentDifficulty)}难度中答对了${currentScore.correct}题,表现优秀,建议挑战高级难度。`;
nextBtn.onclick = () => switchDifficulty('hard');
} else {
navigationMessage.textContent = `您在${getDifficultyName(currentDifficulty)}难度中答对了${currentScore.correct}题,建议先尝试简单难度巩固基础。`;
nextBtn.onclick = () => switchDifficulty('easy');
}
} else if (currentDifficulty === 'easy') {
navigationMessage.textContent = `您已完成${getDifficultyName(currentDifficulty)}难度,建议继续挑战中等难度。`;
nextBtn.onclick = () => switchDifficulty('medium');
} else if (currentDifficulty === 'hard') {
navigationMessage.textContent = `恭喜您完成了所有难度级别!您的总分为${scores.easy.score + scores.medium.score + scores.hard.score}分。`;
nextBtn.style.display = 'none';
}
navigationSection.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
// 切换难度
function switchDifficulty(nextDifficulty) {
currentDifficulty = nextDifficulty;
difficultyIndicator.textContent = `当前难度:${getDifficultyName(nextDifficulty)}`;
difficultyIndicator.className = `difficulty-indicator difficulty-${nextDifficulty}`;
// 重置按钮状态
submitBtn.disabled = false;
submitBtn.textContent = '提交答案';
// 隐藏结果和导航区域
resultSection.style.display = 'none';
navigationSection.style.display = 'none';
// 渲染新难度题目
renderQuestions(nextDifficulty);
// 滚动到页面顶部
window.scrollTo({ top: 0, behavior: 'smooth' });
}
// 学伴功能 - 开始录音
function startRecording() {
if (isRecording) return;
navigator.mediaDevices.getUserMedia({ audio: true })
.then(stream => {
mediaRecorder = new MediaRecorder(stream);
audioChunks = [];
mediaRecorder.ondataavailable = event => {
if (event.data.size > 0) audioChunks.push(event.data);
};
mediaRecorder.onstop = () => {
const audioBlob = new Blob(audioChunks, { type: 'audio/wav' });
uploadAudioToServer(audioBlob);
};
mediaRecorder.start();
isRecording = true;
recordingIndicator.style.display = 'flex';
// 设置最长录音时间为60秒
setTimeout(stopRecording, 60000);
})
.catch(error => {
console.error("获取麦克风权限失败:", error);
alert("请授权麦克风权限以使用录音功能");
});
}
// 学伴功能 - 停止录音
function stopRecording() {
if (!isRecording || !mediaRecorder) return;
mediaRecorder.stop();
isRecording = false;
recordingIndicator.style.display = 'none';
if (mediaRecorder.stream) {
mediaRecorder.stream.getTracks().forEach(track => track.stop());
}
}
// 学伴功能 - 上传音频到服务器
function uploadAudioToServer(audioBlob) {
thinkingIndicator.style.display = 'flex';
resultContainer.style.display = 'none';
const formData = new FormData();
formData.append('file', audioBlob, 'recording.wav');
if (currentQuestion) {
formData.append('question_number', currentQuestion.number);
formData.append('difficulty', currentQuestion.difficulty);
}
fetch('/api/xueban/upload-audio', { method: 'POST', body: formData })
.then(response => response.json())
.then(data => {
thinkingIndicator.style.display = 'none';
if (data.success) {
asrResultText.textContent = data.asr_text || '未识别到内容';
feedbackResultText.textContent = data.feedback_text || '无反馈内容';
resultContainer.style.display = 'flex';
if (data.audio_url) {
audioElement = new Audio(data.audio_url);
audioElement.onloadedmetadata = function() { updateAudioTimeDisplay(); };
audioElement.ontimeupdate = function() { updateAudioProgress(); updateAudioTimeDisplay(); };
audioElement.onended = function() { isPlaying = false; updatePlayButton(); };
playAudioBtn.onclick = togglePlayAudio;
audioProgress.onclick = function(e) {
const rect = audioProgress.getBoundingClientRect();
const clickPosition = (e.clientX - rect.left) / rect.width;
audioElement.currentTime = clickPosition * audioElement.duration;
};
}
} else {
alert('音频处理失败: ' + data.message);
}
})
.catch(error => {
thinkingIndicator.style.display = 'none';
alert('上传音频失败: ' + error.message);
});
}
// 学伴功能 - 切换音频播放/暂停
function togglePlayAudio() {
if (!audioElement) return;
isPlaying ? audioElement.pause() : audioElement.play();
isPlaying = !isPlaying;
updatePlayButton();
}
// 学伴功能 - 更新播放按钮状态
function updatePlayButton() {
playAudioBtn.innerHTML = isPlaying ?
`<svg width="20" height="20" viewBox="0 0 24 24" fill="none"><path d="M6 19H10V5H6V19ZM14 19H18V5H14V19Z" fill="white"/></svg>` :
`<svg width="20" height="20" viewBox="0 0 24 24" fill="none"><path d="M8 5V19L19 12L8 5Z" fill="white"/></svg>`;
}
// 学伴功能 - 更新音频进度条
function updateAudioProgress() {
if (audioElement && audioElement.duration) {
progressBar.style.width = `${(audioElement.currentTime / audioElement.duration) * 100}%`;
}
}
// 学伴功能 - 更新音频时间显示
function updateAudioTimeDisplay() {
if (audioElement && audioElement.duration) {
const currentTime = formatTime(audioElement.currentTime);
const duration = formatTime(audioElement.duration);
audioTime.textContent = `${currentTime} / ${duration}`;
}
}
// 学伴功能 - 格式化时间为 MM:SS
function formatTime(seconds) {
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
}