Quellcode durchsuchen

优化AI诗词课组件+评价提示词

liyanbo vor 1 Woche
Ursprung
Commit
a8bfa67aa2

+ 105 - 62
src/components/aiCourse/DialogEngine.vue

@@ -28,6 +28,7 @@
         :dialogue="currentDialogue"
         :script-roles="scriptRoles"
         :index="currentDialogueIndex"
+        :is-playback-started="isPlaybackStarted"
         :previous-quest="previousQuestDialogue"
         :poem-show="showPoem"
         :poem-content="currentPoemContent"
@@ -41,9 +42,9 @@
       <!-- 控制按钮 -->
       <InputButtons
         :can-prev="!isAtFirstDialogue"
-        :can-next="!isAtLastDialogue"
+        :can-next="Boolean(currentDialogue)"
         @prev="playPrevious"
-        @next="playNext"
+        @next="handleNextClick"
       />
     </div>
   </div>
@@ -57,6 +58,7 @@
 import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue';
 import { useRouter } from 'vue-router';
 import { marked } from 'marked';
+import { ElMessage } from 'element-plus';
 import { CreateDialogue, sendChatMessageStream } from "@/api/questions.js";
 import { useAudioPlayer } from "@/api/tts/useAudioPlayer.js";
 
@@ -78,7 +80,6 @@ const router = useRouter();
  * @prop {Object} scriptData - 剧本数据(包含章节和对话)
  * @prop {Array} scriptRoles - 角色列表
  * @prop {String} backText - 返回按钮文本
- * @prop {Boolean} isLastCourse - 是否为最后一节课程
  */
 const props = defineProps({
   scriptData: {
@@ -92,10 +93,6 @@ const props = defineProps({
   backText: {
     type: String,
     default: '返回课程'
-  },
-  isLastCourse: {
-    type: Boolean,
-    default: false
   }
 });
 
@@ -174,13 +171,16 @@ const isAtFirstDialogue = computed(() => {
   return currentSectionIndex.value === 0 && currentDialogueIndex.value === 0;
 });
 
-// 是否在最后一个对话
-const isAtLastDialogue = computed(() => {
-  if (!props.scriptData.sections?.length) return false;
-  const lastSectionIdx = props.scriptData.sections.length - 1;
-  const lastSection = props.scriptData.sections[lastSectionIdx];
+// 是否在当前课程的最后一个对话
+const isAtLastDialogueOfCourse = computed(() => {
+  const sections = props.scriptData.sections;
+  if (!sections?.length) return false;
+
+  const lastSectionIndex = sections.length - 1;
+  const lastSection = sections[lastSectionIndex];
   if (!lastSection?.dialogues?.length) return false;
-  return currentSectionIndex.value === lastSectionIdx &&
+
+  return currentSectionIndex.value === lastSectionIndex &&
     currentDialogueIndex.value === lastSection.dialogues.length - 1;
 });
 
@@ -255,9 +255,7 @@ const playDialogueAudio = (isAutoPlay = false) => {
     dialogueAudio.value = audio;
 
     audio.onended = () => {
-      if (isAtLastDialogue.value) {
-        if (currentDialogue.value?.type === 'user') return;
-        emit('dialogueEnded', props.isLastCourse);
+      if (isAtLastDialogueOfCourse.value) {
         isPlaying.value = false;
         return;
       }
@@ -274,9 +272,7 @@ const playDialogueAudio = (isAutoPlay = false) => {
 
     audio.play().catch(e => {
       console.error('对话语音播放失败:', e);
-      if (isAtLastDialogue.value) {
-        if (currentDialogue.value?.type === 'user') return;
-        emit('dialogueEnded', props.isLastCourse);
+      if (isAtLastDialogueOfCourse.value) {
         isPlaying.value = false;
         return;
       }
@@ -290,9 +286,7 @@ const playDialogueAudio = (isAutoPlay = false) => {
       }
     });
   } else if (currentDialogue.value?.type !== 'video') {
-    if (isAtLastDialogue.value) {
-      if (currentDialogue.value?.type === 'user') return;
-      emit('dialogueEnded', props.isLastCourse);
+    if (isAtLastDialogueOfCourse.value) {
       isPlaying.value = false;
       return;
     }
@@ -338,10 +332,6 @@ const handleVideoEnded = () => {
   if (isPlaying.value) {
     setTimeout(() => {
       if (!playNext(true)) {
-        if (isAtLastDialogue.value) {
-          console.log('视频序列:已到达最后一个对话');
-          emit('dialogueEnded', props.isLastCourse);
-        }
         isPlaying.value = false;
         stopAllAudio();
       }
@@ -368,10 +358,6 @@ const playSequence = () => {
     } else {
       setTimeout(() => {
         if (!playNext(true)) {
-          if (isAtLastDialogue.value) {
-            console.log('诗词序列:已到达最后一个对话');
-            emit('dialogueEnded', props.isLastCourse);
-          }
           isPlaying.value = false;
           stopAllAudio();
         }
@@ -445,13 +431,27 @@ const playPrevious = () => {
   });
 };
 
+/**
+ * 处理用户点击“下一个”按钮
+ */
+const handleNextClick = () => {
+  if (!currentDialogue.value) return;
+
+  if (isAtLastDialogueOfCourse.value) {
+    emit('dialogueEnded');
+    return;
+  }
+
+  playNext(false);
+};
+
 /**
  * 播放下一条对话
  * @param {Boolean} isAutoPlay - 是否自动播放
  * @returns {Boolean} 是否成功切换
  */
 const playNext = (isAutoPlay = false) => {
-  if (isAtLastDialogue.value) return false;
+  if (isAtLastDialogueOfCourse.value) return false;
 
   if (dialogueAudio.value) {
     dialogueAudio.value.pause();
@@ -507,7 +507,6 @@ const playNext = (isAutoPlay = false) => {
         if (currentDialogue.value?.voiceoverUrl) {
           playDialogueAudio(isPlaying.value);
         } else {
-          // 诗词没有语音,延迟后继续切换下一句(避免连续递归)
           setTimeout(() => {
             playNext(isAutoPlay);
           }, 100);
@@ -574,7 +573,7 @@ const handleKeydown = (event) => {
       event.preventDefault();
       break;
     case 'ArrowRight':
-      playNext();
+      handleNextClick();
       event.preventDefault();
       break;
     default:
@@ -585,16 +584,42 @@ const handleKeydown = (event) => {
 /**
  * 创建AI对话会话
  */
-const createAiChart = async () => {
-  let role = props.scriptRoles.find(r => r.name === currentDialogue.value.roleName);
-  await CreateDialogue({ roleId: role.id })
-    .then(res => {
-      console.log("创建会话:", res.data);
-      activeConversationId.value = res.data;
-    })
-    .catch(error => {
-      console.error('请求出错:', error);
+const createAiChart = async (preferredRoleName = '') => {
+  activeConversationId.value = null;
+
+  // user 对话通常没有角色,优先使用对应问题的角色,再向前查找最近的有效角色。
+  const roleNames = [preferredRoleName, currentDialogue.value?.roleName];
+  for (let i = currentDialogueIndex.value - 1; i >= 0; i--) {
+    roleNames.push(currentSection.value?.dialogues?.[i]?.roleName);
+  }
+
+  const role = roleNames
+    .filter(Boolean)
+    .map(roleName => props.scriptRoles.find(item => item.name === roleName))
+    .find(Boolean);
+
+  if (!role || role.id == null) {
+    console.error('创建AI会话失败:找不到对话角色', {
+      preferredRoleName,
+      scriptRoles: props.scriptRoles
     });
+    ElMessage.error('未找到对应的AI角色,请检查课程角色配置');
+    return null;
+  }
+
+  try {
+    const res = await CreateDialogue({ roleId: role.id });
+    console.log('创建会话:', res.data);
+    activeConversationId.value = res.data;
+    return {
+      conversationId: res.data,
+      roleName: role.name
+    };
+  } catch (error) {
+    console.error('创建AI会话失败:', error);
+    ElMessage.error('创建AI会话失败,请稍后重试');
+    return null;
+  }
 };
 
 /**
@@ -602,15 +627,25 @@ const createAiChart = async () => {
  * @param {Object} data - 用户输入数据
  */
 const handleUserInputSubmit = async (data) => {
-  console.log('用户输入:', data.content);
-  await createAiChart();
+  const content = data?.content?.trim();
+  if (!content) return;
 
-  let userInputTemp = data.content;
-  let currentDialogueTemp = currentSection.value.dialogues[currentDialogueIndex.value - 1];
-  userInputTemp += "(此内容是帮我解答的问题,问题是:" + currentDialogueTemp.content + ",回复要求:根据问题回复我回答的内容是否正确,并给予鼓励或夸赞;注意请使用精简回答,尽量控制字体数量在50个字内)";
+  const questionDialogue = previousQuestDialogue.value;
+  if (!questionDialogue) {
+    console.error('用户输入提交失败:找不到上一条提问对话');
+    ElMessage.error('找不到对应的问题,请返回上一条后重试');
+    return;
+  }
+
+  console.log('用户输入:', content);
+  const session = await createAiChart(questionDialogue.roleName);
+  if (!session) return;
+
+  const userInputTemp = `${content}(此内容是帮我解答的问题,问题是:${questionDialogue.content},回复要求:根据问题回复我回答的内容是否正确,并给予鼓励或夸赞;注意请使用精简回答,尽量控制字体数量在50个字内)`;
 
   await doSendMessageStream({
-    conversationId: activeConversationId.value,
+    conversationId: session.conversationId,
+    roleName: session.roleName,
     content: userInputTemp,
     contentAnswer: null,
   });
@@ -625,13 +660,19 @@ const handleWriteReadingSubmit = async (data) => {
   const imitation = data.content?.trim();
   if (!originalPoem || !imitation) return;
 
-  await createAiChart();
-  if (!activeConversationId.value) return;
+  const session = await createAiChart(currentDialogue.value?.roleName);
+  if (!session) return;
 
-  const content = `你是一位古诗词仿写指导老师。请根据“原诗词”和“学生仿写作品”,给出友善、具体、鼓励式的评价。\n\n原诗词:\n${originalPoem}\n\n学生仿写:\n${imitation}\n\n请从主题意境、结构句式、语言表达和创新性四方面评价。先肯定亮点,再指出1~2个可操作的改进建议;不苛求严格格律;控制在150字以内。`;
+  const content = `你是一位古诗词仿写指导老师。请根据“原诗词”和“学生仿写作品”,给出友善、具体、鼓励式的评价。\n\n原诗词:\n${originalPoem}\n\n学生仿写:\n${imitation}\n\n
+  请从文学价值、感情价值、社会价值三个方面评价。\n\n
+  1.文学价值:先评价学生的仿写,然后按照格律诗的要求修改这首诗。
+  2.感情价值:先评价学生的仿写,然后让这首诗更具有怎么样的情感?
+  3.社会价值:先评价学生的仿写,然后让这首诗更具有正能量,具有推广价值。
+  控制在150字以内。`;
 
   await doSendMessageStream({
-    conversationId: activeConversationId.value,
+    conversationId: session.conversationId,
+    roleName: session.roleName,
     content,
     contentAnswer: null,
   });
@@ -643,14 +684,17 @@ const handleWriteReadingSubmit = async (data) => {
  */
 const handleSingleChoiceSubmit = async (data) => {
   console.log('用户选择:', data.label);
-  await createAiChart();
 
   const dialogue = previousQuestDialogue.value;
   if (!dialogue) {
     console.error('找不到上一条quest对话!');
+    ElMessage.error('找不到对应的问题,请返回上一条后重试');
     return;
   }
 
+  const session = await createAiChart(dialogue.roleName);
+  if (!session) return;
+
   const optionLabels = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'];
   const optionsStr = dialogue.options.map((opt, idx) => `${optionLabels[idx]}. ${opt.content}`).join(';');
   const content = `问题:${dialogue.content}\n选项:${optionsStr}\n我的答案:${data.label}\n正确答案:${dialogue.answer}\n\n请判断我的答案是否正确(如果没有正确答案则提示此题没有标准答案),并给予鼓励或夸赞,回复请精简,控制在50字内。`;
@@ -658,7 +702,8 @@ const handleSingleChoiceSubmit = async (data) => {
   console.log('发送单选问题:', content);
 
   await doSendMessageStream({
-    conversationId: activeConversationId.value,
+    conversationId: session.conversationId,
+    roleName: session.roleName,
     content: content,
     contentAnswer: null,
   });
@@ -667,9 +712,10 @@ const handleSingleChoiceSubmit = async (data) => {
 /**
  * 显示问题回答对话(AI思考中)
  */
-const showQuestAnswerDialogue = () => {
+const showQuestAnswerDialogue = (roleName) => {
   currentDialogueCache.value = JSON.parse(JSON.stringify(currentDialogue.value));
   currentDialogue.value.type = "digital";
+  currentDialogue.value.roleName = roleName || currentDialogue.value.roleName;
   currentDialogue.value.content = "让我思考一下...";
 };
 
@@ -736,7 +782,7 @@ const doSendMessageStream = async (userMessage) => {
   conversationInProgress.value = true;
   receiveMessageFullText.value = '';
 
-  showQuestAnswerDialogue();
+  showQuestAnswerDialogue(userMessage.roleName);
 
   try {
     let isFirstChunk = true;
@@ -776,9 +822,7 @@ const doSendMessageStream = async (userMessage) => {
       () => {
         console.log(`结束对话!`);
         stopStream();
-        if (isAtLastDialogue.value && currentDialogue.value?.type === 'digital') {
-          console.log('AI回答完成,触发 dialogueEnded 事件');
-          emit('dialogueEnded', props.isLastCourse);
+        if (isAtLastDialogueOfCourse.value && currentDialogue.value?.type === 'digital') {
           isPlaying.value = false;
         }
       }
@@ -809,9 +853,7 @@ const handleAudioPlaybackComplete = () => {
   setOnPlaybackComplete(null);
   stopAllAudio();
 
-  if (isAtLastDialogue.value) {
-    console.log('已到达最后一个对话,触发 dialogueEnded 事件');
-    emit('dialogueEnded', props.isLastCourse);
+  if (isAtLastDialogueOfCourse.value) {
     isPlaying.value = false;
     return;
   }
@@ -851,6 +893,7 @@ watch(() => props.scriptData, (newVal, oldVal) => {
     currentDialogueIndex.value = 0;
     isPlaying.value = false;
     showMask.value = true;
+    isPlaybackStarted.value = false;
     currentDialogueCache.value = null;
     showPoem.value = false;
     currentPoemContent.value = '';

+ 5 - 0
src/components/aiCourse/dialog/DialogCard.vue

@@ -32,6 +32,7 @@
     <VideoDisplay
       v-else-if="dialogue?.type === 'video'"
       :video-url="dialogue?.videoUrl"
+      :is-playback-started="isPlaybackStarted"
       @ended="$emit('video-ended', $event)"
     />
 
@@ -103,6 +104,10 @@ defineProps({
     type: Number,
     default: 0
   },
+  isPlaybackStarted: {
+    type: Boolean,
+    default: false
+  },
   previousQuest: {
     type: Object,
     default: null

+ 22 - 5
src/components/aiCourse/dialog/dialogType/VideoDisplay.vue

@@ -6,7 +6,7 @@
         class="dialogue-video"
         ref="videoRef"
         controls
-        autoplay
+        preload="metadata"
         @ended="$emit('ended')"
         @contextmenu.prevent
         controlslist="nodownload"
@@ -18,7 +18,7 @@
 </template>
 
 <script setup>
-import { ref, onMounted, onUnmounted } from 'vue';
+import { ref, watch, onMounted, onUnmounted, nextTick } from 'vue';
 
 /**
  * VideoDisplay - 视频播放组件
@@ -28,11 +28,16 @@ import { ref, onMounted, onUnmounted } from 'vue';
 /**
  * Props 定义
  * @prop {String} videoUrl - 视频URL
+ * @prop {Boolean} isPlaybackStarted - 用户是否已点击初始遮罩层的播放按钮
  */
 const props = defineProps({
   videoUrl: {
     type: String,
     default: ''
+  },
+  isPlaybackStarted: {
+    type: Boolean,
+    default: false
   }
 });
 
@@ -45,14 +50,26 @@ defineEmits(['ended']);
 const videoRef = ref(null);
 
 /**
- * 组件挂载时自动播放视频
+ * 根据课程播放状态控制视频,初始遮罩未点击时保持暂停
  */
 onMounted(() => {
-  if (videoRef.value && props.videoUrl) {
+  if (videoRef.value && props.videoUrl && props.isPlaybackStarted) {
     videoRef.value.play().catch(e => console.error('对话视频播放失败:', e));
   }
 });
 
+watch(() => props.isPlaybackStarted, (started) => {
+  nextTick(() => {
+    if (!videoRef.value) return;
+
+    if (started && props.videoUrl) {
+      videoRef.value.play().catch(e => console.error('对话视频播放失败:', e));
+    } else {
+      videoRef.value.pause();
+    }
+  });
+});
+
 /**
  * 组件卸载时清理视频
  */
@@ -121,4 +138,4 @@ onUnmounted(() => {
     transform: translate(-50%, -50%) scale(1);
   }
 }
-</style>
+</style>

+ 1 - 1
src/components/aiCourse/dialog/dialogType/WriteReadingDialogue.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="write-reading-dialogue">
     <PoemDisplay :content="content" />
-    <UserInputCard @submit="$emit('submit', $event)" />
+    <UserInputCard input-type="write" @submit="$emit('submit', $event)" />
   </div>
 </template>
 

+ 19 - 4
src/components/aiCourse/dialog/dialogType/questType/UserInputCard.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="user-input-card">
-    <div class="dialogue-header">
-      <span class="role-name"></span>
+    <div class="dialogue-header" :class="`dialogue-header--${inputType}`">
+      <span class="role-name">{{ inputType === 'write' ? '仿写' : '回复' }}</span>
     </div>
     <div class="dialogue-content">
       <textarea
@@ -37,6 +37,14 @@ import VoiceInput from '@/components/ai/voice/VoiceInput_Api.vue';
  * 用于AI Q&A类型问题的用户输入
  */
 
+defineProps({
+  inputType: {
+    type: String,
+    default: 'reply',
+    validator: (value) => ['reply', 'write'].includes(value)
+  }
+});
+
 /**
  * Emits 定义
  * @event submit - 用户提交输入内容
@@ -136,7 +144,6 @@ const handleRecordingStatusChanged = (status) => {
   position: absolute;
   top: rpx(-11);
   left: rpx(12);
-  background: #409EFF;
   color: white;
   padding: rpx(1.2) rpx(6);
   border-radius: rpx(5);
@@ -144,6 +151,14 @@ const handleRecordingStatusChanged = (status) => {
   box-shadow: 0 rpx(2.5) rpx(10) rgba(0, 0, 0, 0.2);
 }
 
+.dialogue-header--reply {
+  background: #409EFF;
+}
+
+.dialogue-header--write {
+  background: #F59E0B;
+}
+
 .role-name {
   font-weight: 600;
   color: white;
@@ -364,4 +379,4 @@ const handleRecordingStatusChanged = (status) => {
 .cancel-btn:active, .submit-btn:active {
   transform: scale(0.95);
 }
-</style>
+</style>

+ 13 - 5
src/components/aiCourse/engine/InputButtons.vue

@@ -1,11 +1,11 @@
 <template>
   <div class="input-buttons-container">
     <!-- 上一个对话按钮 -->
-    <div class="arrow-icon-circle" @click="$emit('prev')" :class="{ 'disabled': !canPrev }">
+    <div class="arrow-icon-circle" @click="handlePrev" :class="{ 'disabled': !canPrev }">
       <el-icon class="arrow-icon"><CaretLeft /></el-icon>
     </div>
     <!-- 下一个对话按钮 -->
-    <div class="arrow-icon-circle" @click="$emit('next')" :class="{ 'disabled': !canNext }">
+    <div class="arrow-icon-circle" @click="handleNext" :class="{ 'disabled': !canNext }">
       <el-icon class="arrow-icon"><CaretRight /></el-icon>
     </div>
   </div>
@@ -24,7 +24,7 @@ import { CaretLeft, CaretRight } from '@element-plus/icons-vue';
  * @prop {Boolean} canPrev - 是否可以切换到上一句
  * @prop {Boolean} canNext - 是否可以切换到下一句
  */
-defineProps({
+const props = defineProps({
   canPrev: {
     type: Boolean,
     default: true
@@ -40,7 +40,15 @@ defineProps({
  * @event prev - 切换到上一句
  * @event next - 切换到下一句
  */
-defineEmits(['prev', 'next']);
+const emit = defineEmits(['prev', 'next']);
+
+const handlePrev = () => {
+  if (props.canPrev) emit('prev');
+};
+
+const handleNext = () => {
+  if (props.canNext) emit('next');
+};
 </script>
 
 <style scoped lang="scss">
@@ -100,4 +108,4 @@ defineEmits(['prev', 'next']);
     }
   }
 
-</style>
+</style>

+ 29 - 14
src/views/AIPage/AIDevelop.vue

@@ -177,7 +177,6 @@
             :scriptRoles="scriptRoles"
             :scriptData="course.courseContent"
             :back-text="boxIconTitle"
-            :is-last-course="isLastCourse"
             @dialogue-ended="handleDialogueEnded"
             @go-back="goBack"
           />
@@ -233,6 +232,7 @@
 import { ref, onMounted, onBeforeUnmount, computed } from 'vue'
 import { useRoute, useRouter } from 'vue-router'
 import { Search, ArrowLeftBold } from '@element-plus/icons-vue'
+import { ElMessageBox } from 'element-plus'
 import isDisabledImage from '@/assets/images/permission/isDisabled.png'
 
 import classImages from '@/assets/icon/class.png'
@@ -311,20 +311,35 @@ const isLastCourse = computed(() => {
   return currentIndexInList === allIndices.length - 1
 })
 
-// 处理 DialogContent 对话结束事件
-const handleDialogueEnded = (isLast) => {
-  if (isLast) {
-    // 最后一节课,检查课程类型
-    if (course.value.courseContentType === 'ailab') {
-      // ailab类型显示已经是最后一节课提示
-      Message().notifyWarning('已经是最后一节课', true)
-      return
+const isCourseSwitchPromptOpen = ref(false)
+
+// 当前对话课程播放完毕后,由父组件判断并切换目录中的下一节课
+const handleDialogueEnded = async () => {
+  if (isCourseSwitchPromptOpen.value) return
+
+  if (isLastCourse.value) {
+    Message().notifySuccess('本课程全部小节都播放完毕', true)
+    return
+  }
+
+  isCourseSwitchPromptOpen.value = true
+  try {
+    await ElMessageBox.confirm(
+      '当前小节已播放完毕,是否播放下一小节?',
+      '小节播放完成',
+      {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'info'
+      }
+    )
+    playNextVideo()
+  } catch (action) {
+    if (action !== 'cancel' && action !== 'close') {
+      console.error('切换下一小节失败:', action)
     }
-    // 其他类型显示返回提示弹窗
-    promptPopupVisible.value = true
-  } else {
-    // 不是最后一节课,显示播放提示
-    playPromptVisible.value = true
+  } finally {
+    isCourseSwitchPromptOpen.value = false
   }
 }
 

+ 2 - 7
src/views/AIPage/aiGenerate/DialogContent.vue

@@ -3,7 +3,6 @@
     :script-data="scriptData"
     :script-roles="scriptRoles"
     :back-text="backText"
-    :is-last-course="isLastCourse"
     @dialogue-ended="handleDialogueEnded"
     @go-back="handleGoBack"
   />
@@ -27,17 +26,13 @@ const props = defineProps({
   backText: {
     type: String,
     default: '返回课程'
-  },
-  isLastCourse: {
-    type: Boolean,
-    default: false
   }
 });
 
 const emit = defineEmits(['dialogueEnded', 'goBack']);
 
-const handleDialogueEnded = (isLastCourse) => {
-  emit('dialogueEnded', isLastCourse);
+const handleDialogueEnded = () => {
+  emit('dialogueEnded');
 };
 
 const handleGoBack = () => {

+ 29 - 14
src/views/AiPoetry/AIPoetryDevelop.vue

@@ -177,7 +177,6 @@
             :scriptRoles="scriptRoles"
             :scriptData="course.courseContent"
             :back-text="boxIconTitle"
-            :is-last-course="isLastCourse"
             @dialogue-ended="handleDialogueEnded"
             @go-back="goBack"
           />
@@ -233,6 +232,7 @@
 import { ref, onMounted, onBeforeUnmount, computed } from 'vue'
 import { useRoute, useRouter } from 'vue-router'
 import { Search, ArrowLeftBold } from '@element-plus/icons-vue'
+import { ElMessageBox } from 'element-plus'
 import isDisabledImage from '@/assets/images/permission/isDisabled.png'
 
 import classImages from '@/assets/icon/class.png'
@@ -311,20 +311,35 @@ const isLastCourse = computed(() => {
   return currentIndexInList === allIndices.length - 1
 })
 
-// 处理 DialogContent 对话结束事件
-const handleDialogueEnded = (isLast) => {
-  if (isLast) {
-    // 最后一节课,检查课程类型
-    if (course.value.courseContentType === 'ailab') {
-      // ailab类型显示已经是最后一节课提示
-      Message().notifyWarning('已经是最后一节课', true)
-      return
+const isCourseSwitchPromptOpen = ref(false)
+
+// 当前对话课程播放完毕后,由父组件判断并切换目录中的下一节课
+const handleDialogueEnded = async () => {
+  if (isCourseSwitchPromptOpen.value) return
+
+  if (isLastCourse.value) {
+    Message().notifySuccess('本课程全部小节都播放完毕', true)
+    return
+  }
+
+  isCourseSwitchPromptOpen.value = true
+  try {
+    await ElMessageBox.confirm(
+      '当前小节已播放完毕,是否播放下一小节?',
+      '小节播放完成',
+      {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'info'
+      }
+    )
+    playNextVideo()
+  } catch (action) {
+    if (action !== 'cancel' && action !== 'close') {
+      console.error('切换下一小节失败:', action)
     }
-    // 其他类型显示返回提示弹窗
-    promptPopupVisible.value = true
-  } else {
-    // 不是最后一节课,显示播放提示
-    playPromptVisible.value = true
+  } finally {
+    isCourseSwitchPromptOpen.value = false
   }
 }