|
@@ -41,7 +41,7 @@
|
|
|
<!-- AI对话框 -->
|
|
<!-- AI对话框 -->
|
|
|
<div class="chat-dialog">
|
|
<div class="chat-dialog">
|
|
|
<!-- 对话消息列表 -->
|
|
<!-- 对话消息列表 -->
|
|
|
- <div class="message-list">
|
|
|
|
|
|
|
+ <div class="message-list" ref="messageListRef">
|
|
|
<div v-for="(item, index) in messageList" :key="index">
|
|
<div v-for="(item, index) in messageList" :key="index">
|
|
|
<!-- AI消息 -->
|
|
<!-- AI消息 -->
|
|
|
<div class="ai-message" v-if="item.type !== 'user'">
|
|
<div class="ai-message" v-if="item.type !== 'user'">
|
|
@@ -80,7 +80,7 @@
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup>
|
|
<script setup>
|
|
|
-import { ref, onMounted, computed,watch } from "vue";
|
|
|
|
|
|
|
+import {ref, onMounted, computed, watch, nextTick} from "vue";
|
|
|
import { CreateDialogue, sendChatMessageStream } from "@/api/questions.js";
|
|
import { CreateDialogue, sendChatMessageStream } from "@/api/questions.js";
|
|
|
import { useRouter, useRoute } from "vue-router";
|
|
import { useRouter, useRoute } from "vue-router";
|
|
|
import { saveRecord } from '@/api/personalized/index.js'
|
|
import { saveRecord } from '@/api/personalized/index.js'
|
|
@@ -179,6 +179,8 @@ const enableContext = ref(true); // 是否开启上下文
|
|
|
// 接收 Stream 消息
|
|
// 接收 Stream 消息
|
|
|
const receiveMessageFullText = ref("");
|
|
const receiveMessageFullText = ref("");
|
|
|
const receiveMessageDisplayedText = ref("");
|
|
const receiveMessageDisplayedText = ref("");
|
|
|
|
|
+const messageListRef = ref(null);
|
|
|
|
|
+
|
|
|
|
|
|
|
|
// =========== 【聊天对话】相关 ===========
|
|
// =========== 【聊天对话】相关 ===========
|
|
|
|
|
|
|
@@ -299,6 +301,11 @@ const doSendMessage = async (content) => {
|
|
|
});
|
|
});
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
+import { useAudioPlayer } from '@/components/TTS/useAudioPlayer';
|
|
|
|
|
+
|
|
|
|
|
+const { playAudioChunk } = useAudioPlayer();
|
|
|
|
|
+
|
|
|
/** 真正执行【发送】消息操作 */
|
|
/** 真正执行【发送】消息操作 */
|
|
|
const doSendMessageStream = async (userMessage) => {
|
|
const doSendMessageStream = async (userMessage) => {
|
|
|
// 创建 AbortController 实例,以便中止请求
|
|
// 创建 AbortController 实例,以便中止请求
|
|
@@ -343,20 +350,29 @@ const doSendMessageStream = async (userMessage) => {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
// 如果内容为空,就不处理。
|
|
// 如果内容为空,就不处理。
|
|
|
- // if (data.receive.content === '') {
|
|
|
|
|
|
|
+ // if (data.receive?.content === '') {
|
|
|
// return
|
|
// return
|
|
|
// }
|
|
// }
|
|
|
- receiveMessageFullText.value =
|
|
|
|
|
- receiveMessageFullText.value + data.receive.content;
|
|
|
|
|
- // 首次返回需要添加一个 message 到页面,后面的都是更新
|
|
|
|
|
- if (isFirstChunk) {
|
|
|
|
|
- isFirstChunk = false;
|
|
|
|
|
- // 弹出两个假数据
|
|
|
|
|
- activeMessageList.value.pop();
|
|
|
|
|
- activeMessageList.value.pop();
|
|
|
|
|
- // 更新返回的数据
|
|
|
|
|
- activeMessageList.value.push(data.send);
|
|
|
|
|
- activeMessageList.value.push(data.receive);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 根据事件类型处理
|
|
|
|
|
+ if (data.eventType === 'TEXT') {
|
|
|
|
|
+
|
|
|
|
|
+ // 处理文本消息
|
|
|
|
|
+ receiveMessageFullText.value += data.receive.content;
|
|
|
|
|
+
|
|
|
|
|
+ // 首次返回需要添加一个 message 到页面,后面的都是更新
|
|
|
|
|
+ if (isFirstChunk) {
|
|
|
|
|
+ isFirstChunk = false;
|
|
|
|
|
+ // 弹出两个假数据
|
|
|
|
|
+ activeMessageList.value.pop();
|
|
|
|
|
+ activeMessageList.value.pop();
|
|
|
|
|
+ // 更新返回的数据
|
|
|
|
|
+ activeMessageList.value.push(data.send);
|
|
|
|
|
+ activeMessageList.value.push(data.receive);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (data.eventType === 'AUDIO') {
|
|
|
|
|
+ // 处理音频消息
|
|
|
|
|
+ await playAudioChunk(data.audioData);
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
(error) => {
|
|
(error) => {
|
|
@@ -408,10 +424,11 @@ const messageList = computed(() => {
|
|
|
// ============== 【消息滚动】相关 =============
|
|
// ============== 【消息滚动】相关 =============
|
|
|
|
|
|
|
|
/** 滚动到 message 底部 */
|
|
/** 滚动到 message 底部 */
|
|
|
-const scrollToBottom = async (isIgnore) => {
|
|
|
|
|
- // if (messageRef.value) {
|
|
|
|
|
- // messageRef.value.scrollToBottom(isIgnore)
|
|
|
|
|
- // }
|
|
|
|
|
|
|
+const scrollToBottom = async (isIgnore = false) => {
|
|
|
|
|
+ await nextTick();
|
|
|
|
|
+ if (messageListRef.value) {
|
|
|
|
|
+ messageListRef.value.scrollTop = messageListRef.value.scrollHeight;
|
|
|
|
|
+ }
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
/** 自提滚动效果 */
|
|
/** 自提滚动效果 */
|
|
@@ -446,8 +463,7 @@ const textRoll = async () => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (index < receiveMessageFullText.value.length) {
|
|
if (index < receiveMessageFullText.value.length) {
|
|
|
- receiveMessageDisplayedText.value +=
|
|
|
|
|
- receiveMessageFullText.value[index];
|
|
|
|
|
|
|
+ receiveMessageDisplayedText.value += receiveMessageFullText.value[index];
|
|
|
index++;
|
|
index++;
|
|
|
|
|
|
|
|
// 更新 message
|
|
// 更新 message
|