diff --git a/src/views/AIchat.vue b/src/views/AIchat.vue index 1fec0e0..796e40a 100644 --- a/src/views/AIchat.vue +++ b/src/views/AIchat.vue @@ -791,8 +791,9 @@ watch( marketList: userStore.aiGoldMarketList, }); - const HomePage = result20.data.HomePage; - const AIGoldBull = result20.data.AIGoldBull; + // 添加空值检查防止访问null对象的属性 + const HomePage = result20.data?.HomePage || null; + const AIGoldBull = result20.data?.AIGoldBull || null; const isLiuSe = HomePage ? true : false; const isAIGoldBull = @@ -1787,7 +1788,7 @@ watch( const ac44 = `${arr[2]},${arr[3]}

`; const ac45 = `

【时间维度】

`; const ac46 = `${result23.data.shijian}

`; - const ac47 = `

【能量维度】

`; + const ac47 = `

【能量维度】

`; const ac48 = `${result23.data.nengliang}

`; // const pc4 = marked( diff --git a/src/views/AiEmotion.vue b/src/views/AiEmotion.vue index 06b3039..02276f2 100644 --- a/src/views/AiEmotion.vue +++ b/src/views/AiEmotion.vue @@ -168,11 +168,12 @@ - +
- +
@@ -797,18 +798,6 @@ function startTypewriterEffect(conclusion, onComplete) { for (let i = 0; i <= disclaimerText.length; i++) { const timer = setTimeout(() => { displayedTexts.value.disclaimer = disclaimerText.substring(0, i); - // 在打字机效果的最后一个字符显示完成后,滚动到底部并调用完成回调 - if (i === disclaimerText.length) { - setTimeout(() => { - scrollToBottom(); - // 调用完成回调,重新启用输入框 - if (onComplete && typeof onComplete === 'function') { - onComplete(); - // 清除保存的回调函数 - currentOnCompleteCallback.value = null; - } - }, 100); - } }, totalDelay + i * typeSpeed); typewriterTimers.value.push(timer); } @@ -858,9 +847,9 @@ function playAudio(url) { emotionAudioStore.isPlaying = true; console.log('开始播放场景应用语音'); // 音频开始播放时,滚动到底部 - setTimeout(() => { - scrollToBottom(); - }, 100); + // setTimeout(() => { + // scrollToBottom(); + // }, 100); }, onend: () => { isAudioPlaying.value = false; @@ -964,27 +953,27 @@ async function handleSendMessage(input, onComplete) { } // 检查用户是否有使用次数(检查是否有任何权限) - const hasPermission = userStore.brainPerssion || userStore.swordPerssion || - userStore.pricePerssion || userStore.timePerssion || - userStore.aibullPerssion || userStore.aiGnbullPerssion || - userStore.airadarPerssion; - - if (!hasPermission) { - const userMessage = reactive({ sender: 'user', text: input }); - messages.value.push(userMessage); - const aiMessage = reactive({ sender: 'ai', text: '您当前没有可用权限,请联系客服或购买服务包。' }); - messages.value.push(aiMessage); - // 停止图片旋转,恢复历史数据 - isRotating.value = false; - messages.value = [...previousMessages, ...messages.value]; - // 调用完成回调,重新启用输入框 - if (onComplete && typeof onComplete === 'function') { - onComplete(); - // 清除保存的回调函数 - currentOnCompleteCallback.value = null; - } - return; - } + // const hasPermission = userStore.brainPerssion || userStore.swordPerssion || + // userStore.pricePerssion || userStore.timePerssion || + // userStore.aibullPerssion || userStore.aiGnbullPerssion || + // userStore.airadarPerssion; + + // if (!hasPermission) { + // const userMessage = reactive({ sender: 'user', text: input }); + // messages.value.push(userMessage); + // const aiMessage = reactive({ sender: 'ai', text: '您当前没有可用权限,请联系客服或购买服务包。' }); + // messages.value.push(aiMessage); + // // 停止图片旋转,恢复历史数据 + // isRotating.value = false; + // messages.value = [...previousMessages, ...messages.value]; + // // 调用完成回调,重新启用输入框 + // if (onComplete && typeof onComplete === 'function') { + // onComplete(); + // // 清除保存的回调函数 + // currentOnCompleteCallback.value = null; + // } + // return; + // } const userMessage = reactive({ sender: 'user', text: input }); messages.value.push(userMessage); @@ -996,14 +985,22 @@ async function handleSendMessage(input, onComplete) { userData: { token: localStorage.getItem('localToken'), language: "cn", - brainPrivilegeState: userStore.brainPerssion, - swordPrivilegeState: userStore.swordPerssion, - stockForecastPrivilegeState: userStore.pricePerssion, - spaceForecastPrivilegeState: userStore.timePerssion, - aibullPrivilegeState: userStore.aibullPerssion, - aigoldBullPrivilegeState: userStore.aiGnbullPerssion, - airadarPrivilegeState: userStore.airadarPerssion, - marketList: userStore.aiGoldMarketList, + // brainPrivilegeState: userStore.brainPerssion, + // swordPrivilegeState: userStore.swordPerssion, + // stockForecastPrivilegeState: userStore.pricePerssion, + // spaceForecastPrivilegeState: userStore.timePerssion, + // aibullPrivilegeState: userStore.aibullPerssion, + // aigoldBullPrivilegeState: userStore.aiGnbullPerssion, + // airadarPrivilegeState: userStore.airadarPerssion, + // marketList: userStore.aiGoldMarketList, + brainPrivilegeState: '1', + swordPrivilegeState: '1', + stockForecastPrivilegeState: '1', + spaceForecastPrivilegeState: '1', + aibullPrivilegeState: '1', + aigoldBullPrivilegeState: '1', + airadarPrivilegeState: '1', + marketList: "hk,cn,usa,my,sg,vi,in,gb", }, }; @@ -1116,7 +1113,7 @@ async function handleSendMessage(input, onComplete) { } isRotating.value = false; messages.value = [...previousMessages, ...messages.value]; - + // 如果有之前的股票数据且页面已加载,重新渲染图表 if (isPageLoaded.value && emotionStore.activeStock && emotionStore.activeStock.apiData) { nextTick(() => { @@ -1124,7 +1121,7 @@ async function handleSendMessage(input, onComplete) { console.log('搜索失败,恢复显示之前股票的图表:', emotionStore.activeStock.stockInfo.name); }); } - + // 调用完成回调,重新启用输入框 if (onComplete && typeof onComplete === 'function') { onComplete(); @@ -1136,7 +1133,7 @@ async function handleSendMessage(input, onComplete) { } catch (error) { // 请求失败时关闭加载状态 isLoading.value = false; - + // 如果有之前的股票数据,恢复显示状态;否则设置为false if (emotionStore.stockList.length > 0 && emotionStore.activeStock) { isPageLoaded.value = true; @@ -1149,13 +1146,13 @@ async function handleSendMessage(input, onComplete) { } else { isPageLoaded.value = false; } - + const aiMessage = reactive({ sender: 'ai', text: '请求工作流接口失败,请检查网络连接' }); messages.value.push(aiMessage); // 请求失败时停止图片旋转,恢复历史数据 isRotating.value = false; messages.value = [...previousMessages, ...messages.value]; - + // 如果有之前的股票数据且页面已加载,重新渲染图表 if (isPageLoaded.value && emotionStore.activeStock && emotionStore.activeStock.apiData) { nextTick(() => { @@ -1163,7 +1160,7 @@ async function handleSendMessage(input, onComplete) { console.log('请求失败,恢复显示之前股票的图表:', emotionStore.activeStock.stockInfo.name); }); } - + // 调用完成回调,重新启用输入框 if (onComplete && typeof onComplete === 'function') { onComplete(); @@ -1208,13 +1205,13 @@ async function fetchData(code, market, stockName, queryText) { if (stockDataResponse.code === 200 && stockDataResponse.data) { // 检查关键数据字段是否完整 const validation = validateRequiredFields(stockDataResponse.data); - + // 如果有关键数据缺失,返回失败,不添加到StockTabs if (!validation.isValid) { console.log('API返回数据不完整,缺失字段:', validation.missingFields); // 关闭加载状态 isLoading.value = false; - + // 如果有之前的股票数据,恢复显示状态;否则设置为false if (emotionStore.stockList.length > 0 && emotionStore.activeStock) { isPageLoaded.value = true; @@ -1227,15 +1224,15 @@ async function fetchData(code, market, stockName, queryText) { } else { isPageLoaded.value = false; } - - const aiMessage = reactive({ - sender: 'ai', - text: `数据丢失了,请稍后重试。` + + const aiMessage = reactive({ + sender: 'ai', + text: `数据丢失了,请稍后重试。` }); messages.value.push(aiMessage); return false; // 返回失败标识,不添加股票到标签 } - + // 只有数据完整时才创建股票数据对象并添加到store const stockData = { queryText: queryText, @@ -1254,7 +1251,7 @@ async function fetchData(code, market, stockName, queryText) { } else { // 关闭加载状态 isLoading.value = false; - + // 如果有之前的股票数据,恢复显示状态;否则设置为false if (emotionStore.stockList.length > 0 && emotionStore.activeStock) { isPageLoaded.value = true; @@ -1267,7 +1264,7 @@ async function fetchData(code, market, stockName, queryText) { } else { isPageLoaded.value = false; } - + const aiMessage = reactive({ sender: 'ai', text: '图表数据请求失败,请检查网络连接' }); messages.value.push(aiMessage); return false; // 返回失败标识 @@ -1275,7 +1272,7 @@ async function fetchData(code, market, stockName, queryText) { } catch (error) { // 关闭加载状态 isLoading.value = false; - + // 如果有之前的股票数据,恢复显示状态;否则设置为false if (emotionStore.stockList.length > 0 && emotionStore.activeStock) { isPageLoaded.value = true; @@ -1288,7 +1285,7 @@ async function fetchData(code, market, stockName, queryText) { } else { isPageLoaded.value = false; } - + const aiMessage = reactive({ sender: 'ai', text: '图表数据请求失败,请检查网络连接' }); messages.value.push(aiMessage); return false; // 返回失败标识 @@ -1299,15 +1296,15 @@ async function fetchData(code, market, stockName, queryText) { function validateRequiredFields(data) { const requiredFields = ['GSWDJ', 'KLine20', 'QXJMQ', 'QXTDLD', 'WDRL']; const missingFields = []; - + for (const field of requiredFields) { - if (!data[field] || - (Array.isArray(data[field]) && data[field].length === 0) || - (typeof data[field] === 'object' && !hasValidData(data[field]))) { + if (!data[field] || + (Array.isArray(data[field]) && data[field].length === 0) || + (typeof data[field] === 'object' && !hasValidData(data[field]))) { missingFields.push(field); } } - + return { isValid: missingFields.length === 0, missingFields: missingFields @@ -1319,15 +1316,15 @@ function hasValidData(obj) { if (!obj || typeof obj !== 'object') { return false; } - + // 定义可以为空的数组字段 const allowedEmptyArrays = ['lowxh', 'qixh', 'topxh']; - + // 检查对象的所有属性值 for (const key in obj) { if (obj.hasOwnProperty(key)) { const value = obj[key]; - + // 如果是字符串字段 if (typeof value === 'string') { // 字符串字段必须有内容,为空则表示异常 @@ -1358,36 +1355,36 @@ function hasValidData(obj) { } } } - + return false; } // 渲染组件图表的方法 function renderCharts(data) { console.log('开始渲染图表,数据:', data); - + // 深拷贝数据避免污染原始数据 const clonedData = JSON.parse(JSON.stringify(data)); - + // 检查关键数据字段是否完整 const validation = validateRequiredFields(clonedData); - + // 如果有任何关键数据缺失,不渲染页面并返回提示 if (!validation.isValid) { console.log('关键数据缺失:', validation.missingFields); - const aiMessage = reactive({ - sender: 'ai', - text: `数据不完整,缺少以下关键数据:${validation.missingFields.join('、')}。请稍后重试或联系客服。` + const aiMessage = reactive({ + sender: 'ai', + text: `数据不完整,缺少以下关键数据:${validation.missingFields.join('、')}。请稍后重试或联系客服。` }); messages.value.push(aiMessage); - + // 隐藏页面内容 isPageLoaded.value = false; isLoading.value = false; - + return; // 直接返回,不进行后续渲染 } - + // 先设置图表组件显示状态 chartVisibility.value = { marketTemperature: !!(clonedData.GSWDJ && clonedData.GSWDJ.length > 0), @@ -1395,7 +1392,7 @@ function renderCharts(data) { emotionalBottomRadar: !!(clonedData.QXTDLD && clonedData.QXTDLD.length > 0), emoEnergyConverter: !!(clonedData.QXNLZHQ && (Array.isArray(clonedData.QXNLZHQ) ? clonedData.QXNLZHQ.length > 0 : hasValidData(clonedData.QXNLZHQ))) }; - + console.log('图表显示状态:', chartVisibility.value); console.log('数据检查:', { GSWDJ: !!(clonedData.GSWDJ && clonedData.GSWDJ.length > 0), @@ -1404,7 +1401,7 @@ function renderCharts(data) { QXNLZHQ: !!(clonedData.QXNLZHQ && (Array.isArray(clonedData.QXNLZHQ) ? clonedData.QXNLZHQ.length > 0 : hasValidData(clonedData.QXNLZHQ))) }); console.log('QXNLZHQ数据详情:', clonedData.QXNLZHQ); - + nextTick(() => { // 增加延迟确保DOM完全更新和组件完全挂载 setTimeout(() => { @@ -1415,7 +1412,7 @@ function renderCharts(data) { emotionalBottomRadarRef: !!emotionalBottomRadarRef.value, emoEnergyConverterRef: !!emoEnergyConverterRef.value }); - + // 检查DOM元素是否存在 console.log('DOM元素检查:', { marketTemperatureDOM: !!document.querySelector('.class03'), @@ -1423,11 +1420,11 @@ function renderCharts(data) { emotionalBottomRadarDOM: !!document.querySelector('.class05'), emoEnergyConverterDOM: !!document.querySelector('.class06') }); - + // 检查具体的组件元素 const emoEnergyElement = document.querySelector('emo-energy-converter'); console.log('emoEnergyConverter元素:', emoEnergyElement); - + // 等待更长时间再次检查ref setTimeout(() => { console.log('延迟检查emoEnergyConverterRef:', !!emoEnergyConverterRef.value); @@ -1435,7 +1432,7 @@ function renderCharts(data) { console.log('emoEnergyConverter方法:', typeof emoEnergyConverterRef.value.initQXNLZHEcharts); } }, 1000); - + // 渲染股市温度计图表 if (marketTemperatureRef.value && chartVisibility.value.marketTemperature) { console.log('开始渲染股市温度计图表'); @@ -1453,7 +1450,7 @@ function renderCharts(data) { } else { console.log('股市温度计图表未渲染,ref存在:', !!marketTemperatureRef.value, '数据存在:', chartVisibility.value.marketTemperature); } - + // 渲染情绪解码器图表 if (emotionDecodRef.value && chartVisibility.value.emotionDecod) { console.log('开始渲染情绪解码器图表'); @@ -1471,7 +1468,7 @@ function renderCharts(data) { } else { console.log('情绪解码器图表未渲染,ref存在:', !!emotionDecodRef.value, '数据存在:', chartVisibility.value.emotionDecod); } - + // 渲染情绪探底雷达图表 if (emotionalBottomRadarRef.value && chartVisibility.value.emotionalBottomRadar) { console.log('开始渲染情绪探底雷达图表'); @@ -1492,7 +1489,7 @@ function renderCharts(data) { } else { console.log('情绪探底雷达图表未渲染,ref存在:', !!emotionalBottomRadarRef.value, '数据存在:', chartVisibility.value.emotionalBottomRadar); } - + // 渲染情绪能量转化器图表 if (emoEnergyConverterRef.value && chartVisibility.value.emoEnergyConverter) { console.log('开始渲染情绪能量转化器图表'); @@ -1510,7 +1507,7 @@ function renderCharts(data) { } else { console.log('情绪能量转化器图表未渲染,ref存在:', !!emoEnergyConverterRef.value, '数据存在:', chartVisibility.value.emoEnergyConverter); } - + console.log('图表渲染完成'); } catch (error) { console.error('图表渲染错误:', error); @@ -1521,21 +1518,21 @@ function renderCharts(data) { }); } -const scrollToBottom = async () => { - // 如果用户正在手动滚动,则不执行自动滚动 - if (isUserScrolling.value) { - console.log('用户正在手动滚动,跳过自动滚动'); - return; - } +// const scrollToBottom = async () => { +// // 如果用户正在手动滚动,则不执行自动滚动 +// if (isUserScrolling.value) { +// console.log('用户正在手动滚动,跳过自动滚动'); +// return; +// } - const container = userInputDisplayRef.value; - if (!container) return; - await nextTick(); - console.log(container.scrollHeight, "container.scrollHeight"); - console.log(container.scrollTop, "container.scrollTop"); - console.log(container.offsetHeight, "container.offsetHeight"); - container.scrollTop = container.scrollHeight - container.offsetHeight; -}; +// const container = userInputDisplayRef.value; +// if (!container) return; +// await nextTick(); +// console.log(container.scrollHeight, "container.scrollHeight"); +// console.log(container.scrollTop, "container.scrollTop"); +// console.log(container.offsetHeight, "container.offsetHeight"); +// container.scrollTop = container.scrollHeight - container.offsetHeight; +// }; // 处理用户滚动事件 const handleUserScroll = () => { @@ -1704,7 +1701,7 @@ function triggerAutoScroll() { const scrollToTop = () => { const topAnchor = document.getElementById('top-anchor'); if (topAnchor) { - topAnchor.scrollIntoView({ + topAnchor.scrollIntoView({ behavior: 'smooth', block: 'start', inline: 'nearest' @@ -1712,7 +1709,7 @@ const scrollToTop = () => { } else { window.scrollTo({ top: 0, behavior: 'smooth' }); } - + // 备用方案:直接滚动到页面顶部 setTimeout(() => { const currentScrollTop = window.pageYOffset || document.documentElement.scrollTop; @@ -1816,7 +1813,7 @@ onMounted(async () => { // 添加页面滚动监听器,控制返回顶部按钮显示 window.addEventListener('scroll', handlePageScroll, { passive: true }); - + // 添加document滚动监听器(备用方案) document.addEventListener('scroll', handlePageScroll, { passive: true }); @@ -1876,13 +1873,13 @@ onMounted(async () => { // 组件卸载时清理定时器、音频和observer onUnmounted(() => { clearTypewriterTimers(); - + // 如果有未完成的回调函数,调用它来重新启用输入框 if (currentOnCompleteCallback.value && typeof currentOnCompleteCallback.value === 'function') { currentOnCompleteCallback.value(); currentOnCompleteCallback.value = null; } - + stopAudio(); // 重置触发状态 @@ -3362,7 +3359,7 @@ defineExpose({ width: 40px !important; height: 40px !important; } - + .back-to-top svg { width: 20px; height: 20px; diff --git a/src/views/components/emoEnergyConverter.vue b/src/views/components/emoEnergyConverter.vue index dfba893..e57e7cf 100644 --- a/src/views/components/emoEnergyConverter.vue +++ b/src/views/components/emoEnergyConverter.vue @@ -524,17 +524,34 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { z: 2 }, { + name: '止盈线描边', + type: 'line', + data: takeProfitData, + symbol: 'none', + lineStyle: { + normal: { + color: '#ffffff', // 白色描边 + width: 6, + type: 'solid' + } + }, + z: 1, + silent: true, + showInLegend: false + }, + { name: '止盈线', type: 'line', data: takeProfitData, symbol: 'none', lineStyle: { normal: { - color: '#FF0000', // 蓝色 + color: '#FF0000', // 红色 width: 2, type: 'solid' } }, + z: 2, markPoint: { symbol: 'circle', symbolSize: 1, @@ -564,6 +581,22 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { } }, { + name: '止损线描边', + type: 'line', + data: stopLossData, + symbol: 'none', + lineStyle: { + normal: { + color: '#ffffff', // 白色描边 + width: 6, + type: 'solid' + } + }, + z: 1, + silent: true, + showInLegend: false + }, + { name: '止损线', type: 'line', data: stopLossData, @@ -575,6 +608,7 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { type: 'solid' } }, + z: 2, markPoint: { symbol: 'circle', symbolSize: 1, @@ -636,7 +670,7 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { textBorderWidth: 2, } }, - offset: [0, -30] + offset: [-25, -40] } } } @@ -676,7 +710,7 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { textBorderWidth: 2, } }, - offset: [0, 30] + offset: [-25, 40] } } } diff --git a/src/views/components/emotionDecod.vue b/src/views/components/emotionDecod.vue index cf94c09..d694853 100644 --- a/src/views/components/emotionDecod.vue +++ b/src/views/components/emotionDecod.vue @@ -154,6 +154,13 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { gridIndex: 2, data: dealData.categoryData, axisLine: { lineStyle: { color: "white" } }, + axisPointer: { + show: false, + label: { + show: false, + }, + type: "line", + }, }, ], yAxis: [ @@ -224,6 +231,13 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { }, axisTick: { show: false }, // 隐藏刻度线 axisLabel: { show: false }, // 隐藏刻度标签 + axisPointer: { + show: false, + label: { + show: false, + }, + type: "line", + }, }, ], // 下拉条 @@ -289,11 +303,11 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { tooltip: { trigger: "item", axisPointer: { - type: 'cross', // 十字准星效果 - crossStyle: { - color: '#999' - } - }, + type: 'cross', // 十字准星效果 + crossStyle: { + color: '#999' + } + }, // 覆盖全局 tooltip 配置 formatter: function (params) { return `${params.value[2]}`; // 直接取数据中的第3个元素(数值) @@ -355,7 +369,7 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { // 创造echarts图 KlineCanvsChart = echarts.init(KlineCanvs.value); KlineCanvsChart.setOption(KlineOption); - + // 防抖函数,避免频繁触发resize const debounce = (func, wait) => { let timeout; @@ -368,7 +382,7 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { timeout = setTimeout(later, wait); }; }; - + // 监听窗口大小变化,参考股市温度计的实现 const resizeHandler = debounce(() => { if (KlineCanvsChart && !KlineCanvsChart.isDisposed()) { @@ -380,18 +394,18 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { } } }, 100); // 100ms防抖延迟 - + // 移除之前的监听器(如果存在) if (window.emotionDecodResizeHandler) { window.removeEventListener('resize', window.emotionDecodResizeHandler); } - + // 添加新的监听器 window.addEventListener('resize', resizeHandler); - + // 存储resize处理器以便后续清理 window.emotionDecodResizeHandler = resizeHandler; - + // 添加容器大小监听器 if (KlineCanvs.value && window.ResizeObserver) { const containerObserver = new ResizeObserver(debounce(() => { @@ -404,7 +418,7 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { } } }, 100)); - + containerObserver.observe(KlineCanvs.value); window.emotionDecodContainerObserver = containerObserver; } @@ -428,19 +442,19 @@ onBeforeUnmount(() => { KlineCanvsChart.dispose(); KlineCanvsChart = null; } - + // 移除窗口resize监听器 if (window.emotionDecodResizeHandler) { window.removeEventListener('resize', window.emotionDecodResizeHandler); window.emotionDecodResizeHandler = null; } - + // 移除窗口高度监听器 if (window.emotionDecodHeightHandler) { window.removeEventListener('resize', window.emotionDecodHeightHandler); window.emotionDecodHeightHandler = null; } - + // 清理容器观察器 if (window.emotionDecodContainerObserver) { window.emotionDecodContainerObserver.disconnect(); @@ -452,7 +466,7 @@ onBeforeUnmount(() => { .qxjmqbox { height: auto; width: 100%; - margin:0 auto; + margin: 0 auto; } #qxjmqEcharts {