diff --git a/dsLightRag/static/XueBan.html b/dsLightRag/static/XueBan.html index aee173a3..c0021cc6 100644 --- a/dsLightRag/static/XueBan.html +++ b/dsLightRag/static/XueBan.html @@ -179,287 +179,298 @@ 有权限问题的话,请点击这里 - + + +
+ + +
- // 录音相关变量 - let mediaRecorder; let audioChunks = []; let isRecording = false; - // 音频播放相关变量 - let audioElement = null; let isPlaying = false; + // 模型配置 - 使用与Sample.html相同的CDN链接 + const models = { + shizuku: { jsonPath: "https://unpkg.com/live2d-widget-model-shizuku@1.0.5/assets/shizuku.model.json", name: "小智" }, + koharu: { jsonPath: "https://unpkg.com/live2d-widget-model-koharu@1.0.5/assets/koharu.model.json", name: "小荷" }, + wanko: { jsonPath: "https://unpkg.com/live2d-widget-model-wanko@1.0.5/assets/wanko.model.json", name: "汪喵" } + }; - // 获取URL参数 - function getUrlParam(name) { - const reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)'); - const r = window.location.search.substr(1).match(reg); - return r ? unescape(r[2]) : null; - } + // 录音相关变量 + let mediaRecorder; let audioChunks = []; let isRecording = false; + // 音频播放相关变量 + let audioElement = null; let isPlaying = false; - // 开始录音 - function startRecording() { - if (isRecording) return; + // 获取URL参数 + function getUrlParam(name) { + const reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)'); + const r = window.location.search.substr(1).match(reg); + return r ? unescape(r[2]) : null; + } - console.log("尝试开始录音"); - navigator.mediaDevices.getUserMedia({ audio: true }) - .then(stream => { - mediaRecorder = new MediaRecorder(stream); - audioChunks = []; + // 开始录音 + function startRecording() { + if (isRecording) return; - mediaRecorder.ondataavailable = event => { - if (event.data.size > 0) audioChunks.push(event.data); - }; + console.log("尝试开始录音"); + navigator.mediaDevices.getUserMedia({ audio: true }) + .then(stream => { + mediaRecorder = new MediaRecorder(stream); + audioChunks = []; - mediaRecorder.onstop = () => { - const audioBlob = new Blob(audioChunks, { type: 'audio/wav' }); - console.log("录音完成,音频数据大小:", audioBlob.size); - const audioUrl = URL.createObjectURL(audioBlob); - console.log("录音URL:", audioUrl); - // 这里可以调用ASR服务 - uploadAudioToServer(audioBlob); - }; + mediaRecorder.ondataavailable = event => { + if (event.data.size > 0) audioChunks.push(event.data); + }; - mediaRecorder.start(); - isRecording = true; - document.getElementById('recordingIndicator').style.display = 'flex'; - document.getElementById('startRecordBtn').style.display = 'none'; - document.getElementById('stopRecordBtn').style.display = 'flex'; - console.log("开始录音成功"); + mediaRecorder.onstop = () => { + const audioBlob = new Blob(audioChunks, { type: 'audio/wav' }); + console.log("录音完成,音频数据大小:", audioBlob.size); + const audioUrl = URL.createObjectURL(audioBlob); + console.log("录音URL:", audioUrl); + // 这里可以调用ASR服务 + uploadAudioToServer(audioBlob); + }; - // 设置最长录音时间为60秒 - setTimeout(stopRecording, 60000); - }) - .catch(error => { - console.error("获取麦克风权限失败:", error); - alert("请授权麦克风权限以使用录音功能"); - }); - } + mediaRecorder.start(); + isRecording = true; + document.getElementById('recordingIndicator').style.display = 'flex'; + document.getElementById('startRecordBtn').style.display = 'none'; + document.getElementById('stopRecordBtn').style.display = 'flex'; + console.log("开始录音成功"); - // 停止录音 - function stopRecording() { - if (!isRecording || !mediaRecorder) return; - - mediaRecorder.stop(); - isRecording = false; - document.getElementById('recordingIndicator').style.display = 'none'; - document.getElementById('startRecordBtn').style.display = 'flex'; - document.getElementById('stopRecordBtn').style.display = 'none'; - console.log("停止录音"); - - if (mediaRecorder.stream) { - mediaRecorder.stream.getTracks().forEach(track => track.stop()); - } - } - - // 上传音频到服务器 - function uploadAudioToServer(audioBlob) { - console.log("开始上传音频到服务器"); - // 显示思考中动画 - document.getElementById('thinkingIndicator').style.display = 'flex'; - - const formData = new FormData(); - formData.append('file', audioBlob, 'recording.wav'); - - fetch('/api/xueban/upload-audio', { - method: 'POST', - body: formData - }) - .then(response => { - if (!response.ok) { - throw new Error('服务器响应错误'); - } - return response.json(); - }) - .then(data => { - console.log("处理结果:", data); - // 隐藏思考中动画 - document.getElementById('thinkingIndicator').style.display = 'none'; - - if (data.success) { - showResults(data.data); - } else { - alert('音频处理失败: ' + data.message); - } + // 设置最长录音时间为60秒 + setTimeout(stopRecording, 60000); }) .catch(error => { - console.error("上传音频失败:", error); - // 隐藏思考中动画 - document.getElementById('thinkingIndicator').style.display = 'none'; - - alert('上传音频失败: ' + error.message); + console.error("获取麦克风权限失败:", error); + alert("请授权麦克风权限以使用录音功能"); }); + } + + // 停止录音 + function stopRecording() { + if (!isRecording || !mediaRecorder) return; + + mediaRecorder.stop(); + isRecording = false; + document.getElementById('recordingIndicator').style.display = 'none'; + document.getElementById('startRecordBtn').style.display = 'flex'; + document.getElementById('stopRecordBtn').style.display = 'none'; + console.log("停止录音"); + + if (mediaRecorder.stream) { + mediaRecorder.stream.getTracks().forEach(track => track.stop()); } + } - // 显示ASR识别结果和反馈 - function showResults(data) { - // 更新结果显示容器 - const resultContainer = document.getElementById('resultContainer'); - resultContainer.style.display = 'flex'; + // 上传音频到服务器 + function uploadAudioToServer(audioBlob) { + console.log("开始上传音频到服务器"); + // 显示思考中动画 + document.getElementById('thinkingIndicator').style.display = 'flex'; + + const formData = new FormData(); + formData.append('file', audioBlob, 'recording.wav'); - // 显示ASR结果 - document.getElementById('asrResultText').textContent = data.asr_text || '未识别到内容'; - - // 显示反馈文本 - document.getElementById('feedbackResultText').textContent = data.feedback_text || '无反馈内容'; - - // 准备音频播放 - if (data.audio_url) { - if (audioElement) { - audioElement.pause(); - audioElement = null; - } - - audioElement = new Audio(data.audio_url); - audioElement.onloadedmetadata = function() { - updateAudioTimeDisplay(); - // 音频加载完成后自动播放 - try { - audioElement.play(); - isPlaying = true; - updatePlayButton(); - } catch (e) { - console.error("自动播放失败:", e); - } - // 无论自动播放是否成功,都显示播放按钮 - document.getElementById('playAudioBtn').style.display = 'flex'; - }; - - audioElement.ontimeupdate = function() { - updateAudioProgress(); - updateAudioTimeDisplay(); - }; - - audioElement.onended = function() { - isPlaying = false; - updatePlayButton(); - }; - - // 绑定播放按钮事件 - document.getElementById('playAudioBtn').onclick = togglePlayAudio; - - // 绑定进度条点击事件 - document.getElementById('audioProgress').onclick = function(e) { - if (!audioElement) return; - - const progressBar = document.getElementById('audioProgress'); - const rect = progressBar.getBoundingClientRect(); - const clickPosition = (e.clientX - rect.left) / rect.width; - audioElement.currentTime = clickPosition * audioElement.duration; - }; + fetch('/api/xueban/upload-audio', { + method: 'POST', + body: formData + }) + .then(response => { + if (!response.ok) { + throw new Error('服务器响应错误'); } - } + return response.json(); + }) + .then(data => { + console.log("处理结果:", data); + // 隐藏思考中动画 + document.getElementById('thinkingIndicator').style.display = 'none'; + + if (data.success) { + showResults(data.data); + } else { + alert('音频处理失败: ' + data.message); + } + }) + .catch(error => { + console.error("上传音频失败:", error); + // 隐藏思考中动画 + document.getElementById('thinkingIndicator').style.display = 'none'; + + alert('上传音频失败: ' + error.message); + }); + } - // 切换音频播放/暂停 - function togglePlayAudio() { - if (!audioElement) return; + // 显示ASR识别结果和反馈 + function showResults(data) { + // 更新结果显示容器 + const resultContainer = document.getElementById('resultContainer'); + resultContainer.style.display = 'flex'; - if (isPlaying) { + // 显示ASR结果 + document.getElementById('asrResultText').textContent = data.asr_text || '未识别到内容'; + + // 显示反馈文本 + document.getElementById('feedbackResultText').textContent = data.feedback_text || '无反馈内容'; + + // 准备音频播放 + if (data.audio_url) { + if (audioElement) { audioElement.pause(); - } else { - audioElement.play(); + audioElement = null; } - isPlaying = !isPlaying; - updatePlayButton(); + + audioElement = new Audio(data.audio_url); + audioElement.onloadedmetadata = function() { + updateAudioTimeDisplay(); + // 音频加载完成后自动播放 + try { + audioElement.play(); + isPlaying = true; + updatePlayButton(); + } catch (e) { + console.error("自动播放失败:", e); + } + // 无论自动播放是否成功,都显示播放按钮 + document.getElementById('playAudioBtn').style.display = 'flex'; + }; + + audioElement.ontimeupdate = function() { + updateAudioProgress(); + updateAudioTimeDisplay(); + }; + + audioElement.onended = function() { + isPlaying = false; + updatePlayButton(); + }; + + // 绑定播放按钮事件 + document.getElementById('playAudioBtn').onclick = togglePlayAudio; + + // 绑定进度条点击事件 + document.getElementById('audioProgress').onclick = function(e) { + if (!audioElement) return; + + const progressBar = document.getElementById('audioProgress'); + const rect = progressBar.getBoundingClientRect(); + const clickPosition = (e.clientX - rect.left) / rect.width; + audioElement.currentTime = clickPosition * audioElement.duration; + }; } + } - // 更新播放按钮状态 - function updatePlayButton() { - const playButton = document.getElementById('playAudioBtn'); - if (isPlaying) { - playButton.innerHTML = ` - - - - `; - } else { - playButton.innerHTML = ` - - - - `; - } + // 切换音频播放/暂停 + function togglePlayAudio() { + if (!audioElement) return; + + if (isPlaying) { + audioElement.pause(); + } else { + audioElement.play(); } + isPlaying = !isPlaying; + updatePlayButton(); + } - // 更新音频进度条 - function updateAudioProgress() { - if (!audioElement || !audioElement.duration) return; - - const progress = (audioElement.currentTime / audioElement.duration) * 100; - document.getElementById('progressBar').style.width = `${progress}%`; + // 更新播放按钮状态 + function updatePlayButton() { + const playButton = document.getElementById('playAudioBtn'); + if (isPlaying) { + playButton.innerHTML = ` + + + + `; + } else { + playButton.innerHTML = ` + + + + `; } + } - // 更新音频时间显示 - function updateAudioTimeDisplay() { - if (!audioElement || !audioElement.duration) return; + // 更新音频进度条 + function updateAudioProgress() { + if (!audioElement || !audioElement.duration) return; - const currentTime = formatTime(audioElement.currentTime); - const duration = formatTime(audioElement.duration); - document.getElementById('audioTime').textContent = `${currentTime} / ${duration}`; - } + const progress = (audioElement.currentTime / audioElement.duration) * 100; + document.getElementById('progressBar').style.width = `${progress}%`; + } - // 格式化时间为 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')}`; - } + // 更新音频时间显示 + function updateAudioTimeDisplay() { + if (!audioElement || !audioElement.duration) return; - // 初始化看板娘 - 简化为Sample.html的工作版本 - function initL2Dwidget() { - const modelId = getUrlParam('id') || 'koharu'; - const model = models[modelId] || models.koharu; + const currentTime = formatTime(audioElement.currentTime); + const duration = formatTime(audioElement.duration); + document.getElementById('audioTime').textContent = `${currentTime} / ${duration}`; + } - document.getElementById('model-select').value = modelId; - console.log('加载模型:', model.name, model.jsonPath); + // 格式化时间为 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')}`; + } - // 初始化模型 - 与Sample.html相同的配置 - L2Dwidget.init({ - "model": { "jsonPath": model.jsonPath, "scale": 1 }, - "display": { - "position": "right", - "width": 150, - "height": 300, - "hOffset": 0, // 重置水平偏移 - "vOffset": -20 // 重置垂直偏移 - }, - "mobile": { "show": true, "scale": 0.5 }, - "react": { "opacityDefault": 0.8, "opacityOnHover": 1 }, - "dialog": { "enable": true, "script": { - 'tap body': `你好啊,我是${model.name}。`, - 'tap face': '有什么问题或者烦心事都可以和我聊聊~' - }} + // 初始化看板娘 - 简化为Sample.html的工作版本 + function initL2Dwidget() { + const modelId = getUrlParam('id') || 'koharu'; + const model = models[modelId] || models.koharu; + + document.getElementById('model-select').value = modelId; + console.log('加载模型:', model.name, model.jsonPath); + + // 初始化模型 - 与Sample.html相同的配置 + L2Dwidget.init({ + "model": { "jsonPath": model.jsonPath, "scale": 1 }, + "display": { + "position": "right", + "width": 150, + "height": 300, + "hOffset": 0, // 重置水平偏移 + "vOffset": -20 // 重置垂直偏移 + }, + "mobile": { "show": true, "scale": 0.5 }, + "react": { "opacityDefault": 0.8, "opacityOnHover": 1 }, + "dialog": { "enable": true, "script": { + 'tap body': `你好啊,我是${model.name}。`, + 'tap face': '有什么问题或者烦心事都可以和我聊聊~' + }} + }); + } + + // 页面加载完成后初始化 + window.onload = function() { + // 直接初始化看板娘,不添加额外延迟 + initL2Dwidget(); + + // 监听下拉框变化(使用独立JS暴露的接口) + document.getElementById('model-select').addEventListener('change', function() { + window.switchL2DModel(this.value); + }); + + // 绑定录音按钮事件 + document.getElementById('startRecordBtn').addEventListener('click', startRecording); + document.getElementById('stopRecordBtn').addEventListener('click', stopRecording); + + // 页面加载时请求麦克风权限 + navigator.mediaDevices.getUserMedia({ audio: true }) + .then(stream => { + console.log("麦克风权限已授予"); + // 立即停止流,只获取权限 + stream.getTracks().forEach(track => track.stop()); + }) + .catch(error => { + console.error("获取麦克风权限失败:", error); + alert("请授权麦克风权限以使用录音功能"); }); - } - - // 页面加载完成后初始化 - window.onload = function() { - // 直接初始化看板娘,不添加额外延迟 - initL2Dwidget(); - - // 监听下拉框变化 - document.getElementById('model-select').addEventListener('change', function() { - window.location.search = '?id=' + this.value; - }); - - // 绑定录音按钮事件 - document.getElementById('startRecordBtn').addEventListener('click', startRecording); - document.getElementById('stopRecordBtn').addEventListener('click', stopRecording); - - // 页面加载时请求麦克风权限 - navigator.mediaDevices.getUserMedia({ audio: true }) - .then(stream => { - console.log("麦克风权限已授予"); - // 立即停止流,只获取权限 - stream.getTracks().forEach(track => track.stop()); - }) - .catch(error => { - console.error("获取麦克风权限失败:", error); - alert("请授权麦克风权限以使用录音功能"); - }); - }; - + }; \ No newline at end of file diff --git a/dsLightRag/static/YunXiao/live2d_widget.js b/dsLightRag/static/YunXiao/live2d_widget.js index b42c83a7..351964d5 100644 --- a/dsLightRag/static/YunXiao/live2d_widget.js +++ b/dsLightRag/static/YunXiao/live2d_widget.js @@ -1,51 +1,31 @@ -// 看板娘初始化 -L2Dwidget.init({ - "model": { - "jsonPath": "https://unpkg.com/live2d-widget-model-shizuku@1.0.5/assets/shizuku.model.json", - "scale": 1 - }, - "display": { - "position": "right", - "width": 150, - "height": 300, - "hOffset": 0, - "vOffset": -20 - }, - "mobile": { - "show": true, - "scale": 0.5 - }, - "react": { - "opacityDefault": 0.8, - "opacityOnHover": 0.2 - } -}); +// 看板娘模型配置(基于原始代码提取) +const models = { + shizuku: { jsonPath: "https://unpkg.com/live2d-widget-model-shizuku@1.0.5/assets/shizuku.model.json", name: "小智" }, + koharu: { jsonPath: "https://unpkg.com/live2d-widget-model-koharu@1.0.5/assets/koharu.model.json", name: "小荷" }, + wanko: { jsonPath: "https://unpkg.com/live2d-widget-model-wanko@1.0.5/assets/wanko.model.json", name: "汪喵" } +}; -// 模型选择器事件监听 -document.getElementById('model-select').addEventListener('change', function() { - const selectedModel = this.value; - let modelPath; - - switch(selectedModel) { - case 'shizuku': - modelPath = "https://unpkg.com/live2d-widget-model-shizuku@1.0.5/assets/shizuku.model.json"; - break; - case 'koharu': - modelPath = "https://unpkg.com/live2d-widget-model-koharu@1.0.5/assets/koharu.model.json"; - break; - case 'wanko': - modelPath = "https://unpkg.com/live2d-widget-model-wanko@1.0.5/assets/wanko.model.json"; - break; - default: - modelPath = "https://unpkg.com/live2d-widget-model-shizuku@1.0.5/assets/shizuku.model.json"; +// 获取URL参数工具函数 +function getUrlParam(name) { + const reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)'); + const r = window.location.search.substr(1).match(reg); + return r ? unescape(r[2]) : null; +} + +// 看板娘初始化函数 +function initL2Dwidget() { + const modelId = getUrlParam('id') || 'koharu'; + const model = models[modelId] || models.koharu; + + // 设置下拉框选中状态 + if (document.getElementById('model-select')) { + document.getElementById('model-select').value = modelId; } - - // 重新初始化看板娘 + console.log('加载模型:', model.name, model.jsonPath); + + // 初始化L2Dwidget(保留原始配置) L2Dwidget.init({ - "model": { - "jsonPath": modelPath, - "scale": 1 - }, + "model": { "jsonPath": model.jsonPath, "scale": 1 }, "display": { "position": "right", "width": 150, @@ -53,13 +33,19 @@ document.getElementById('model-select').addEventListener('change', function() { "hOffset": 0, "vOffset": -20 }, - "mobile": { - "show": true, - "scale": 0.5 - }, - "react": { - "opacityDefault": 0.8, - "opacityOnHover": 0.2 - } + "mobile": { "show": true, "scale": 0.5 }, + "react": { "opacityDefault": 0.8, "opacityOnHover": 1 }, + "dialog": { "enable": true, "script": { + 'tap body': `你好啊,我是${model.name}。`, + 'tap face': '有什么问题或者烦心事都可以和我聊聊~' + }} }); -}); \ No newline at end of file +} + +// 页面加载完成后初始化看板娘 +window.addEventListener('load', initL2Dwidget); + +// 暴露模型切换功能接口 +window.switchL2DModel = function(modelId) { + window.location.search = '?id=' + modelId; +}; \ No newline at end of file diff --git a/dsLightRag/static/YunXiao/physics_quiz.css b/dsLightRag/static/YunXiao/physics_quiz.css index fe66bfc5..fa9d940f 100644 --- a/dsLightRag/static/YunXiao/physics_quiz.css +++ b/dsLightRag/static/YunXiao/physics_quiz.css @@ -113,8 +113,8 @@ h1 { } .option { - margin-bottom: 12px; - padding: 12px 15px; + margin-bottom: 15px; /* 从12px增加到15px */ + padding: 15px 18px; /* 从12px 15px增加到15px 18px */ border-radius: 8px; border: 2px solid #e0e0e0; cursor: pointer; @@ -124,8 +124,9 @@ h1 { align-items: center; } -.option input[type="radio"] { - margin-right: 10px; +.option input[type="radio"], +.option input[type="checkbox"] { + margin-right: 20px; /* 增加间距到20px */ cursor: pointer; flex-shrink: 0; } diff --git a/dsLightRag/static/YunXiao/physics_quiz.html b/dsLightRag/static/YunXiao/physics_quiz.html index 0ea97c0d..7de63f88 100644 --- a/dsLightRag/static/YunXiao/physics_quiz.html +++ b/dsLightRag/static/YunXiao/physics_quiz.html @@ -7,108 +7,113 @@ -
-
-

物理知识测验 - 万有引力定律

-
当前难度:中等
-
- 共15道题,分三个难度级别。完成当前难度后将根据您的表现推荐下一难度。 -
-
- -
- +
+
+

物理知识测验 - 万有引力定律

+
当前难度:中等
+
+ 共15道题,分三个难度级别。完成当前难度后将根据您的表现推荐下一难度。
+
- - -
- -
-
测验结果
-
您的得分:0/100分
-
正确题数:0
-
错误题数:0
-
-
+
+
- - -
- - + -
-
- 正在录音... -
- -
-
-
-
-
-
- 学伴正在思考中... -
- -
- - -
- -
-
-
- - - - - - 你讲的话 -
-
-
-
- -
-
-
- -
-
-
-
00:00 / 00:00
+
+ +
+
测验结果
+
您的得分:0/100分
+
正确题数:0
+
错误题数:0
+
+ + +
+ + +
+ +
+
+ 正在录音... +
+ +
+
+
+
+
+
+ 学伴正在思考中... +
+ +
+ + +
+ +
- 有权限问题? +
+ + + + + + 你讲的话 +
+
- - - - - - +
+ +
+
+
+ +
+
+
+
00:00 / 00:00
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/dsLightRag/static/YunXiao/physics_quiz.js b/dsLightRag/static/YunXiao/physics_quiz.js index eeed1e33..c0e3f0e9 100644 --- a/dsLightRag/static/YunXiao/physics_quiz.js +++ b/dsLightRag/static/YunXiao/physics_quiz.js @@ -18,8 +18,8 @@ const correctAnswers = { hq1: 'C', hq2: 'A', hq3: 'C', - hq4: 'A', - hq5: 'B' + hq4: ['C', 'D'], // 多选题,正确答案是CD + hq5: ['A', 'C'] // 多选题,正确答案是AC } }; @@ -47,10 +47,10 @@ const quizQuestions = [ points: 20, text: '地球质量为M,半径为R,引力常量为G。一颗质量为m的人造地球卫星在距离地面高度为h的轨道上做匀速圆周运动,则(  )', options: [ - { id: 'mq2-a', label: 'A', text: '卫星的线速度大小为公式' }, - { id: 'mq2-b', label: 'B', text: '卫星的角速度大小为公式' }, - { id: 'mq2-c', label: 'C', text: '卫星的向心加速度大小为公式' }, - { id: 'mq2-d', label: 'D', text: '卫星的周期为公式' } + { id: 'mq2-a', label: 'A', text: '卫星的线速度大小为公式' }, + { id: 'mq2-b', label: 'B', text: '卫星的角速度大小为公式' }, + { id: 'mq2-c', label: 'C', text: '卫星的向心加速度大小为公式' }, + { id: 'mq2-d', label: 'D', text: '卫星的周期为公式' } ], explanation: '正确答案:C
解析:A.卫星的线速度大小为v=√(GM/(R+h)),故A错误;B.卫星的角速度大小为ω=√(GM/(R+h)³),故B错误;C.卫星的向心加速度大小为a=GM/(R+h)²,故C正确;D.卫星的周期为T=2π√((R+h)³/GM),故D错误。故答案为:C。' }, @@ -87,11 +87,11 @@ const quizQuestions = [ difficulty: 'medium', number: 5, points: 20, - text: '我国首颗超百Gbps容量高通量地球静止轨道通信卫星中星26号卫星,于北京时间2023年2月23日在西昌卫星发射中心成功发射,该卫星主要用于为固定端及车、船、机载终端提供高速宽带接入服务。如图,某时刻中星26与椭圆轨道侦察卫星恰好位于C、D两点,两星轨道相交于A、B两点,C、D连线过地心,D点为远地点,两卫星运行周期都为T。下列说法正确的是(  )

轨道图', + text: '我国首颗超百Gbps容量高通量地球静止轨道通信卫星中星26号卫星,于北京时间2023年2月23日在西昌卫星发射中心成功发射,该卫星主要用于为固定端及车、船、机载终端提供高速宽带接入服务。如图,某时刻中星26与椭圆轨道侦察卫星恰好位于C、D两点,两星轨道相交于A、B两点,C、D连线过地心,D点为远地点,两卫星运行周期都为T。下列说法正确的是(  )

轨道图', options: [ { id: 'mq5-a', label: 'A', text: '中星26与侦察卫星可能在A点或B点相遇' }, { id: 'mq5-b', label: 'B', text: '侦查卫星从D点运动到A点过程中机械能增大' }, - { id: 'mq5-c', label: 'C', text: '中星26在C点线速度公式与侦察卫星在D点线速度公式相等' }, + { id: 'mq5-c', label: 'C', text: '中星26在C点线速度公式与侦察卫星在D点线速度公式相等' }, { id: 'mq5-d', label: 'D', text: '相等时间内中星26与地球的连线扫过的面积大于侦察卫星与地球的连线扫过的面积' } ], explanation: '正确答案:D
解析:A.中星26与侦察卫星周期相同,并且当中星26在下半周运动时,卫星在上半周运动,故不可能相遇,故A错误;B.侦查卫星在D到A点过程中只有引力做功故机械能不变,故B错误;C.开普勒第二定律可知,在近地点速度大于远地点速度故中星26在C点线速度大于侦察卫星在D点线速度,故C错误;D.中星26与侦察卫星的周期相同,由开普勒第三定律,中星26轨道半径等于侦察卫星的半长轴,运动一个周期中星26是一个圆,而侦察卫星是一个椭圆,由于圆的面积大于椭圆的面积,故相等时间内中星26与地球的连线扫过的面积大于侦察卫星与地球的连线扫过的面积,故D正确;故答案为:D。' @@ -259,7 +259,7 @@ const incorrectCount = document.getElementById('incorrect-count'); const difficultyIndicator = document.querySelector('.difficulty-indicator'); const questionSection = document.querySelector('.question-section'); -// 获取难度名称 - 移到全局作用域 +// 获取难度名称 function getDifficultyName(difficulty) { const names = { easy: '简单', @@ -273,199 +273,183 @@ function getDifficultyName(difficulty) { document.addEventListener('DOMContentLoaded', function() { // 渲染题目 renderQuestions(); - // 修改录音按钮事件绑定代码 + + // 录音按钮事件绑定 const startRecordBtn = document.getElementById('startRecordBtn'); const stopRecordBtn = document.getElementById('stopRecordBtn'); const recordingIndicator = document.getElementById('recordingIndicator'); - // 隐藏停止讲话按钮,因为我们不需要它 - if (stopRecordBtn) { - stopRecordBtn.style.display = 'none'; - } - if (startRecordBtn && stopRecordBtn && recordingIndicator) { - // 点击"帮我讲题"按钮 - startRecordBtn.addEventListener('click', function() { - // 显示录音指示器 - recordingIndicator.style.display = 'flex'; - // 隐藏"帮我讲题"按钮,显示"停止讲话"按钮 - startRecordBtn.style.display = 'none'; - stopRecordBtn.style.display = 'flex'; - // 这里添加实际录音功能逻辑 - console.log('开始录音...'); - // 示例:调用录音API或相关函数 - // startRecording(); - }); - - // 点击"停止讲话"按钮 - stopRecordBtn.addEventListener('click', function() { - // 隐藏录音指示器 - recordingIndicator.style.display = 'none'; - // 隐藏"停止讲话"按钮,显示"帮我讲题"按钮 - stopRecordBtn.style.display = 'none'; - startRecordBtn.style.display = 'flex'; - // 这里添加停止录音功能逻辑 - console.log('停止录音...'); - // 示例:停止录音并处理音频 - // stopRecording(); - }); + startRecordBtn.addEventListener('click', function() { + recordingIndicator.style.display = 'flex'; + startRecordBtn.style.display = 'none'; + stopRecordBtn.style.display = 'flex'; + console.log('开始录音...'); + }); + + stopRecordBtn.addEventListener('click', function() { + recordingIndicator.style.display = 'none'; + stopRecordBtn.style.display = 'none'; + startRecordBtn.style.display = 'flex'; + console.log('停止录音...'); + }); } else { - console.error('录音相关DOM元素未找到'); + console.error('录音相关DOM元素未找到'); } - // 提交当前难度的答案 - // 修改提交按钮的事件处理逻辑 + // 提交按钮事件 submitBtn.addEventListener('click', function() { - const currentAnswers = correctAnswers[quizState.currentDifficulty]; - const totalQuestions = Object.keys(currentAnswers).length; - let score = 0; - let correct = 0; - let incorrect = 0; - - // 检查所有问题的答案 - for (const [questionName, correctOptions] of Object.entries(currentAnswers)) { - // 获取用户选择的所有选项 - const selectedOptions = Array.from(document.querySelectorAll(`input[name="${questionName}"]:checked`)) - .map(checkbox => checkbox.value); - - const questionNumber = questionName.substring(2); // 从mq1提取1 - const explanationElement = document.querySelector(`[data-difficulty="${quizState.currentDifficulty}"][data-question="${questionNumber}"] .question-explanation`); - - // 显示解析 - explanationElement.style.display = 'block'; - - if (selectedOptions.length > 0) { - // 标记用户选择的选项 - let allCorrect = true; - - // 标记用户选择的每个选项 - document.querySelectorAll(`input[name="${questionName}"]:checked`).forEach(checkbox => { - const userAnswer = checkbox.value; - if (correctOptions.includes(userAnswer)) { - checkbox.closest('.option').style.backgroundColor = '#e8f5e9'; - checkbox.closest('.option').style.borderColor = '#4caf50'; - } else { - checkbox.closest('.option').style.backgroundColor = '#ffebee'; - checkbox.closest('.option').style.borderColor = '#c62828'; - allCorrect = false; - } + const currentAnswers = correctAnswers[quizState.currentDifficulty]; + const totalQuestions = Object.keys(currentAnswers).length; + let score = 0; + let correct = 0; + let incorrect = 0; + + // 检查所有问题的答案 + for (const [questionName, correctOptions] of Object.entries(currentAnswers)) { + // 获取用户选择的所有选项 + const selectedOptions = Array.from(document.querySelectorAll(`input[name="${questionName}"]:checked`)) + .map(checkbox => checkbox.value); + + const questionNumber = questionName.substring(2); // 从mq1提取1 + const explanationElement = document.querySelector(`[data-difficulty="${quizState.currentDifficulty}"][data-question="${questionNumber}"] .question-explanation`); + + // 显示解析 + explanationElement.style.display = 'block'; + + if (selectedOptions.length > 0) { + // 标记用户选择的选项 + let allCorrect = true; + + // 标记用户选择的每个选项 + document.querySelectorAll(`input[name="${questionName}"]:checked`).forEach(checkbox => { + const userAnswer = checkbox.value; + if (correctOptions.includes(userAnswer)) { + checkbox.closest('.option').style.backgroundColor = '#e8f5e9'; + checkbox.closest('.option').style.borderColor = '#4caf50'; + } else { + checkbox.closest('.option').style.backgroundColor = '#ffebee'; + checkbox.closest('.option').style.borderColor = '#c62828'; + allCorrect = false; + } + }); + + // 标记用户未选择但正确的选项 + document.querySelectorAll(`input[name="${questionName}"]`).forEach(checkbox => { + if (correctOptions.includes(checkbox.value) && !checkbox.checked) { + checkbox.closest('.option').style.backgroundColor = '#e3f2fd'; + checkbox.closest('.option').style.borderColor = '#2196f3'; + allCorrect = false; + } + }); + + // 判断是否全对 + if (allCorrect && selectedOptions.length === correctOptions.length) { + score += 20; // 每题20分 + correct++; + } else { + incorrect++; + } + } else { + // 未答题 + incorrect++; + const questionElement = document.querySelector(`[data-difficulty="${quizState.currentDifficulty}"][data-question="${questionNumber}"]`); + questionElement.style.border = '2px solid #ff9800'; + + // 显示所有正确答案 + document.querySelectorAll(`input[name="${questionName}"]`).forEach(checkbox => { + if (correctOptions.includes(checkbox.value)) { + checkbox.closest('.option').style.backgroundColor = '#e3f2fd'; + checkbox.closest('.option').style.borderColor = '#2196f3'; + } + }); + } + } + + // 保存当前难度得分 + quizState.scores[quizState.currentDifficulty] = { + score: score, + correct: correct, + incorrect: incorrect + }; + quizState.completedDifficulties.push(quizState.currentDifficulty); + + // 显示当前难度结果 + scoreValue.textContent = score; + correctCount.textContent = correct; + incorrectCount.textContent = incorrect; + result.style.display = 'block'; + + // 隐藏提交按钮 + submitBtn.style.display = 'none'; + + // 根据当前难度和得分决定下一步 + if (quizState.currentDifficulty === 'medium') { + if (correct < 3) { + // 中等难度答对少于3题,推荐简单难度 + showNavigation('您在中等难度题目中表现不佳,建议先完成简单难度题目来巩固基础。', 'easy'); + } else { + // 中等难度答对3题及以上,推荐高级难度 + showNavigation('恭喜您完成中等难度题目!接下来挑战高级难度题目吧。', 'hard'); + } + } else { + // 简单或高级难度完成后,显示最终结果 + showFinalResult(); + } }); - // 标记用户未选择但正确的选项 - document.querySelectorAll(`input[name="${questionName}"]`).forEach(checkbox => { - if (correctOptions.includes(checkbox.value) && !checkbox.checked) { - checkbox.closest('.option').style.backgroundColor = '#e3f2fd'; - checkbox.closest('.option').style.borderColor = '#2196f3'; - allCorrect = false; - } + // 开始下一组题目 + nextBtn.addEventListener('click', function() { + const nextDifficulty = navigationSection.dataset.nextDifficulty; + if (!nextDifficulty) return; + + // 更新当前难度 + quizState.currentDifficulty = nextDifficulty; + + // 隐藏导航和结果 + navigationSection.style.display = 'none'; + result.style.display = 'none'; + + // 显示提交按钮 + submitBtn.style.display = 'inline-block'; + + // 更新难度指示器 + difficultyIndicator.textContent = `当前难度:${getDifficultyName(nextDifficulty)}`; + difficultyIndicator.className = `difficulty-indicator difficulty-${nextDifficulty}`; + + // 隐藏所有题目 + document.querySelectorAll('.question').forEach(question => { + question.classList.add('hidden'); + }); + + // 显示下一组题目 + document.querySelectorAll(`[data-difficulty="${nextDifficulty}"]`).forEach(question => { + question.classList.remove('hidden'); + }); + + // 滚动到页面顶部 + window.scrollTo(0, 0); }); - - // 判断是否全对 - if (allCorrect && selectedOptions.length === correctOptions.length) { - score += 20; // 每题20分 - correct++; - } else { - incorrect++; - } - } else { - // 未答题 - incorrect++; - const questionElement = document.querySelector(`[data-difficulty="${quizState.currentDifficulty}"][data-question="${questionNumber}"]`); - questionElement.style.border = '2px solid #ff9800'; - - // 显示所有正确答案 - document.querySelectorAll(`input[name="${questionName}"]`).forEach(checkbox => { - if (correctOptions.includes(checkbox.value)) { - checkbox.closest('.option').style.backgroundColor = '#e3f2fd'; - checkbox.closest('.option').style.borderColor = '#2196f3'; - } - }); - } - } - - // 保存当前难度得分 - quizState.scores[quizState.currentDifficulty] = { - score: score, - correct: correct, - incorrect: incorrect - }; - quizState.completedDifficulties.push(quizState.currentDifficulty); +}); - // 显示当前难度结果 - scoreValue.textContent = score; - correctCount.textContent = correct; - incorrectCount.textContent = incorrect; - result.style.display = 'block'; - - // 隐藏提交按钮 - submitBtn.style.display = 'none'; - - // 根据当前难度和得分决定下一步 - if (quizState.currentDifficulty === 'medium') { - if (correct < 3) { - // 中等难度答对少于3题,推荐简单难度 - showNavigation('您在中等难度题目中表现不佳,建议先完成简单难度题目来巩固基础。', 'easy'); - } else { - // 中等难度答对3题及以上,推荐高级难度 - showNavigation('恭喜您完成中等难度题目!接下来挑战高级难度题目吧。', 'hard'); - } - } else { - // 简单或高级难度完成后,显示最终结果 - showFinalResult(); - } - }); - - // 显示导航信息 - function showNavigation(message, nextDifficulty) { +// 显示导航信息 +function showNavigation(message, nextDifficulty) { navigationMessage.textContent = message; navigationSection.dataset.nextDifficulty = nextDifficulty; navigationSection.style.display = 'block'; - } +} - // 开始下一组题目 - nextBtn.addEventListener('click', function() { - const nextDifficulty = navigationSection.dataset.nextDifficulty; - if (!nextDifficulty) return; - - // 更新当前难度 - quizState.currentDifficulty = nextDifficulty; - - // 隐藏导航和结果 - navigationSection.style.display = 'none'; - result.style.display = 'none'; - - // 显示提交按钮 - submitBtn.style.display = 'inline-block'; - - // 更新难度指示器 - difficultyIndicator.textContent = `当前难度:${getDifficultyName(nextDifficulty)}`; - difficultyIndicator.className = `difficulty-indicator difficulty-${nextDifficulty}`; - - // 隐藏所有题目 - document.querySelectorAll('.question').forEach(question => { - question.classList.add('hidden'); - }); - - // 显示下一组题目 - document.querySelectorAll(`[data-difficulty="${nextDifficulty}"]`).forEach(question => { - question.classList.remove('hidden'); - }); - - // 滚动到页面顶部 - window.scrollTo(0, 0); - }); - - // 显示最终结果 - function showFinalResult() { +// 显示最终结果 +function showFinalResult() { // 计算总分 let totalScore = 0; let totalCorrect = 0; let totalIncorrect = 0; for (const difficulty of quizState.completedDifficulties) { - totalScore += quizState.scores[difficulty].score; - totalCorrect += quizState.scores[difficulty].correct; - totalIncorrect += quizState.scores[difficulty].incorrect; + totalScore += quizState.scores[difficulty].score; + totalCorrect += quizState.scores[difficulty].correct; + totalIncorrect += quizState.scores[difficulty].incorrect; } // 更新结果显示 @@ -478,8 +462,7 @@ document.addEventListener('DOMContentLoaded', function() { navigationMessage.textContent = '恭喜您完成所有推荐题目!'; navigationSection.style.display = 'block'; document.getElementById('next-btn').style.display = 'none'; - } -}); +} // 渲染题目函数 function renderQuestions() { diff --git a/dsLightRag/static/YunXiao/xueban.js b/dsLightRag/static/YunXiao/xueban.js new file mode 100644 index 00000000..a9671232 --- /dev/null +++ b/dsLightRag/static/YunXiao/xueban.js @@ -0,0 +1,152 @@ +// 学伴录音功能核心逻辑 +// 模型配置 + +// 录音相关变量 +let mediaRecorder; let audioChunks = []; let isRecording = false; +// 音频播放相关变量 +let audioElement = null; let isPlaying = false; + +// 获取URL参数 +function getUrlParam(name) { + const reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)'); + const r = window.location.search.substr(1).match(reg); + return r ? unescape(r[2]) : null; +} + +// 开始录音 +function startRecording() { + if (isRecording) return; + + console.log("尝试开始录音"); + 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' }); + console.log("录音完成,音频数据大小:", audioBlob.size); + uploadAudioToServer(audioBlob); + }; + + mediaRecorder.start(); + isRecording = true; + document.getElementById('recordingIndicator').style.display = 'flex'; + document.getElementById('startRecordBtn').style.display = 'none'; + document.getElementById('stopRecordBtn').style.display = 'flex'; + console.log("开始录音成功"); + + // 设置最长录音时间为60秒 + setTimeout(stopRecording, 60000); + }) + .catch(error => { + console.error("获取麦克风权限失败:", error); + alert("请授权麦克风权限以使用录音功能"); + }); +} + +// 停止录音 +function stopRecording() { + if (!isRecording || !mediaRecorder) return; + + mediaRecorder.stop(); + isRecording = false; + document.getElementById('recordingIndicator').style.display = 'none'; + document.getElementById('startRecordBtn').style.display = 'flex'; + document.getElementById('stopRecordBtn').style.display = 'none'; + console.log("停止录音"); + + if (mediaRecorder.stream) { + mediaRecorder.stream.getTracks().forEach(track => track.stop()); + } +} + +// 上传音频到服务器 +function uploadAudioToServer(audioBlob) { + console.log("开始上传音频到服务器"); + document.getElementById('thinkingIndicator').style.display = 'flex'; + + const formData = new FormData(); + formData.append('file', audioBlob, 'recording.wav'); + + fetch('/api/xueban/upload-audio', { + method: 'POST', + body: formData + }) + .then(response => { + if (!response.ok) throw new Error('服务器响应错误'); + return response.json(); + }) + .then(data => { + console.log("处理结果:", data); + document.getElementById('thinkingIndicator').style.display = 'none'; + + if (data.success) { + showResults(data.data); + } else { + alert('音频处理失败: ' + data.message); + } + }) + .catch(error => { + console.error("上传音频失败:", error); + document.getElementById('thinkingIndicator').style.display = 'none'; + alert('上传音频失败: ' + error.message); + }); +} + +// 显示ASR识别结果和反馈 +function showResults(data) { + const resultContainer = document.getElementById('resultContainer'); + resultContainer.style.display = 'flex'; + + document.getElementById('asrResultText').textContent = data.asr_text || '未识别到内容'; + document.getElementById('feedbackResultText').textContent = data.feedback_text || '无反馈内容'; + + if (data.audio_url) { + if (audioElement) audioElement.pause(); + audioElement = new Audio(data.audio_url); + audioElement.onloadedmetadata = function() { + updateAudioTimeDisplay(); + try { audioElement.play(); isPlaying = true; updatePlayButton(); } + catch (e) { console.error("自动播放失败:", e); } + }; + audioElement.ontimeupdate = function() { updateAudioProgress(); updateAudioTimeDisplay(); }; + audioElement.onended = function() { isPlaying = false; updatePlayButton(); }; + document.getElementById('playAudioBtn').onclick = togglePlayAudio; + document.getElementById('audioProgress').onclick = function(e) { + const rect = this.getBoundingClientRect(); + audioElement.currentTime = (e.clientX - rect.left) / rect.width * audioElement.duration; + }; + } +} + +// 音频播放控制函数 +function togglePlayAudio() { + if (!audioElement) return; + isPlaying ? audioElement.pause() : audioElement.play(); + isPlaying = !isPlaying; + updatePlayButton(); +} + +function updatePlayButton() { + const btn = document.getElementById('playAudioBtn'); + btn.innerHTML = isPlaying ? + '' : + ''; +} + +function updateAudioProgress() { + if (!audioElement) return; + const progress = (audioElement.currentTime / audioElement.duration) * 100; + document.getElementById('progressBar').style.width = `${progress}%`; +} + +function updateAudioTimeDisplay() { + if (!audioElement) return; + const format = s => `${Math.floor(s/60).toString().padStart(2,'0')}:${Math.floor(s%60).toString().padStart(2,'0')}`; + document.getElementById('audioTime').textContent = `${format(audioElement.currentTime)} / ${format(audioElement.duration)}`; +}