Browse Source

父组件小喇叭删除,功能移植到每一个问题左侧;情绪大模型聊天记录存入store中。

master
宋杰 1 week ago
parent
commit
d03230bddf
  1. 63
      src/store/emotion.ts
  2. 94
      src/views/AIchat.vue
  3. 184
      src/views/AiEmotion.vue
  4. 133
      src/views/homePage.vue

63
src/store/emotion.ts

@ -7,11 +7,13 @@ export const useEmotionStore = defineStore('emotion', {
stockList: [] as StockData[], // 当前显示的股票列表 stockList: [] as StockData[], // 当前显示的股票列表
activeStockIndex: 0, // 当前激活的股票索引 activeStockIndex: 0, // 当前激活的股票索引
maxStocks: 10, // 最大股票数量限制 maxStocks: 10, // 最大股票数量限制
conversations: [] as ConversationMessage[], // 对话消息数组
maxConversations: 100, // 最大对话数量限制
}), }),
persist: { persist: {
key: 'emotion-store', key: 'emotion-store',
storage: sessionStorage, storage: sessionStorage,
paths: ['history', 'stockList', 'activeStockIndex']
paths: ['history', 'stockList', 'activeStockIndex', 'conversations']
}, },
getters: { getters: {
// 获取当前激活的股票 // 获取当前激活的股票
@ -22,6 +24,14 @@ export const useEmotionStore = defineStore('emotion', {
stockCount: (state) => state.stockList.length, stockCount: (state) => state.stockList.length,
// 是否达到最大股票数量 // 是否达到最大股票数量
isMaxStocks: (state) => state.stockList.length >= state.maxStocks, isMaxStocks: (state) => state.stockList.length >= state.maxStocks,
// 获取对话数量
conversationCount: (state) => state.conversations.length,
// 是否达到最大对话数量
isMaxConversations: (state) => state.conversations.length >= state.maxConversations,
// 获取最近的对话消息
recentConversations: (state) => {
return state.conversations.slice(-20); // 返回最近20条对话
},
}, },
actions: { actions: {
// 添加历史记录 // 添加历史记录
@ -118,6 +128,48 @@ export const useEmotionStore = defineStore('emotion', {
updateActiveStockConclusion(conclusionData: string) { updateActiveStockConclusion(conclusionData: string) {
this.updateStockConclusion(this.activeStockIndex, conclusionData); this.updateStockConclusion(this.activeStockIndex, conclusionData);
}, },
// 添加对话消息
addConversation(message: ConversationMessage) {
// 如果达到最大数量,移除最旧的消息
if (this.conversations.length >= this.maxConversations) {
this.conversations.shift();
}
// 添加新消息到数组末尾
this.conversations.push({
...message,
timestamp: message.timestamp || new Date().toISOString()
});
},
// 批量添加对话消息
addConversations(messages: ConversationMessage[]) {
messages.forEach(message => {
this.addConversation(message);
});
},
// 清空对话记录
clearConversations() {
this.conversations = [];
},
// 获取对话记录
getConversations() {
return this.conversations;
},
// 删除指定索引的对话消息
removeConversation(index: number) {
if (index >= 0 && index < this.conversations.length) {
this.conversations.splice(index, 1);
}
},
// 更新对话消息
updateConversation(index: number, updatedMessage: Partial<ConversationMessage>) {
if (index >= 0 && index < this.conversations.length) {
this.conversations[index] = {
...this.conversations[index],
...updatedMessage,
timestamp: new Date().toISOString()
};
}
},
}, },
}); });
@ -146,3 +198,12 @@ interface StockData {
conclusionData?: string; // 第二个工作流接口返回的结论数据 conclusionData?: string; // 第二个工作流接口返回的结论数据
timestamp: string; // 数据获取时间 timestamp: string; // 数据获取时间
} }
// 定义对话消息的类型
interface ConversationMessage {
sender: 'user' | 'ai'; // 消息发送者类型
text: string; // 消息内容
timestamp?: string; // 消息时间戳
id?: string; // 消息唯一标识(可选)
type?: string; // 消息类型(可选,如 'text', 'error', 'system' 等)
}

94
src/views/AIchat.vue

@ -32,9 +32,34 @@ import logo1 from "@/assets/img/AIchat/夺宝奇兵logo.png";
import logo2 from "@/assets/img/AIchat/开启无限财富.png"; import logo2 from "@/assets/img/AIchat/开启无限财富.png";
import bgc from "@/assets/img/AIchat/圈.png"; import bgc from "@/assets/img/AIchat/圈.png";
import bgc1 from "@/assets/img/AIchat/圈1.png"; import bgc1 from "@/assets/img/AIchat/圈1.png";
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";
const chatStore = useChatStore(); const chatStore = useChatStore();
const audioStore = useAudioStore(); const audioStore = useAudioStore();
const dataStore = useDataStore(); const dataStore = useDataStore();
//
const toggleVoiceForUser = () => {
if (!audioStore.isVoiceEnabled) {
//
audioStore.toggleVoice();
} else {
// /
if (audioStore.currentAudioUrl || audioStore.ttsUrl) {
// /
audioStore.togglePlayPause();
} else {
//
audioStore.toggleVoice();
}
}
};
//
const isVoice = computed(() => {
return audioStore.isVoiceEnabled;
});
// GIF // GIF
const currentGif = ref(""); const currentGif = ref("");
@ -4023,9 +4048,32 @@ onUnmounted(() => {
<img :src="logo2" alt="夺宝奇兵大模型logo" class="logo2" /> <img :src="logo2" alt="夺宝奇兵大模型logo" class="logo2" />
</div> </div>
<div v-for="(msg, index) in chatMsg" :key="index">
<!-- 用户消息容器包含喇叭按钮 -->
<div v-if="msg.sender === 'user'" class="user-message-container">
<img
:src="isVoice && audioStore.isPlaying ? voiceNoActive : voice"
class="user-message-speaker"
:class="{
'speaker-active': isVoice && audioStore.isPlaying
}"
@click="toggleVoiceForUser"
alt="喇叭"
/>
<div <div
v-for="(msg, index) in chatMsg"
:key="index"
:class="{
'message-bubble': true,
[msg.sender]: msg.sender,
[msg.class]: msg.class,
}"
>
<div v-html="msg.content"></div>
</div>
</div>
<!-- AI消息和其他类型消息 -->
<div
v-else
:class="{ :class="{
'message-bubble': true, 'message-bubble': true,
[msg.sender]: msg.sender, [msg.sender]: msg.sender,
@ -4099,6 +4147,7 @@ onUnmounted(() => {
<div v-else v-html="msg.content"></div> <div v-else v-html="msg.content"></div>
</div> </div>
</div> </div>
</div>
</template> </template>
<style scoped> <style scoped>
@ -4292,13 +4341,50 @@ p {
position: relative; position: relative;
} }
/* 用户消息容器样式 */
.user-message-container {
display: flex;
align-items: flex-start;
margin: 10px 0;
justify-content: flex-end;
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);
}
}
.message-bubble.user { .message-bubble.user {
color: #6d22f8; color: #6d22f8;
background: white; background: white;
font-weight: bold; font-weight: bold;
margin-left: auto;
border-radius: 10px; border-radius: 10px;
margin-right: 20px;
margin: 0;
/* border-bottom-right-radius: 5px; */ /* border-bottom-right-radius: 5px; */
} }

184
src/views/AiEmotion.vue

@ -12,9 +12,20 @@
<div class="user-input-display"> <div class="user-input-display">
<div v-for="(message, index) in messages" :key="index" class="message-container"> <div v-for="(message, index) in messages" :key="index" class="message-container">
<!-- 用户输入内容 --> <!-- 用户输入内容 -->
<div v-if="message.sender === 'user'" class="message-bubble user-message">
<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 }} {{ message.text }}
</div> </div>
</div>
<!-- AI返回结果 --> <!-- AI返回结果 -->
<div v-if="message.sender === 'ai'" class="message-bubble ai-message"> <div v-if="message.sender === 'ai'" class="message-bubble ai-message">
{{ message.text }} {{ message.text }}
@ -192,6 +203,9 @@ import { ElMessage } from 'element-plus'; // 接口失败提示已改为对话
import { useEmotionStore } from '@/store/emotion'; // Pinia store import { useEmotionStore } from '@/store/emotion'; // Pinia store
import { useEmotionAudioStore } from '@/store/emotionAudio.js'; // store import { useEmotionAudioStore } from '@/store/emotionAudio.js'; // store
import { useChatStore } from '@/store/chat.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 { Howl, Howler } from 'howler'; //
import { reactive } from 'vue'; import { reactive } from 'vue';
import { marked } from 'marked'; // marked import { marked } from 'marked'; // marked
@ -200,6 +214,28 @@ import { useUserStore } from "../store/userPessionCode";
// 使Pinia store // 使Pinia store
const emotionStore = useEmotionStore(); const emotionStore = useEmotionStore();
const emotionAudioStore = useEmotionAudioStore(); 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 chatStore = useChatStore();
// //
const userStore = useUserStore(); const userStore = useUserStore();
@ -241,6 +277,27 @@ const userInputDisplayRef = ref(null);//消息区域的引用
// //
const messages = ref([]); 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 isPageLoaded = ref(false); //
const isLoading = ref(false); // const isLoading = ref(false); //
const isRotating = ref(false);// const isRotating = ref(false);//
@ -943,6 +1000,13 @@ async function handleSendMessage(input, onComplete) {
messages.value.push(userMessage); messages.value.push(userMessage);
const aiMessage = reactive({ sender: 'ai', text: '您的剩余次数为0,无法使用情绪大模型,请联系客服或购买服务包。' }); const aiMessage = reactive({ sender: 'ai', text: '您的剩余次数为0,无法使用情绪大模型,请联系客服或购买服务包。' });
messages.value.push(aiMessage); messages.value.push(aiMessage);
// AIemotion store
emotionStore.addConversation({
sender: 'ai',
text: '您的剩余次数为0,无法使用情绪大模型,请联系客服或购买服务包。',
timestamp: new Date().toISOString()
});
// //
isRotating.value = false; isRotating.value = false;
messages.value = [...previousMessages, ...messages.value]; messages.value = [...previousMessages, ...messages.value];
@ -981,6 +1045,13 @@ async function handleSendMessage(input, onComplete) {
const userMessage = reactive({ sender: 'user', text: input }); const userMessage = reactive({ sender: 'user', text: input });
messages.value.push(userMessage); messages.value.push(userMessage);
// emotion store
emotionStore.addConversation({
sender: 'user',
text: input,
timestamp: new Date().toISOString()
});
try { try {
// //
const params = { const params = {
@ -1018,6 +1089,13 @@ async function handleSendMessage(input, onComplete) {
isPageLoaded.value = false; isPageLoaded.value = false;
const aiMessage = reactive({ sender: 'ai', text: processRefuseMessage(parsedData.refuse) }); const aiMessage = reactive({ sender: 'ai', text: processRefuseMessage(parsedData.refuse) });
messages.value.push(aiMessage); messages.value.push(aiMessage);
// AIemotion store
emotionStore.addConversation({
sender: 'ai',
text: processRefuseMessage(parsedData.refuse),
timestamp: new Date().toISOString()
});
isRotating.value = false; isRotating.value = false;
messages.value = [...previousMessages, ...messages.value]; messages.value = [...previousMessages, ...messages.value];
// //
@ -1152,6 +1230,13 @@ async function handleSendMessage(input, onComplete) {
const aiMessage = reactive({ sender: 'ai', text: '请求工作流接口失败,请检查网络连接' }); const aiMessage = reactive({ sender: 'ai', text: '请求工作流接口失败,请检查网络连接' });
messages.value.push(aiMessage); messages.value.push(aiMessage);
// AIemotion store
emotionStore.addConversation({
sender: 'ai',
text: '请求工作流接口失败,请检查网络连接',
timestamp: new Date().toISOString()
});
// //
isRotating.value = false; isRotating.value = false;
messages.value = [...previousMessages, ...messages.value]; messages.value = [...previousMessages, ...messages.value];
@ -1233,6 +1318,13 @@ async function fetchData(code, market, stockName, queryText) {
text: `数据丢失了,请稍后重试。` text: `数据丢失了,请稍后重试。`
}); });
messages.value.push(aiMessage); messages.value.push(aiMessage);
// AIemotion store
emotionStore.addConversation({
sender: 'ai',
text: '数据丢失了,请稍后重试。',
timestamp: new Date().toISOString()
});
return false; // return false; //
} }
@ -1270,6 +1362,13 @@ async function fetchData(code, market, stockName, queryText) {
const aiMessage = reactive({ sender: 'ai', text: '图表数据请求失败,请检查网络连接' }); const aiMessage = reactive({ sender: 'ai', text: '图表数据请求失败,请检查网络连接' });
messages.value.push(aiMessage); messages.value.push(aiMessage);
// AIemotion store
emotionStore.addConversation({
sender: 'ai',
text: '图表数据请求失败,请检查网络连接',
timestamp: new Date().toISOString()
});
return false; // return false; //
} }
} catch (error) { } catch (error) {
@ -1291,6 +1390,13 @@ async function fetchData(code, market, stockName, queryText) {
const aiMessage = reactive({ sender: 'ai', text: '图表数据请求失败,请检查网络连接' }); const aiMessage = reactive({ sender: 'ai', text: '图表数据请求失败,请检查网络连接' });
messages.value.push(aiMessage); messages.value.push(aiMessage);
// AIemotion store
emotionStore.addConversation({
sender: 'ai',
text: '图表数据请求失败,请检查网络连接',
timestamp: new Date().toISOString()
});
return false; // return false; //
} }
} }
@ -1381,6 +1487,13 @@ function renderCharts(data) {
}); });
messages.value.push(aiMessage); messages.value.push(aiMessage);
// AIemotion store
emotionStore.addConversation({
sender: 'ai',
text: `数据不完整,缺少以下关键数据:${validation.missingFields.join('、')}。请稍后重试或联系客服。`,
timestamp: new Date().toISOString()
});
// //
isPageLoaded.value = false; isPageLoaded.value = false;
isLoading.value = false; isLoading.value = false;
@ -1516,6 +1629,13 @@ function renderCharts(data) {
console.error('图表渲染错误:', error); console.error('图表渲染错误:', error);
const aiMessage = reactive({ sender: 'ai', text: '图表渲染失败,请重试' }); const aiMessage = reactive({ sender: 'ai', text: '图表渲染失败,请重试' });
messages.value.push(aiMessage); messages.value.push(aiMessage);
// AIemotion store
emotionStore.addConversation({
sender: 'ai',
text: '图表渲染失败,请重试',
timestamp: new Date().toISOString()
});
} }
}, 500); // 500msDOM }, 500); // 500msDOM
}); });
@ -1707,13 +1827,16 @@ const handleContainerScroll = () => {
// //
onMounted(async () => { 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 // resize
const globalResizeHandler = debounce(() => { const globalResizeHandler = debounce(() => {
@ -1885,10 +2008,7 @@ onUnmounted(() => {
// //
const emit = defineEmits(['updateMessage', 'sendMessage', 'ensureAIchat']); const emit = defineEmits(['updateMessage', 'sendMessage', 'ensureAIchat']);
// 使
defineExpose({
handleSendMessage
});
// 使
</script> </script>
<style scoped> <style scoped>
@ -2452,18 +2572,54 @@ defineExpose({
width: 100%; 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 { .user-message {
color: #6d22f8; color: #6d22f8;
background: white; background: white;
font-weight: bold; font-weight: bold;
padding: 10px 15px;
padding: 20px 20px;
border-radius: 15px; border-radius: 15px;
max-width: 60%; max-width: 60%;
text-align: left; text-align: left;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
margin-left: auto;
margin: 0;
/* 将用户消息推到右边 */ /* 将用户消息推到右边 */
padding: 20px 20px;
} }
.ai-message { .ai-message {

133
src/views/homePage.vue

@ -381,7 +381,7 @@ const fnGetToken = () => {
}; };
// console.log('') // console.log('')
// App // App
useAppBridge().packageFun("JWwebReady", () => {}, 5, {});
useAppBridge().packageFun("JWwebReady", () => { }, 5, {});
}; };
// setTimeout // setTimeout
@ -584,14 +584,11 @@ onUnmounted(() => {
<template> <template>
<div class="homepage" id="testId"> <div class="homepage" id="testId">
<!-- 历史记录组件 --> <!-- 历史记录组件 -->
<HistoryRecord
ref="historyRecordRef"
:current-type="activeTab"
@selectRecord="handleHistorySelect"
@recordAdded="handleHistoryAdded"
/>
<el-container v-if="!dataStore.isFeedback" class="main-container" :class="{ 'collapsed': historyRecordRef?.isCollapsed }">
<HistoryRecord ref="historyRecordRef" :current-type="activeTab" @selectRecord="handleHistorySelect"
@recordAdded="handleHistoryAdded" />
<el-container v-if="!dataStore.isFeedback" class="main-container"
:class="{ 'collapsed': historyRecordRef?.isCollapsed }">
<!-- AI小财神头部 logo 次数 公告 --> <!-- AI小财神头部 logo 次数 公告 -->
<el-header class="homepage-head"> <el-header class="homepage-head">
<!-- logo --> <!-- logo -->
@ -605,16 +602,8 @@ onUnmounted(() => {
<img :src="getCountAll" class="action-btn" /> <img :src="getCountAll" class="action-btn" />
<div class="count-number">{{ UserCount }}</div> <div class="count-number">{{ UserCount }}</div>
</div> </div>
<img
:src="announcementBtn"
class="announcement-btn action-btn"
@click="showAnnouncement"
/>
<img
:src="feedbackBtn"
class="announcement-btn action-btn"
@click="showFeedback"
/>
<img :src="announcementBtn" class="announcement-btn action-btn" @click="showAnnouncement" />
<img :src="feedbackBtn" class="announcement-btn action-btn" @click="showFeedback" />
</div> </div>
</el-header> </el-header>
@ -623,29 +612,17 @@ onUnmounted(() => {
<div class="main-wrapper"> <div class="main-wrapper">
<section class="tab-section"> <section class="tab-section">
<div class="tab-container"> <div class="tab-container">
<div
v-for="(tab, index) in tabs"
:key="tab.name"
@click="setActiveTab(tab.name, index)"
:class="[
<div v-for="(tab, index) in tabs" :key="tab.name" @click="setActiveTab(tab.name, index)" :class="[
'tab-item', 'tab-item',
{ active: activeIndex === index && !isAnnouncementVisible }, { active: activeIndex === index && !isAnnouncementVisible },
]"
>
]">
<span>{{ tab.label }}</span> <span>{{ tab.label }}</span>
</div> </div>
</div> </div>
</section> </section>
<div class="tab-content" ref="tabContent"> <div class="tab-content" ref="tabContent">
<component
:is="activeComponent"
:messages="messages"
@updateMessage="updateMessage"
@sendMessage="sendMessage"
@ensureAIchat="ensureAIchat"
@enableInput="enableInput"
ref="aiEmotionRef"
/>
<component :is="activeComponent" :messages="messages" @updateMessage="updateMessage"
@sendMessage="sendMessage" @ensureAIchat="ensureAIchat" @enableInput="enableInput" ref="aiEmotionRef" />
</div> </div>
</div> </div>
</el-main> </el-main>
@ -658,71 +635,33 @@ onUnmounted(() => {
<img v-else :src="thinkNoActive" @click="toggleThink" class="action-btn" /> <img v-else :src="thinkNoActive" @click="toggleThink" class="action-btn" />
<img :src="languageBtn" @click="changeLanguage" class="action-btn" /> --> <img :src="languageBtn" @click="changeLanguage" class="action-btn" /> -->
<!-- 夺宝奇兵大模型按钮 --> <!-- 夺宝奇兵大模型按钮 -->
<img
:src="activeTab === 'AIchat' ? dbqbButton01 : dbqbButton02"
@click="setActiveTab('AIchat', 0)"
class="action-btn model-btn"
alt="夺宝奇兵大模型"
/>
<img :src="activeTab === 'AIchat' ? dbqbButton01 : dbqbButton02" @click="setActiveTab('AIchat', 0)"
class="action-btn model-btn" alt="夺宝奇兵大模型" />
<!-- AI情绪大模型按钮 --> <!-- AI情绪大模型按钮 -->
<img
:src="
activeTab === 'AiEmotion' ? emotionButton01 : emotionButton02
"
@click="setActiveTab('AiEmotion', 1)"
class="action-btn model-btn"
alt="AI情绪大模型"
/>
<img
v-if="
<img :src="activeTab === 'AiEmotion' ? emotionButton01 : emotionButton02
" @click="setActiveTab('AiEmotion', 1)" class="action-btn model-btn" alt="AI情绪大模型" />
<!-- <img v-if="
getCurrentAudioStore().isVoiceEnabled && getCurrentAudioStore().isVoiceEnabled &&
getCurrentAudioStore().isPlaying getCurrentAudioStore().isPlaying
"
:src="voice"
@click="toggleVoice"
class="action-btn"
style="animation: pulse 1.5s infinite"
/>
<img
v-else-if="
" :src="voice" @click="toggleVoice" class="action-btn" style="animation: pulse 1.5s infinite" />
<img v-else-if="
getCurrentAudioStore().isVoiceEnabled && getCurrentAudioStore().isVoiceEnabled &&
!getCurrentAudioStore().isPlaying !getCurrentAudioStore().isPlaying
"
:src="voiceNoActive"
@click="toggleVoice"
class="action-btn"
/>
<img
v-else
:src="voiceNoActive"
@click="toggleVoice"
class="action-btn"
/>
" :src="voiceNoActive" @click="toggleVoice" class="action-btn" />
<img v-else :src="voiceNoActive" @click="toggleVoice" class="action-btn" /> -->
</div> </div>
</div> </div>
<!-- 第二行输入框 --> <!-- 第二行输入框 -->
<div class="footer-second-line"> <div class="footer-second-line">
<img :src="msgBtn" class="msg-icon" /> <img :src="msgBtn" class="msg-icon" />
<el-input
type="textarea"
v-model="message"
@focus="onFocus"
@blur="onBlur"
:autosize="{ minRows: 1, maxRows: 4 }"
placeholder="请输入股票名称或股票代码..."
class="msg-input"
<el-input type="textarea" v-model="message" @focus="onFocus" @blur="onBlur"
:autosize="{ minRows: 1, maxRows: 4 }" placeholder="请输入股票名称或股票代码..." class="msg-input"
@keydown.enter.exact.prevent="(isLoading || isInputDisabled) ? null : sendMessage()" @keydown.enter.exact.prevent="(isLoading || isInputDisabled) ? null : sendMessage()"
:disabled="isInputDisabled"
resize="none"
>
:disabled="isInputDisabled" resize="none">
</el-input> </el-input>
<img
:src="sendBtn"
@click="sendMessage"
class="action-btn send-btn"
:style="{ opacity: isInputDisabled ? 0.5 : 1, cursor: isInputDisabled ? 'not-allowed' : 'pointer' }"
/>
<img :src="sendBtn" @click="sendMessage" class="action-btn send-btn"
:style="{ opacity: isInputDisabled ? 0.5 : 1, cursor: isInputDisabled ? 'not-allowed' : 'pointer' }" />
</div> </div>
</el-footer> </el-footer>
</el-container> </el-container>
@ -739,16 +678,8 @@ onUnmounted(() => {
<img :src="getCountAll" class="action-btn" /> <img :src="getCountAll" class="action-btn" />
<div class="count-number">{{ UserCount }}</div> <div class="count-number">{{ UserCount }}</div>
</div> </div>
<img
:src="announcementBtn"
class="announcement-btn action-btn"
@click="showAnnouncement"
/>
<img
:src="feedbackBtn"
class="announcement-btn action-btn"
@click="showFeedback"
/>
<img :src="announcementBtn" class="announcement-btn action-btn" @click="showAnnouncement" />
<img :src="feedbackBtn" class="announcement-btn action-btn" @click="showFeedback" />
</div> </div>
</el-header> </el-header>
@ -882,7 +813,8 @@ body {
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: center; background-position: center;
display: flex; display: flex;
flex-direction: row; /* 改为水平布局 */
flex-direction: row;
/* 改为水平布局 */
overflow: hidden; overflow: hidden;
position: fixed; position: fixed;
top: 0; top: 0;
@ -895,7 +827,8 @@ body {
.main-container { .main-container {
flex: 1; flex: 1;
margin-left: 300px; /* 为历史记录组件留出空间 */
margin-left: 300px;
/* 为历史记录组件留出空间 */
transition: margin-left 0.3s ease; transition: margin-left 0.3s ease;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -1125,9 +1058,11 @@ body {
0% { 0% {
transform: scale(1); transform: scale(1);
} }
50% { 50% {
transform: scale(1.1); transform: scale(1.1);
} }
100% { 100% {
transform: scale(1); transform: scale(1);
} }

Loading…
Cancel
Save