|
|
@@ -192,6 +192,7 @@
|
|
|
import { ref, computed, watch, nextTick, onMounted, onUnmounted } from 'vue'
|
|
|
import { ArrowUp, ArrowDown, Microphone, PhoneFilled, ChatDotRound, Mute } from '@element-plus/icons-vue'
|
|
|
import { useVoiceRecognition } from './composables/useVoiceRecognition.js'
|
|
|
+import { useStreamChat } from './composables/useStreamChat.js'
|
|
|
import { ElMessage } from 'element-plus'
|
|
|
|
|
|
// 获取请求头(包含token)
|
|
|
@@ -216,6 +217,9 @@ const chatContent = ref(null)
|
|
|
// 语音识别
|
|
|
const { isRecording, currentTranscription, tempTranscription, startRecording, stopRecording } = useVoiceRecognition()
|
|
|
|
|
|
+// 流式聊天
|
|
|
+const { displayText, conversationId: streamConversationId, sendMessage: sendStreamMessage, stopAudio: stopStreamAudio } = useStreamChat()
|
|
|
+
|
|
|
// 当前播放的音频对象
|
|
|
const currentAudio = ref(null)
|
|
|
|
|
|
@@ -224,7 +228,9 @@ const currentRequestId = ref(0)
|
|
|
|
|
|
// 停止当前的音频播放和输出
|
|
|
const stopCurrentOutput = () => {
|
|
|
- // 停止音频播放
|
|
|
+ // 停止流式音频播放
|
|
|
+ stopStreamAudio()
|
|
|
+ // 停止本地音频播放
|
|
|
if (currentAudio.value) {
|
|
|
currentAudio.value.pause()
|
|
|
currentAudio.value.currentTime = 0
|
|
|
@@ -342,52 +348,21 @@ watch(currentTranscription, async (newVal, oldVal) => {
|
|
|
})
|
|
|
scrollToBottom()
|
|
|
|
|
|
- // 发送到后端处理
|
|
|
+ // 发送到后端处理(使用流式接口)
|
|
|
try {
|
|
|
// 停止当前的音频播放
|
|
|
stopCurrentOutput()
|
|
|
|
|
|
- // 增加请求ID,标记这是最新的请求
|
|
|
- currentRequestId.value++
|
|
|
- const thisRequestId = currentRequestId.value
|
|
|
-
|
|
|
const selectedAgentData = agents.value.find(a => a.id === selectedAgent.value)
|
|
|
- const response = await fetch('http://localhost:8080/talk/message', {
|
|
|
- method: 'POST',
|
|
|
- headers: getHeaders(),
|
|
|
- body: JSON.stringify({
|
|
|
- message: newContent,
|
|
|
- agentId: selectedAgent.value,
|
|
|
- agentGender: selectedAgentData?.gender === 'male' ? '0' : '1',
|
|
|
- ttsVcnList: ttsVcnList.value,
|
|
|
- conversationId: currentConversationId.value,
|
|
|
- requestId: thisRequestId
|
|
|
- })
|
|
|
- })
|
|
|
|
|
|
- const data = await response.json()
|
|
|
-
|
|
|
- // 保存 conversationId
|
|
|
- if (data.conversationId) {
|
|
|
- currentConversationId.value = data.conversationId
|
|
|
- }
|
|
|
-
|
|
|
- // 只有当这是最新的请求时,才显示回复和播放音频
|
|
|
- if (thisRequestId === currentRequestId.value) {
|
|
|
- // 添加客服回复
|
|
|
- chatHistory.value.push({
|
|
|
- type: 'agent',
|
|
|
- content: data.reply
|
|
|
- })
|
|
|
- scrollToBottom()
|
|
|
-
|
|
|
- // 播放语音
|
|
|
- if (data.audio) {
|
|
|
- playAudio(data.audio)
|
|
|
- }
|
|
|
- } else {
|
|
|
- console.log('忽略旧请求的回复')
|
|
|
- }
|
|
|
+ // 使用流式接口发送消息
|
|
|
+ await sendStreamMessage(
|
|
|
+ newContent,
|
|
|
+ selectedAgent.value,
|
|
|
+ selectedAgentData?.gender === 'male' ? '0' : '1',
|
|
|
+ ttsVcnList.value,
|
|
|
+ false
|
|
|
+ )
|
|
|
} catch (error) {
|
|
|
console.error('发送消息失败:', error)
|
|
|
ElMessage.error('发送消息失败')
|
|
|
@@ -396,6 +371,30 @@ watch(currentTranscription, async (newVal, oldVal) => {
|
|
|
}
|
|
|
})
|
|
|
|
|
|
+// 监听流式文本变化,实时更新聊天历史
|
|
|
+watch(displayText, (newText) => {
|
|
|
+ if (newText && showChat.value) {
|
|
|
+ const lastMessage = chatHistory.value[chatHistory.value.length - 1]
|
|
|
+ if (lastMessage && lastMessage.type === 'agent' && !lastMessage.isGreeting) {
|
|
|
+ lastMessage.content = newText
|
|
|
+ } else {
|
|
|
+ chatHistory.value.push({
|
|
|
+ type: 'agent',
|
|
|
+ content: newText,
|
|
|
+ isGreeting: false
|
|
|
+ })
|
|
|
+ }
|
|
|
+ scrollToBottom()
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+// 同步conversationId
|
|
|
+watch(streamConversationId, (newId) => {
|
|
|
+ if (newId) {
|
|
|
+ currentConversationId.value = newId
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
// 监听 showChat 变化,开始对话时自动启动语音识别
|
|
|
watch(showChat, async (newVal) => {
|
|
|
if (newVal && !isRecording.value) {
|
|
|
@@ -415,7 +414,12 @@ watch(showChat, async (newVal) => {
|
|
|
}
|
|
|
|
|
|
// 停止音频播放
|
|
|
- stopAudio()
|
|
|
+ stopStreamAudio()
|
|
|
+ if (currentAudio.value) {
|
|
|
+ currentAudio.value.pause()
|
|
|
+ currentAudio.value.currentTime = 0
|
|
|
+ currentAudio.value = null
|
|
|
+ }
|
|
|
|
|
|
// 清理波形动画
|
|
|
if (waveformInterval) {
|
|
|
@@ -510,53 +514,21 @@ const sendTextMessage = async () => {
|
|
|
// 清空输入框
|
|
|
textMessage.value = ''
|
|
|
|
|
|
- // 发送到后端处理
|
|
|
+ // 发送到后端处理(使用流式接口)
|
|
|
try {
|
|
|
// 停止当前的音频播放
|
|
|
stopCurrentOutput()
|
|
|
|
|
|
- // 增加请求ID,标记这是最新的请求
|
|
|
- currentRequestId.value++
|
|
|
- const thisRequestId = currentRequestId.value
|
|
|
-
|
|
|
const selectedAgentData = agents.value.find(a => a.id === selectedAgent.value)
|
|
|
- const response = await fetch('http://localhost:8080/talk/message', {
|
|
|
- method: 'POST',
|
|
|
- headers: getHeaders(),
|
|
|
- body: JSON.stringify({
|
|
|
- message: content,
|
|
|
- agentId: selectedAgent.value,
|
|
|
- agentGender: selectedAgentData?.gender === 'male' ? '0' : '1',
|
|
|
- ttsVcnList: ttsVcnList.value,
|
|
|
- conversationId: currentConversationId.value,
|
|
|
- requestId: thisRequestId
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- const data = await response.json()
|
|
|
|
|
|
- // 保存 conversationId
|
|
|
- if (data.conversationId) {
|
|
|
- currentConversationId.value = data.conversationId
|
|
|
- }
|
|
|
- console.log('解析后的audio字段长度:', data.audio ? data.audio.length : 0)
|
|
|
-
|
|
|
- // 只有当这是最新的请求时,才显示回复和播放音频
|
|
|
- if (thisRequestId === currentRequestId.value) {
|
|
|
- // 添加客服回复
|
|
|
- chatHistory.value.push({
|
|
|
- type: 'agent',
|
|
|
- content: data.reply
|
|
|
- })
|
|
|
- scrollToBottom()
|
|
|
-
|
|
|
- // 播放语音
|
|
|
- if (data.audio) {
|
|
|
- playAudio(data.audio)
|
|
|
- }
|
|
|
- } else {
|
|
|
- console.log('忽略旧请求的回复')
|
|
|
- }
|
|
|
+ // 使用流式接口发送消息
|
|
|
+ await sendStreamMessage(
|
|
|
+ content,
|
|
|
+ selectedAgent.value,
|
|
|
+ selectedAgentData?.gender === 'male' ? '0' : '1',
|
|
|
+ ttsVcnList.value,
|
|
|
+ false
|
|
|
+ )
|
|
|
} catch (error) {
|
|
|
console.error('发送消息失败:', error)
|
|
|
ElMessage.error('发送消息失败')
|
|
|
@@ -752,31 +724,19 @@ const startChat = async () => {
|
|
|
if (selectedAgentData.greetingMessage) {
|
|
|
chatHistory.value = [{
|
|
|
type: 'agent',
|
|
|
- content: selectedAgentData.greetingMessage
|
|
|
+ content: selectedAgentData.greetingMessage,
|
|
|
+ isGreeting: true
|
|
|
}]
|
|
|
|
|
|
- // 调用后端生成欢迎语语音(isGreeting=true 标识不发送到 dify 工作流)
|
|
|
+ // 使用流式接口生成欢迎语语音
|
|
|
try {
|
|
|
- const ttsResponse = await fetch('http://localhost:8080/talk/message', {
|
|
|
- method: 'POST',
|
|
|
- headers: getHeaders(),
|
|
|
- body: JSON.stringify({
|
|
|
- message: selectedAgentData.greetingMessage,
|
|
|
- agentId: selectedAgent.value,
|
|
|
- isGreeting: true,
|
|
|
- conversationId: currentConversationId.value
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- const ttsData = await ttsResponse.json()
|
|
|
-
|
|
|
- // 保存 conversationId
|
|
|
- if (ttsData.conversationId) {
|
|
|
- currentConversationId.value = ttsData.conversationId
|
|
|
- }
|
|
|
- if (ttsData.audio) {
|
|
|
- playAudio(ttsData.audio)
|
|
|
- }
|
|
|
+ await sendStreamMessage(
|
|
|
+ selectedAgentData.greetingMessage,
|
|
|
+ selectedAgent.value,
|
|
|
+ null,
|
|
|
+ [],
|
|
|
+ true
|
|
|
+ )
|
|
|
} catch (error) {
|
|
|
console.error('生成欢迎语语音失败:', error)
|
|
|
}
|