Browse Source

增加历史记录

wangyi/feature-20251022162725-启动页登录注册
wangyetao 4 weeks ago
parent
commit
6eed33e9c2
  1. 252
      pages/deepMate/deepMate.vue

252
pages/deepMate/deepMate.vue

@ -3,23 +3,17 @@
<!-- 顶部导航栏 -->
<view class="header" :style="{ paddingTop: safeAreaInsets?.top + 'px' }">
<view class="header-left">
<image
src="https://d31zlh4on95l9h.cloudfront.net/images/f91e09b5987802185e7679055dafd272.svg"
class="icon"
></image>
<image src="https://d31zlh4on95l9h.cloudfront.net/images/f91e09b5987802185e7679055dafd272.svg" class="icon">
</image>
</view>
<view class="header-center">
<text class="title">DeepMate</text>
</view>
<view class="header-right">
<image
src="https://d31zlh4on95l9h.cloudfront.net/images/d7c4e74201213a25dd9574e908233928.svg"
class="icon"
></image>
<image
src="https://d31zlh4on95l9h.cloudfront.net/images/099903c4aabf5713488b5cb60815e3f7.svg"
class="icon"
></image>
<image src="https://d31zlh4on95l9h.cloudfront.net/images/d7c4e74201213a25dd9574e908233928.svg" class="icon">
</image>
<image src="https://d31zlh4on95l9h.cloudfront.net/images/099903c4aabf5713488b5cb60815e3f7.svg" class="icon"
@click="openHistoryDrawer"></image>
<!-- 新增新会话按钮
<button class="new-chat-button" @click="newChat">
<text class="new-chat-text">新会话</text>
@ -30,11 +24,13 @@
<!-- 主要内容区域 -->
<view class="main-content">
<view class="banner-panel" v-if="messages.length === 0">
<image src="https://d31zlh4on95l9h.cloudfront.net/images/42e18bd7fe97d4f4f37aa70439a0990b.svg" class="pray-banner" ></image>
<image src="https://d31zlh4on95l9h.cloudfront.net/images/42e18bd7fe97d4f4f37aa70439a0990b.svg"
class="pray-banner"></image>
<view class="contain">
<!-- 机器人头像和欢迎语 -->
<view class="robot-container" v-if="messages.length === 0">
<image src="https://d31zlh4on95l9h.cloudfront.net/images/61fa384381c88ad80be28f41827fe0e5.svg" class="robot-avatar"></image>
<image src="https://d31zlh4on95l9h.cloudfront.net/images/61fa384381c88ad80be28f41827fe0e5.svg"
class="robot-avatar"></image>
<view class="welcome-message">
<text class="greeting">Hi, 我是您的股市随身顾问~</text>
<text class="description">个股诊断市场情绪解读都可以找我</text>
@ -57,7 +53,8 @@
<text class="main-question">当前特斯拉该如何布局</text>
<text class="stock-code">TSLA</text>
</view>
<image src="https://d31zlh4on95l9h.cloudfront.net/images/40d94054644f6e3f1c366751f07f0010.svg" class="arrow-icon" @click="goBlank"></image>
<image src="https://d31zlh4on95l9h.cloudfront.net/images/40d94054644f6e3f1c366751f07f0010.svg"
class="arrow-icon" @click="goBlank"></image>
</view>
</view>
</view>
@ -78,35 +75,30 @@
<!-- 聊天区域 -->
<!-- 顶部粘性欢迎块始终保留在聊天上方 -->
<view class="chat-header" v-if="messages.length > 0"> <view class="robot-container"> <image src="https://d31zlh4on95l9h.cloudfront.net/images/61fa384381c88ad80be28f41827fe0e5.svg" class="robot-avatar"></image>
<view class="welcome-message"> <text class="greeting">Hi, 我是您的股市随身顾问~</text> <text class="description">个股诊断市场情绪解读都可以找我</text>
</view> </view>
<view class="chat-header" v-if="messages.length > 0">
<view class="robot-container">
<image src="https://d31zlh4on95l9h.cloudfront.net/images/61fa384381c88ad80be28f41827fe0e5.svg"
class="robot-avatar"></image>
<view class="welcome-message"> <text class="greeting">Hi, 我是您的股市随身顾问~</text> <text
class="description">个股诊断市场情绪解读都可以找我</text>
</view>
</view>
<scroll-view class="chat-container" scroll-y="true" :scroll-top="chatScrollTop" @scroll="onChatScroll" v-if="messages.length > 0">
</view>
<scroll-view class="chat-container" scroll-y="true" :scroll-top="chatScrollTop" @scroll="onChatScroll"
v-if="messages.length > 0">
<view class="message-list" id="messageList">
<view
v-for="(message, index) in messages"
:key="index"
:class="
message.isUser ? 'message user-message' : 'message bot-message'
"
>
<view v-for="(message, index) in messages" :key="index" :class="message.isUser ? 'message user-message' : 'message bot-message'
">
<!-- 会话图标 -->
<text
:class="
message.isUser
<text :class="message.isUser
? 'fa-solid fa-user message-icon'
: 'fa-solid fa-robot message-icon'
"
></text>
"></text>
<!-- 会话内容 -->
<view class="message-content">
<!-- <text class="message-text">{{ message.content }}</text> -->
<!-- loading -->
<view
class="loading-dots"
v-if="message.isThinking || message.isTyping"
>
<view class="loading-dots" v-if="message.isThinking || message.isTyping">
<div class="thinking-process">
<div class="thinking-header">
<div class="thinking-icon"></div>
@ -134,11 +126,8 @@
</div>
</view>
<!-- 使用 rich-text 渲染 Markdown 内容 -->
<rich-text
v-if="!message.isUser"
class="message-text"
:nodes="renderMarkdown(message.content)"
></rich-text>
<rich-text v-if="!message.isUser" class="message-text"
:nodes="renderMarkdown(message.content)"></rich-text>
<text v-else class="message-text">{{ message.content }}</text>
</view>
</view>
@ -149,14 +138,8 @@
<!-- 输入框区域 -->
<view class="input-area">
<view class="input-wrapper">
<input
type="text"
placeholder="请输入股票代码/名称,获取AI洞察"
placeholder-style="color:#fff;opacity:1"
class="input-field"
v-model="inputMessage"
@confirm="sendMessage"
/>
<input type="text" placeholder="请输入股票代码/名称,获取AI洞察" placeholder-style="color:#fff;opacity:1" class="input-field"
v-model="inputMessage" @confirm="sendMessage" />
<image class="send-button" @click="sendMessage" :disabled="isSending">
<!-- <image
src="https://d31zlh4on95l9h.cloudfront.net/images/95f1ea2262e9157db13c93c0dc1c5d96.svg"
@ -164,20 +147,31 @@
></image> -->
</image>
</view>
<text class="disclaimer"
>以上数据由AI生成不作为最终投资建议决策需独立</text
>
<text class="disclaimer">以上数据由AI生成不作为最终投资建议决策需独立</text>
</view>
<image class="back-to-top" src="https://d31zlh4on95l9h.cloudfront.net/images/ba357635d2bb480241952bb1cabacd73.svg"
:style="{ transform: 'translate3d(' + backTopX + 'px,' + backTopY + 'px,0)' }" @touchstart="onBackTopTouchStart"
@touchmove="onBackTopTouchMove" @touchend="onBackTopTouchEnd" @click="onBackTopClick"></image>
<!-- 搜索历史侧拉框 -->
<view class="drawer-overlay" v-show="showHistoryDrawer" @click="closeHistoryDrawer"></view>
<view class="drawer-panel" v-show="showHistoryDrawer" @click.stop>
<view class="drawer-header">
<text class="drawer-title">搜索历史</text>
<text class="drawer-close" @click="closeHistoryDrawer">×</text>
</view>
<scroll-view scroll-y="true" class="drawer-content">
<view v-if="searchHistory.length === 0" class="empty-history">
<text>暂无搜索历史</text>
</view>
<view v-for="(item, idx) in searchHistory" :key="idx" class="history-card">
<text class="history-query">{{ item.query }}</text>
<text class="history-time">{{ formatTime(item.time) }}</text>
</view>
</scroll-view>
</view>
<image
class="back-to-top"
src="https://d31zlh4on95l9h.cloudfront.net/images/ba357635d2bb480241952bb1cabacd73.svg"
:style="{ transform: 'translate3d(' + backTopX + 'px,' + backTopY + 'px,0)' }"
@touchstart="onBackTopTouchStart"
@touchmove="onBackTopTouchMove"
@touchend="onBackTopTouchEnd"
@click="onBackTopClick"
></image>
<footerBar class="static-footer" :type="type"></footerBar>
</view>
</template>
@ -215,6 +209,8 @@ const chatScrollTop = ref(0);
const chatContainerHeight = ref(0);
const uuid = ref("");
const messages = ref([]);
const showHistoryDrawer = ref(false);
const searchHistory = ref([]);
const hotTopics = ref([
{
id: 1,
@ -255,10 +251,16 @@ onMounted(() => {
backTopY.value = initY;
initUUID();
if (messages.value.length === 0) {
nextTick(startTabsMarquee);
}
if (messages.value.length > 0) {
nextTick(() => { measureChatContainer(); scrollToBottom(); });
}
//
const hist = uni.getStorageSync("search_history") || [];
searchHistory.value = Array.isArray(hist) ? hist : [];
});
// UUID
@ -303,6 +305,22 @@ const goBlank = () => {
});
};
//
const openHistoryDrawer = () => { showHistoryDrawer.value = true; };
const closeHistoryDrawer = () => { showHistoryDrawer.value = false; };
// YYYY-MM-DD HH:mm
const pad2 = (n) => (n < 10 ? '0' + n : '' + n);
const formatTime = (t) => {
const d = new Date(t);
const y = d.getFullYear();
const m = pad2(d.getMonth() + 1);
const day = pad2(d.getDate());
const hh = pad2(d.getHours());
const mm = pad2(d.getMinutes());
return `${y}-${m}-${day} ${hh}:${mm}`;
};
//
const sendMessage = () => {
if (inputMessage.value.trim() === "" || isSending.value) return;
@ -317,6 +335,11 @@ const sendMessage = () => {
messages.value.push(userMessage);
inputMessage.value = "";
//
const entry = { query: userMessage.content, time: Date.now() };
searchHistory.value.unshift(entry);
uni.setStorageSync('search_history', searchHistory.value);
//
shouldAutoScroll.value = true;
nextTick(() => { forceScrollToBottom(); });
@ -545,10 +568,15 @@ const onBackTopClick = () => {
.deepMate-page {
display: flex;
flex-direction: column;
position: fixed; /* 充满视口,彻底禁用页面滚动 */
top: 0; left: 0; right: 0; bottom: 0;
position: fixed;
/* 充满视口,彻底禁用页面滚动 */
top: 0;
left: 0;
right: 0;
bottom: 0;
height: 100vh;
overflow: hidden; /* 锁定页面滚动 */
overflow: hidden;
/* 锁定页面滚动 */
background-color: #ffffff;
padding: 20rpx 0rpx;
}
@ -598,7 +626,8 @@ const onBackTopClick = () => {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden; /* 内部滚动交给聊天容器 */
overflow: hidden;
/* 内部滚动交给聊天容器 */
padding: 20rpx;
margin-top: 1rpx;
margin-bottom: 120rpx;
@ -607,7 +636,8 @@ const onBackTopClick = () => {
/* 聊天顶部粘性欢迎块样式 */
.chat-header {
position: sticky;
top: 0; /* 在页面滚动时始终贴顶 */
top: 0;
/* 在页面滚动时始终贴顶 */
z-index: 50;
background-color: #ffffff;
padding: 3rpx 20rpx;
@ -784,7 +814,8 @@ const onBackTopClick = () => {
.chat-container {
margin-top: 30rpx;
border-radius: 10rpx;
height: 65%; /* 缩短滚动区域,避免被输入框覆盖 */
height: 65%;
/* 缩短滚动区域,避免被输入框覆盖 */
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
@ -891,11 +922,13 @@ const onBackTopClick = () => {
}
@keyframes loading {
0%,
80%,
100% {
transform: scale(0);
}
40% {
transform: scale(1);
}
@ -948,6 +981,7 @@ const onBackTopClick = () => {
color: #ffffff !important;
opacity: 1;
}
/* .uni-scroll-view{
height: 92%;
} */
@ -975,12 +1009,16 @@ const onBackTopClick = () => {
justify-content: center;
margin-top: 15rpx;
}
.banner-panel {
position: relative;
height: 480rpx; /* 拉长容器,灰色背景跟随变高 */
overflow: hidden; /* 让圆角和内部层剪裁一致 */
height: 480rpx;
/* 拉长容器,灰色背景跟随变高 */
overflow: hidden;
/* 让圆角和内部层剪裁一致 */
border-radius: 15rpx;
}
.panelShow {
height: 12%;
}
@ -988,11 +1026,13 @@ const onBackTopClick = () => {
.pray-banner {
position: absolute;
/* background-size: 100% 100%; */
inset: 0; /* 顶部、底部、左、右都贴合容器 */
inset: 0;
/* 顶部、底部、左、右都贴合容器 */
width: 100%;
height: 81%;
border-radius: 15rpx;
z-index: 1; /* 在灰底之上、内容之下 */
z-index: 1;
/* 在灰底之上、内容之下 */
}
@ -1000,12 +1040,14 @@ const onBackTopClick = () => {
margin: 0 20rpx;
gap: 5rpx;
}
.banner-panel .robot-container,
.banner-panel .function-tabs,
.banner-panel .recommend-card {
position: relative;
z-index: 2;
}
.back-to-top {
position: fixed;
left: 0;
@ -1014,13 +1056,81 @@ const onBackTopClick = () => {
height: 100rpx;
z-index: 1000;
}
.back-to-top:active {
transform: scale(0.96);
}
.static-footer {
position: fixed;
bottom: 0;
}
/* 搜索历史侧拉框样式 */
.drawer-overlay {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.35);
z-index: 900;
}
.drawer-panel {
position: fixed;
top: 0;
right: 0;
bottom: 0;
width: 600rpx;
max-width: 75%;
background: #ffffff;
box-shadow: -8rpx 0 20rpx rgba(0, 0, 0, 0.08);
z-index: 901;
display: flex;
flex-direction: column;
}
.drawer-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx 28rpx;
border-bottom: 2rpx solid #f0f0f0;
}
.drawer-title {
font-size: 32rpx;
font-weight: 600;
color: #333333;
}
.drawer-close {
font-size: 42rpx;
color: #999999;
}
.drawer-content {
flex: 1;
padding: 20rpx 24rpx;
}
.history-card {
background-color: #fff;
border: 2rpx solid #f2f2f2;
border-left: 8rpx solid rgb(220, 31, 29);
border-radius: 12rpx;
padding: 18rpx 20rpx;
margin-bottom: 16rpx;
box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.03);
}
.history-query {
font-size: 28rpx;
color: #333333;
font-weight: 500; }
.history-time { margin-top: 8rpx; font-size: 22rpx; color: #888888; }
.empty-history { padding: 40rpx; color: #999999; text-align: center; }
.thinking-process {
margin: 20rpx 0;
border: 2rpx solid #e5e5e5;

Loading…
Cancel
Save