Commit c9c5ae56 authored by liujiangnan's avatar liujiangnan

feat

parent 32916f24
/* 基于视口单位的等比例缩放系统 */ /* 全局样式重置 */
/* 设计基准:1920x1080 桌面横屏 */ * {
margin: 0;
padding: 0;
box-sizing: border-box;
}
#app { body {
margin: 0 auto; min-height: 100vh;
border-radius: calc(15 * 100vw / 1920);
box-shadow: 0 calc(10 * 100vw / 1920) calc(30 * 100vw / 1920) rgba(0, 0, 0, 0.2);
overflow: hidden; overflow: hidden;
} }
#app {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
}
.question-container { .question-container {
position: absolute; width: 100%;
top: calc(10 * 100vw / 1920); height: 100vh;
left: calc(10 * 100vw / 1920); display: flex;
bottom: calc(10 * 100vw / 1920); flex-direction: column;
right: calc(10 * 100vw / 1920);
} }
/* 题干部分样式 */ /* 题干部分样式 - 占据屏幕上50% */
.question-section { .question-section {
margin-bottom: calc(30 * 100vw / 1920); height: 50vh;
padding: calc(25 * 100vw / 1920); display: flex;
align-items: center;
justify-content: center;
padding: 20px;
position: relative;
}
.question-content {
background: rgba(0, 0, 0, 0.2);
border: 2px solid white;
border-radius: 12px;
padding: 30px;
max-width: 80vw;
width: fit-content;
text-align: center; text-align: center;
color: white;
display: flex;
flex-direction: column;
align-items: center;
gap: 20px;
} }
.question-image { .question-image {
text-align: center; order: -1; /* 图片在最上面 */
margin-bottom: calc(20 * 100vw / 1920);
} }
.question-image img { .question-image img {
max-width: 100%; max-width: 100%;
max-height: calc(250 * 100vw / 1920); max-height: 200px;
border-radius: calc(10 * 100vw / 1920); border-radius: 8px;
box-shadow: 0 calc(4 * 100vw / 1920) calc(12 * 100vw / 1920) rgba(0, 0, 0, 0.1); object-fit: contain;
} }
.question-audio { .question-audio {
margin-bottom: calc(20 * 100vw / 1920); display: flex;
align-items: center;
gap: 10px;
margin-bottom: 15px;
}
.audio-icon {
width: 24px;
height: 24px;
background: white;
border-radius: 50%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
gap: calc(15 * 100vw / 1920); cursor: pointer;
transition: all 0.3s ease;
flex-shrink: 0;
}
.audio-icon:hover {
background: #409EFF;
color: white;
}
.audio-icon i {
font-size: 12px;
color: #333;
} }
.question-audio .audio-info { .audio-icon:hover i {
color: #606266; color: white;
font-size: calc(14 * 100vw / 1920); }
.question-text {
font-size: 22px;
line-height: 1.6;
word-wrap: break-word;
max-width: 100%;
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
justify-content: center;
} }
.question-text h3 { .question-text h3 {
margin: 0; margin: 0;
color: #303133; font-size: 22px;
font-size: calc(20 * 100vw / 1920);
line-height: 1.6; line-height: 1.6;
font-weight: 600; word-wrap: break-word;
max-width: 100%;
} }
/* 选项部分样式 */ /* 选项部分样式 - 占据屏幕下50% */
.options-section { .options-section {
margin-bottom: calc(30 * 100vw / 1920); height: 50vh;
padding: 20px;
display: flex;
align-items: center;
justify-content: center;
}
.options-grid {
display: flex; display: flex;
gap: 20px;
max-width: 90vw;
flex-wrap: wrap; flex-wrap: wrap;
gap: calc(15 * 100vw / 1920);
justify-content: center; justify-content: center;
} }
/* 选项卡片基础样式 */
.option-item { .option-item {
flex: 0 0 calc(300 * 100vw / 1920); background: rgba(0, 0, 0, 0.2);
max-width: calc(300 * 100vw / 1920); border: 2px solid white;
min-height: calc(200 * 100vw / 1920); border-radius: 12px;
display: flex; padding: 30px;
flex-direction: column;
align-items: center;
margin-bottom: 0;
padding: calc(20 * 100vw / 1920);
border: calc(2 * 100vw / 1920) solid #e4e7ed;
border-radius: calc(10 * 100vw / 1920);
cursor: pointer; cursor: pointer;
transition: all 0.3s ease; transition: all 0.3s ease;
background: white; color: white;
text-align: center; display: flex;
align-items: center;
gap: 15px;
min-width: 200px;
max-width: 300px;
position: relative; /* 为绝对定位的子元素提供参考 */
} }
.option-item:hover { .option-item:hover {
border-color: #409EFF; border-color: #409EFF;
box-shadow: 0 calc(4 * 100vw / 1920) calc(12 * 100vw / 1920) rgba(64, 158, 255, 0.15); background: rgba(64, 158, 255, 0.2);
transform: translateY(calc(-2 * 100vw / 1920));
} }
.option-item.selected { .option-item.selected {
border-color: #409EFF; border-color: #67C23A;
background: #f0f9ff; background: rgba(103, 194, 58, 0.2);
}
.option-item.selected .option-selected {
color: #409EFF;
font-size: calc(24 * 100vw / 1920);
} }
/* 选项内容布局 */
.option-content { .option-content {
flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: calc(10 * 100vw / 1920);
width: 100%;
align-items: center; align-items: center;
gap: 15px;
width: 100%;
} }
.option-image { .option-audio {
text-align: center; flex-shrink: 0;
margin-bottom: calc(10 * 100vw / 1920);
}
.option-image img {
max-width: calc(120 * 100vw / 1920);
max-height: calc(120 * 100vw / 1920);
border-radius: calc(6 * 100vw / 1920);
object-fit: cover;
} }
.option-audio { .option-text {
flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; gap: 8px;
gap: calc(8 * 100vw / 1920);
margin-bottom: calc(10 * 100vw / 1920);
}
.option-audio .audio-info {
color: #606266;
font-size: calc(12 * 100vw / 1920);
text-align: center; text-align: center;
width: 100%;
} }
.option-text { .option-text-content {
font-size: 22px;
line-height: 1.4;
color: white;
display: flex; display: flex;
flex-direction: column;
align-items: center; align-items: center;
gap: calc(8 * 100vw / 1920); gap: 8px;
justify-content: flex-start;
text-align: left;
width: 100%; width: 100%;
} }
.option-label { .option-label {
position: absolute;
top: -10px;
left: -10px;
font-weight: bold; font-weight: bold;
color: #409EFF; font-size: 22px;
font-size: calc(18 * 100vw / 1920); color: white;
margin-bottom: calc(5 * 100vw / 1920); background: rgba(0, 0, 0, 0.5);
border-radius: 50%;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
} }
.option-text-content { .option-image {
color: #303133; flex-shrink: 0;
font-size: calc(16 * 100vw / 1920); order: -1; /* 图片在最上面 */
line-height: 1.5; }
text-align: center;
word-wrap: break-word; .option-image img {
max-width: 80px;
max-height: 80px;
border-radius: 8px;
object-fit: cover;
}
/* 有图片时的横向布局 */
.options-grid.has-images {
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
}
.options-grid.has-images .option-item {
flex: 0 0 auto;
min-width: 250px;
} }
.option-status { /* 纯文本时的两列布局 */
margin-top: calc(10 * 100vw / 1920); .options-grid.text-only {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
width: 100%;
} }
.option-selected { .options-grid.text-only .option-item {
color: #409EFF; width: 100%;
font-size: calc(24 * 100vw / 1920); max-width: none;
} }
/* 提交按钮部分 */ /* 提交按钮 */
.submit-section { .submit-section {
position: absolute; position: fixed;
right: calc(30 * 100vw / 1920); bottom: 60px;
bottom: calc(30 * 100vw / 1920); right: 60px;
z-index: 1000;
} }
.submit-section .el-button { .submit-section .el-button {
...@@ -179,6 +261,18 @@ ...@@ -179,6 +261,18 @@
border-radius: calc(8 * 100vw / 1920); border-radius: calc(8 * 100vw / 1920);
} }
.submit-section .el-button:hover {
background: rgba(103, 194, 58, 1);
transform: translateY(-2px);
}
.submit-section .el-button:disabled {
background: rgba(0, 0, 0, 0.3);
border-color: rgba(255, 255, 255, 0.3);
cursor: not-allowed;
transform: none;
}
/* 庆祝动画弹窗 */ /* 庆祝动画弹窗 */
.celebration-modal { .celebration-modal {
position: fixed; position: fixed;
...@@ -186,7 +280,7 @@ ...@@ -186,7 +280,7 @@
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
background: rgba(0, 0, 0, 0.7); background: rgba(0, 0, 0, 0.8);
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
...@@ -195,37 +289,38 @@ ...@@ -195,37 +289,38 @@
} }
.celebration-content { .celebration-content {
background: white; background: rgba(0, 0, 0, 0.9);
padding: calc(40 * 100vw / 1920); border: 2px solid #67C23A;
border-radius: calc(20 * 100vw / 1920); border-radius: 20px;
padding: 40px;
text-align: center; text-align: center;
box-shadow: 0 calc(10 * 100vw / 1920) calc(30 * 100vw / 1920) rgba(0, 0, 0, 0.3); color: white;
animation: bounceIn 0.6s ease-out; animation: bounceIn 0.6s ease-out;
position: relative; position: relative;
z-index: 10000; z-index: 10000;
} }
.celebration-icon { .celebration-icon {
font-size: calc(80 * 100vw / 1920); font-size: 80px;
margin-bottom: calc(20 * 100vw / 1920); margin-bottom: 20px;
animation: bounce 1s infinite; animation: bounce 1s infinite;
} }
.celebration-text { .celebration-text {
font-size: calc(32 * 100vw / 1920); font-size: 32px;
font-weight: bold; font-weight: bold;
color: #67c23a; color: #67C23A;
margin-bottom: calc(20 * 100vw / 1920); margin-bottom: 20px;
} }
.celebration-stars { .celebration-stars {
display: flex; display: flex;
justify-content: center; justify-content: center;
gap: calc(10 * 100vw / 1920); gap: 10px;
} }
.star { .star {
font-size: calc(24 * 100vw / 1920); font-size: 24px;
animation: twinkle 1.5s infinite; animation: twinkle 1.5s infinite;
} }
...@@ -244,7 +339,7 @@ ...@@ -244,7 +339,7 @@
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
background: rgba(0, 0, 0, 0.7); background: rgba(0, 0, 0, 0.8);
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
...@@ -253,36 +348,37 @@ ...@@ -253,36 +348,37 @@
} }
.error-content { .error-content {
background: white; background: rgba(0, 0, 0, 0.9);
padding: calc(40 * 100vw / 1920); border: 2px solid #f56c6c;
border-radius: calc(20 * 100vw / 1920); border-radius: 20px;
padding: 40px;
text-align: center; text-align: center;
box-shadow: 0 calc(10 * 100vw / 1920) calc(30 * 100vw / 1920) rgba(0, 0, 0, 0.3); color: white;
animation: shake 0.5s ease-in-out; animation: shake 0.5s ease-in-out;
position: relative; position: relative;
z-index: 10000; z-index: 10000;
} }
.error-icon { .error-icon {
font-size: calc(80 * 100vw / 1920); font-size: 80px;
margin-bottom: calc(20 * 100vw / 1920); margin-bottom: 20px;
animation: shake 0.5s ease-in-out; animation: shake 0.5s ease-in-out;
} }
.error-text { .error-text {
font-size: calc(32 * 100vw / 1920); font-size: 32px;
font-weight: bold; font-weight: bold;
color: #f56c6c; color: #f56c6c;
margin-bottom: calc(20 * 100vw / 1920); margin-bottom: 20px;
} }
.correct-answer-text { .correct-answer-text {
font-size: calc(18 * 100vw / 1920); font-size: 18px;
color: #606266; color: white;
background: #f5f7fa; background: rgba(64, 158, 255, 0.2);
padding: calc(15 * 100vw / 1920); padding: 15px;
border-radius: calc(8 * 100vw / 1920); border-radius: 8px;
border-left: calc(4 * 100vw / 1920) solid #409EFF; border-left: 4px solid #409EFF;
} }
/* 彩带动画 */ /* 彩带动画 */
...@@ -297,8 +393,8 @@ ...@@ -297,8 +393,8 @@
.confetti { .confetti {
position: absolute; position: absolute;
width: calc(10 * 100vw / 1920); width: 10px;
height: calc(10 * 100vw / 1920); height: 10px;
background: linear-gradient(45deg, #ff6b6b, #4ecdc4, #45b7d1, #96ceb4, #feca57); background: linear-gradient(45deg, #ff6b6b, #4ecdc4, #45b7d1, #96ceb4, #feca57);
animation: confettiFall 3s linear infinite; animation: confettiFall 3s linear infinite;
} }
...@@ -365,69 +461,75 @@ ...@@ -365,69 +461,75 @@
} }
} }
/* 响应式断点 - 保持布局不变,只调整基准值 */ /* 响应式设计 */
@media (max-width: 1200px) { @media (max-width: 768px) {
/* 中等屏幕:使用较小的基准值 */ .question-content {
.option-item { padding: 20px;
flex: 0 0 calc(280 * 100vw / 1920); max-width: 90vw;
max-width: calc(280 * 100vw / 1920);
} }
}
@media (max-width: 768px) { .question-text {
/* 小屏幕:调整基准值但保持比例 */ font-size: 16px;
.question-container {
padding: calc(20 * 100vw / 1920);
} }
.options-section { .question-text h3 {
flex-direction: column; font-size: 16px;
align-items: center; }
.options-grid.text-only {
grid-template-columns: 1fr;
gap: 15px;
} }
.option-item { .option-item {
flex: 0 0 auto; min-width: 150px;
max-width: 100%; padding: 15px;
width: 100%;
min-height: auto;
} }
.option-content { .option-content {
flex-direction: row; gap: 10px;
align-items: center;
text-align: left;
} }
.option-image { .option-image img {
margin-bottom: 0; max-width: 60px;
margin-right: calc(15 * 100vw / 1920); max-height: 60px;
} }
.option-audio { .submit-section {
flex-direction: row; bottom: 10px;
margin-bottom: 0; right: 10px;
margin-right: calc(15 * 100vw / 1920);
} }
.option-text { .submit-section .el-button {
flex-direction: row; padding: 10px 20px;
align-items: center; font-size: 14px;
flex: 1;
} }
}
.option-label { @media (max-width: 480px) {
margin-bottom: 0; .question-section,
margin-right: calc(8 * 100vw / 1920); .options-section {
padding: 10px;
} }
.option-status { .question-content {
margin-top: 0; padding: 15px;
margin-left: calc(15 * 100vw / 1920);
} }
.submit-section .el-button { .options-grid {
display: block; gap: 10px;
width: 100%; }
margin: calc(10 * 100vw / 1920) 0;
.option-item {
padding: 12px;
min-width: 120px;
}
.option-text-content {
font-size: 12px;
}
.option-label {
font-size: 14px;
} }
} }
\ No newline at end of file
...@@ -19,49 +19,44 @@ ...@@ -19,49 +19,44 @@
<div class="question-container" v-if="questionData"> <div class="question-container" v-if="questionData">
<!-- 题干部分 --> <!-- 题干部分 -->
<div class="question-section"> <div class="question-section">
<div class="question-text" v-if="questionData.question"> <div class="question-content">
<h3>{{ questionData.question }}</h3>
</div>
<div class="question-image" v-if="questionData.image_url"> <div class="question-image" v-if="questionData.image_url">
<img :src="questionData.image_url" alt="题干图片"> <img :src="questionData.image_url" alt="题干图片">
</div> </div>
<div class="question-audio" v-if="questionData.audio_url"> <div class="question-text" v-if="questionData.question">
<el-button size="small" type="primary" @click="toggleQuestionAudio"> <div class="audio-icon" v-if="questionData.audio_url" @click="toggleQuestionAudio">
<i :class="questionData.isPlaying ? 'el-icon-video-pause' : 'el-icon-video-play'"></i> <i :class="questionData.isPlaying ? 'el-icon-video-pause' : 'el-icon-video-play'"></i>
{{ questionData.isPlaying ? '暂停' : '播放' }}题目音频 </div>
</el-button>
<audio ref="questionAudio" :src="questionData.audio_url" @ended="questionAudioEnded" style="display: none;"></audio> <audio ref="questionAudio" :src="questionData.audio_url" @ended="questionAudioEnded" style="display: none;"></audio>
<h3>{{ questionData.question }}</h3>
</div>
</div> </div>
</div> </div>
<!-- 选项部分 --> <!-- 选项部分 -->
<div class="options-section"> <div class="options-section">
<div class="options-grid" :class="{ 'has-images': hasOptionImages, 'text-only': !hasOptionImages }">
<div class="option-item" <div class="option-item"
v-for="(option, index) in questionData.options" v-for="(option, index) in questionData.options"
:key="index" :key="index"
@click="toggleOption(index)" @click="toggleOption(index)"
:class="{ 'selected': selectedOptions.includes(index) }"> :class="{ 'selected': selectedOptions.includes(index) }">
<div class="option-content"> <div class="option-content">
<div class="option-text" v-if="option.text"> <div class="option-text">
<span class="option-text-content">{{ option.text }}</span> <div class="option-label">{{ String.fromCharCode(65 + index) }}</div>
<div class="option-text-content" v-if="option.text">
<div class="audio-icon" v-if="option.audio_url" @click.stop="toggleOptionAudio(index)">
<i :class="option.isPlaying ? 'el-icon-video-pause' : 'el-icon-video-play'"></i>
</div>
<audio :ref="'optionAudio' + index" :src="option.audio_url" @ended="optionAudioEnded(index)" style="display: none;"></audio>
<span>{{ option.text }}</span>
</div>
</div> </div>
<div class="option-image" v-if="option.image_url"> <div class="option-image" v-if="option.image_url">
<img :src="option.image_url" alt="选项图片"> <img :src="option.image_url" alt="选项图片">
</div> </div>
<div class="option-audio" v-if="option.audio_url">
<el-button size="mini" type="primary" @click.stop="toggleOptionAudio(index)">
<i :class="option.isPlaying ? 'el-icon-video-pause' : 'el-icon-video-play'"></i>
{{ option.isPlaying ? '暂停' : '播放' }}
</el-button>
<audio :ref="'optionAudio' + index" :src="option.audio_url" @ended="optionAudioEnded(index)" style="display: none;"></audio>
</div>
<div class="option-text">
<span class="option-label">{{ String.fromCharCode(65 + index) }}</span>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -28,6 +28,15 @@ new Vue({ ...@@ -28,6 +28,15 @@ new Vue({
showError: false showError: false
} }
}, },
computed: {
// 判断是否有选项包含图片
hasOptionImages() {
if (!this.questionData || !this.questionData.options) {
return false;
}
return this.questionData.options.some(option => option.image_url && option.image_url.trim() !== '');
}
},
created() { created() {
window.courseware.getData((data) => { window.courseware.getData((data) => {
if (data) { if (data) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment