|
|
@ -3,18 +3,29 @@ |
|
|
<!-- 顶部导航栏 --> |
|
|
<!-- 顶部导航栏 --> |
|
|
<view class="header" :style="{ paddingTop: safeAreaInsets?.top + 'px' }"> |
|
|
<view class="header" :style="{ paddingTop: safeAreaInsets?.top + 'px' }"> |
|
|
<view class="header-left"> |
|
|
<view class="header-left"> |
|
|
<image src="https://d31zlh4on95l9h.cloudfront.net/images/f91e09b5987802185e7679055dafd272.svg" class="icon"> |
|
|
|
|
|
|
|
|
<image |
|
|
|
|
|
src="https://d31zlh4on95l9h.cloudfront.net/images/f91e09b5987802185e7679055dafd272.svg" |
|
|
|
|
|
class="icon" |
|
|
|
|
|
> |
|
|
</image> |
|
|
</image> |
|
|
</view> |
|
|
</view> |
|
|
<view class="header-center"> |
|
|
<view class="header-center"> |
|
|
<text class="title" :style="{ paddingTop: safeAreaInsets?.top + 'px' }">DeepMate</text> |
|
|
|
|
|
|
|
|
<text class="title" :style="{ paddingTop: safeAreaInsets?.top + 'px' }" |
|
|
|
|
|
>DeepMate</text |
|
|
|
|
|
> |
|
|
</view> |
|
|
</view> |
|
|
<view class="header-right"> |
|
|
<view class="header-right"> |
|
|
<image src="https://d31zlh4on95l9h.cloudfront.net/images/d7c4e74201213a25dd9574e908233928.svg" class="icon"> |
|
|
|
|
|
|
|
|
<image |
|
|
|
|
|
src="https://d31zlh4on95l9h.cloudfront.net/images/d7c4e74201213a25dd9574e908233928.svg" |
|
|
|
|
|
class="icon" |
|
|
|
|
|
> |
|
|
</image> |
|
|
</image> |
|
|
<image style="margin-left: 10px" |
|
|
|
|
|
src="https://d31zlh4on95l9h.cloudfront.net/images/099903c4aabf5713488b5cb60815e3f7.svg" class="icon" |
|
|
|
|
|
@click="openHistoryDrawer"></image> |
|
|
|
|
|
|
|
|
<image |
|
|
|
|
|
style="margin-left: 10px" |
|
|
|
|
|
src="https://d31zlh4on95l9h.cloudfront.net/images/099903c4aabf5713488b5cb60815e3f7.svg" |
|
|
|
|
|
class="icon" |
|
|
|
|
|
@click="openHistoryDrawer" |
|
|
|
|
|
></image> |
|
|
<!-- 新增新会话按钮 |
|
|
<!-- 新增新会话按钮 |
|
|
<button class="new-chat-button" @click="newChat"> |
|
|
<button class="new-chat-button" @click="newChat"> |
|
|
<text class="new-chat-text">新会话</text> |
|
|
<text class="new-chat-text">新会话</text> |
|
|
@ -25,15 +36,19 @@ |
|
|
<!-- 主要内容区域 --> |
|
|
<!-- 主要内容区域 --> |
|
|
<view class="main-content"> |
|
|
<view class="main-content"> |
|
|
<view class="banner-panel" v-if="messages.length === 0"> |
|
|
<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="contain"> |
|
|
<!-- 机器人头像和欢迎语 --> |
|
|
<!-- 机器人头像和欢迎语 --> |
|
|
<view class="robot-container" v-if="messages.length === 0"> |
|
|
<view class="robot-container" v-if="messages.length === 0"> |
|
|
<image src="/static/icons/机器人.svg" class="robot-avatar"></image> |
|
|
<image src="/static/icons/机器人.svg" class="robot-avatar"></image> |
|
|
<view class="welcome-message"> |
|
|
<view class="welcome-message"> |
|
|
<text class="greeting">Hi, 我是您的股市随身顾问~</text> |
|
|
<text class="greeting">Hi, 我是您的股市随身顾问~</text> |
|
|
<text class="description">个股诊断、市场情绪解读,都可以找我。</text> |
|
|
|
|
|
|
|
|
<text class="description" |
|
|
|
|
|
>个股诊断、市场情绪解读,都可以找我。</text |
|
|
|
|
|
> |
|
|
</view> |
|
|
</view> |
|
|
</view> |
|
|
</view> |
|
|
|
|
|
|
|
|
@ -45,19 +60,28 @@ |
|
|
</view> --> |
|
|
</view> --> |
|
|
|
|
|
|
|
|
<!-- 特斯拉推荐卡片 --> |
|
|
<!-- 特斯拉推荐卡片 --> |
|
|
<view class="recommend-card" v-if="messages.length === 0" @click="goBlank"> |
|
|
|
|
|
|
|
|
<view |
|
|
|
|
|
class="recommend-card" |
|
|
|
|
|
v-if="messages.length === 0" |
|
|
|
|
|
@click="goBlank" |
|
|
|
|
|
> |
|
|
<view class="arrow" v-if="messages.length === 0"></view> |
|
|
<view class="arrow" v-if="messages.length === 0"></view> |
|
|
<view class="card-content"> |
|
|
<view class="card-content"> |
|
|
<image src="../../static/images/tesla-logo.png" class="logo"></image> |
|
|
|
|
|
|
|
|
<image |
|
|
|
|
|
src="../../static/images/tesla-logo.png" |
|
|
|
|
|
class="logo" |
|
|
|
|
|
></image> |
|
|
<view class="card-text"> |
|
|
<view class="card-text"> |
|
|
<text class="main-question">当前特斯拉该如何布局?</text> |
|
|
<text class="main-question">当前特斯拉该如何布局?</text> |
|
|
<text class="stock-code">TSLA</text> |
|
|
<text class="stock-code">TSLA</text> |
|
|
</view> |
|
|
</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> |
|
|
</view> |
|
|
|
|
|
|
|
|
</view> |
|
|
</view> |
|
|
</view> |
|
|
</view> |
|
|
|
|
|
|
|
|
@ -73,33 +97,50 @@ |
|
|
</view> |
|
|
</view> |
|
|
</view> |
|
|
</view> |
|
|
</view> --> |
|
|
</view> --> |
|
|
<view v-if="messages.length === 0" class="welcome-section"> |
|
|
|
|
|
</view> |
|
|
|
|
|
|
|
|
<view v-if="messages.length === 0" class="welcome-section"> </view> |
|
|
|
|
|
|
|
|
<!-- 聊天区域 --> |
|
|
<!-- 聊天区域 --> |
|
|
<!-- 顶部粘性欢迎块:始终保留在聊天上方 --> |
|
|
<!-- 顶部粘性欢迎块:始终保留在聊天上方 --> |
|
|
<view class="chat-header" v-if="messages.length > 0"> |
|
|
<view class="chat-header" v-if="messages.length > 0"> |
|
|
<view class="robot-container"> |
|
|
<view class="robot-container"> |
|
|
<image src="/static/icons/机器人.svg" class="robot-avatar"></image> |
|
|
<image src="/static/icons/机器人.svg" class="robot-avatar"></image> |
|
|
<view class="welcome-message"> <text class="greeting">Hi, 我是您的股市随身顾问~</text> |
|
|
|
|
|
|
|
|
<view class="welcome-message"> |
|
|
|
|
|
<text class="greeting">Hi, 我是您的股市随身顾问~</text> |
|
|
</view> |
|
|
</view> |
|
|
</view> |
|
|
</view> |
|
|
</view> |
|
|
</view> |
|
|
<scroll-view class="chat-container" scroll-y="true" :scroll-top="chatScrollTop" |
|
|
|
|
|
:scroll-with-animation="!isSending" @scroll="onChatScroll" v-if="messages.length > 0"> |
|
|
|
|
|
|
|
|
<scroll-view |
|
|
|
|
|
class="chat-container" |
|
|
|
|
|
scroll-y="true" |
|
|
|
|
|
:scroll-top="chatScrollTop" |
|
|
|
|
|
:scroll-with-animation="!isSending" |
|
|
|
|
|
@scroll="onChatScroll" |
|
|
|
|
|
v-if="messages.length > 0" |
|
|
|
|
|
> |
|
|
<view class="message-list" id="messageList"> |
|
|
<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-user message-icon' |
|
|
: 'fa-solid fa-robot message-icon' |
|
|
: 'fa-solid fa-robot message-icon' |
|
|
"></text> |
|
|
|
|
|
|
|
|
" |
|
|
|
|
|
></text> |
|
|
<!-- 会话内容 --> |
|
|
<!-- 会话内容 --> |
|
|
<view class="message-content"> |
|
|
<view class="message-content"> |
|
|
<!-- <text class="message-text">{{ message.content }}</text> --> |
|
|
<!-- <text class="message-text">{{ message.content }}</text> --> |
|
|
<!-- loading --> |
|
|
<!-- loading --> |
|
|
<view class="loading-dots" v-if="message.isThinking || !message.isUser"> |
|
|
|
|
|
|
|
|
<view |
|
|
|
|
|
class="loading-dots" |
|
|
|
|
|
v-if="message.isThinking || !message.isUser" |
|
|
|
|
|
> |
|
|
<view class="thinking-process"> |
|
|
<view class="thinking-process"> |
|
|
<view class="thinking-header"> |
|
|
<view class="thinking-header"> |
|
|
<view class="thinking-icon">∞</view> |
|
|
<view class="thinking-icon">∞</view> |
|
|
@ -107,7 +148,10 @@ |
|
|
message.isTyping ? "正在思考" : "思考完成" |
|
|
message.isTyping ? "正在思考" : "思考完成" |
|
|
}}</view> |
|
|
}}</view> |
|
|
<view class="thinking-count"> </view> |
|
|
<view class="thinking-count"> </view> |
|
|
<view class="thinking-toggle" @click="message.isThinking = !message.isThinking"> |
|
|
|
|
|
|
|
|
<view |
|
|
|
|
|
class="thinking-toggle" |
|
|
|
|
|
@click="message.isThinking = !message.isThinking" |
|
|
|
|
|
> |
|
|
<span v-if="message.isThinking">↑</span> |
|
|
<span v-if="message.isThinking">↑</span> |
|
|
<span v-else>↓</span> |
|
|
<span v-else>↓</span> |
|
|
</view> |
|
|
</view> |
|
|
@ -129,8 +173,11 @@ |
|
|
</view> |
|
|
</view> |
|
|
</view> |
|
|
</view> |
|
|
<!-- 使用 rich-text 渲染 Markdown 内容 --> |
|
|
<!-- 使用 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> |
|
|
<text v-else class="message-text">{{ message.content }}</text> |
|
|
</view> |
|
|
</view> |
|
|
</view> |
|
|
</view> |
|
|
@ -141,8 +188,14 @@ |
|
|
<!-- 输入框区域 --> |
|
|
<!-- 输入框区域 --> |
|
|
<view class="input-area"> |
|
|
<view class="input-area"> |
|
|
<view class="input-wrapper"> |
|
|
<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 class="send-button" @click="sendMessage" :disabled="isSending"> |
|
|
<!-- <image |
|
|
<!-- <image |
|
|
src="https://d31zlh4on95l9h.cloudfront.net/images/95f1ea2262e9157db13c93c0dc1c5d96.svg" |
|
|
src="https://d31zlh4on95l9h.cloudfront.net/images/95f1ea2262e9157db13c93c0dc1c5d96.svg" |
|
|
@ -150,18 +203,26 @@ |
|
|
></image> --> |
|
|
></image> --> |
|
|
</image> |
|
|
</image> |
|
|
</view> |
|
|
</view> |
|
|
<text class="disclaimer">以上数据由AI生成,不作为最终投资建议,决策需独立!</text> |
|
|
|
|
|
|
|
|
<text class="disclaimer" |
|
|
|
|
|
>以上数据由AI生成,不作为最终投资建议,决策需独立!</text |
|
|
|
|
|
> |
|
|
</view> |
|
|
</view> |
|
|
|
|
|
|
|
|
<image class="back-to-top" src="https://d31zlh4on95l9h.cloudfront.net/images/ba357635d2bb480241952bb1cabacd73.svg" |
|
|
|
|
|
|
|
|
<image |
|
|
|
|
|
class="back-to-top" |
|
|
|
|
|
src="https://d31zlh4on95l9h.cloudfront.net/images/ba357635d2bb480241952bb1cabacd73.svg" |
|
|
:style="{ |
|
|
:style="{ |
|
|
transform: 'translate3d(' + backTopX + 'px,' + backTopY + 'px,0)', |
|
|
transform: 'translate3d(' + backTopX + 'px,' + backTopY + 'px,0)', |
|
|
}" @touchstart="onBackTopTouchStart" @touchmove="onBackTopTouchMove" @touchend="onBackTopTouchEnd" |
|
|
|
|
|
@click="onBackTopClick"></image> |
|
|
|
|
|
|
|
|
}" |
|
|
|
|
|
@touchstart="onBackTopTouchStart" |
|
|
|
|
|
@touchmove="onBackTopTouchMove" |
|
|
|
|
|
@touchend="onBackTopTouchEnd" |
|
|
|
|
|
@click="onBackTopClick" |
|
|
|
|
|
></image> |
|
|
|
|
|
|
|
|
<!-- 搜索历史侧拉框 --> |
|
|
<!-- 搜索历史侧拉框 --> |
|
|
<view class="drawer-overlay" v-show="showHistoryDrawer"></view> |
|
|
<view class="drawer-overlay" v-show="showHistoryDrawer"></view> |
|
|
<view class="drawer-panel" v-show="showHistoryDrawer" @click.stop @touchmove.stop.prevent |
|
|
|
|
|
|
|
|
<!-- <view class="drawer-panel" v-show="showHistoryDrawer" @click.stop @touchmove.stop.prevent |
|
|
:style="{ transform: 'translateY(' + drawerOffsetY + 'px)' }"> |
|
|
:style="{ transform: 'translateY(' + drawerOffsetY + 'px)' }"> |
|
|
|
|
|
|
|
|
<view class="drawer-header"> |
|
|
<view class="drawer-header"> |
|
|
@ -193,6 +254,59 @@ |
|
|
</view> |
|
|
</view> |
|
|
</view> |
|
|
</view> |
|
|
</scroll-view> |
|
|
</scroll-view> |
|
|
|
|
|
</view> --> |
|
|
|
|
|
<view |
|
|
|
|
|
class="drawer-panel" |
|
|
|
|
|
v-show="showHistoryDrawer" |
|
|
|
|
|
@click.stop |
|
|
|
|
|
@touchmove.stop.prevent |
|
|
|
|
|
:style="{ transform: 'translateY(' + drawerOffsetY + 'px)' }" |
|
|
|
|
|
> |
|
|
|
|
|
<view class="drawer-header"> |
|
|
|
|
|
<text class="drawer-title">历史对话</text> |
|
|
|
|
|
<view class="drawer-actions"> |
|
|
|
|
|
<view class="delete-all-container"> |
|
|
|
|
|
<image |
|
|
|
|
|
class="delete-icon" |
|
|
|
|
|
src="/static/icons/Group_48095481.svg" |
|
|
|
|
|
></image> |
|
|
|
|
|
<text class="delete-all" @click="clearAllHistory">删除全部</text> |
|
|
|
|
|
</view> |
|
|
|
|
|
<view class="drawer-close" @click="onDrawerBackClick" |
|
|
|
|
|
><text class="drawer-close-icon"></text |
|
|
|
|
|
></view> |
|
|
|
|
|
</view> |
|
|
|
|
|
</view> |
|
|
|
|
|
<scroll-view scroll-y="true" class="drawer-content"> |
|
|
|
|
|
<view class="drawer-inner"> |
|
|
|
|
|
<view v-if="historyList.length === 0" class="empty-history"> |
|
|
|
|
|
<text>暂无历史记录</text> |
|
|
|
|
|
</view> |
|
|
|
|
|
<view |
|
|
|
|
|
v-for="(section, sIdx) in historyList" |
|
|
|
|
|
:key="sIdx" |
|
|
|
|
|
class="history-section" |
|
|
|
|
|
> |
|
|
|
|
|
<text class="section-title">{{ section.title }}</text> |
|
|
|
|
|
<view |
|
|
|
|
|
v-for="(item, idx) in section.items" |
|
|
|
|
|
:key="idx" |
|
|
|
|
|
class="history-item" |
|
|
|
|
|
> |
|
|
|
|
|
<view class="history-left"> |
|
|
|
|
|
<view class="flag-circle" |
|
|
|
|
|
><text class="flag-emoji">🇺🇸</text></view |
|
|
|
|
|
> |
|
|
|
|
|
</view> |
|
|
|
|
|
<view class="history-main" @click="itemClick(item)"> |
|
|
|
|
|
<text class="history-query">{{ item.stockName }}</text> |
|
|
|
|
|
<text class="history-query">({{ item.stockCode }})</text> |
|
|
|
|
|
</view> |
|
|
|
|
|
<text class="history-time">{{ item.createdTime }}</text> |
|
|
|
|
|
</view> |
|
|
|
|
|
</view> |
|
|
|
|
|
</view> |
|
|
|
|
|
</scroll-view> |
|
|
</view> |
|
|
</view> |
|
|
|
|
|
|
|
|
<footerBar class="static-footer" :type="type"></footerBar> |
|
|
<footerBar class="static-footer" :type="type"></footerBar> |
|
|
@ -206,7 +320,12 @@ import { ref, computed, onMounted, onUnmounted, watch, nextTick } from "vue"; |
|
|
import footerBar from "../../components/footerBar"; |
|
|
import footerBar from "../../components/footerBar"; |
|
|
import marked from "marked"; // 引入 marked 库 |
|
|
import marked from "marked"; // 引入 marked 库 |
|
|
import { onPageScroll } from "@dcloudio/uni-app"; |
|
|
import { onPageScroll } from "@dcloudio/uni-app"; |
|
|
import { postStock, postIntent } from "../../api/deepMate/deepMate"; |
|
|
|
|
|
|
|
|
import { |
|
|
|
|
|
postStock, |
|
|
|
|
|
postIntent, |
|
|
|
|
|
postHistory, |
|
|
|
|
|
postHistoryDetail, |
|
|
|
|
|
} from "../../api/deepMate/deepMate"; |
|
|
// 设置 marked 选项 |
|
|
// 设置 marked 选项 |
|
|
marked.setOptions({ |
|
|
marked.setOptions({ |
|
|
renderer: new marked.Renderer(), |
|
|
renderer: new marked.Renderer(), |
|
|
@ -237,6 +356,7 @@ const messages = ref([]); |
|
|
const showHistoryDrawer = ref(false); |
|
|
const showHistoryDrawer = ref(false); |
|
|
const drawerOffsetY = ref(0); |
|
|
const drawerOffsetY = ref(0); |
|
|
const searchHistory = ref([]); |
|
|
const searchHistory = ref([]); |
|
|
|
|
|
const historyList = ref([]); |
|
|
const hotTopics = ref([ |
|
|
const hotTopics = ref([ |
|
|
{ |
|
|
{ |
|
|
id: 1, |
|
|
id: 1, |
|
|
@ -289,7 +409,7 @@ onMounted(() => { |
|
|
|
|
|
|
|
|
// 缓存今天日期(YYYY-MM-DD) |
|
|
// 缓存今天日期(YYYY-MM-DD) |
|
|
const todayStr = new Date().toISOString().slice(0, 10); |
|
|
const todayStr = new Date().toISOString().slice(0, 10); |
|
|
uni.setStorageSync('today_date', todayStr); |
|
|
|
|
|
|
|
|
uni.setStorageSync("today_date", todayStr); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// 初始化 UUID |
|
|
// 初始化 UUID |
|
|
@ -336,12 +456,26 @@ const goBlank = () => { |
|
|
|
|
|
|
|
|
// 历史抽屉控制 |
|
|
// 历史抽屉控制 |
|
|
const openHistoryDrawer = () => { |
|
|
const openHistoryDrawer = () => { |
|
|
|
|
|
const res = postHistory({ |
|
|
|
|
|
model: 5, |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
if (res.code === 200) { |
|
|
|
|
|
historyList.value = res.data; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
console.log("historyList.value", historyList.value); |
|
|
|
|
|
|
|
|
const hideDistance = uni.upx2px(900); |
|
|
const hideDistance = uni.upx2px(900); |
|
|
drawerOffsetY.value = hideDistance; |
|
|
drawerOffsetY.value = hideDistance; |
|
|
showHistoryDrawer.value = true; |
|
|
showHistoryDrawer.value = true; |
|
|
setTimeout(() => { drawerOffsetY.value = 0; }, 10); |
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
|
|
|
drawerOffsetY.value = 0; |
|
|
|
|
|
}, 10); |
|
|
|
|
|
}; |
|
|
|
|
|
const closeHistoryDrawer = () => { |
|
|
|
|
|
showHistoryDrawer.value = false; |
|
|
}; |
|
|
}; |
|
|
const closeHistoryDrawer = () => { showHistoryDrawer.value = false; }; |
|
|
|
|
|
const onDrawerBackClick = () => { |
|
|
const onDrawerBackClick = () => { |
|
|
const hideDistance = uni.upx2px(900); |
|
|
const hideDistance = uni.upx2px(900); |
|
|
drawerOffsetY.value = hideDistance; |
|
|
drawerOffsetY.value = hideDistance; |
|
|
@ -363,65 +497,65 @@ const formatTime = (t) => { |
|
|
return `${y}-${m}-${day} ${hh}:${mm}`; |
|
|
return `${y}-${m}-${day} ${hh}:${mm}`; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
// 历史分组(今天/昨天/近一周/按月) |
|
|
|
|
|
const groupedHistory = computed(() => { |
|
|
|
|
|
const sections = []; |
|
|
|
|
|
|
|
|
|
|
|
// 从缓存获取今天日期,如果没有则使用当前日期 |
|
|
|
|
|
const cachedTodayStr = uni.getStorageSync('today_date'); |
|
|
|
|
|
const now = cachedTodayStr ? new Date(cachedTodayStr + 'T00:00:00') : new Date(); |
|
|
|
|
|
|
|
|
|
|
|
const startOfDay = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate()); |
|
|
|
|
|
const isSameDay = (a, b) => startOfDay(a).getTime() === startOfDay(b).getTime(); |
|
|
|
|
|
const isYesterday = (d) => { |
|
|
|
|
|
const y = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1); |
|
|
|
|
|
return isSameDay(d, y); |
|
|
|
|
|
}; |
|
|
|
|
|
const isToday = (d) => isSameDay(d, now); |
|
|
|
|
|
const withinLast7Days = (d) => { |
|
|
|
|
|
const seven = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 7); |
|
|
|
|
|
return d >= seven && !isToday(d) && !isYesterday(d); |
|
|
|
|
|
}; |
|
|
|
|
|
const monthLabel = (d) => `${d.getMonth() + 1}月`; |
|
|
|
|
|
|
|
|
|
|
|
const today = []; |
|
|
|
|
|
const yesterday = []; |
|
|
|
|
|
const last7 = []; |
|
|
|
|
|
const byMonth = new Map(); |
|
|
|
|
|
|
|
|
|
|
|
searchHistory.value.forEach((item) => { |
|
|
|
|
|
const dt = new Date(item.time); |
|
|
|
|
|
if (isToday(dt)) { |
|
|
|
|
|
today.push(item); |
|
|
|
|
|
} else if (isYesterday(dt)) { |
|
|
|
|
|
yesterday.push(item); |
|
|
|
|
|
} else if (withinLast7Days(dt)) { |
|
|
|
|
|
last7.push(item); |
|
|
|
|
|
} else { |
|
|
|
|
|
const year = dt.getFullYear(); |
|
|
|
|
|
const month = dt.getMonth() + 1; |
|
|
|
|
|
const key = `${year}-${month}`; |
|
|
|
|
|
if (!byMonth.has(key)) byMonth.set(key, { title: `${month}月`, year, month, items: [] }); |
|
|
|
|
|
byMonth.get(key).items.push(item); |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
if (today.length) sections.push({ title: '今天', items: today }); |
|
|
|
|
|
if (yesterday.length) sections.push({ title: '昨天', items: yesterday }); |
|
|
|
|
|
if (last7.length) sections.push({ title: '近一周', items: last7 }); |
|
|
|
|
|
|
|
|
|
|
|
const monthSections = Array.from(byMonth.values()).sort((a, b) => { |
|
|
|
|
|
if (a.year !== b.year) return b.year - a.year; |
|
|
|
|
|
return b.month - a.month; // 月份倒序,如 10月 在 9月 之前 |
|
|
|
|
|
}); |
|
|
|
|
|
sections.push(...monthSections); |
|
|
|
|
|
|
|
|
|
|
|
return sections; |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
// // 历史分组(今天/昨天/近一周/按月) |
|
|
|
|
|
// const groupedHistory = computed(() => { |
|
|
|
|
|
// const sections = []; |
|
|
|
|
|
|
|
|
|
|
|
// // 从缓存获取今天日期,如果没有则使用当前日期 |
|
|
|
|
|
// const cachedTodayStr = uni.getStorageSync('today_date'); |
|
|
|
|
|
// const now = cachedTodayStr ? new Date(cachedTodayStr + 'T00:00:00') : new Date(); |
|
|
|
|
|
|
|
|
|
|
|
// const startOfDay = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate()); |
|
|
|
|
|
// const isSameDay = (a, b) => startOfDay(a).getTime() === startOfDay(b).getTime(); |
|
|
|
|
|
// const isYesterday = (d) => { |
|
|
|
|
|
// const y = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1); |
|
|
|
|
|
// return isSameDay(d, y); |
|
|
|
|
|
// }; |
|
|
|
|
|
// const isToday = (d) => isSameDay(d, now); |
|
|
|
|
|
// const withinLast7Days = (d) => { |
|
|
|
|
|
// const seven = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 7); |
|
|
|
|
|
// return d >= seven && !isToday(d) && !isYesterday(d); |
|
|
|
|
|
// }; |
|
|
|
|
|
// const monthLabel = (d) => `${d.getMonth() + 1}月`; |
|
|
|
|
|
|
|
|
|
|
|
// const today = []; |
|
|
|
|
|
// const yesterday = []; |
|
|
|
|
|
// const last7 = []; |
|
|
|
|
|
// const byMonth = new Map(); |
|
|
|
|
|
|
|
|
|
|
|
// searchHistory.value.forEach((item) => { |
|
|
|
|
|
// const dt = new Date(item.time); |
|
|
|
|
|
// if (isToday(dt)) { |
|
|
|
|
|
// today.push(item); |
|
|
|
|
|
// } else if (isYesterday(dt)) { |
|
|
|
|
|
// yesterday.push(item); |
|
|
|
|
|
// } else if (withinLast7Days(dt)) { |
|
|
|
|
|
// last7.push(item); |
|
|
|
|
|
// } else { |
|
|
|
|
|
// const year = dt.getFullYear(); |
|
|
|
|
|
// const month = dt.getMonth() + 1; |
|
|
|
|
|
// const key = `${year}-${month}`; |
|
|
|
|
|
// if (!byMonth.has(key)) byMonth.set(key, { title: `${month}月`, year, month, items: [] }); |
|
|
|
|
|
// byMonth.get(key).items.push(item); |
|
|
|
|
|
// } |
|
|
|
|
|
// }); |
|
|
|
|
|
|
|
|
|
|
|
// if (today.length) sections.push({ title: '今天', items: today }); |
|
|
|
|
|
// if (yesterday.length) sections.push({ title: '昨天', items: yesterday }); |
|
|
|
|
|
// if (last7.length) sections.push({ title: '近一周', items: last7 }); |
|
|
|
|
|
|
|
|
|
|
|
// const monthSections = Array.from(byMonth.values()).sort((a, b) => { |
|
|
|
|
|
// if (a.year !== b.year) return b.year - a.year; |
|
|
|
|
|
// return b.month - a.month; // 月份倒序,如 10月 在 9月 之前 |
|
|
|
|
|
// }); |
|
|
|
|
|
// sections.push(...monthSections); |
|
|
|
|
|
|
|
|
|
|
|
// return sections; |
|
|
|
|
|
// }); |
|
|
|
|
|
|
|
|
const clearAllHistory = () => { |
|
|
const clearAllHistory = () => { |
|
|
searchHistory.value = []; |
|
|
searchHistory.value = []; |
|
|
uni.setStorageSync('search_history', []); |
|
|
|
|
|
|
|
|
uni.setStorageSync("search_history", []); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
// 发送消息 |
|
|
// 发送消息 |
|
|
@ -465,15 +599,15 @@ const simulateBotResponse = async (userMessage) => { |
|
|
|
|
|
|
|
|
messages.value.push(botMsg); |
|
|
messages.value.push(botMsg); |
|
|
|
|
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 2000)); |
|
|
|
|
|
isSending.value = true; |
|
|
isSending.value = true; |
|
|
|
|
|
|
|
|
// 首先进行意图识别 |
|
|
// 首先进行意图识别 |
|
|
const res = await postIntent({ |
|
|
const res = await postIntent({ |
|
|
content: userMessage, |
|
|
content: userMessage, |
|
|
language: 'cn', |
|
|
|
|
|
marketList: 'hk,cn,usa,my,sg,vi,in,gb', |
|
|
|
|
|
token: "pCtw6AYK0EHAaIexoFHsbZjtsfEAIhcmwkCFm6uKko8VPfMvyDiODL9v9c0veic9fIpQbvT8zN4sH/Si6Q" |
|
|
|
|
|
|
|
|
language: "cn", |
|
|
|
|
|
marketList: "hk,cn,usa,my,sg,vi,in,gb", |
|
|
|
|
|
token: |
|
|
|
|
|
"pCtw6AYK0EHAaIexoFHsbZjtsfEAIhcmwkCFm6uKko8VPfMvyDiODL9v9c0veic9fIpQbvT8zN4sH/Si6Q", |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
console.log("res" + res); |
|
|
console.log("res" + res); |
|
|
@ -488,13 +622,12 @@ const simulateBotResponse = async (userMessage) => { |
|
|
const parentId = res.data.parentId; |
|
|
const parentId = res.data.parentId; |
|
|
const stockId = res.data.stockId; |
|
|
const stockId = res.data.stockId; |
|
|
|
|
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 2000)); |
|
|
|
|
|
// 获取股票信息 |
|
|
// 获取股票信息 |
|
|
const StockInfo = await postStock({ |
|
|
const StockInfo = await postStock({ |
|
|
recordId, |
|
|
recordId, |
|
|
parentId, |
|
|
parentId, |
|
|
stockId, |
|
|
stockId, |
|
|
token: memberStore.userInfo?.token || '', |
|
|
|
|
|
|
|
|
token: memberStore.userInfo?.token || "", |
|
|
language: "cn", |
|
|
language: "cn", |
|
|
}); |
|
|
}); |
|
|
console.log("StockInfo", StockInfo); |
|
|
console.log("StockInfo", StockInfo); |
|
|
@ -691,6 +824,27 @@ const onBackTopClick = () => { |
|
|
if (backTopDragging.value) return; // 拖拽时不触发点击回到顶部 |
|
|
if (backTopDragging.value) return; // 拖拽时不触发点击回到顶部 |
|
|
scrollToTop(); |
|
|
scrollToTop(); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 历史记录详情 |
|
|
|
|
|
async function itemClick(item) { |
|
|
|
|
|
const res = await postHistoryDetail({ |
|
|
|
|
|
recordId: item.id, |
|
|
|
|
|
parentId: item.parentId, |
|
|
|
|
|
model: 5, |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
if (res.code == 200) { |
|
|
|
|
|
const message = res.data.wokeFlowData.One.markdown; |
|
|
|
|
|
messages.value = []; |
|
|
|
|
|
const botMsg = { |
|
|
|
|
|
content: message, |
|
|
|
|
|
isUser: false, |
|
|
|
|
|
isTyping: false, |
|
|
|
|
|
isThinking: false, |
|
|
|
|
|
}; |
|
|
|
|
|
messages.value.push(botMsg); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
</script> |
|
|
</script> |
|
|
|
|
|
|
|
|
<style scoped> |
|
|
<style scoped> |
|
|
@ -859,7 +1013,7 @@ const onBackTopClick = () => { |
|
|
display: flex; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
justify-content: center; |
|
|
background: url('https://d31zlh4on95l9h.cloudfront.net/images/eca84d9fb54712cb3bc6c6174773b83b.svg'); |
|
|
|
|
|
|
|
|
background: url("https://d31zlh4on95l9h.cloudfront.net/images/eca84d9fb54712cb3bc6c6174773b83b.svg"); |
|
|
background-repeat: no-repeat; |
|
|
background-repeat: no-repeat; |
|
|
background-position: center top; |
|
|
background-position: center top; |
|
|
/* 放在容器顶部,正好在灰色卡片下方 */ |
|
|
/* 放在容器顶部,正好在灰色卡片下方 */ |
|
|
@ -1007,7 +1161,7 @@ const onBackTopClick = () => { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.bot-message .message-icon { |
|
|
.bot-message .message-icon { |
|
|
background: url('/static/images/机器人 (1).svg'); |
|
|
|
|
|
|
|
|
background: url("/static/images/机器人 (1).svg"); |
|
|
|
|
|
|
|
|
color: white; |
|
|
color: white; |
|
|
} |
|
|
} |
|
|
@ -1074,7 +1228,6 @@ const onBackTopClick = () => { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@keyframes loading { |
|
|
@keyframes loading { |
|
|
|
|
|
|
|
|
0%, |
|
|
0%, |
|
|
80%, |
|
|
80%, |
|
|
100% { |
|
|
100% { |
|
|
@ -1278,8 +1431,6 @@ const onBackTopClick = () => { |
|
|
gap: 14rpx; |
|
|
gap: 14rpx; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.delete-icon { |
|
|
.delete-icon { |
|
|
width: 45rpx; |
|
|
width: 45rpx; |
|
|
height: 40rpx; |
|
|
height: 40rpx; |
|
|
@ -1290,7 +1441,7 @@ const onBackTopClick = () => { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.drawer-close { |
|
|
.drawer-close { |
|
|
background: url('/static/icons/关闭2.svg'); |
|
|
|
|
|
|
|
|
background: url("/static/icons/关闭2.svg"); |
|
|
width: 48rpx; |
|
|
width: 48rpx; |
|
|
height: 48rpx; |
|
|
height: 48rpx; |
|
|
border-radius: 24rpx; |
|
|
border-radius: 24rpx; |
|
|
|