- - Hi, 我是您的股市随身顾问~ 个股诊断、市场情绪解读,都可以找我。 + + + Hi, 我是您的股市随身顾问~ + 个股诊断、市场情绪解读,都可以找我。 - + - + - + " + > - - - - - - {{ - message.isTyping ? "正在思考" : "思考完成" - }} - - - - - - - + + + + + + {{ + message.isTyping ? "正在思考" : "思考完成" + }} + + + + - - - - - - 问题分析完成 + + + + + - - - - - 收集相关信息 + 问题分析完成 + + + + + 收集相关信息 + - + {{ message.content }} @@ -151,8 +190,14 @@ - + - 以上数据由AI生成,不作为最终投资建议,决策需独立! + 以上数据由AI生成,不作为最终投资建议,决策需独立! - + - - > + + > 搜索历史 @@ -179,7 +241,11 @@ 暂无搜索历史 - + {{ item.query }} {{ formatTime(item.time) }} @@ -194,10 +260,10 @@ const { safeAreaInsets } = uni.getSystemInfoSync(); import { ref, computed, onMounted, onUnmounted, watch, nextTick } from "vue"; -import footerBar from '../../components/footerBar-cn' +import footerBar from "../../components/footerBar-cn"; import marked from "marked"; // 引入 marked 库 -import { onPageScroll } from '@dcloudio/uni-app' -import {postStock,postIntent} from '../../api/deepMate/deepMate' +import { onPageScroll } from "@dcloudio/uni-app"; +import { postStock, postIntent } from "../../api/deepMate/deepMate"; // 设置 marked 选项 marked.setOptions({ renderer: new marked.Renderer(), @@ -253,7 +319,7 @@ const FAKE_MARKDOWN = `# Tesla Inc.全景作战报告*该内容由AI生成,仅供参考,投资有风险,请注意甄别。*`; -const type = ref('member') +const type = ref("member"); const inputMessage = ref(""); const showThinking = ref(true); const isSending = ref(false); @@ -308,7 +374,10 @@ onMounted(() => { // nextTick(startTabsMarquee); } if (messages.value.length > 0) { - nextTick(() => { measureChatContainer(); scrollToBottom(); }); + nextTick(() => { + measureChatContainer(); + scrollToBottom(); + }); } // 载入历史 @@ -338,7 +407,7 @@ const generateUUID = () => { // 计算聊天容器可视高度 const measureChatContainer = () => { const q = uni.createSelectorQuery(); - q.select('.chat-container').boundingClientRect(); + q.select(".chat-container").boundingClientRect(); q.exec((res) => { chatContainerHeight.value = res[0]?.height || 0; }); @@ -359,8 +428,12 @@ const goBlank = () => { }; // 历史抽屉控制 -const openHistoryDrawer = () => { showHistoryDrawer.value = true; }; -const closeHistoryDrawer = () => { showHistoryDrawer.value = false; }; +const openHistoryDrawer = () => { + showHistoryDrawer.value = true; +}; +const closeHistoryDrawer = () => { + showHistoryDrawer.value = false; +}; const onDrawerBackClick = () => { drawerOffsetX.value = 500; setTimeout(() => { @@ -370,7 +443,7 @@ const onDrawerBackClick = () => { }; // 时间格式化:YYYY-MM-DD HH:mm -const pad2 = (n) => (n < 10 ? '0' + n : '' + n); +const pad2 = (n) => (n < 10 ? "0" + n : "" + n); const formatTime = (t) => { const d = new Date(t); const y = d.getFullYear(); @@ -398,18 +471,20 @@ const sendMessage = () => { // 记录搜索历史 const entry = { query: userMessage.content, time: Date.now() }; searchHistory.value.unshift(entry); - uni.setStorageSync('search_history', searchHistory.value); + uni.setStorageSync("search_history", searchHistory.value); // 发送后强制恢复并滚到底部 shouldAutoScroll.value = true; - nextTick(() => { scrollToBottom(); }); + nextTick(() => { + scrollToBottom(); + }); // 模拟机器人回复 simulateBotResponse(userMessage.content); }; // 模拟机器人回复 -const simulateBotResponse = async(userMessage) => { +const simulateBotResponse = async (userMessage) => { isSending.value = true; // 添加机器人加载消息 @@ -422,60 +497,55 @@ const simulateBotResponse = async(userMessage) => { messages.value.push(botMsg); - - await new Promise((resolve) => setTimeout(resolve, 2000)); + await new Promise((resolve) => setTimeout(resolve, 2000)); // 首先进行意图识别 const res = await postIntent({ - content:"森那美", + content: "森那美", language: "cn", marketlist: "hk,cn,usa,my,sg,vi,in,gb", - token: "9ior41AF0xTIbIG2pRnnbZi0+fEeMx8pywnilrmTwo5FbqJ91WrSWOxp9MkpKiNtedtUafqvzIwpFKrwuMs", + token: + "9ior41AF0xTIbIG2pRnnbZi0+fEeMx8pywnilrmTwo5FbqJ91WrSWOxp9MkpKiNtedtUafqvzIwpFKrwuMs", model: "1", }); - console.log("res"+res); - + console.log("res" + res); + // 意图识别不通过 - if (res.code !== 200) { - return ; + if (res.code !== 200) { + return; } - // 获取意图识别结果 + // 获取意图识别结果 const recordId = res.data.recordId; const parentId = res.data.parentId; const stockId = res.data.stockId; - - await new Promise((resolve) => setTimeout(resolve, 2000)); + await new Promise((resolve) => setTimeout(resolve, 2000)); // 获取股票信息 const StockInfo = await postStock({ recordId, parentId, stockId, - token: "9ior41AF0xTIbIG2pRnnbZi0+fEeMx8pywnilrmTwo5FbqJ91WrSWOxp9MkpKiNtedtUafqvzIwpFKrwuMs", - language:'cn' + token: + "9ior41AF0xTIbIG2pRnnbZi0+fEeMx8pywnilrmTwo5FbqJ91WrSWOxp9MkpKiNtedtUafqvzIwpFKrwuMs", + language: "cn", }); console.log("StockInfo", StockInfo); - - // if (StockInfo.code !== 200) { + + // if (StockInfo.code !== 200) { // return ; // } const markdown = StockInfo.markdown; console.log("StockInfo", StockInfo); - - - - - // 添加请求延迟 + // 添加请求延迟 // const toDataInfo = await getData(); // console.log(toDataInfo); // dataInfo.value = toDataInfo.data; // console.log(dataInfo.value); messages.value[messages.value.length - 1].isThinking = false; - // 滚动到底部 nextTick(() => { scrollToBottom(); @@ -486,39 +556,45 @@ const simulateBotResponse = async(userMessage) => { let index = 0; const botIndex = messages.value.length - 1; - const baseDelay = 165; // 普通字符基础延迟(毫秒) - const slowPunct = /[。!?!?;;]/; // 句号、感叹号、分号等较长停顿 - const midPunct = /[,、,::]/; // 逗号、顿号、冒号等中等停顿 + const baseDelay = 5; // 普通字符基础延迟(毫秒) + const slowPunct = /[。!?!?;;]/; // 句号、感叹号、分号等较长停顿 + const midPunct = /[,、,::]/; // 逗号、顿号、冒号等中等停顿 const typeWriter = () => { if (index < responseText.length) { const ch = responseText.charAt(index); const current = messages.value[botIndex]; // 通过数组替换触发渲染,避免部分平台对子项属性变更不响应 - messages.value.splice( - botIndex, - 1, - { ...current, content: current.content + ch, isTyping: true } - ); + messages.value.splice(botIndex, 1, { + ...current, + content: current.content + ch, + isTyping: true, + }); index++; scrollToBottom(); - const delay = slowPunct.test(ch) ? 220 : midPunct.test(ch) ? 120 : baseDelay; + const delay = slowPunct.test(ch) + ? 220 + : midPunct.test(ch) + ? 120 + : baseDelay; setTimeout(typeWriter, delay); } else { const current = messages.value[botIndex]; - messages.value.splice( - botIndex, - 1, - { ...current, isTyping: false } - ); + messages.value.splice(botIndex, 1, { ...current, isTyping: false }); isSending.value = false; - nextTick(() => { scrollToBottom(); }); + nextTick(() => { + scrollToBottom(); + }); } }; // 启动前稍作停顿,避免过快开始 setTimeout(typeWriter, 500); + + + console.log("messages", messages); + }; // 当消息出现或变化时,测量容器并滚到底部 @@ -535,7 +611,7 @@ watch(messages, (arr) => { const scrollToBottom = () => { if (!shouldAutoScroll.value) return; const query = uni.createSelectorQuery(); - query.select('#messageList').boundingClientRect(); + query.select("#messageList").boundingClientRect(); query.exec((res) => { if (res[0]) { latestContentHeight.value = res[0].height; @@ -563,7 +639,8 @@ const onChatScroll = (e) => { return; } - const distanceToBottom = latestContentHeight.value - st - chatContainerHeight.value; + const distanceToBottom = + latestContentHeight.value - st - chatContainerHeight.value; if (distanceToBottom <= AUTO_SCROLL_REENABLE_THRESHOLD) { shouldAutoScroll.value = true; } @@ -579,8 +656,14 @@ const backTopTargetY = ref(0); let backTopRAF = 0; const backTopSmoothing = 0.15; // 越大越跟手,越小越顺滑 const backTopEpsilon = 0.5; // 收敛阈值 -const raf = typeof requestAnimationFrame === 'function' ? requestAnimationFrame : (fn) => setTimeout(fn, 16); -const caf = typeof cancelAnimationFrame === 'function' ? cancelAnimationFrame : (id) => clearTimeout(id); +const raf = + typeof requestAnimationFrame === "function" + ? requestAnimationFrame + : (fn) => setTimeout(fn, 16); +const caf = + typeof cancelAnimationFrame === "function" + ? cancelAnimationFrame + : (id) => clearTimeout(id); function stepBackTop() { const dx = backTopTargetX.value - backTopX.value; @@ -624,7 +707,10 @@ const onBackTopTouchMove = (e) => { const ny = t.pageY - backTopDragOffset.value.y; const clampedX = clamp(nx, 0, sys.windowWidth - iconSize); const clampedY = clamp(ny, 0, sys.windowHeight - reserveBottom - iconSize); - if (Math.abs(clampedX - backTopX.value) + Math.abs(clampedY - backTopY.value) > 3) { + if ( + Math.abs(clampedX - backTopX.value) + Math.abs(clampedY - backTopY.value) > + 3 + ) { backTopDragging.value = true; } backTopTargetX.value = clampedX; @@ -640,8 +726,6 @@ const onBackTopClick = () => { if (backTopDragging.value) return; // 拖拽时不触发点击回到顶部 scrollToTop(); }; - -