commit
523ef9ce7b
3 changed files with 753 additions and 0 deletions
@ -0,0 +1,201 @@ |
|||
<script setup> |
|||
import { ref, onMounted, watch, nextTick } from 'vue' |
|||
import { ElDialog } from 'element-plus' |
|||
import { getReplyStreamAPI } from '../api/AIxiaocaishen' |
|||
|
|||
// 随机GIF |
|||
const currentGif = ref('') |
|||
|
|||
// 新闻飘屏数据 |
|||
const newsList = ref(Array(10).fill().map((_, i) => ({ |
|||
title: `引导提出问题 ${i + 1}`, |
|||
content: `新闻 ${i + 1} 的详细内容...` |
|||
}))) |
|||
|
|||
|
|||
// 弹窗控制 |
|||
const dialogVisible = ref(false) |
|||
const currentNews = ref('') |
|||
const showNews = (news) => { |
|||
currentNews.value = news |
|||
dialogVisible.value = true |
|||
} |
|||
|
|||
// 获取消息 |
|||
const props = defineProps({ |
|||
messages: Array, |
|||
}) |
|||
|
|||
watch(() => props.messages, async (newVal, oldVal) => { |
|||
console.log('消息列表已更新,最新消息:', newVal[newVal.length - 1]) |
|||
// 在此处添加需要执行的逻辑 |
|||
const response = await getReplyStreamAPI({ |
|||
"workflow_id": "7480464341100494863", |
|||
"parameters": { |
|||
"input": newVal[newVal.length - 1].content |
|||
} |
|||
}); |
|||
const reader = response.body.getReader(); |
|||
|
|||
console.log(response, 'response') |
|||
console.log(reader, 'reader') |
|||
|
|||
}, { deep: true, immediate: true }) |
|||
|
|||
// 初始化随机GIF |
|||
onMounted(() => { |
|||
const random = Math.floor(Math.random() * 4) + 1 |
|||
console.log(random, 'random') |
|||
currentGif.value = `src/assets/img/AIchat/AIgif${random}.gif` |
|||
}) |
|||
</script> |
|||
|
|||
<template> |
|||
<div class="chat-container"> |
|||
<!-- GIF区域 --> |
|||
<div class="gif-area"> |
|||
<img :src="currentGif" alt="AI动画"> |
|||
|
|||
<div class="marquee-container"> |
|||
<div class="marquee-row top"> |
|||
<div v-for="(news, index) in newsList.slice(0, 5)" :key="'top' + index" class="marquee-item" |
|||
@click="showNews(news)"> |
|||
{{ news.title }} |
|||
</div> |
|||
</div> |
|||
<div class="marquee-row bottom"> |
|||
<div v-for="(news, index) in newsList.slice(5, 10)" :key="'bottom' + index" class="marquee-item" |
|||
@click="showNews(news)"> |
|||
{{ news.title }} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- 消息区域 --> |
|||
<div class="message-area"> |
|||
<div v-for="(msg, index) in messages" :key="index" :class="['message-bubble', msg.sender]"> |
|||
{{ msg.content }} |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- 新闻弹窗 --> |
|||
<el-dialog v-model="dialogVisible" title="新闻详情" width="60%"> |
|||
<p>{{ currentNews.content }}</p> |
|||
</el-dialog> |
|||
</div> |
|||
</template> |
|||
|
|||
<style scoped> |
|||
.chat-container { |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.gif-area { |
|||
/* position: relative; */ |
|||
height: 30vh; |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
|
|||
.gif-area img { |
|||
width: 30%; |
|||
/* 改为百分比单位 */ |
|||
min-width: 200px; |
|||
/* 最小尺寸 */ |
|||
max-width: 400px; |
|||
/* 最大尺寸 */ |
|||
height: auto; |
|||
left: 50%; |
|||
transition: all 0.3s; |
|||
/* 添加过渡效果 */ |
|||
|
|||
} |
|||
|
|||
.marquee-container { |
|||
/* position: absolute; */ |
|||
bottom: 0; |
|||
width: 100%; |
|||
} |
|||
|
|||
.marquee-row { |
|||
white-space: nowrap; |
|||
overflow: visible; |
|||
padding: 8px 0; |
|||
width: 100%; |
|||
} |
|||
|
|||
.marquee-item { |
|||
display: inline-block; |
|||
margin: 0 15px; |
|||
padding: 8px 20px; |
|||
background: rgba(255, 255, 255, 0.9); |
|||
/* 白色背景 */ |
|||
border-radius: 10px; |
|||
/* 圆角矩形 */ |
|||
color: #333; |
|||
/* 文字颜色改为深色 */ |
|||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
|||
/* 添加阴影 */ |
|||
transition: all 0.3s; |
|||
transition: color 0.3s; |
|||
} |
|||
|
|||
.top { |
|||
animation: marquee 15s linear infinite; |
|||
} |
|||
|
|||
.bottom { |
|||
animation: marquee 15s linear infinite reverse; |
|||
} |
|||
|
|||
@keyframes marquee { |
|||
0% { |
|||
transform: translateX(100%); |
|||
} |
|||
|
|||
100% { |
|||
transform: translateX(-150%); |
|||
} |
|||
} |
|||
|
|||
.message-area { |
|||
flex: 1; |
|||
overflow: auto; |
|||
padding: 20px; |
|||
} |
|||
|
|||
.message-bubble { |
|||
max-width: 70%; |
|||
margin: 10px 20px; |
|||
padding: 15px 25px; |
|||
border-radius: 10px; |
|||
position: relative; |
|||
} |
|||
|
|||
.message-bubble.user { |
|||
background: #8263f0; |
|||
color: white; |
|||
margin-left: auto; |
|||
/* border-bottom-right-radius: 5px; */ |
|||
} |
|||
|
|||
.message-bubble.ai { |
|||
background: #f0f0f0; |
|||
color: #333; |
|||
margin-right: auto; |
|||
border-bottom-left-radius: 5px; |
|||
} |
|||
|
|||
.message-area { |
|||
flex: 1; |
|||
overflow-y: auto; |
|||
padding: 20px; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 15px; |
|||
} |
|||
</style> |
@ -0,0 +1,84 @@ |
|||
<script setup> |
|||
import { ref } from 'vue' |
|||
// 新闻飘屏数据 |
|||
const newsList = ref(Array(10).fill().map((_, i) => ({ |
|||
title: `每日资讯 ${i + 1}`, |
|||
content: `资讯 ${i + 1} 的详细内容...` |
|||
}))) |
|||
|
|||
// 弹窗控制 |
|||
const dialogVisible = ref(false) |
|||
const currentNews = ref('') |
|||
const showNews = (news) => { |
|||
currentNews.value = news |
|||
dialogVisible.value = true |
|||
} |
|||
|
|||
</script> |
|||
|
|||
<template> |
|||
<div class="news-container"> |
|||
<el-text v-for="(news, index) in newsList" :key="index" class="news-item" @click="showNews(news)"> |
|||
{{ news.title }} |
|||
</el-text> |
|||
|
|||
<el-text class="daily-item"> |
|||
<span>【</span> |
|||
<span class="purple-text">每日复盘</span> |
|||
<span>】 即将上线,</span> |
|||
<br> |
|||
<span>敬请期待!</span> |
|||
</el-text> |
|||
</div> |
|||
|
|||
<!-- 新闻弹窗 --> |
|||
<el-dialog v-model="dialogVisible" title="每日资讯详情" width="60%" > |
|||
<p>{{ currentNews.content }}</p> |
|||
</el-dialog> |
|||
</template> |
|||
|
|||
<style scoped> |
|||
.news-container { |
|||
height: auto; |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
/* 水平居中 */ |
|||
gap: 10px; |
|||
} |
|||
|
|||
.news-item { |
|||
width: 60%; |
|||
/* 拉长宽度 */ |
|||
padding: 10px; |
|||
border: 2px solid #af84e0 !important; |
|||
/* 紫色边框 */ |
|||
border-radius: 4px; |
|||
text-align: center; |
|||
/* 文字居中 */ |
|||
font-size: 18px; |
|||
font-weight: bold; |
|||
color: #000; |
|||
} |
|||
|
|||
/* 响应式调整 */ |
|||
@media (max-width: 768px) { |
|||
.news-item { |
|||
width: 60%; |
|||
font-size: 16px; |
|||
} |
|||
} |
|||
|
|||
.purple-text { |
|||
color: #7315df !important; |
|||
} |
|||
|
|||
.daily-item { |
|||
font-size: 20px; |
|||
font-weight: bold; |
|||
line-height: 1.8; |
|||
white-space: nowrap; |
|||
text-align: center; |
|||
/* 保持首行不换行 */ |
|||
} |
|||
</style> |
@ -0,0 +1,468 @@ |
|||
<script setup> |
|||
// 导入 |
|||
import { ref, computed, onMounted, watch, nextTick } from 'vue' |
|||
import { setHeight } from '../utils/setHeight' |
|||
import { getReplyAPI } from '../api/AIxiaocaishen' |
|||
import AIchat from '../views/AIchat.vue' |
|||
import AIfind from '../views/AIfind.vue' |
|||
// 变量 |
|||
// 将默认值改为从 sessionStorage 中获取,如果没有则使用默认值 'aifindCow'为第一个默认tab |
|||
const activeTab = ref(sessionStorage.getItem('activeTabAI') || 'AIchat') |
|||
const activeIndex = ref(parseInt(sessionStorage.getItem('activeIndexAI') || '0')) |
|||
|
|||
const tabs = computed(() => [ |
|||
{ |
|||
name: 'AIchat', |
|||
label: 'AI对话' |
|||
}, |
|||
{ |
|||
name: 'AIfind', |
|||
label: '发现' |
|||
} |
|||
]) |
|||
const setActiveTab = (tab, index) => { |
|||
activeTab.value = tab |
|||
activeIndex.value = index // 给索引值 |
|||
// 存储当前选中的 tab 和 index 到 sessionStorage |
|||
sessionStorage.setItem('activeTabAI', tab) |
|||
sessionStorage.setItem('activeIndexAI', index.toString()) |
|||
setHeight(document.getElementById('testId')) // 给父组件发送窗口高度 |
|||
} |
|||
// 跳转 |
|||
const activeComponent = computed(() => { |
|||
return activeTab.value === 'AIchat' ? AIchat : AIfind |
|||
}) |
|||
|
|||
// 获取次数 |
|||
const getCount = () => { |
|||
console.log('获取次数') |
|||
} |
|||
|
|||
// 深度思考 |
|||
const isThinking = ref(false) |
|||
const toggleThink = () => { |
|||
isThinking.value = !isThinking.value |
|||
} |
|||
|
|||
// 发送消息 |
|||
const message = ref(''); |
|||
// 传输对象 |
|||
const messages = ref([]) |
|||
// 信息加载状态 |
|||
const isLoading = ref(false) |
|||
// 在 sendMessage 方法中添加触发逻辑 |
|||
const triggerFetch = ref(false) |
|||
|
|||
const sendMessage = async () => { |
|||
if (!message.value) return |
|||
if (isLoading.value) return |
|||
|
|||
// 添加用户消息 |
|||
messages.value.push({ |
|||
sender: 'user', |
|||
content: message.value, |
|||
timestamp: new Date().toISOString() |
|||
}) |
|||
|
|||
// 重置消息输入框 |
|||
message.value = '' |
|||
|
|||
} |
|||
|
|||
// 公告 |
|||
const showAnnouncement = () => { |
|||
console.log('打开公告') |
|||
} |
|||
// 获取次数 |
|||
const showCount = () => { |
|||
console.log('显示剩余次数') |
|||
} |
|||
|
|||
// 保证发送消息时,滚动屏在底部 |
|||
const tabContent = ref(null) |
|||
watch(messages, async () => { |
|||
await nextTick() |
|||
if (tabContent.value) { |
|||
tabContent.value.scrollTop = tabContent.value.scrollHeight |
|||
} |
|||
}, { deep: true, immediate: true }) |
|||
|
|||
onMounted(() => { |
|||
setHeight(document.getElementById('testId')) // 给父组件发送窗口高度 |
|||
}) |
|||
|
|||
</script> |
|||
|
|||
<template> |
|||
<div class="homepage" id="testId"> |
|||
<el-container> |
|||
<!-- AI小财神头部: logo 次数 公告 --> |
|||
<el-header class="homepage-head"> |
|||
<!-- logo --> |
|||
<div class="homepage-logo"> |
|||
<img src="src\assets\img\homePage\logo.png" alt="图片加载失败" class="logo1"> |
|||
<img src="src\assets\img\homePage\madeInHL.png" alt="图片加载失败" class="logo2"> |
|||
</div> |
|||
|
|||
<div class="homepage-right-group"> |
|||
<div class="count-badge" @click="showCount"> |
|||
<img src="src\assets\img\homePage\get-count-all.png" class="action-btn"> |
|||
<div class="count-number">1000次</div> |
|||
</div> |
|||
<img src="src\assets\img\homePage\announcement.png" class="announcement-btn action-btn" |
|||
@click="showAnnouncement"> |
|||
</div> |
|||
|
|||
</el-header> |
|||
<!-- 主体部分:小人 问题轮询图 对话内容 --> |
|||
<el-main class="homepage-body"> |
|||
<div class="main-wrapper"> |
|||
<section class="tab-section"> |
|||
<div class="tab-container"> |
|||
<div v-for="(tab, index) in tabs" :key="tab.name" @click="setActiveTab(tab.name, index)" |
|||
:class="['tab-item', { active: activeIndex === index }]"> |
|||
<span>{{ tab.label }}</span> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
<div class="tab-content" ref="tabContent"> |
|||
<component :is="activeComponent" :messages="messages" /> |
|||
</div> |
|||
</div> |
|||
</el-main> |
|||
<!-- 尾部: 问题输入框 深度思考 多语言 语音播报 --> |
|||
<el-footer class="homepage-footer"> |
|||
<!-- 第一行按钮 --> |
|||
<div class="footer-first-line"> |
|||
<div class="left-group"> |
|||
<img v-if="isThinking" src="src\assets\img\homePage\tail\think-active.png" @click="toggleThink" |
|||
class="action-btn"> |
|||
<img v-else src="src\assets\img\homePage\tail\think-no-active.png" @click="toggleThink" |
|||
class="action-btn"> |
|||
<img src="src\assets\img\homePage\tail\language.png" @click="changeLanguage" class="action-btn"> |
|||
<img src="src\assets\img\homePage\tail\voice.png" @click="toggleVoice" class="action-btn"> |
|||
</div> |
|||
<img src="src\assets\img\homePage\tail\send.png" @click="sendMessage" class="action-btn send-btn"> |
|||
</div> |
|||
|
|||
<!-- 第二行输入框 --> |
|||
<div class="footer-second-line"> |
|||
<img src="src\assets\img\homePage\tail\msg.png" class="msg-icon"> |
|||
<el-input type="textarea" v-model="message" :autosize="{ minRows: 1, maxRows: 4 }" |
|||
placeholder="给AI小财神发消息..." class="msg-input" |
|||
@keydown.enter.exact.prevent="isLoading ? null : sendMessage()" resize="none"> |
|||
</el-input> |
|||
</div> |
|||
</el-footer> |
|||
</el-container> |
|||
</div> |
|||
</template> |
|||
|
|||
<style scoped> |
|||
/* 标签栏 */ |
|||
.tab-container { |
|||
display: flex; |
|||
gap: 30px; |
|||
margin-bottom: 10px; |
|||
padding: 0 20px; |
|||
justify-content: flex-end; |
|||
/* 新增右对齐 */ |
|||
} |
|||
|
|||
.tab-item { |
|||
cursor: pointer; |
|||
padding: 8px 12px; |
|||
font-size: clamp(18px, 3vw, 20px); |
|||
color: #999; |
|||
transition: all 0.3s; |
|||
border-bottom: 2px solid transparent; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
.tab-item.active { |
|||
color: #000; |
|||
border-color: #000; |
|||
} |
|||
|
|||
.tab-item:not(.active):hover { |
|||
color: #666; |
|||
} |
|||
|
|||
.tab-content { |
|||
overflow-y: auto; |
|||
overflow-x: hidden; |
|||
scroll-behavior: smooth; /* 添加平滑滚动效果 */ |
|||
} |
|||
|
|||
@media (max-width: 768px) { |
|||
.tab-container { |
|||
gap: 15px; |
|||
padding: 0 10px; |
|||
} |
|||
|
|||
.tab-item { |
|||
font-size: clamp(14px, 3vw, 16px); |
|||
padding: 6px 10px; |
|||
} |
|||
} |
|||
</style> |
|||
|
|||
<style scoped> |
|||
.homepage { |
|||
height: 100vh; |
|||
margin: 0 auto; |
|||
background-image: url(src/assets/img/homePage/bk.png); |
|||
background-size: 100% 100%; |
|||
background-repeat: no-repeat; |
|||
background-position: center; |
|||
display: flex; |
|||
} |
|||
|
|||
.homepage .el-container { |
|||
height: 100%; |
|||
flex-direction: column; |
|||
/* 明确纵向排列 */ |
|||
display: flex; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
|
|||
.el-container .el-header { |
|||
height: 10%; |
|||
/* 设置头部高度 */ |
|||
margin-top: 5px; |
|||
} |
|||
|
|||
.el-container .el-main { |
|||
overflow-y: hidden; |
|||
overflow-x: hidden; |
|||
/* 新增滚动条 */ |
|||
flex: 1 1 0%; |
|||
min-height: 0; |
|||
scrollbar-width: thin; |
|||
scrollbar-color: #888 transparent; |
|||
} |
|||
|
|||
.el-container .el-footer { |
|||
/* height: 11%; */ |
|||
height: auto; |
|||
min-height: 70px; |
|||
gap: 5px; |
|||
margin-top: 0; |
|||
} |
|||
|
|||
.homepage-head { |
|||
padding: 0px; |
|||
/* 启用 flex 布局 */ |
|||
display: flex; |
|||
position: relative; |
|||
/* 左右分开布局 */ |
|||
justify-content: space-between; |
|||
} |
|||
|
|||
.homepage-right-group { |
|||
display: flex; |
|||
gap: 8px; |
|||
align-items: center; |
|||
margin-left: auto; |
|||
margin-right: 20px; |
|||
} |
|||
|
|||
.homepage-right-group .action-btn { |
|||
height: 40px; |
|||
} |
|||
|
|||
.homepage-right-group .count-badge { |
|||
position: relative; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.homepage-right-group .count-badge .count-number { |
|||
position: absolute; |
|||
top: 6px; |
|||
right: 29px; |
|||
color: #573dfc; |
|||
font-size: 12px; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
.homepage-right-group .announcement-btn { |
|||
cursor: pointer; |
|||
transition: transform 0.2s; |
|||
} |
|||
|
|||
.homepage-right-group .announcement-btn:hover { |
|||
transform: scale(1.05); |
|||
} |
|||
|
|||
.homepage-body { |
|||
padding: 0px; |
|||
height: calc(100% - 70px); |
|||
/* 根据底部高度调整 */ |
|||
} |
|||
|
|||
.main-wrapper { |
|||
height: 100%; |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.tab-section { |
|||
flex-shrink: 0; |
|||
/* 禁止伸缩 */ |
|||
} |
|||
|
|||
.tab-content { |
|||
flex: 1; |
|||
overflow-y: auto; |
|||
min-height: 0; |
|||
/* 关键:允许内容收缩 */ |
|||
} |
|||
|
|||
.homepage-logo { |
|||
height: 100%; |
|||
/* 改为根据内容自适应 */ |
|||
width: fit-content; |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
/* 固定左间距 */ |
|||
margin-left: 20px; |
|||
margin-right: auto; |
|||
/* 新增相对定位 */ |
|||
position: relative; |
|||
} |
|||
|
|||
/* 新增媒体查询适配小屏幕 */ |
|||
@media (max-width: 768px) { |
|||
.homepage-logo { |
|||
margin-left: 10px; |
|||
left: 0; |
|||
} |
|||
} |
|||
|
|||
.logo1 { |
|||
width: 120px; |
|||
/* 固定 logo1 尺寸 */ |
|||
height: auto; |
|||
margin-bottom: 8px; |
|||
/* 添加间距 */ |
|||
} |
|||
|
|||
.logo2 { |
|||
width: 80px; |
|||
/* 缩小 logo2 尺寸 */ |
|||
height: auto; |
|||
} |
|||
|
|||
|
|||
/* 尾部 */ |
|||
.homepage-footer { |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 5px; |
|||
/* margin-top: auto; */ |
|||
margin-bottom: 20px; |
|||
flex-shrink: 0; |
|||
height: auto; |
|||
} |
|||
|
|||
.footer-first-line { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: auto; |
|||
flex-shrink: 0; |
|||
} |
|||
|
|||
.left-group { |
|||
display: flex; |
|||
gap: 15px; |
|||
} |
|||
|
|||
.action-btn { |
|||
cursor: pointer; |
|||
transition: transform 0.2s; |
|||
height: 28px; |
|||
} |
|||
|
|||
.action-btn:hover { |
|||
transform: scale(1.05); |
|||
} |
|||
|
|||
.send-btn { |
|||
margin-left: auto; |
|||
margin-right: 5px; |
|||
} |
|||
|
|||
.footer-second-line { |
|||
position: relative; |
|||
display: flex; |
|||
height: auto; |
|||
align-items: flex-end; |
|||
flex: 1; |
|||
margin-top: auto; |
|||
min-height: 34px; |
|||
} |
|||
|
|||
.msg-icon { |
|||
position: absolute; |
|||
left: 12px; |
|||
top: 50%; |
|||
transform: translateY(-50%); |
|||
width: 24px; |
|||
z-index: 1; |
|||
} |
|||
|
|||
.msg-input:deep(.el-textarea__inner) { |
|||
border: none !important; |
|||
box-shadow: none !important; |
|||
overflow-y: auto !important; |
|||
/* 强制显示滚动条 */ |
|||
transition: all 0.2s ease-out; |
|||
/* 添加过渡效果 */ |
|||
} |
|||
|
|||
/* .msg-input:deep(.el-textarea__inner:focus) { |
|||
border: none !important; |
|||
box-shadow: 0 4px 12px rgba(89, 24, 241, 0.3) !important; |
|||
} */ |
|||
|
|||
|
|||
.msg-input { |
|||
min-height: 34px; |
|||
max-height: 120px; |
|||
width: calc(100% - 65px); |
|||
|
|||
border-radius: 20px; |
|||
|
|||
padding: 0px 20px 0 45px; |
|||
font-size: 16px; |
|||
transition: height 0.2s ease-out; |
|||
/* 添加高度过渡效果 */ |
|||
overflow-y: hidden; |
|||
/* 隐藏垂直滚动条 */ |
|||
box-shadow: 0 4px 12px rgba(89, 24, 241, 0.3); |
|||
background: #fff; |
|||
|
|||
} |
|||
|
|||
.msg-input:focus { |
|||
outline: none; |
|||
} |
|||
|
|||
@media (max-width: 768px) { |
|||
.action-btn { |
|||
height: 28px; |
|||
} |
|||
|
|||
.footer-second-line { |
|||
height: 50px; |
|||
} |
|||
|
|||
.msg-input { |
|||
font-size: 16px; |
|||
} |
|||
} |
|||
</style> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue