Browse Source

Merge remote-tracking branch 'origin/wanzi'

liyanbo 2 tuần trước cách đây
mục cha
commit
d76ae54f38
2 tập tin đã thay đổi với 115 bổ sung16 xóa
  1. 28 1
      src/views/AIPage/AIDevelop.vue
  2. 87 15
      src/views/AIPage/aiGenerate/DialogContent.vue

+ 28 - 1
src/views/AIPage/AIDevelop.vue

@@ -171,11 +171,14 @@
                      @saveProgress="handleSaveProgress"></ImageToVideo>
 
           <!-- 对话界面(ailab课程类型) -->
-          <DialogContent 
+          <DialogContent
             v-if="course.courseContentType === 'ailab'"
+            :key="course.key"
             :scriptRoles="scriptRoles"
             :scriptData="course.courseContent"
             :back-text="boxIconTitle"
+            :is-last-course="isLastCourse"
+            @dialogue-ended="handleDialogueEnded"
           />
         </template>
 
@@ -300,6 +303,30 @@ const promptPopupVisible = ref(false)
 // 即将播放下一节提示显示状态
 const playPromptVisible = ref(false)
 
+// 计算是否是最后一节课
+const isLastCourse = computed(() => {
+  const allIndices = flattenMenuItems()
+  const currentIndexInList = allIndices.indexOf(course.value.key)
+  return currentIndexInList === allIndices.length - 1
+})
+
+// 处理 DialogContent 对话结束事件
+const handleDialogueEnded = (isLast) => {
+  if (isLast) {
+    // 最后一节课,检查课程类型
+    if (course.value.courseContentType === 'ailab') {
+      // ailab类型显示已经是最后一节课提示
+      Message().notifyWarning('已经是最后一节课', true)
+      return
+    }
+    // 其他类型显示返回提示弹窗
+    promptPopupVisible.value = true
+  } else {
+    // 不是最后一节课,显示播放提示
+    playPromptVisible.value = true
+  }
+}
+
 // 脚本角色数据
 const scriptRoles = ref([])
 

+ 87 - 15
src/views/AIPage/aiGenerate/DialogContent.vue

@@ -1,4 +1,4 @@
-<template>
+<template>
   <div class="dialog-content-wrapper">
     <!-- 遮罩层 -->
     <div v-if="showMask" class="mask-layer" ref="maskLayer">
@@ -149,9 +149,16 @@ const props = defineProps({
   backText: {
     type: String,
     default: '返回课程'
+  },
+  // 是否是最后一节课
+  isLastCourse: {
+    type: Boolean,
+    default: false
   }
 })
 
+const emit = defineEmits(['dialogueEnded'])
+
 // 对话相关状态
 // 当前章节索引
 const currentSectionIndex = ref(0)
@@ -261,15 +268,8 @@ const parseMarkdown = (content) => {
 // 格式化诗词内容,在逗号和句号后添加换行
 const formatPoemContent = (content) => {
   if (!content) return ''
-  // 移除markdown格式符号,保留文字
-  let plainText = content.replace(/[\*#`\[\]\(\)]/g, '')
-  // 在逗号和句号后添加换行符(保留标点符号)
-  let formatted = plainText.replace(/([。!?;、])/g, '$1<br/>')
-  
-  // 写死的额外数据
-  // const extraData = '<p>' + formatted + '</p>'
-  
-  return formatted
+  const plainText = content.replace(/[\*#`\[\]\(\)]/g, '')
+  return plainText.replace(/([。!?;、])/g, '$1<br/>')
 }
 
 const getCharacterSide = () => {
@@ -320,19 +320,33 @@ const playDialogueAudio = (isAutoPlay = false) => {
     dialogueAudio.value.pause()
     dialogueAudio.value.currentTime = 0
   }
-  
+
   // 如果是诗词类型,不播放音频
   if (currentDialogue.value?.type === 'poem') {
     return
   }
-  
+
   // 播放当前对话的语音
   if (currentDialogue.value?.voiceoverUrl) {
     const audio = new Audio(currentDialogue.value.voiceoverUrl)
     dialogueAudio.value = audio
-    
+
     // 音频结束事件
     audio.onended = () => {
+      // 检查是否是最后一个对话
+      if (isAtLastDialogue()) {
+        // 如果是用户输入类型,不立即触发dialogueEnded,等待AI回答完成
+        if (currentDialogue.value?.type === 'user') {
+          console.log('用户输入类型,等待AI回答完成后再提示');
+          return;
+        }
+        // 语音播报完成且是最后一个对话,触发事件通知父组件
+        console.log('普通对话语音播放完成,已到达最后一个对话,触发 dialogueEnded 事件');
+        emit('dialogueEnded', props.isLastCourse);
+        isPlaying.value = false;
+        return;
+      }
+
       // 如果是自动播放状态,继续播放下一条
       if (isAutoPlay && isPlaying.value) {
         setTimeout(() => {
@@ -344,10 +358,21 @@ const playDialogueAudio = (isAutoPlay = false) => {
         }, 100)
       }
     }
-    
+
     // 播放音频
     audio.play().catch(e => {
       console.error('对话语音播放失败:', e)
+      // 播放失败时,检查是否是最后一个对话
+      if (isAtLastDialogue()) {
+        // 如果是用户输入类型,不立即触发dialogueEnded,等待AI回答完成
+        if (currentDialogue.value?.type === 'user') {
+          console.log('用户输入类型,等待AI回答完成后再提示');
+          return;
+        }
+        emit('dialogueEnded', props.isLastCourse);
+        isPlaying.value = false;
+        return;
+      }
       // 播放失败时,2秒后跳转
       if (isAutoPlay && isPlaying.value) {
         setTimeout(() => {
@@ -360,6 +385,17 @@ const playDialogueAudio = (isAutoPlay = false) => {
       }
     })
   } else {
+    // 检查是否是最后一个对话
+    if (isAtLastDialogue()) {
+      // 如果是用户输入类型,不立即触发dialogueEnded,等待AI回答完成
+      if (currentDialogue.value?.type === 'user') {
+        console.log('用户输入类型,等待AI回答完成后再提示');
+        return;
+      }
+      emit('dialogueEnded', props.isLastCourse);
+      isPlaying.value = false;
+      return;
+    }
     // 如果没有语音文件,2秒后跳转
     if (isAutoPlay && isPlaying.value && currentDialogue.value?.type !== 'user') {
       setTimeout(() => {
@@ -403,7 +439,11 @@ const playSequence = () => {
     // 自动切换到下一条对话
     setTimeout(() => {
       if (!playNext(true)) {
-        // 播放完毕
+        // 播放完毕,检查是否是最后一个对话
+        if (isAtLastDialogue()) {
+          console.log('诗词序列:已到达最后一个对话,触发 dialogueEnded 事件');
+          emit('dialogueEnded', props.isLastCourse);
+        }
         isPlaying.value = false
         stopAllAudio()
       }
@@ -785,6 +825,12 @@ const doSendMessageStream = async userMessage => {
         () => {
           console.log(`结束对话! `)
           stopStream()
+          // AI回答完成,检查是否是最后一个对话且是用户输入类型
+          if (isAtLastDialogue() && currentDialogue.value?.type === 'user') {
+            console.log('AI回答完成,触发 dialogueEnded 事件');
+            emit('dialogueEnded', props.isLastCourse);
+            isPlaying.value = false;
+          }
         }
     )
   } catch (error) {
@@ -817,6 +863,16 @@ const handleAudioPlaybackComplete = () => {
   setOnPlaybackComplete(null);
 
   stopAllAudio();
+
+  // 检查是否是最后一个对话
+  if (isAtLastDialogue()) {
+    // 语音播报完成且是最后一个对话,触发事件通知父组件
+    console.log('已到达最后一个对话,触发 dialogueEnded 事件');
+    emit('dialogueEnded', props.isLastCourse);
+    isPlaying.value = false;
+    return;
+  }
+
   // 如果处于自动播放状态,继续播放下一条对话
   if (isPlaying.value) {
     // 恢复回调,供下一条 AI 对话使用
@@ -831,6 +887,22 @@ const handleAudioPlaybackComplete = () => {
   }
 };
 
+// 判断是否是最后一个对话
+const isAtLastDialogue = () => {
+  if (!props.scriptData.sections || props.scriptData.sections.length === 0) {
+    return false
+  }
+  const lastSectionIndex = props.scriptData.sections.length - 1
+  const lastSection = props.scriptData.sections[lastSectionIndex]
+  if (!lastSection.dialogues || lastSection.dialogues.length === 0) {
+    return false
+  }
+  const lastDialogueIndex = lastSection.dialogues.length - 1
+
+  return currentSectionIndex.value === lastSectionIndex &&
+         currentDialogueIndex.value === lastDialogueIndex
+}
+
 // 组件挂载时添加键盘事件监听
 onMounted(() => {
   window.addEventListener('keydown', handleKeydown)