Bladeren bron

1、课程小智只能问答加语音播放功能;
自动滚到最底部(手动干预是取消);
将设定好的ai回答内容传到后台tts回来播放

liyanbo 8 maanden geleden
bovenliggende
commit
31c862042e
4 gewijzigde bestanden met toevoegingen van 73 en 22 verwijderingen
  1. 1 0
      src/api/questions.js
  2. 0 0
      src/api/tts/useAudioPlayer.js
  3. 43 18
      src/components/videopage/DialogComponents.vue
  4. 29 4
      src/views/AIQuestions.vue

+ 1 - 0
src/api/questions.js

@@ -17,6 +17,7 @@ export function CreateDialogue (data){
 export async function sendChatMessageStream (
     conversationId,
     content,
+    contentAnswer = undefined,
     ctrl,
     enableContext,
     onMessage,

+ 0 - 0
src/components/TTS/useAudioPlayer.js → src/api/tts/useAudioPlayer.js


+ 43 - 18
src/components/videopage/DialogComponents.vue

@@ -76,7 +76,7 @@
           >
         </div>
         <div class="ai-dialog-content">
-          <div class="ai-message-history" ref="messageContainer">
+          <div class="ai-message-history" ref="messageContainer" @scroll="handleScroll">
             <div
               v-for="(message, index) in messageList"
               :key="index"
@@ -154,6 +154,7 @@ const messageList = ref([])
 const prompt = ref('')
 const messageContainer = ref(null)
 const aiQuestionCount = ref(0)
+const userScrolled = ref(false)//是否用户手动滚动
 const xZAiData = ref({})
 const activeConversationId = ref(null)
 const conversationInProgress = ref(false)
@@ -164,7 +165,7 @@ const inputTimeout = ref()
 const enableContext = ref(true)
 
 //tts
-import { useAudioPlayer } from '@/components/TTS/useAudioPlayer';
+import { useAudioPlayer } from '@/api/tts/useAudioPlayer';
 const { playAudioChunk } = useAudioPlayer();
 
 // 处理选择的默认消息
@@ -189,18 +190,27 @@ const handleSubmitAnswer = () => {
 }
 
 // 处理 AI 助手点击事件
-const handleAIClick = () => {
+const handleAIClick = async () => {
   // 清空输入框
   messageList.value = []
-  if (props.currentQuestion.ccQuestContent) {
-    prompt.value = props.currentQuestion.ccQuestContent
-    sendMessage()
-    prompt.value = ''
-  }
   showAIDialog.value = true
 
   //创建对话
-  createAiChart()
+  await createAiChart()
+
+  if (props.currentQuestion.ccQuestContent) {
+    // prompt.value = props.currentQuestion.ccQuestContent
+    // sendMessage()
+
+    prompt.value = ''
+    // 执行发送
+    await doSendMessageStream({
+      conversationId: activeConversationId.value,
+      content: props.currentQuestion.ccQuestContent,
+      contentAnswer: props.currentQuestion.ccAiAnswer,
+    })
+
+  }
 }
 
 // 数字人接口
@@ -235,8 +245,9 @@ const createAiChart = async () => {
   await getXzAi()
 
   // 智能问答
-  CreateDialogue({ roleId: xZAiData.value.id })
+  await CreateDialogue({ roleId: xZAiData.value.id })
     .then(res => {
+      console.log("创建会话:", res);
       activeConversationId.value = res.data
     })
     .catch(error => {
@@ -267,12 +278,12 @@ const sendMessage = async () => {
       console.error('保存AI问答次数失败:', error)
     }
 
-    // 模拟 AI 回复
-    const aiResponse = await simulateAIResponse(prompt.value)
-    messageList.value.push({
-      type: 'ai',
-      content: aiResponse
-    })
+    // // 模拟 AI 回复
+    // const aiResponse = await simulateAIResponse(prompt.value)
+    // messageList.value.push({
+    //   type: 'ai',
+    //   content: aiResponse
+    // })
 
     // 清空输入框
     prompt.value = ''
@@ -341,7 +352,8 @@ const doSendMessage = async content => {
   // 执行发送
   await doSendMessageStream({
     conversationId: activeConversationId.value,
-    content: content
+    content: content,
+    contentAnswer: null,
   })
 }
 
@@ -373,10 +385,11 @@ const doSendMessageStream = async userMessage => {
 
     // 2. 发送 event stream
     let isFirstChunk = true // 是否是第一个 chunk 消息段
-
+    console.log("userMessage", userMessage)
     await sendChatMessageStream(
       userMessage.conversationId,
       userMessage.content,
+      userMessage.contentAnswer,
       conversationInAbortController.value,
       enableContext.value,
       async res => {
@@ -487,8 +500,20 @@ watch(messageList, () => {
   scrollToBottom()
 }, { deep: true })
 
+//处理滚动事件,判断用户是否手动滚动
+const handleScroll = () => {
+  if (messageContainer.value) {
+    const { scrollTop, scrollHeight, clientHeight } = messageContainer.value
+    // 当用户滚动距离底部超过50px时,认为是手动滚动
+    userScrolled.value = scrollTop + clientHeight < scrollHeight - 50
+  }
+}
 // 单独的滚动到底部函数
 const scrollToBottom = () => {
+
+  // 如果用户手动滚动过,不自动滚动
+  if (userScrolled.value) return
+
   nextTick(() => {
     if (messageContainer.value) {
       // 强制重排以确保获取最新高度

+ 29 - 4
src/views/AIQuestions.vue

@@ -41,7 +41,7 @@
           <!-- AI对话框 -->
           <div class="chat-dialog">
             <!-- 对话消息列表 -->
-            <div class="message-list" ref="messageListRef">
+            <div class="message-list" ref="messageListRef" @scroll="handleScroll">
               <div v-for="(item, index) in messageList" :key="index">
                 <!-- AI消息 -->
                 <div class="ai-message" v-if="item.type !== 'user'">
@@ -201,6 +201,7 @@ const enableContext = ref(true); // 是否开启上下文
 const receiveMessageFullText = ref("");
 const receiveMessageDisplayedText = ref("");
 const messageListRef = ref(null);
+const userScrolled = ref(false)//是否用户手动滚动
 
 
 // =========== 【聊天对话】相关 ===========
@@ -405,7 +406,7 @@ const doSendMessage = async (content) => {
 };
 
 
-import { useAudioPlayer } from '@/components/TTS/useAudioPlayer';
+import { useAudioPlayer } from '@/api/tts/useAudioPlayer';
 
 const { playAudioChunk } = useAudioPlayer();
 
@@ -443,7 +444,7 @@ const doSendMessageStream = async (userMessage) => {
 
     await sendChatMessageStream(
       userMessage.conversationId,
-      userMessage.content,
+      userMessage.content, null,
       conversationInAbortController.value,
       enableContext.value,
       async (res) => {
@@ -527,16 +528,31 @@ const messageList = computed(() => {
 
 // ============== 【消息滚动】相关 =============
 
+//处理滚动事件,判断用户是否手动滚动
+const handleScroll = () => {
+  if (messageListRef.value) {
+    const { scrollTop, scrollHeight, clientHeight } = messageListRef.value
+    // 当用户滚动距离底部超过50px时,认为是手动滚动
+    userScrolled.value = scrollTop + clientHeight < scrollHeight - 50
+  }
+}
+
 /** 滚动到 message 底部 */
 const scrollToBottom = async (isIgnore = false) => {
+  // 如果用户手动滚动过,不自动滚动
+  if (userScrolled.value) return
+
   await nextTick();
   if (messageListRef.value) {
-    messageListRef.value.scrollTop = messageListRef.value.scrollHeight;
+    requestAnimationFrame(() => {
+      messageListRef.value.scrollTop = messageListRef.value.scrollHeight;
+    });
   }
 };
 
 /** 自提滚动效果 */
 const textRoll = async () => {
+
   let index = 0;
   try {
     // 只能执行一次
@@ -574,6 +590,7 @@ const textRoll = async () => {
         const lastMessage =
           activeMessageList.value[activeMessageList.value.length - 1];
         lastMessage.content = receiveMessageDisplayedText.value;
+
         // 滚动到住下面
         await scrollToBottom();
         // 重新设置任务
@@ -593,6 +610,14 @@ const textRoll = async () => {
   } catch {}
 };
 
+// 监听消息列表变化,自动滚动到底部
+watch(
+    () => messageList.value,
+    () => {
+      scrollToBottom();
+    },
+    { deep: true }
+);
 
 /** 初始化 **/
 onMounted(async () => {