|
|
@ -12,8 +12,19 @@ |
|
|
|
<div class="user-input-display"> |
|
|
|
<div v-for="(message, index) in messages" :key="index" class="message-container"> |
|
|
|
<!-- 用户输入内容 --> |
|
|
|
<div v-if="message.sender === 'user'" class="message-bubble user-message"> |
|
|
|
{{ message.text }} |
|
|
|
<div v-if="message.sender === 'user'" class="user-message-container"> |
|
|
|
<img |
|
|
|
:src="isVoice && emotionAudioStore.isPlaying ? voiceNoActive : voice" |
|
|
|
class="user-message-speaker" |
|
|
|
:class="{ |
|
|
|
'speaker-active': isVoice && emotionAudioStore.isPlaying |
|
|
|
}" |
|
|
|
@click="toggleVoiceForUser" |
|
|
|
alt="喇叭" |
|
|
|
/> |
|
|
|
<div class="message-bubble user-message"> |
|
|
|
{{ message.text }} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<!-- AI返回结果 --> |
|
|
|
<div v-if="message.sender === 'ai'" class="message-bubble ai-message"> |
|
|
@ -192,6 +203,9 @@ import { ElMessage } from 'element-plus'; // 接口失败提示已改为对话 |
|
|
|
import { useEmotionStore } from '@/store/emotion'; // 导入Pinia store |
|
|
|
import { useEmotionAudioStore } from '@/store/emotionAudio.js'; // 导入音频store |
|
|
|
import { useChatStore } from '@/store/chat.js'; // 导入聊天store |
|
|
|
import getCountAll from "../assets/img/homePage/get-count-all.png"; |
|
|
|
import voice from "../assets/img/homePage/tail/voice.png"; |
|
|
|
import voiceNoActive from "../assets/img/homePage/tail/voice-no-active.png"; |
|
|
|
import { Howl, Howler } from 'howler'; // 导入音频播放库 |
|
|
|
import { reactive } from 'vue'; |
|
|
|
import { marked } from 'marked'; // 引入marked库 |
|
|
@ -200,6 +214,28 @@ import { useUserStore } from "../store/userPessionCode"; |
|
|
|
// 使用Pinia store |
|
|
|
const emotionStore = useEmotionStore(); |
|
|
|
const emotionAudioStore = useEmotionAudioStore(); |
|
|
|
|
|
|
|
// 语音播放控制函数 |
|
|
|
const toggleVoiceForUser = () => { |
|
|
|
if (!emotionAudioStore.isVoiceEnabled) { |
|
|
|
// 如果语音功能关闭,先开启语音功能 |
|
|
|
emotionAudioStore.toggleVoice(); |
|
|
|
} else { |
|
|
|
// 如果语音功能开启,则切换播放/暂停状态 |
|
|
|
if (emotionAudioStore.currentAudioUrl || emotionAudioStore.ttsUrl) { |
|
|
|
// 有音频时切换播放/暂停 |
|
|
|
emotionAudioStore.togglePlayPause(); |
|
|
|
} else { |
|
|
|
// 没有音频时关闭语音功能 |
|
|
|
emotionAudioStore.toggleVoice(); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
// 计算属性:判断语音是否启用 |
|
|
|
const isVoice = computed(() => { |
|
|
|
return emotionAudioStore.isVoiceEnabled; |
|
|
|
}); |
|
|
|
const chatStore = useChatStore(); |
|
|
|
// 获取权限 |
|
|
|
const userStore = useUserStore(); |
|
|
@ -241,6 +277,27 @@ const userInputDisplayRef = ref(null);//消息区域的引用 |
|
|
|
|
|
|
|
// 响应式数据 |
|
|
|
const messages = ref([]); |
|
|
|
|
|
|
|
// 从emotion store中恢复对话记录 |
|
|
|
const loadConversationsFromStore = () => { |
|
|
|
const storedConversations = emotionStore.getConversations(); |
|
|
|
messages.value = storedConversations.map(conv => ({ |
|
|
|
sender: conv.sender, |
|
|
|
text: conv.text |
|
|
|
})); |
|
|
|
}; |
|
|
|
|
|
|
|
// 清空对话记录 |
|
|
|
const clearConversations = () => { |
|
|
|
messages.value = []; |
|
|
|
emotionStore.clearConversations(); |
|
|
|
}; |
|
|
|
|
|
|
|
// 暴露清空对话记录的方法给父组件 |
|
|
|
defineExpose({ |
|
|
|
handleSendMessage, |
|
|
|
clearConversations |
|
|
|
}); |
|
|
|
const isPageLoaded = ref(false); // 控制页面是否显示 |
|
|
|
const isLoading = ref(false); // 控制加载状态 |
|
|
|
const isRotating = ref(false);//控制旋转 |
|
|
@ -943,6 +1000,13 @@ async function handleSendMessage(input, onComplete) { |
|
|
|
messages.value.push(userMessage); |
|
|
|
const aiMessage = reactive({ sender: 'ai', text: '您的剩余次数为0,无法使用情绪大模型,请联系客服或购买服务包。' }); |
|
|
|
messages.value.push(aiMessage); |
|
|
|
|
|
|
|
// 将AI消息添加到emotion store中 |
|
|
|
emotionStore.addConversation({ |
|
|
|
sender: 'ai', |
|
|
|
text: '您的剩余次数为0,无法使用情绪大模型,请联系客服或购买服务包。', |
|
|
|
timestamp: new Date().toISOString() |
|
|
|
}); |
|
|
|
// 停止图片旋转,恢复历史数据 |
|
|
|
isRotating.value = false; |
|
|
|
messages.value = [...previousMessages, ...messages.value]; |
|
|
@ -980,6 +1044,13 @@ async function handleSendMessage(input, onComplete) { |
|
|
|
|
|
|
|
const userMessage = reactive({ sender: 'user', text: input }); |
|
|
|
messages.value.push(userMessage); |
|
|
|
|
|
|
|
// 将用户消息添加到emotion store中 |
|
|
|
emotionStore.addConversation({ |
|
|
|
sender: 'user', |
|
|
|
text: input, |
|
|
|
timestamp: new Date().toISOString() |
|
|
|
}); |
|
|
|
|
|
|
|
try { |
|
|
|
// 第一步:调用第一个接口验证用户输入内容是否合法 |
|
|
@ -1018,6 +1089,13 @@ async function handleSendMessage(input, onComplete) { |
|
|
|
isPageLoaded.value = false; |
|
|
|
const aiMessage = reactive({ sender: 'ai', text: processRefuseMessage(parsedData.refuse) }); |
|
|
|
messages.value.push(aiMessage); |
|
|
|
|
|
|
|
// 将AI消息添加到emotion store中 |
|
|
|
emotionStore.addConversation({ |
|
|
|
sender: 'ai', |
|
|
|
text: processRefuseMessage(parsedData.refuse), |
|
|
|
timestamp: new Date().toISOString() |
|
|
|
}); |
|
|
|
isRotating.value = false; |
|
|
|
messages.value = [...previousMessages, ...messages.value]; |
|
|
|
// 调用完成回调,重新启用输入框 |
|
|
@ -1152,6 +1230,13 @@ async function handleSendMessage(input, onComplete) { |
|
|
|
|
|
|
|
const aiMessage = reactive({ sender: 'ai', text: '请求工作流接口失败,请检查网络连接' }); |
|
|
|
messages.value.push(aiMessage); |
|
|
|
|
|
|
|
// 将AI消息添加到emotion store中 |
|
|
|
emotionStore.addConversation({ |
|
|
|
sender: 'ai', |
|
|
|
text: '请求工作流接口失败,请检查网络连接', |
|
|
|
timestamp: new Date().toISOString() |
|
|
|
}); |
|
|
|
// 请求失败时停止图片旋转,恢复历史数据 |
|
|
|
isRotating.value = false; |
|
|
|
messages.value = [...previousMessages, ...messages.value]; |
|
|
@ -1233,6 +1318,13 @@ async function fetchData(code, market, stockName, queryText) { |
|
|
|
text: `数据丢失了,请稍后重试。` |
|
|
|
}); |
|
|
|
messages.value.push(aiMessage); |
|
|
|
|
|
|
|
// 将AI消息添加到emotion store中 |
|
|
|
emotionStore.addConversation({ |
|
|
|
sender: 'ai', |
|
|
|
text: '数据丢失了,请稍后重试。', |
|
|
|
timestamp: new Date().toISOString() |
|
|
|
}); |
|
|
|
return false; // 返回失败标识,不添加股票到标签 |
|
|
|
} |
|
|
|
|
|
|
@ -1269,8 +1361,15 @@ async function fetchData(code, market, stockName, queryText) { |
|
|
|
} |
|
|
|
|
|
|
|
const aiMessage = reactive({ sender: 'ai', text: '图表数据请求失败,请检查网络连接' }); |
|
|
|
messages.value.push(aiMessage); |
|
|
|
return false; // 返回失败标识 |
|
|
|
messages.value.push(aiMessage); |
|
|
|
|
|
|
|
// 将AI消息添加到emotion store中 |
|
|
|
emotionStore.addConversation({ |
|
|
|
sender: 'ai', |
|
|
|
text: '图表数据请求失败,请检查网络连接', |
|
|
|
timestamp: new Date().toISOString() |
|
|
|
}); |
|
|
|
return false; // 返回失败标识 |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
// 关闭加载状态 |
|
|
@ -1291,6 +1390,13 @@ async function fetchData(code, market, stockName, queryText) { |
|
|
|
|
|
|
|
const aiMessage = reactive({ sender: 'ai', text: '图表数据请求失败,请检查网络连接' }); |
|
|
|
messages.value.push(aiMessage); |
|
|
|
|
|
|
|
// 将AI消息添加到emotion store中 |
|
|
|
emotionStore.addConversation({ |
|
|
|
sender: 'ai', |
|
|
|
text: '图表数据请求失败,请检查网络连接', |
|
|
|
timestamp: new Date().toISOString() |
|
|
|
}); |
|
|
|
return false; // 返回失败标识 |
|
|
|
} |
|
|
|
} |
|
|
@ -1380,6 +1486,13 @@ function renderCharts(data) { |
|
|
|
text: `数据不完整,缺少以下关键数据:${validation.missingFields.join('、')}。请稍后重试或联系客服。` |
|
|
|
}); |
|
|
|
messages.value.push(aiMessage); |
|
|
|
|
|
|
|
// 将AI消息添加到emotion store中 |
|
|
|
emotionStore.addConversation({ |
|
|
|
sender: 'ai', |
|
|
|
text: `数据不完整,缺少以下关键数据:${validation.missingFields.join('、')}。请稍后重试或联系客服。`, |
|
|
|
timestamp: new Date().toISOString() |
|
|
|
}); |
|
|
|
|
|
|
|
// 隐藏页面内容 |
|
|
|
isPageLoaded.value = false; |
|
|
@ -1516,6 +1629,13 @@ function renderCharts(data) { |
|
|
|
console.error('图表渲染错误:', error); |
|
|
|
const aiMessage = reactive({ sender: 'ai', text: '图表渲染失败,请重试' }); |
|
|
|
messages.value.push(aiMessage); |
|
|
|
|
|
|
|
// 将AI消息添加到emotion store中 |
|
|
|
emotionStore.addConversation({ |
|
|
|
sender: 'ai', |
|
|
|
text: '图表渲染失败,请重试', |
|
|
|
timestamp: new Date().toISOString() |
|
|
|
}); |
|
|
|
} |
|
|
|
}, 500); // 增加延迟到500ms确保DOM和组件完全稳定 |
|
|
|
}); |
|
|
@ -1707,13 +1827,16 @@ const handleContainerScroll = () => { |
|
|
|
|
|
|
|
// 页面挂载完成后触发图片旋转和设置滚动监听 |
|
|
|
onMounted(async () => { |
|
|
|
// 恢复对话记录 |
|
|
|
loadConversationsFromStore(); |
|
|
|
|
|
|
|
// 确保获取用户次数 |
|
|
|
try { |
|
|
|
await chatStore.getUserCount(); |
|
|
|
console.log('情绪大模型页面:用户次数获取成功'); |
|
|
|
} catch (error) { |
|
|
|
console.error('情绪大模型页面:获取用户次数失败', error); |
|
|
|
} |
|
|
|
// try { |
|
|
|
// await chatStore.getUserCount(); |
|
|
|
// console.log('情绪大模型页面:用户次数获取成功'); |
|
|
|
// } catch (error) { |
|
|
|
// console.error('情绪大模型页面:获取用户次数失败', error); |
|
|
|
// } |
|
|
|
|
|
|
|
// 添加全局resize监听器,确保所有图表和容器响应页面宽度变化 |
|
|
|
const globalResizeHandler = debounce(() => { |
|
|
@ -1885,10 +2008,7 @@ onUnmounted(() => { |
|
|
|
// 声明组件可以触发的事件 |
|
|
|
const emit = defineEmits(['updateMessage', 'sendMessage', 'ensureAIchat']); |
|
|
|
|
|
|
|
// 导出方法供外部使用 |
|
|
|
defineExpose({ |
|
|
|
handleSendMessage |
|
|
|
}); |
|
|
|
// 导出方法供外部使用(已在上方定义) |
|
|
|
</script> |
|
|
|
|
|
|
|
<style scoped> |
|
|
@ -2452,18 +2572,54 @@ defineExpose({ |
|
|
|
width: 100%; |
|
|
|
} |
|
|
|
|
|
|
|
/* 用户消息容器样式 */ |
|
|
|
.user-message-container { |
|
|
|
display: flex; |
|
|
|
align-items: flex-start; |
|
|
|
margin-left: auto; |
|
|
|
gap: 10px; |
|
|
|
} |
|
|
|
|
|
|
|
.user-message-speaker { |
|
|
|
width: 32px; |
|
|
|
height: 32px; |
|
|
|
object-fit: contain; |
|
|
|
margin-top: 5px; |
|
|
|
cursor: pointer; |
|
|
|
transition: all 0.3s ease; |
|
|
|
} |
|
|
|
|
|
|
|
.user-message-speaker:hover { |
|
|
|
transform: scale(1.1); |
|
|
|
} |
|
|
|
|
|
|
|
.user-message-speaker.speaker-active { |
|
|
|
animation: pulse 1.5s infinite; |
|
|
|
} |
|
|
|
|
|
|
|
@keyframes pulse { |
|
|
|
0% { |
|
|
|
transform: scale(1); |
|
|
|
} |
|
|
|
50% { |
|
|
|
transform: scale(1.1); |
|
|
|
} |
|
|
|
100% { |
|
|
|
transform: scale(1); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.user-message { |
|
|
|
color: #6d22f8; |
|
|
|
background: white; |
|
|
|
font-weight: bold; |
|
|
|
padding: 10px 15px; |
|
|
|
padding: 20px 20px; |
|
|
|
border-radius: 15px; |
|
|
|
max-width: 60%; |
|
|
|
text-align: left; |
|
|
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); |
|
|
|
margin-left: auto; |
|
|
|
margin: 0; |
|
|
|
/* 将用户消息推到右边 */ |
|
|
|
padding: 20px 20px; |
|
|
|
} |
|
|
|
|
|
|
|
.ai-message { |
|
|
|