'commit'
This commit is contained in:
2
dsLightRag/static/YunXiao/jquery.min.js
vendored
2
dsLightRag/static/YunXiao/jquery.min.js
vendored
File diff suppressed because one or more lines are too long
65
dsLightRag/static/YunXiao/live2d_widget.js
Normal file
65
dsLightRag/static/YunXiao/live2d_widget.js
Normal file
@@ -0,0 +1,65 @@
|
||||
// 看板娘初始化
|
||||
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
|
||||
}
|
||||
});
|
||||
|
||||
// 模型选择器事件监听
|
||||
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";
|
||||
}
|
||||
|
||||
// 重新初始化看板娘
|
||||
L2Dwidget.init({
|
||||
"model": {
|
||||
"jsonPath": modelPath,
|
||||
"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
|
||||
}
|
||||
});
|
||||
});
|
504
dsLightRag/static/YunXiao/physics_quiz.css
Normal file
504
dsLightRag/static/YunXiao/physics_quiz.css
Normal file
@@ -0,0 +1,504 @@
|
||||
/* 全局样式 */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Microsoft YaHei', Arial, sans-serif;
|
||||
background-color: #f5f7fa;
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
background-color: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
header {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 2px solid #eaeaea;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #2c3e50;
|
||||
font-size: 28px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.difficulty-indicator {
|
||||
display: inline-block;
|
||||
padding: 8px 16px;
|
||||
border-radius: 20px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 15px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.difficulty-medium {
|
||||
background-color: #3498db;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.difficulty-easy {
|
||||
background-color: #2ecc71;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.difficulty-hard {
|
||||
background-color: #e74c3c;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.quiz-info {
|
||||
color: #7f8c8d;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
/* 题目样式 */
|
||||
.question {
|
||||
margin-bottom: 35px;
|
||||
padding: 25px;
|
||||
border-radius: 10px;
|
||||
background-color: #f9f9f9;
|
||||
border-left: 4px solid #3498db;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.question:hover {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.question-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.question-number {
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.question-points {
|
||||
background-color: #3498db;
|
||||
color: white;
|
||||
padding: 4px 10px;
|
||||
border-radius: 15px;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.question-text {
|
||||
font-size: 18px;
|
||||
margin-bottom: 20px;
|
||||
color: #34495e;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.options {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.option {
|
||||
margin-bottom: 12px;
|
||||
padding: 12px 15px;
|
||||
border-radius: 8px;
|
||||
border: 2px solid #e0e0e0;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
background-color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.option input[type="radio"] {
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.option label {
|
||||
cursor: pointer;
|
||||
display: inline;
|
||||
flex-grow: 1;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.option:hover {
|
||||
border-color: #3498db;
|
||||
background-color: #f0f8ff;
|
||||
}
|
||||
|
||||
.question-explanation {
|
||||
display: none;
|
||||
margin-top: 15px;
|
||||
padding: 15px;
|
||||
background-color: #e8f4fd;
|
||||
border-radius: 8px;
|
||||
border-left: 4px solid #3498db;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 导航按钮样式 */
|
||||
.navigation-section {
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
padding: 25px;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 10px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#next-btn {
|
||||
background: linear-gradient(45deg, #667eea, #764ba2);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 30px;
|
||||
border-radius: 30px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
#next-btn:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
#next-btn:hover:before {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
#next-btn:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
#next-btn:active {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 3px 10px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
.navigation-message {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
margin-bottom: 20px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.submit-section {
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
#submit-btn {
|
||||
background-color: #2980b9;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 30px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
#submit-btn:hover {
|
||||
background-color: #1e6fa5;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
#result {
|
||||
margin-top: 30px;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.result-header {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 15px;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.score {
|
||||
font-size: 18px;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.correct-answers {
|
||||
background-color: #e8f5e9;
|
||||
color: #2e7d32;
|
||||
padding: 10px 15px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.incorrect-answers {
|
||||
background-color: #ffebee;
|
||||
color: #c62828;
|
||||
padding: 10px 15px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
/* 学伴模型选择器样式 */
|
||||
.model-selector {
|
||||
position: fixed;
|
||||
top: 40px;
|
||||
left: 20px;
|
||||
z-index: 1000;
|
||||
padding: 10px;
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
select {
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
background-color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 录音控制样式 */
|
||||
.recording-controls {
|
||||
position: static;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.record-button {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
border-radius: 50%;
|
||||
background-color: #dc3545;
|
||||
border: none;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 1 !important;
|
||||
visibility: visible !important;
|
||||
}
|
||||
|
||||
.record-button:hover {
|
||||
background-color: #c82333;
|
||||
}
|
||||
|
||||
.stop-button {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
border-radius: 50%;
|
||||
background-color: #6c757d;
|
||||
border: none;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
display: none;
|
||||
opacity: 1 !important;
|
||||
visibility: visible !important;
|
||||
}
|
||||
|
||||
/* 录音指示器样式 */
|
||||
.recording-indicator {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
left: 20px;
|
||||
z-index: 1000;
|
||||
padding: 10px 15px;
|
||||
background-color: rgba(220, 53, 69, 0.9);
|
||||
color: white;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
||||
display: none;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.recording-dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background-color: white;
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
animation: pulse 1.5s infinite;
|
||||
}
|
||||
|
||||
/* 思考中动画样式 */
|
||||
.thinking-indicator {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 120px;
|
||||
z-index: 1000;
|
||||
padding: 10px 15px;
|
||||
background-color: rgba(0, 123, 255, 0.9);
|
||||
color: white;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
||||
display: none;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.thinking-dots {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.thinking-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: white;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.thinking-dot:nth-child(1) {
|
||||
animation: pulse 1.5s infinite 0s;
|
||||
}
|
||||
|
||||
.thinking-dot:nth-child(2) {
|
||||
animation: pulse 1.5s infinite 0.3s;
|
||||
}
|
||||
|
||||
.thinking-dot:nth-child(3) {
|
||||
animation: pulse 1.5s infinite 0.6s;
|
||||
}
|
||||
|
||||
/* 结果显示容器样式 */
|
||||
.result-container {
|
||||
position: fixed;
|
||||
bottom: 80px;
|
||||
left: 20px;
|
||||
z-index: 1000;
|
||||
padding: 15px;
|
||||
background-color: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
|
||||
max-width: 400px;
|
||||
width: 90%;
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.result-header {
|
||||
font-weight: bold;
|
||||
color: #495057;
|
||||
margin-bottom: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.result-header.asr {
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.result-header.feedback {
|
||||
color: #28a745;
|
||||
}
|
||||
|
||||
.result-text {
|
||||
color: #333;
|
||||
line-height: 1.5;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
padding-right: 5px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
/* 音频播放器样式 */
|
||||
.audio-player-container {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.play-button {
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.play-button:hover {
|
||||
background-color: #218838;
|
||||
}
|
||||
|
||||
.audio-progress {
|
||||
flex-grow: 1;
|
||||
height: 6px;
|
||||
background-color: #e9ecef;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
height: 100%;
|
||||
background-color: #28a745;
|
||||
width: 0%;
|
||||
}
|
||||
|
||||
.audio-time {
|
||||
font-size: 12px;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
@@ -4,383 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>物理知识测验 - 万有引力定律</title>
|
||||
<style>
|
||||
/* 全局样式 */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Microsoft YaHei', Arial, sans-serif;
|
||||
background-color: #f5f7fa;
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
background-color: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
header {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 2px solid #eaeaea;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #2c3e50;
|
||||
font-size: 28px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.difficulty-indicator {
|
||||
display: inline-block;
|
||||
padding: 8px 16px;
|
||||
border-radius: 20px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 15px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.difficulty-medium {
|
||||
background-color: #3498db;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.difficulty-easy {
|
||||
background-color: #2ecc71;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.difficulty-hard {
|
||||
background-color: #e74c3c;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.quiz-info {
|
||||
color: #7f8c8d;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
/* 题目样式 */
|
||||
.question {
|
||||
margin-bottom: 35px;
|
||||
padding: 25px;
|
||||
border-radius: 10px;
|
||||
background-color: #f9f9f9;
|
||||
border-left: 4px solid #3498db;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.question:hover {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.question-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.question-number {
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.question-points {
|
||||
background-color: #3498db;
|
||||
color: white;
|
||||
padding: 4px 10px;
|
||||
border-radius: 15px;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.question-text {
|
||||
font-size: 18px;
|
||||
margin-bottom: 20px;
|
||||
color: #34495e;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.options {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.option {
|
||||
margin-bottom: 12px;
|
||||
padding: 12px 15px;
|
||||
border-radius: 8px;
|
||||
border: 2px solid #e0e0e0;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
background-color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.option input[type="radio"] {
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.option label {
|
||||
cursor: pointer;
|
||||
display: inline;
|
||||
flex-grow: 1;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.option:hover {
|
||||
border-color: #3498db;
|
||||
background-color: #f0f8ff;
|
||||
}
|
||||
|
||||
.option input[type="radio"] {
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.option label {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.question-explanation {
|
||||
display: none;
|
||||
margin-top: 15px;
|
||||
padding: 15px;
|
||||
background-color: #e8f4fd;
|
||||
border-radius: 8px;
|
||||
border-left: 4px solid #3498db;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 导航按钮样式 */
|
||||
.navigation-section {
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
padding: 25px;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 10px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#next-btn {
|
||||
background: linear-gradient(45deg, #667eea, #764ba2);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 30px;
|
||||
border-radius: 30px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
#next-btn:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
#next-btn:hover:before {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
#next-btn:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
#next-btn:active {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 3px 10px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
.navigation-message {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
margin-bottom: 20px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.submit-section {
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
#submit-btn {
|
||||
background-color: #2980b9;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 30px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
#submit-btn:hover {
|
||||
background-color: #1e6fa5;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
#result {
|
||||
margin-top: 30px;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.result-header {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 15px;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.score {
|
||||
font-size: 18px;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.correct-answers {
|
||||
background-color: #e8f5e9;
|
||||
color: #2e7d32;
|
||||
padding: 10px 15px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.incorrect-answers {
|
||||
background-color: #ffebee;
|
||||
color: #c62828;
|
||||
padding: 10px 15px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
/* 学伴模型选择器样式 */
|
||||
.model-selector {
|
||||
position: fixed; top: 40px; left: 20px; z-index: 1000;
|
||||
padding: 10px; background-color: rgba(255, 255, 255, 0.8);
|
||||
border-radius: 5px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
display: flex; flex-direction: column; gap: 15px;
|
||||
}
|
||||
select { padding: 8px 12px; border: 1px solid #ccc; border-radius: 4px; background-color: white; font-size: 14px; }
|
||||
|
||||
/* 录音控制样式 */
|
||||
.recording-controls {
|
||||
position: static; display: flex; flex-direction: column; gap: 10px; margin: 0;
|
||||
}
|
||||
.record-button {
|
||||
width: 70px; height: 70px; border-radius: 50%;
|
||||
background-color: #dc3545; border: none; color: white;
|
||||
font-size: 16px; cursor: pointer; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
opacity: 1 !important; visibility: visible !important;
|
||||
}
|
||||
.record-button:hover { background-color: #c82333; }
|
||||
.stop-button {
|
||||
width: 70px; height: 70px; border-radius: 50%;
|
||||
background-color: #6c757d; border: none; color: white;
|
||||
font-size: 16px; cursor: pointer; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
display: flex; align-items: center; justify-content: center; display: none;
|
||||
opacity: 1 !important; visibility: visible !important;
|
||||
}
|
||||
|
||||
/* 录音指示器样式 */
|
||||
.recording-indicator {
|
||||
position: fixed; bottom: 20px; left: 20px; z-index: 1000;
|
||||
padding: 10px 15px; background-color: rgba(220, 53, 69, 0.9);
|
||||
color: white; border-radius: 20px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
||||
display: none; align-items: center;
|
||||
}
|
||||
.recording-dot {
|
||||
width: 10px; height: 10px; background-color: white;
|
||||
border-radius: 50%; margin-right: 8px; animation: pulse 1.5s infinite;
|
||||
}
|
||||
|
||||
/* 思考中动画样式 */
|
||||
.thinking-indicator {
|
||||
position: fixed; bottom: 20px; right: 120px; z-index: 1000;
|
||||
padding: 10px 15px; background-color: rgba(0, 123, 255, 0.9);
|
||||
color: white; border-radius: 20px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
||||
display: none; align-items: center;
|
||||
}
|
||||
.thinking-dots { display: flex; gap: 4px; margin-right: 8px; }
|
||||
.thinking-dot {
|
||||
width: 8px; height: 8px; background-color: white; border-radius: 50%;
|
||||
}
|
||||
.thinking-dot:nth-child(1) { animation: pulse 1.5s infinite 0s; }
|
||||
.thinking-dot:nth-child(2) { animation: pulse 1.5s infinite 0.3s; }
|
||||
.thinking-dot:nth-child(3) { animation: pulse 1.5s infinite 0.6s; }
|
||||
|
||||
/* 结果显示容器样式 */
|
||||
.result-container {
|
||||
position: fixed; bottom: 80px; left: 20px; z-index: 1000;
|
||||
padding: 15px; background-color: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 8px; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
|
||||
max-width: 400px; width: 90%;
|
||||
display: none; flex-direction: column; gap: 10px;
|
||||
}
|
||||
.result-header {
|
||||
font-weight: bold; color: #495057; margin-bottom: 5px;
|
||||
display: flex; align-items: center; gap: 5px;
|
||||
}
|
||||
.result-header.asr { color: #007bff; }
|
||||
.result-header.feedback { color: #28a745; }
|
||||
.result-text {
|
||||
color: #333; line-height: 1.5; max-height: 200px; overflow-y: auto;
|
||||
padding-right: 5px; word-break: break-all;
|
||||
}
|
||||
|
||||
/* 音频播放器样式 */
|
||||
.audio-player-container {
|
||||
margin-top: 10px; display: flex; align-items: center; gap: 10px;
|
||||
}
|
||||
.play-button {
|
||||
background-color: #28a745; color: white; border: none;
|
||||
border-radius: 50%; width: 40px; height: 40px;
|
||||
cursor: pointer; display: flex; align-items: center; justify-content: center;
|
||||
}
|
||||
.play-button:hover { background-color: #218838; }
|
||||
.audio-progress {
|
||||
flex-grow: 1; height: 6px; background-color: #e9ecef; border-radius: 3px;
|
||||
overflow: hidden; cursor: pointer;
|
||||
}
|
||||
.progress-bar {
|
||||
height: 100%; background-color: #28a745; width: 0%;
|
||||
}
|
||||
.audio-time { font-size: 12px; color: #6c757d; }
|
||||
|
||||
@keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.5; } 100% { opacity: 1; } }
|
||||
</style>
|
||||
<link rel="stylesheet" href="physics_quiz.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
@@ -482,260 +106,9 @@
|
||||
<div>
|
||||
有权限问题的话,请点击<a href="https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/11%E3%80%81%E5%AD%A6%E4%BC%B4Chrome%E5%BD%95%E9%9F%B3%E9%85%8D%E7%BD%AE%E4%BF%AE%E6%94%B9.pdf">这里</a>
|
||||
</div>
|
||||
|
||||
<!-- 引入看板娘相关脚本 -->
|
||||
<script src="https://l2dwidget.js.org/lib/L2Dwidget.min.js"></script>
|
||||
<script>
|
||||
// 录音相关变量
|
||||
let mediaRecorder; let audioChunks = []; let isRecording = false;
|
||||
// 音频播放相关变量
|
||||
let audioElement = null; let isPlaying = false;
|
||||
|
||||
// 开始录音
|
||||
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); }
|
||||
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 rect = this.getBoundingClientRect();
|
||||
const clickPosition = (e.clientX - rect.left) / rect.width;
|
||||
audioElement.currentTime = clickPosition * 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 ? `
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 19H10V5H6V19ZM14 19H18V5H14V19Z" fill="white"/>
|
||||
</svg>` : `
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 5V19L19 12L8 5Z" fill="white"/>
|
||||
</svg>`;
|
||||
}
|
||||
function updateAudioProgress() { if (audioElement) document.getElementById('progressBar').style.width = `${(audioElement.currentTime/audioElement.duration)*100}%`; }
|
||||
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)}`;
|
||||
}
|
||||
|
||||
// 模型配置映射
|
||||
const modelConfig = {
|
||||
shizuku: {
|
||||
jsonPath: "https://unpkg.com/live2d-widget-model-shizuku@1.0.5/assets/shizuku.model.json"
|
||||
},
|
||||
koharu: {
|
||||
jsonPath: "https://unpkg.com/live2d-widget-model-koharu@1.0.5/assets/koharu.model.json"
|
||||
},
|
||||
wanko: {
|
||||
jsonPath: "https://unpkg.com/live2d-widget-model-wanko@1.0.5/assets/wanko.model.json"
|
||||
}
|
||||
};
|
||||
|
||||
let currentL2DWidget = null;
|
||||
let modelElement = null;
|
||||
|
||||
// 获取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(modelName) {
|
||||
// 如果没有传入modelName,则从URL参数获取模型名称,默认使用shizuku
|
||||
if (!modelName) {
|
||||
modelName = getUrlParam('model') || 'shizuku';
|
||||
}
|
||||
console.log("切换模型: ", modelName);
|
||||
const config = modelConfig[modelName];
|
||||
if (!config) {
|
||||
console.error("模型配置不存在: ", modelName);
|
||||
return;
|
||||
}
|
||||
console.log("模型JSON路径: ", config.jsonPath);
|
||||
|
||||
// 彻底清理现有实例
|
||||
if (currentL2DWidget) {
|
||||
try {
|
||||
currentL2DWidget.destroy();
|
||||
console.log("旧模型实例已销毁");
|
||||
} catch (e) {
|
||||
console.error("销毁实例失败: ", e);
|
||||
}
|
||||
// 修复:强制移除所有canvas元素
|
||||
const oldCanvases = document.querySelectorAll('canvas#l2dcanvas');
|
||||
oldCanvases.forEach(canvas => canvas.remove());
|
||||
currentL2DWidget = null;
|
||||
modelElement = null;
|
||||
}
|
||||
|
||||
// 创建新实例
|
||||
try {
|
||||
currentL2DWidget = L2Dwidget.init({
|
||||
model: {
|
||||
jsonPath: config.jsonPath,
|
||||
scale: 1
|
||||
},
|
||||
display: {
|
||||
position: "right",
|
||||
width: 150,
|
||||
height: 300,
|
||||
hOffset: 0,
|
||||
vOffset: -20
|
||||
},
|
||||
mobile: {
|
||||
show: true,
|
||||
scale: 0.5
|
||||
},
|
||||
react: {
|
||||
opacityDefault: 0.7,
|
||||
opacityOnHover: 0.8
|
||||
}
|
||||
});
|
||||
// 保存模型DOM元素引用
|
||||
// 修复:使用setTimeout确保DOM更新完成后再获取元素
|
||||
setTimeout(() => {
|
||||
modelElement = document.querySelector('canvas#l2dcanvas');
|
||||
if (modelElement) {
|
||||
console.log("新模型DOM元素已获取");
|
||||
} else {
|
||||
console.warn("未找到新模型canvas元素");
|
||||
}
|
||||
console.log("新模型初始化成功");
|
||||
}, 100);
|
||||
} catch (e) {
|
||||
console.error("模型初始化失败: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 监听模型选择变化
|
||||
const modelSelect = document.getElementById('model-select');
|
||||
if (modelSelect) {
|
||||
// 设置当前选中项
|
||||
const currentModel = getUrlParam('model') || 'shizuku';
|
||||
modelSelect.value = currentModel;
|
||||
|
||||
modelSelect.addEventListener('change', function() {
|
||||
console.log("选择模型: ", this.value);
|
||||
// 通过URL参数刷新页面实现模型切换
|
||||
window.location.search = '?model=' + this.value;
|
||||
});
|
||||
console.log("模型选择监听器已绑定");
|
||||
} else {
|
||||
console.error("未找到模型选择器元素");
|
||||
}
|
||||
|
||||
// 初始加载默认模型
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
initL2DWidget();
|
||||
// 添加录音按钮事件绑定
|
||||
document.getElementById('startRecordBtn').addEventListener('click', startRecording);
|
||||
document.getElementById('stopRecordBtn').addEventListener('click', stopRecording);
|
||||
});
|
||||
</script>
|
||||
<script src="live2d_widget.js"></script>
|
||||
</body>
|
||||
</html>
|
Reference in New Issue
Block a user