|
|
@ -624,11 +624,10 @@ const addStock = (stockData) => { |
|
|
|
|
|
|
|
// 4. 标记历史记录股票已显示过,避免重复触发 |
|
|
|
if (stockData.conclusionData) { |
|
|
|
const stockCode = |
|
|
|
stockData.stockInfo?.code || stockData.stockInfo?.symbol; |
|
|
|
if (stockCode) { |
|
|
|
stockTypewriterShown.value.set(stockCode, true); |
|
|
|
stockAudioPlayed.value.set(stockCode, true); |
|
|
|
const stockUniqueId = getStockUniqueId(stockData); |
|
|
|
if (stockUniqueId) { |
|
|
|
stockTypewriterShown.value.set(stockUniqueId, true); |
|
|
|
stockAudioPlayed.value.set(stockUniqueId, true); |
|
|
|
} |
|
|
|
console.log("历史记录股票已标记为已显示"); |
|
|
|
} |
|
|
@ -688,6 +687,13 @@ const stockTypewriterVisibility = ref(new Map()); |
|
|
|
const stockAudioPlayed = ref(new Map()); |
|
|
|
// 跟踪每个股票的音频播放状态 |
|
|
|
const stockAudioStates = ref(new Map()); |
|
|
|
|
|
|
|
// 生成股票唯一标识符的辅助函数 |
|
|
|
const getStockUniqueId = (stock) => { |
|
|
|
const stockCode = stock.stockInfo?.code || stock.stockInfo?.symbol; |
|
|
|
const timestamp = stock.timestamp; |
|
|
|
return stockCode && timestamp ? `${stockCode}_${timestamp}` : stockCode; |
|
|
|
}; |
|
|
|
// 存储当前的完成回调函数 |
|
|
|
const currentOnCompleteCallback = ref(null); |
|
|
|
|
|
|
@ -697,11 +703,11 @@ const isAudioPlaying = ref(false); |
|
|
|
|
|
|
|
// 获取股票的音频播放状态 |
|
|
|
const getStockAudioState = (stock) => { |
|
|
|
const stockCode = stock.stockInfo?.code || stock.stockInfo?.symbol; |
|
|
|
if (!stockCode) return { isPlaying: false, isPaused: false }; |
|
|
|
const stockUniqueId = getStockUniqueId(stock); |
|
|
|
if (!stockUniqueId) return { isPlaying: false, isPaused: false }; |
|
|
|
|
|
|
|
return ( |
|
|
|
stockAudioStates.value.get(stockCode) || { |
|
|
|
stockAudioStates.value.get(stockUniqueId) || { |
|
|
|
isPlaying: false, |
|
|
|
isPaused: false, |
|
|
|
} |
|
|
@ -710,10 +716,10 @@ const getStockAudioState = (stock) => { |
|
|
|
|
|
|
|
// 设置股票的音频播放状态 |
|
|
|
const setStockAudioState = (stock, state) => { |
|
|
|
const stockCode = stock.stockInfo?.code || stock.stockInfo?.symbol; |
|
|
|
if (!stockCode) return; |
|
|
|
const stockUniqueId = getStockUniqueId(stock); |
|
|
|
if (!stockUniqueId) return; |
|
|
|
|
|
|
|
stockAudioStates.value.set(stockCode, { ...state }); |
|
|
|
stockAudioStates.value.set(stockUniqueId, { ...state }); |
|
|
|
}; |
|
|
|
|
|
|
|
// 清除所有股票的播放状态(当开始播放新音频时) |
|
|
@ -735,6 +741,10 @@ const stockCode = computed( |
|
|
|
currentStock.value?.stockInfo.symbol || |
|
|
|
"" |
|
|
|
); |
|
|
|
const currentStockUniqueId = computed(() => { |
|
|
|
if (!currentStock.value) return ""; |
|
|
|
return getStockUniqueId(currentStock.value); |
|
|
|
}); |
|
|
|
const displayDate = computed(() => { |
|
|
|
if (!currentStock.value?.apiData) return ""; |
|
|
|
const lastData = currentStock.value.apiData.GSWDJ?.at(-1); |
|
|
@ -855,23 +865,23 @@ const getStockConclusion = (stock) => { |
|
|
|
|
|
|
|
// 辅助函数:获取股票的打字机文本状态 |
|
|
|
const getStockTypewriterTexts = (stock) => { |
|
|
|
const stockCode = stock.stockInfo?.code || stock.stockInfo?.symbol; |
|
|
|
if (!stockCode) return null; |
|
|
|
return stockTypewriterTexts.value.get(stockCode) || null; |
|
|
|
const stockUniqueId = getStockUniqueId(stock); |
|
|
|
if (!stockUniqueId) return null; |
|
|
|
return stockTypewriterTexts.value.get(stockUniqueId) || null; |
|
|
|
}; |
|
|
|
|
|
|
|
// 辅助函数:获取股票的打字机可见性状态 |
|
|
|
const getStockTypewriterVisibility = (stock) => { |
|
|
|
const stockCode = stock.stockInfo?.code || stock.stockInfo?.symbol; |
|
|
|
if (!stockCode) return null; |
|
|
|
return stockTypewriterVisibility.value.get(stockCode) || null; |
|
|
|
const stockUniqueId = getStockUniqueId(stock); |
|
|
|
if (!stockUniqueId) return null; |
|
|
|
return stockTypewriterVisibility.value.get(stockUniqueId) || null; |
|
|
|
}; |
|
|
|
|
|
|
|
// 辅助函数:检查股票是否正在进行打字机效果 |
|
|
|
const isStockTypewriting = (stock) => { |
|
|
|
const stockCode = stock.stockInfo?.code || stock.stockInfo?.symbol; |
|
|
|
if (!stockCode) return false; |
|
|
|
return stockTypewriterShown.value.has(stockCode) && !stockTypewriterTexts.value.has(stockCode); |
|
|
|
const stockUniqueId = getStockUniqueId(stock); |
|
|
|
if (!stockUniqueId) return false; |
|
|
|
return stockTypewriterShown.value.has(stockUniqueId) && !stockTypewriterTexts.value.has(stockUniqueId); |
|
|
|
}; |
|
|
|
|
|
|
|
// 监听股票列表变化,当列表为空时隐藏页面数据 |
|
|
@ -929,8 +939,8 @@ watch( |
|
|
|
hasTriggeredAudio.value = false; |
|
|
|
hasTriggeredTypewriter.value = false; |
|
|
|
|
|
|
|
// 获取股票代码作为唯一标识 |
|
|
|
const stockCode = newStock.stockInfo?.code || newStock.stockInfo?.symbol; |
|
|
|
// 获取股票唯一标识 |
|
|
|
const stockUniqueId = getStockUniqueId(newStock); |
|
|
|
|
|
|
|
// 处理当前股票的音频URL |
|
|
|
if (newStock.conclusionData) { |
|
|
@ -942,7 +952,7 @@ watch( |
|
|
|
: JSON.parse(newStock.conclusionData); |
|
|
|
|
|
|
|
// 检查该股票是否已经显示过打字机效果 |
|
|
|
if (stockCode && stockTypewriterShown.value.has(stockCode)) { |
|
|
|
if (stockUniqueId && stockTypewriterShown.value.has(stockUniqueId)) { |
|
|
|
// 如果已经显示过,直接显示完整内容,不需要打字机效果 |
|
|
|
|
|
|
|
// 提取音频URL但不自动播放,等待用户手动点击 |
|
|
@ -1037,11 +1047,8 @@ watch( |
|
|
|
} |
|
|
|
|
|
|
|
if (parsedConclusion.value) { |
|
|
|
const stockCode = |
|
|
|
newStock.stockInfo?.code || newStock.stockInfo?.symbol; |
|
|
|
|
|
|
|
// 如果该股票已经显示过,不需要再处理 |
|
|
|
if (stockCode && stockTypewriterShown.value.has(stockCode)) { |
|
|
|
if (stockUniqueId && stockTypewriterShown.value.has(stockUniqueId)) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
@ -1052,9 +1059,9 @@ watch( |
|
|
|
if (isInViewport) { |
|
|
|
console.log("股票切换后检测到场景应用部分在视口中"); |
|
|
|
|
|
|
|
if (stockCode) { |
|
|
|
if (stockUniqueId) { |
|
|
|
// 检查该股票是否是第一次触发 |
|
|
|
if (!stockTypewriterShown.value.has(stockCode)) { |
|
|
|
if (!stockTypewriterShown.value.has(stockUniqueId)) { |
|
|
|
// 如果是用户主动搜索,启动打字机效果和音频播放 |
|
|
|
if (isUserInitiated.value && audioUrl.value) { |
|
|
|
console.log( |
|
|
@ -1063,16 +1070,16 @@ watch( |
|
|
|
hasTriggeredTypewriter.value = true; |
|
|
|
hasTriggeredAudio.value = true; |
|
|
|
|
|
|
|
if (!stockAudioPlayed.value.has(stockCode)) { |
|
|
|
if (!stockAudioPlayed.value.has(stockUniqueId)) { |
|
|
|
console.log("开始音频播放和打字机效果"); |
|
|
|
stockAudioPlayed.value.set(stockCode, true); |
|
|
|
stockAudioPlayed.value.set(stockUniqueId, true); |
|
|
|
playAudioQueue(parsedConclusion.value, true); |
|
|
|
} else { |
|
|
|
// 如果音频已播放过,只启动打字机效果 |
|
|
|
startTypewriterEffect(parsedConclusion.value, stockCode); |
|
|
|
startTypewriterEffect(parsedConclusion.value, stockUniqueId); |
|
|
|
} |
|
|
|
|
|
|
|
stockTypewriterShown.value.set(stockCode, true); |
|
|
|
stockTypewriterShown.value.set(stockUniqueId, true); |
|
|
|
} else if (isUserInitiated.value && !audioUrl.value) { |
|
|
|
console.log( |
|
|
|
"音频尚未准备好,等待音频加载完成后再触发效果(股票切换后)" |
|
|
@ -1087,8 +1094,8 @@ watch( |
|
|
|
const conclusion = parsedConclusion.value; |
|
|
|
// 结论内容现在直接通过parsedConclusion计算属性显示 |
|
|
|
|
|
|
|
stockTypewriterShown.value.set(stockCode, true); |
|
|
|
stockAudioPlayed.value.set(stockCode, true); |
|
|
|
stockTypewriterShown.value.set(stockUniqueId, true); |
|
|
|
stockAudioPlayed.value.set(stockUniqueId, true); |
|
|
|
} |
|
|
|
} else { |
|
|
|
// 非第一次或已经触发过:直接显示完整内容,不播放音频和打字机效果 |
|
|
@ -1218,10 +1225,9 @@ watch( |
|
|
|
|
|
|
|
// 打字机效果函数 |
|
|
|
function startTypewriterEffect(conclusion, stockId, onComplete) { |
|
|
|
// 如果没有传入stockId,使用当前活跃股票 |
|
|
|
// 如果没有传入stockId,使用当前活跃股票的唯一标识符 |
|
|
|
if (!stockId && emotionStore.activeStock) { |
|
|
|
const stock = emotionStore.activeStock; |
|
|
|
stockId = stock.stockInfo?.code || stock.stockInfo?.symbol; |
|
|
|
stockId = getStockUniqueId(emotionStore.activeStock); |
|
|
|
} |
|
|
|
|
|
|
|
if (!stockId) { |
|
|
@ -1501,7 +1507,7 @@ const playNextAudio = () => { |
|
|
|
parsedConclusion.value |
|
|
|
) { |
|
|
|
console.log("🎬 第一个音频开始播放,同时启动打字机效果"); |
|
|
|
const stockId = currentStock?.stockInfo?.code || currentStock?.stockInfo?.symbol; |
|
|
|
const stockId = currentStock ? getStockUniqueId(currentStock) : null; |
|
|
|
startTypewriterEffect(parsedConclusion.value, stockId, audioInfo.onComplete); |
|
|
|
} |
|
|
|
}, |
|
|
@ -1766,7 +1772,7 @@ function playAudioQueue( |
|
|
|
if (shouldStartTypewriter) { |
|
|
|
console.log("没有音频但需要启动打字机效果"); |
|
|
|
const currentStock = emotionStore.activeStock; |
|
|
|
const stockId = currentStock?.stockInfo?.code || currentStock?.stockInfo?.symbol; |
|
|
|
const stockId = currentStock ? getStockUniqueId(currentStock) : null; |
|
|
|
startTypewriterEffect(conclusion, stockId, onComplete); |
|
|
|
} |
|
|
|
} else { |
|
|
@ -2229,19 +2235,16 @@ async function handleSendMessage(input, onComplete) { |
|
|
|
parsedConclusion.value && |
|
|
|
audioUrl.value |
|
|
|
) { |
|
|
|
const stockCode = |
|
|
|
currentStock.value.stockInfo?.code || |
|
|
|
currentStock.value.stockInfo?.symbol; |
|
|
|
if (stockCode && !stockTypewriterShown.value.has(stockCode)) { |
|
|
|
if (!stockAudioPlayed.value.has(stockCode)) { |
|
|
|
stockAudioPlayed.value.set(stockCode, true); |
|
|
|
const stockUniqueId = getStockUniqueId(currentStock.value); |
|
|
|
if (stockUniqueId && !stockTypewriterShown.value.has(stockUniqueId)) { |
|
|
|
if (!stockAudioPlayed.value.has(stockUniqueId)) { |
|
|
|
stockAudioPlayed.value.set(stockUniqueId, true); |
|
|
|
playAudioQueue(parsedConclusion.value, true, onComplete); |
|
|
|
} else { |
|
|
|
// 如果音频已播放过,只启动打字机效果 |
|
|
|
const stockCode = currentStock.value?.stockInfo?.code || currentStock.value?.stockInfo?.symbol; |
|
|
|
startTypewriterEffect(parsedConclusion.value, stockCode, onComplete); |
|
|
|
startTypewriterEffect(parsedConclusion.value, stockUniqueId, onComplete); |
|
|
|
} |
|
|
|
stockTypewriterShown.value.set(stockCode, true); |
|
|
|
stockTypewriterShown.value.set(stockUniqueId, true); |
|
|
|
} else { |
|
|
|
// 如果不需要打字机效果,直接调用完成回调 |
|
|
|
if (onComplete && typeof onComplete === "function") { |
|
|
@ -2899,14 +2902,12 @@ function setupIntersectionObserver() { |
|
|
|
if (entry.isIntersecting) { |
|
|
|
console.log("场景应用部分进入视口"); |
|
|
|
|
|
|
|
// 获取当前股票代码 |
|
|
|
const stockCode = |
|
|
|
currentStock.value?.stockInfo?.code || |
|
|
|
currentStock.value?.stockInfo?.symbol; |
|
|
|
// 获取当前股票唯一标识 |
|
|
|
const stockUniqueId = currentStock.value ? getStockUniqueId(currentStock.value) : null; |
|
|
|
|
|
|
|
if (parsedConclusion.value && stockCode) { |
|
|
|
if (parsedConclusion.value && stockUniqueId) { |
|
|
|
// 检查该股票是否是第一次触发 |
|
|
|
if (!stockTypewriterShown.value.has(stockCode)) { |
|
|
|
if (!stockTypewriterShown.value.has(stockUniqueId)) { |
|
|
|
// 如果是用户主动搜索,启动打字机效果和音频播放 |
|
|
|
if (isUserInitiated.value && audioUrl.value) { |
|
|
|
console.log( |
|
|
@ -2915,10 +2916,10 @@ function setupIntersectionObserver() { |
|
|
|
|
|
|
|
// 用户主动搜索时,每次都播放音频和打字机效果 |
|
|
|
console.log("开始音频播放和打字机效果"); |
|
|
|
stockAudioPlayed.value.set(stockCode, true); |
|
|
|
stockAudioPlayed.value.set(stockUniqueId, true); |
|
|
|
playAudioQueue(parsedConclusion.value, true); |
|
|
|
|
|
|
|
stockTypewriterShown.value.set(stockCode, true); |
|
|
|
stockTypewriterShown.value.set(stockUniqueId, true); |
|
|
|
} else { |
|
|
|
// 非用户主动搜索(如历史记录恢复),直接显示完整内容 |
|
|
|
console.log( |
|
|
@ -2928,8 +2929,8 @@ function setupIntersectionObserver() { |
|
|
|
// 结论内容现在直接通过parsedConclusion计算属性显示 |
|
|
|
|
|
|
|
// 记录该股票已显示过 |
|
|
|
stockTypewriterShown.value.set(stockCode, true); |
|
|
|
stockAudioPlayed.value.set(stockCode, true); |
|
|
|
stockTypewriterShown.value.set(stockUniqueId, true); |
|
|
|
stockAudioPlayed.value.set(stockUniqueId, true); |
|
|
|
} |
|
|
|
} else { |
|
|
|
// 非第一次或已经触发过:直接显示完整内容,不播放音频和打字机效果 |
|
|
@ -3257,12 +3258,10 @@ onMounted(async () => { |
|
|
|
conclusionData.value = currentStockData.conclusionData; |
|
|
|
|
|
|
|
// 标记该股票的打字机效果和音频已经显示过,避免后续自动触发 |
|
|
|
const stockCode = |
|
|
|
currentStockData.stockInfo?.code || |
|
|
|
currentStockData.stockInfo?.symbol; |
|
|
|
if (stockCode) { |
|
|
|
stockTypewriterShown.value.set(stockCode, true); |
|
|
|
stockAudioPlayed.value.set(stockCode, true); |
|
|
|
const stockUniqueId = getStockUniqueId(currentStockData); |
|
|
|
if (stockUniqueId) { |
|
|
|
stockTypewriterShown.value.set(stockUniqueId, true); |
|
|
|
stockAudioPlayed.value.set(stockUniqueId, true); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -4090,11 +4089,10 @@ const emit = defineEmits(["updateMessage", "sendMessage", "ensureAIchat", "enabl |
|
|
|
background-color: #f1f1f1; |
|
|
|
border-radius: 15px; |
|
|
|
align-items: flex-start; |
|
|
|
gap: 10px; |
|
|
|
margin-right: auto; |
|
|
|
/* max-width: 80%; */ |
|
|
|
max-width: 80%; |
|
|
|
width: 100%; |
|
|
|
white-space: normal; |
|
|
|
width: fit-content; |
|
|
|
overflow: visible; |
|
|
|
align-items: center; |
|
|
|
display: flex; |
|
|
@ -4171,12 +4169,11 @@ const emit = defineEmits(["updateMessage", "sendMessage", "ensureAIchat", "enabl |
|
|
|
.ai-message { |
|
|
|
color: #000000; |
|
|
|
font-weight: bold; |
|
|
|
padding: 20px 30px; |
|
|
|
padding-left: 0px; |
|
|
|
padding: 10px 10px; |
|
|
|
border-radius: 15px; |
|
|
|
text-align: left; |
|
|
|
margin-right: auto; |
|
|
|
width: fit-content; |
|
|
|
width: 100%; |
|
|
|
overflow: visible; |
|
|
|
align-items: center; |
|
|
|
display: flex; |
|
|
|