Bladeren bron

Merge branch 'master' of http://59.110.91.129:3000/zhangmengying/AIClass into wanzi

丸子 2 weken geleden
bovenliggende
commit
4ab16b8e65

+ 7 - 6
README.md

@@ -101,9 +101,10 @@
 
 项目采用模块化设计,主要包含以下目录:
 
-- `src/views/` - 页面组件
-- `src/components/` - 可复用组件
-- `src/api/` - API接口和工具函数
-- `src/router/` - 路由配置
-- `src/utils/` - 工具函数
-- `src/assets/` - 静态资源
+- `src/` - 项目源代码目录
+  - `views/` - 页面组件
+  - `components/` - 可复用组件
+  - `api/` - API接口和工具函数
+  - `router/` - 路由配置
+  - `utils/` - 工具函数
+  - `assets/` - 静态资源

+ 1 - 1
index.html

@@ -2,7 +2,7 @@
 <html lang="en">
   <head>
     <meta charset="UTF-8" />
-    <!-- <link rel="icon" type="image/svg+xml" href="/vite.svg" /> -->
+     <link rel="icon" type="image/svg+xml" href="/src/assets/images/logo.png" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
       <title>%VITE_APP_TITLE%</title>
   </head>

+ 0 - 1
src/api/blockly/blockly.js

@@ -739,7 +739,6 @@ ${pythonGenerator.prefixLines(branchCode, pythonGenerator.INDENT)}
 ${pythonGenerator.prefixLines(branchCode, pythonGenerator.INDENT)}
 `;
     }
-debugger
     // 生成else条件
     const elseCode = pythonGenerator.statementToCode(block, 'ELSE');
     if (elseCode) {

+ 0 - 1
src/utils/roleUtils.js

@@ -28,7 +28,6 @@ export const refreshRoleRoute = async () => {
 
             // 存储到localStorage并设置过期时间
             setCacheWithExpiry(CONFIG.USER_ROLE_ROUTE_KEY, res.data.routeList);
-            debugger
             setCacheWithExpiry(CONFIG.USER_ROLE_ROUTE_MENU_KEY, res.data.courseRouteMenuList);
 
             return res.data.routeList;

+ 185 - 34
src/views/AIPage/aiGenerate/DialogContent.vue

@@ -68,6 +68,19 @@
         </div>
       </div>
 
+      <!-- 视频显示区域 -->
+      <div v-if="currentDialogue && currentDialogue.type === 'video'" class="video-display">
+        <video
+          :src="currentDialogue.videoUrl"
+          class="dialogue-video"
+          controls
+          autoplay
+          @ended="handleVideoEnded"
+        >
+          您的浏览器不支持视频播放
+        </video>
+      </div>
+
       <!-- 用户输入卡片 -->
       <div
         v-if="currentDialogue.type === 'user'"
@@ -116,7 +129,12 @@
       </div>
     </div>
 
-    <img :src="currentBackgroundImage" alt="背景图" class="background-image">
+    <!-- 背景图 -->
+    <img v-if="currentBackgroundType === 'imageAudio'" :src="currentBackgroundImage" alt="背景图" class="background-image">
+    <!-- 背景视频 -->
+    <video v-else-if="currentBackgroundType === 'video'" ref="backgroundVideoRef" :src="currentBackgroundVideo" class="background-video" loop muted playsinline>
+      您的浏览器不支持视频播放
+    </video>
   </div>
 </template>
 
@@ -182,8 +200,12 @@ const currentPoemContent = ref('')
 const backgroundAudio = ref(null)
 // 对话音频
 const dialogueAudio = ref(null)
+// 背景视频
+const backgroundVideoRef = ref(null)
 // 遮罩层显示状态
 const showMask = ref(true)
+// 是否开始播放
+const isPlaybackStarted = ref(false)
 
 // 计算属性
 // 当前章节信息
@@ -203,6 +225,19 @@ const currentBackgroundImage = computed(() => {
   return currentSection.value.backgroundImage.url
 })
 
+// 当前背景类型
+const currentBackgroundType = computed(() => {
+  return currentSection.value?.backgroundType || 'imageAudio'
+})
+
+// 当前背景视频
+const currentBackgroundVideo = computed(() => {
+  if (!currentSection.value || !currentSection.value.backgroundVideo || !currentSection.value.backgroundVideo.url) {
+    return ''
+  }
+  return currentSection.value.backgroundVideo.url
+})
+
 // 当前对话缓存
 const currentDialogueCache = ref(null)
 
@@ -296,6 +331,9 @@ const stopAllAudio = () => {
     dialogueAudio.value.pause()
     dialogueAudio.value.currentTime = 0
   }
+  if (backgroundVideoRef.value) {
+    backgroundVideoRef.value.pause()
+  }
 }
 
 const playBackgroundAudio = () => {
@@ -305,13 +343,24 @@ const playBackgroundAudio = () => {
     backgroundAudio.value.currentTime = 0
   }
   
-  // 播放当前环节的背景音
-  if (currentSection.value?.backgroundAudio?.url && isPlaying.value) {
+  // 只有当背景类型为 imageAudio 时才播放背景音
+  if (currentBackgroundType.value === 'imageAudio' && currentSection.value?.backgroundAudio?.url && isPlaying.value) {
     backgroundAudio.value = new Audio(currentSection.value.backgroundAudio.url)
     backgroundAudio.value.loop = true
     backgroundAudio.value.volume = 1
     backgroundAudio.value.play().catch(e => console.error('背景音播放失败:', e))
   }
+
+  // 处理背景视频
+  if (currentBackgroundType.value === 'video' && isPlaybackStarted.value && isPlaying.value) {
+    // 视频已经在模板中渲染,这里只需要确保它在播放状态
+    if (backgroundVideoRef.value) {
+      backgroundVideoRef.value.play().catch(e => console.error('背景视频播放失败:', e))
+    }
+  } else if (backgroundVideoRef.value) {
+    // 暂停视频
+    backgroundVideoRef.value.pause()
+  }
 }
 
 const playDialogueAudio = (isAutoPlay = false) => {
@@ -321,11 +370,6 @@ const playDialogueAudio = (isAutoPlay = false) => {
     dialogueAudio.value.currentTime = 0
   }
 
-  // 如果是诗词类型,不播放音频
-  if (currentDialogue.value?.type === 'poem') {
-    return
-  }
-
   // 播放当前对话的语音
   if (currentDialogue.value?.voiceoverUrl) {
     const audio = new Audio(currentDialogue.value.voiceoverUrl)
@@ -414,16 +458,36 @@ const togglePlay = () => {
   if (isPlaying.value) {
     // 播放背景音
     playBackgroundAudio()
+    // 继续播放背景视频
+    if (backgroundVideoRef.value) {
+      backgroundVideoRef.value.play().catch(e => console.error('背景视频播放失败:', e))
+    }
     if(!getIsPlaying() && !conversationInProgress.value){
       // 开始播放序列
       playSequence()
     }
   } else {
-    // 暂停所有音频
+    // 暂停所有音频和视频
     stopAllAudio()
   }
 }
 
+// 处理视频结束事件
+const handleVideoEnded = () => {
+  // 视频播放完毕后,继续播放下一条对话
+  if (isPlaying.value) {
+    if (!playNext(true)) {
+      // 播放完毕,检查是否是最后一个对话
+      if (isAtLastDialogue()) {
+        console.log('视频序列:已到达最后一个对话,触发 dialogueEnded 事件');
+        emit('dialogueEnded', props.isLastCourse);
+      }
+      isPlaying.value = false
+      stopAllAudio()
+    }
+  }
+}
+
 // 自动播放序列
 const playSequence = () => {
   if (!isPlaying.value) return
@@ -436,18 +500,26 @@ const playSequence = () => {
     // 显示诗词并替换内容为最新的诗词
     showPoem.value = true
     currentPoemContent.value = currentDialogue.value.content
-    // 自动切换到下一条对话
-    setTimeout(() => {
-      if (!playNext(true)) {
-        // 播放完毕,检查是否是最后一个对话
-        if (isAtLastDialogue()) {
-          console.log('诗词序列:已到达最后一个对话,触发 dialogueEnded 事件');
-          emit('dialogueEnded', props.isLastCourse);
+
+    // 检查是否有语音
+    if (currentDialogue.value?.voiceoverUrl) {
+      // 播放诗词语音
+      playDialogueAudio(true)
+    } else {
+      // 没有语音,直接切换到下一条对话
+      setTimeout(() => {
+        if (!playNext(true)) {
+          // 播放完毕,检查是否是最后一个对话
+          if (isAtLastDialogue()) {
+            console.log('诗词序列:已到达最后一个对话,触发 dialogueEnded 事件');
+            emit('dialogueEnded', props.isLastCourse);
+          }
+          // 播放完毕
+          isPlaying.value = false
+          stopAllAudio()
         }
-        isPlaying.value = false
-        stopAllAudio()
-      }
-    }, 500)
+      }, 500)
+    }
   } else {
     // 播放当前对话语音,传递isAutoPlay参数
     playDialogueAudio(true)
@@ -486,6 +558,10 @@ const playPrevious = () => {
     // 显示诗词并替换内容为最新的诗词
     showPoem.value = true
     currentPoemContent.value = currentDialogue.value.content
+  } else if (currentDialogue.value?.type === 'video') {
+    // 视频类型对话,隐藏诗词
+    showPoem.value = false
+    currentPoemContent.value = ""
   } else {
     showPoem.value = false
     currentPoemContent.value = ""
@@ -539,10 +615,26 @@ const playNext = (isAutoPlay = false) => {
       // 显示诗词并替换内容为最新的诗词
       showPoem.value = true
       currentPoemContent.value = currentDialogue.value.content
-      // 自动切换到下一条对话,不需要播放音频
-      setTimeout(() => {
-        playNext(isAutoPlay)
-      }, 500)
+
+      // 检查是否有语音
+      if (currentDialogue.value?.voiceoverUrl) {
+        // 播放诗词语音
+        if (isPlaying.value) {
+          playDialogueAudio(true)
+        } else {
+          playDialogueAudio()
+        }
+      } else {
+        // 没有语音,直接切换到下一条对话
+        setTimeout(() => {
+          playNext(isAutoPlay)
+        }, 500)
+      }
+      return true
+    } else if (currentDialogue.value?.type === 'video') {
+      // 视频类型对话,隐藏诗词,不自动播放下一条
+      showPoem.value = false
+      currentPoemContent.value = ''
       return true
     }
     
@@ -566,10 +658,25 @@ const playNext = (isAutoPlay = false) => {
       // 显示诗词
       showPoem.value = true
       currentPoemContent.value = currentDialogue.value.content
-      // 自动切换到下一条对话,不需要播放音频
-      setTimeout(() => {
-        playNext(isAutoPlay)
-      }, 500)
+
+      // 检查是否有语音
+      if (currentDialogue.value?.voiceoverUrl) {
+        // 播放诗词语音
+        if (isPlaying.value) {
+          playDialogueAudio(true)
+        } else {
+          playDialogueAudio()
+        }
+      } else {
+        // 没有语音,直接切换到下一条对话
+        setTimeout(() => {
+          playNext(isAutoPlay)
+        }, 500)
+      }
+    } else if (currentDialogue.value?.type === 'video') {
+      // 视频类型对话,隐藏诗词
+      showPoem.value = false
+      currentPoemContent.value = ''
     } else {
       // 根据是否为自动播放状态决定如何播放语音
       if (isPlaying.value) {
@@ -594,6 +701,14 @@ const goBackToMain = () => {
 // 开始播放
 const maskLayer = ref(null)
 const startPlayback = () => {
+  // 设置开始播放状态
+  isPlaybackStarted.value = true
+
+  // 开始播放背景视频
+  if (backgroundVideoRef.value) {
+    backgroundVideoRef.value.play().catch(e => console.error('背景视频播放失败:', e))
+  }
+
   // 消失动画
   if (maskLayer.value) {
     maskLayer.value.classList.add('fade-out')
@@ -608,10 +723,21 @@ const startPlayback = () => {
     // 显示诗词
     showPoem.value = true
     currentPoemContent.value = currentDialogue.value.content
-    // 自动切换到下一条对话
-    setTimeout(() => {
-      playNext()
-    }, 500)
+
+    // 检查是否有语音
+    if (currentDialogue.value?.voiceoverUrl) {
+      // 播放诗词语音
+      playDialogueAudio()
+    } else {
+      // 没有语音,直接切换到下一条对话
+      setTimeout(() => {
+        playNext()
+      }, 500)
+    }
+  } else if (currentDialogue.value?.type === 'video') {
+    // 视频类型对话,隐藏诗词
+    showPoem.value = false
+    currentPoemContent.value = ''
   } else {
     // 播放当前对话语音
     playDialogueAudio()
@@ -1059,6 +1185,14 @@ onUnmounted(() => {
   position: relative;
 }
 
+.background-video {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+  z-index: 1;
+  position: relative;
+}
+
 /* 遮罩层样式 */
 .mask-layer {
   position: absolute;
@@ -1732,7 +1866,7 @@ onUnmounted(() => {
   background-size: 100%;
   background-repeat: no-repeat;
   background-position: center;
-  opacity: 0.5;
+  opacity: 0.8;
   z-index: -1;
 }
 
@@ -1755,7 +1889,8 @@ onUnmounted(() => {
   display: flex;
   align-items: center;
   justify-content: center;
-  flex-direction: column;
+  //flex-direction: column;
+  text-align: center;
   transition: all 0.5s ease-in-out;
 }
 
@@ -1775,6 +1910,22 @@ onUnmounted(() => {
   background: rgba(0, 0, 0, 0.5);
 }
 
+.video-display {
+  position: absolute;
+  top: 45%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  z-index: 5;
+  width: 65%;
+}
+
+.dialogue-video {
+  width: 100%;
+  height: 100%;
+  border-radius: rpx(10);
+  box-shadow: 0 rpx(5) rpx(15) rgba(0, 0, 0, 0.2);
+}
+
 .poem-text::before {
   content: '';
   position: absolute;

+ 0 - 1
src/views/laboratory/ExperimentalInterface.vue

@@ -524,7 +524,6 @@ const handleSaveProgress = async (type, progress) => {
     saveProgressData.arpCourseId = course.value.id;
   }
   try {
-    debugger
     await saveRecordAiCourse(saveProgressData)
   } catch (error) {
     console.error(`保存${type}进度失败:`, error)