|
@ -123,25 +123,25 @@ |
|
|
<img src="@/assets/img/AiEmotion/场景应用.png" alt="场景应用标题"> |
|
|
<img src="@/assets/img/AiEmotion/场景应用.png" alt="场景应用标题"> |
|
|
<div class="bk-image"> |
|
|
<div class="bk-image"> |
|
|
<div class="conclusion-container" v-if="parsedConclusion"> |
|
|
<div class="conclusion-container" v-if="parsedConclusion"> |
|
|
<div class="conclusion-item" v-if="parsedConclusion.one1 || parsedConclusion.one2"> |
|
|
|
|
|
<h4 class="conclusion-title">L1: 情绪监控</h4> |
|
|
|
|
|
|
|
|
<div class="conclusion-item" v-if="(parsedConclusion.one1 || parsedConclusion.one2) && moduleVisibility.one"> |
|
|
|
|
|
<h4 class="conclusion-title">{{ displayedTitles.one }}</h4> |
|
|
<p class="conclusion-text" v-if="parsedConclusion.one1">{{ displayedTexts.one1 }}</p> |
|
|
<p class="conclusion-text" v-if="parsedConclusion.one1">{{ displayedTexts.one1 }}</p> |
|
|
<p class="conclusion-text" v-if="parsedConclusion.one2">{{ displayedTexts.one2 }}</p> |
|
|
<p class="conclusion-text" v-if="parsedConclusion.one2">{{ displayedTexts.one2 }}</p> |
|
|
</div> |
|
|
</div> |
|
|
<div class="conclusion-item" v-if="parsedConclusion.two"> |
|
|
|
|
|
<h4 class="conclusion-title">L2: 情绪解码</h4> |
|
|
|
|
|
|
|
|
<div class="conclusion-item" v-if="parsedConclusion.two && moduleVisibility.two"> |
|
|
|
|
|
<h4 class="conclusion-title">{{ displayedTitles.two }}</h4> |
|
|
<p class="conclusion-text">{{ displayedTexts.two }}</p> |
|
|
<p class="conclusion-text">{{ displayedTexts.two }}</p> |
|
|
</div> |
|
|
</div> |
|
|
<div class="conclusion-item" v-if="parsedConclusion.three"> |
|
|
|
|
|
<h4 class="conclusion-title">L3: 情绪推演</h4> |
|
|
|
|
|
|
|
|
<div class="conclusion-item" v-if="parsedConclusion.three && moduleVisibility.three"> |
|
|
|
|
|
<h4 class="conclusion-title">{{ displayedTitles.three }}</h4> |
|
|
<p class="conclusion-text">{{ displayedTexts.three }}</p> |
|
|
<p class="conclusion-text">{{ displayedTexts.three }}</p> |
|
|
</div> |
|
|
</div> |
|
|
<div class="conclusion-item" v-if="parsedConclusion.four"> |
|
|
|
|
|
<h4 class="conclusion-title">L4: 情绪套利</h4> |
|
|
|
|
|
|
|
|
<div class="conclusion-item" v-if="parsedConclusion.four && moduleVisibility.four"> |
|
|
|
|
|
<h4 class="conclusion-title">{{ displayedTitles.four }}</h4> |
|
|
<p class="conclusion-text">{{ displayedTexts.four }}</p> |
|
|
<p class="conclusion-text">{{ displayedTexts.four }}</p> |
|
|
</div> |
|
|
</div> |
|
|
<!-- AI生成内容免责声明 --> |
|
|
<!-- AI生成内容免责声明 --> |
|
|
<div class="disclaimer-item" v-if="parsedConclusion"> |
|
|
|
|
|
|
|
|
<div class="disclaimer-item" v-if="parsedConclusion && moduleVisibility.disclaimer"> |
|
|
<p class="disclaimer-text">{{ displayedTexts.disclaimer }}</p> |
|
|
<p class="disclaimer-text">{{ displayedTexts.disclaimer }}</p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
@ -196,7 +196,7 @@ const hasTriggeredAudio = ref(false); // 是否已触发音频播放 |
|
|
const hasTriggeredTypewriter = ref(false); // 是否已触发打字机效果 |
|
|
const hasTriggeredTypewriter = ref(false); // 是否已触发打字机效果 |
|
|
const intersectionObserver = ref(null); // 存储observer实例 |
|
|
const intersectionObserver = ref(null); // 存储observer实例 |
|
|
|
|
|
|
|
|
// 打字机效果相关数据 |
|
|
|
|
|
|
|
|
// 显示的文本内容(用于打字机效果) |
|
|
const displayedTexts = ref({ |
|
|
const displayedTexts = ref({ |
|
|
one1: '', |
|
|
one1: '', |
|
|
one2: '', |
|
|
one2: '', |
|
@ -205,9 +205,28 @@ const displayedTexts = ref({ |
|
|
four: '', |
|
|
four: '', |
|
|
disclaimer: '' |
|
|
disclaimer: '' |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
// 显示的标题内容(用于打字机效果) |
|
|
|
|
|
const displayedTitles = ref({ |
|
|
|
|
|
one: '', |
|
|
|
|
|
two: '', |
|
|
|
|
|
three: '', |
|
|
|
|
|
four: '' |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
// 模块显示状态 |
|
|
|
|
|
const moduleVisibility = ref({ |
|
|
|
|
|
one: false, |
|
|
|
|
|
two: false, |
|
|
|
|
|
three: false, |
|
|
|
|
|
four: false, |
|
|
|
|
|
disclaimer: false |
|
|
|
|
|
}); |
|
|
const typewriterTimers = ref([]); |
|
|
const typewriterTimers = ref([]); |
|
|
// 记录每个股票是否已经显示过打字机效果 |
|
|
// 记录每个股票是否已经显示过打字机效果 |
|
|
const stockTypewriterShown = ref(new Map()); |
|
|
const stockTypewriterShown = ref(new Map()); |
|
|
|
|
|
// 记录每个股票是否已经播放过音频 |
|
|
|
|
|
const stockAudioPlayed = ref(new Map()); |
|
|
|
|
|
|
|
|
// 音频播放相关数据 |
|
|
// 音频播放相关数据 |
|
|
const audioUrl = ref(''); |
|
|
const audioUrl = ref(''); |
|
@ -261,7 +280,7 @@ watch(currentStock, (newStock) => { |
|
|
|
|
|
|
|
|
// 检查该股票是否已经显示过打字机效果 |
|
|
// 检查该股票是否已经显示过打字机效果 |
|
|
if (stockCode && stockTypewriterShown.value.has(stockCode)) { |
|
|
if (stockCode && stockTypewriterShown.value.has(stockCode)) { |
|
|
// 如果已经显示过,直接显示完整文本 |
|
|
|
|
|
|
|
|
// 如果已经显示过,直接显示完整文本和标题 |
|
|
if (newStock.conclusionData) { |
|
|
if (newStock.conclusionData) { |
|
|
try { |
|
|
try { |
|
|
const conclusion = JSON.parse(newStock.conclusionData); |
|
|
const conclusion = JSON.parse(newStock.conclusionData); |
|
@ -273,6 +292,20 @@ watch(currentStock, (newStock) => { |
|
|
four: conclusion.four || '', |
|
|
four: conclusion.four || '', |
|
|
disclaimer: '该内容由AI内容生成,请注意甄别' |
|
|
disclaimer: '该内容由AI内容生成,请注意甄别' |
|
|
}; |
|
|
}; |
|
|
|
|
|
displayedTitles.value = { |
|
|
|
|
|
one: 'L1: 情绪监控', |
|
|
|
|
|
two: 'L2: 情绪解码', |
|
|
|
|
|
three: 'L3: 情绪推演', |
|
|
|
|
|
four: 'L4: 情绪套利' |
|
|
|
|
|
}; |
|
|
|
|
|
// 显示所有有内容的模块 |
|
|
|
|
|
moduleVisibility.value = { |
|
|
|
|
|
one: !!(conclusion.one1 || conclusion.one2), |
|
|
|
|
|
two: !!conclusion.two, |
|
|
|
|
|
three: !!conclusion.three, |
|
|
|
|
|
four: !!conclusion.four, |
|
|
|
|
|
disclaimer: true |
|
|
|
|
|
}; |
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
console.error('解析结论数据失败:', error); |
|
|
console.error('解析结论数据失败:', error); |
|
|
} |
|
|
} |
|
@ -287,10 +320,88 @@ watch(currentStock, (newStock) => { |
|
|
four: '', |
|
|
four: '', |
|
|
disclaimer: '' |
|
|
disclaimer: '' |
|
|
}; |
|
|
}; |
|
|
|
|
|
displayedTitles.value = { |
|
|
|
|
|
one: '', |
|
|
|
|
|
two: '', |
|
|
|
|
|
three: '', |
|
|
|
|
|
four: '' |
|
|
|
|
|
}; |
|
|
|
|
|
moduleVisibility.value = { |
|
|
|
|
|
one: false, |
|
|
|
|
|
two: false, |
|
|
|
|
|
three: false, |
|
|
|
|
|
four: false, |
|
|
|
|
|
disclaimer: false |
|
|
|
|
|
}; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
nextTick(() => { |
|
|
nextTick(() => { |
|
|
renderCharts(newStock.apiData); |
|
|
renderCharts(newStock.apiData); |
|
|
|
|
|
|
|
|
|
|
|
// 检查场景应用部分是否已经在视口中,如果是则立即触发效果 |
|
|
|
|
|
setTimeout(() => { |
|
|
|
|
|
if (scenarioApplicationRef.value && parsedConclusion.value) { |
|
|
|
|
|
const rect = scenarioApplicationRef.value.getBoundingClientRect(); |
|
|
|
|
|
const isInViewport = rect.top < window.innerHeight && rect.bottom > 0; |
|
|
|
|
|
|
|
|
|
|
|
if (isInViewport && !hasTriggeredTypewriter.value) { |
|
|
|
|
|
console.log('股票切换后检测到场景应用部分在视口中,立即触发效果'); |
|
|
|
|
|
const stockCode = newStock.stockInfo?.code || newStock.stockInfo?.symbol; |
|
|
|
|
|
|
|
|
|
|
|
if (stockCode) { |
|
|
|
|
|
if (!stockTypewriterShown.value.has(stockCode)) { |
|
|
|
|
|
// 检查音频是否准备好,如果没有准备好则不触发效果 |
|
|
|
|
|
if (!audioUrl.value) { |
|
|
|
|
|
console.log('音频尚未准备好,等待音频加载完成后再触发效果(股票切换后)'); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
console.log('开始场景应用打字机效果和音频播放(股票切换后首次)'); |
|
|
|
|
|
hasTriggeredTypewriter.value = true; |
|
|
|
|
|
hasTriggeredAudio.value = true; |
|
|
|
|
|
|
|
|
|
|
|
startTypewriterEffect(parsedConclusion.value); |
|
|
|
|
|
|
|
|
|
|
|
if (!stockAudioPlayed.value.has(stockCode)) { |
|
|
|
|
|
console.log('同时开始音频播放(股票切换后)'); |
|
|
|
|
|
stockAudioPlayed.value.set(stockCode, true); |
|
|
|
|
|
playAudio(audioUrl.value); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
stockTypewriterShown.value.set(stockCode, true); |
|
|
|
|
|
} else { |
|
|
|
|
|
console.log('该股票已显示过,直接显示完整内容(股票切换后)'); |
|
|
|
|
|
hasTriggeredTypewriter.value = true; |
|
|
|
|
|
hasTriggeredAudio.value = true; |
|
|
|
|
|
|
|
|
|
|
|
// 直接显示完整内容 |
|
|
|
|
|
const conclusion = parsedConclusion.value; |
|
|
|
|
|
displayedTexts.value = { |
|
|
|
|
|
one1: conclusion.one1 || '', |
|
|
|
|
|
one2: conclusion.one2 || '', |
|
|
|
|
|
two: conclusion.two || '', |
|
|
|
|
|
three: conclusion.three || '', |
|
|
|
|
|
four: conclusion.four || '', |
|
|
|
|
|
disclaimer: '该内容由AI内容生成,请注意甄别' |
|
|
|
|
|
}; |
|
|
|
|
|
displayedTitles.value = { |
|
|
|
|
|
one: 'L1: 情绪监控', |
|
|
|
|
|
two: 'L2: 情绪解码', |
|
|
|
|
|
three: 'L3: 情绪推演', |
|
|
|
|
|
four: 'L4: 情绪套利' |
|
|
|
|
|
}; |
|
|
|
|
|
moduleVisibility.value = { |
|
|
|
|
|
one: !!(conclusion.one1 || conclusion.one2), |
|
|
|
|
|
two: !!conclusion.two, |
|
|
|
|
|
three: !!conclusion.three, |
|
|
|
|
|
four: !!conclusion.four, |
|
|
|
|
|
disclaimer: true |
|
|
|
|
|
}; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
}, 500); // 延迟500ms确保数据完全加载 |
|
|
}); |
|
|
}); |
|
|
} else { |
|
|
} else { |
|
|
isPageLoaded.value = false; |
|
|
isPageLoaded.value = false; |
|
@ -321,7 +432,36 @@ watch(parsedConclusion, (newConclusion) => { |
|
|
if (voiceUrl && voiceUrl.startsWith('http')) { |
|
|
if (voiceUrl && voiceUrl.startsWith('http')) { |
|
|
console.log('找到并清理后的语音URL:', voiceUrl); |
|
|
console.log('找到并清理后的语音URL:', voiceUrl); |
|
|
audioUrl.value = voiceUrl; |
|
|
audioUrl.value = voiceUrl; |
|
|
console.log('音频URL已准备,等待滚动触发播放'); |
|
|
|
|
|
|
|
|
console.log('音频URL已准备,检查是否需要立即触发效果'); |
|
|
|
|
|
|
|
|
|
|
|
// 音频准备好后,检查场景应用部分是否已经在视口中 |
|
|
|
|
|
nextTick(() => { |
|
|
|
|
|
setTimeout(() => { |
|
|
|
|
|
if (scenarioApplicationRef.value && currentStock.value?.stockInfo) { |
|
|
|
|
|
const stockCode = currentStock.value.stockInfo.code || currentStock.value.stockInfo.symbol; |
|
|
|
|
|
const rect = scenarioApplicationRef.value.getBoundingClientRect(); |
|
|
|
|
|
const isInViewport = rect.top < window.innerHeight && rect.bottom > 0; |
|
|
|
|
|
|
|
|
|
|
|
if (isInViewport && !hasTriggeredTypewriter.value && parsedConclusion.value && stockCode) { |
|
|
|
|
|
if (!stockTypewriterShown.value.has(stockCode)) { |
|
|
|
|
|
console.log('音频准备完成且场景应用部分在视口中,立即触发效果'); |
|
|
|
|
|
hasTriggeredTypewriter.value = true; |
|
|
|
|
|
hasTriggeredAudio.value = true; |
|
|
|
|
|
|
|
|
|
|
|
startTypewriterEffect(parsedConclusion.value); |
|
|
|
|
|
|
|
|
|
|
|
if (!stockAudioPlayed.value.has(stockCode)) { |
|
|
|
|
|
console.log('立即开始音频播放'); |
|
|
|
|
|
stockAudioPlayed.value.set(stockCode, true); |
|
|
|
|
|
playAudio(audioUrl.value); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
stockTypewriterShown.value.set(stockCode, true); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
}, 100); // 短暂延迟确保DOM更新完成 |
|
|
|
|
|
}); |
|
|
} else { |
|
|
} else { |
|
|
console.log('未找到有效的语音URL,原始URL:', newConclusion.url); |
|
|
console.log('未找到有效的语音URL,原始URL:', newConclusion.url); |
|
|
console.log('结论数据中的所有字段:', Object.keys(newConclusion)); |
|
|
console.log('结论数据中的所有字段:', Object.keys(newConclusion)); |
|
@ -331,11 +471,20 @@ watch(parsedConclusion, (newConclusion) => { |
|
|
|
|
|
|
|
|
// 打字机效果函数 |
|
|
// 打字机效果函数 |
|
|
function startTypewriterEffect(conclusion) { |
|
|
function startTypewriterEffect(conclusion) { |
|
|
|
|
|
console.log('开始打字机效果,结论数据:', conclusion); |
|
|
|
|
|
|
|
|
|
|
|
// 详细调试各个字段 |
|
|
|
|
|
console.log('L1字段 - one1:', conclusion.one1); |
|
|
|
|
|
console.log('L1字段 - one2:', conclusion.one2); |
|
|
|
|
|
console.log('L2字段 - two:', conclusion.two); |
|
|
|
|
|
console.log('L3字段 - three:', conclusion.three); |
|
|
|
|
|
console.log('L4字段 - four:', conclusion.four); |
|
|
|
|
|
|
|
|
// 清除之前的定时器 |
|
|
// 清除之前的定时器 |
|
|
typewriterTimers.value.forEach(timer => clearTimeout(timer)); |
|
|
typewriterTimers.value.forEach(timer => clearTimeout(timer)); |
|
|
typewriterTimers.value = []; |
|
|
typewriterTimers.value = []; |
|
|
|
|
|
|
|
|
// 清空之前的显示文本 |
|
|
|
|
|
|
|
|
// 重置显示文本和状态 |
|
|
displayedTexts.value = { |
|
|
displayedTexts.value = { |
|
|
one1: '', |
|
|
one1: '', |
|
|
one2: '', |
|
|
one2: '', |
|
@ -344,41 +493,117 @@ function startTypewriterEffect(conclusion) { |
|
|
four: '', |
|
|
four: '', |
|
|
disclaimer: '' |
|
|
disclaimer: '' |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
displayedTitles.value = { |
|
|
|
|
|
one: '', |
|
|
|
|
|
two: '', |
|
|
|
|
|
three: '', |
|
|
|
|
|
four: '' |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
moduleVisibility.value = { |
|
|
|
|
|
one: false, |
|
|
|
|
|
two: false, |
|
|
|
|
|
three: false, |
|
|
|
|
|
four: false, |
|
|
|
|
|
disclaimer: false |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
// 定义打字速度(毫秒) |
|
|
// 定义打字速度(毫秒) |
|
|
const typeSpeed = 250; |
|
|
|
|
|
|
|
|
const typeSpeed = 200; |
|
|
let totalDelay = 0; |
|
|
let totalDelay = 0; |
|
|
|
|
|
|
|
|
// 为每个文本创建打字机效果 |
|
|
|
|
|
const textKeys = ['one1', 'one2', 'two', 'three', 'four']; |
|
|
|
|
|
|
|
|
|
|
|
textKeys.forEach((key) => { |
|
|
|
|
|
if (conclusion[key]) { |
|
|
|
|
|
const text = conclusion[key]; |
|
|
|
|
|
const startDelay = totalDelay; |
|
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i <= text.length; i++) { |
|
|
|
|
|
const timer = setTimeout(() => { |
|
|
|
|
|
displayedTexts.value[key] = text.substring(0, i); |
|
|
|
|
|
}, startDelay + i * typeSpeed); |
|
|
|
|
|
|
|
|
// 定义模块配置 |
|
|
|
|
|
const modules = [ |
|
|
|
|
|
{ |
|
|
|
|
|
key: 'one', |
|
|
|
|
|
title: 'L1: 情绪监控', |
|
|
|
|
|
contents: [ |
|
|
|
|
|
{ key: 'one1', text: conclusion.one1 }, |
|
|
|
|
|
{ key: 'one2', text: conclusion.one2 } |
|
|
|
|
|
] |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
key: 'two', |
|
|
|
|
|
title: 'L2: 情绪解码', |
|
|
|
|
|
contents: [ |
|
|
|
|
|
{ key: 'two', text: conclusion.two } |
|
|
|
|
|
] |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
key: 'three', |
|
|
|
|
|
title: 'L3: 情绪推演', |
|
|
|
|
|
contents: [ |
|
|
|
|
|
{ key: 'three', text: conclusion.three } |
|
|
|
|
|
] |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
key: 'four', |
|
|
|
|
|
title: 'L4: 情绪套利', |
|
|
|
|
|
contents: [ |
|
|
|
|
|
{ key: 'four', text: conclusion.four } |
|
|
|
|
|
] |
|
|
|
|
|
} |
|
|
|
|
|
]; |
|
|
|
|
|
|
|
|
typewriterTimers.value.push(timer); |
|
|
|
|
|
|
|
|
// 按模块顺序处理 |
|
|
|
|
|
modules.forEach((module) => { |
|
|
|
|
|
// 检查模块是否有内容 |
|
|
|
|
|
const hasContent = module.contents.some(content => content.text && content.text.trim()); |
|
|
|
|
|
console.log(`模块 ${module.key} 是否有内容:`, hasContent, '内容:', module.contents.map(c => c.text)); |
|
|
|
|
|
if (!hasContent) return; |
|
|
|
|
|
|
|
|
|
|
|
console.log(`开始显示模块 ${module.key}`); |
|
|
|
|
|
// 显示模块 |
|
|
|
|
|
const showModuleTimer = setTimeout(() => { |
|
|
|
|
|
moduleVisibility.value[module.key] = true; |
|
|
|
|
|
console.log(`模块 ${module.key} 已设置为可见`); |
|
|
|
|
|
}, totalDelay); |
|
|
|
|
|
typewriterTimers.value.push(showModuleTimer); |
|
|
|
|
|
totalDelay += 100; |
|
|
|
|
|
|
|
|
|
|
|
// 打字机效果显示标题 |
|
|
|
|
|
const title = module.title; |
|
|
|
|
|
for (let i = 0; i <= title.length; i++) { |
|
|
|
|
|
const timer = setTimeout(() => { |
|
|
|
|
|
displayedTitles.value[module.key] = title.substring(0, i); |
|
|
|
|
|
}, totalDelay + i * typeSpeed); |
|
|
|
|
|
typewriterTimers.value.push(timer); |
|
|
|
|
|
} |
|
|
|
|
|
totalDelay += title.length * typeSpeed + 300; // 标题完成后间隔 |
|
|
|
|
|
|
|
|
|
|
|
// 打字机效果显示内容 |
|
|
|
|
|
module.contents.forEach((content) => { |
|
|
|
|
|
if (content.text && content.text.trim()) { |
|
|
|
|
|
const text = content.text; |
|
|
|
|
|
for (let i = 0; i <= text.length; i++) { |
|
|
|
|
|
const timer = setTimeout(() => { |
|
|
|
|
|
displayedTexts.value[content.key] = text.substring(0, i); |
|
|
|
|
|
}, totalDelay + i * typeSpeed); |
|
|
|
|
|
typewriterTimers.value.push(timer); |
|
|
|
|
|
} |
|
|
|
|
|
totalDelay += text.length * typeSpeed + 500; // 内容完成后间隔 |
|
|
} |
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
// 更新总延迟,为下一个文本留出时间 |
|
|
|
|
|
totalDelay += text.length * typeSpeed + 300; // 额外300ms间隔 |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
totalDelay += 800; // 模块间间隔 |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// 添加免责声明的打字机效果(在所有内容显示完成后) |
|
|
|
|
|
|
|
|
// 添加免责声明的打字机效果(在所有模块显示完成后) |
|
|
const disclaimerText = '该内容由AI内容生成,请注意甄别'; |
|
|
const disclaimerText = '该内容由AI内容生成,请注意甄别'; |
|
|
const disclaimerStartDelay = totalDelay + 500; // 额外500ms间隔 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 显示免责声明模块 |
|
|
|
|
|
const showDisclaimerTimer = setTimeout(() => { |
|
|
|
|
|
moduleVisibility.value.disclaimer = true; |
|
|
|
|
|
}, totalDelay); |
|
|
|
|
|
typewriterTimers.value.push(showDisclaimerTimer); |
|
|
|
|
|
totalDelay += 100; |
|
|
|
|
|
|
|
|
|
|
|
// 打字机效果显示免责声明 |
|
|
for (let i = 0; i <= disclaimerText.length; i++) { |
|
|
for (let i = 0; i <= disclaimerText.length; i++) { |
|
|
const timer = setTimeout(() => { |
|
|
const timer = setTimeout(() => { |
|
|
displayedTexts.value.disclaimer = disclaimerText.substring(0, i); |
|
|
displayedTexts.value.disclaimer = disclaimerText.substring(0, i); |
|
|
}, disclaimerStartDelay + i * typeSpeed); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}, totalDelay + i * typeSpeed); |
|
|
typewriterTimers.value.push(timer); |
|
|
typewriterTimers.value.push(timer); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
@ -521,6 +746,10 @@ async function handleSendMessage(input) { |
|
|
}; |
|
|
}; |
|
|
console.log('======================================') |
|
|
console.log('======================================') |
|
|
|
|
|
|
|
|
|
|
|
// 在调用第二个工作流接口前立即开始缓慢滚动 |
|
|
|
|
|
console.log('第二个工作流接口开始调用,立即开始缓慢滚动'); |
|
|
|
|
|
startAutoScroll(); |
|
|
|
|
|
|
|
|
// 同时调用第二个数据流接口和fetchData方法 |
|
|
// 同时调用第二个数据流接口和fetchData方法 |
|
|
const [conclusionResult, fetchDataResult] = await Promise.all([ |
|
|
const [conclusionResult, fetchDataResult] = await Promise.all([ |
|
|
getConclusionAPI(conclusionParams), |
|
|
getConclusionAPI(conclusionParams), |
|
@ -766,27 +995,63 @@ function setupIntersectionObserver() { |
|
|
// 获取当前股票代码 |
|
|
// 获取当前股票代码 |
|
|
const stockCode = currentStock.value?.stockInfo?.code || currentStock.value?.stockInfo?.symbol; |
|
|
const stockCode = currentStock.value?.stockInfo?.code || currentStock.value?.stockInfo?.symbol; |
|
|
|
|
|
|
|
|
// 触发打字机效果 |
|
|
|
|
|
|
|
|
// 同时触发打字机效果和音频播放 |
|
|
if (!hasTriggeredTypewriter.value && parsedConclusion.value && stockCode) { |
|
|
if (!hasTriggeredTypewriter.value && parsedConclusion.value && stockCode) { |
|
|
// 检查该股票是否已经显示过打字机效果 |
|
|
// 检查该股票是否已经显示过打字机效果 |
|
|
if (!stockTypewriterShown.value.has(stockCode)) { |
|
|
if (!stockTypewriterShown.value.has(stockCode)) { |
|
|
console.log('开始场景应用打字机效果'); |
|
|
|
|
|
|
|
|
// 检查音频是否准备好,如果没有准备好则不触发效果 |
|
|
|
|
|
if (!audioUrl.value) { |
|
|
|
|
|
console.log('音频尚未准备好,等待音频加载完成后再触发效果'); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
console.log('开始场景应用打字机效果和音频播放(首次加载)'); |
|
|
hasTriggeredTypewriter.value = true; |
|
|
hasTriggeredTypewriter.value = true; |
|
|
|
|
|
hasTriggeredAudio.value = true; |
|
|
|
|
|
|
|
|
|
|
|
// 同时开始打字机效果和音频播放 |
|
|
startTypewriterEffect(parsedConclusion.value); |
|
|
startTypewriterEffect(parsedConclusion.value); |
|
|
|
|
|
|
|
|
|
|
|
// 立即开始播放音频 |
|
|
|
|
|
if (!stockAudioPlayed.value.has(stockCode)) { |
|
|
|
|
|
console.log('同时开始音频播放'); |
|
|
|
|
|
stockAudioPlayed.value.set(stockCode, true); |
|
|
|
|
|
playAudio(audioUrl.value); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 记录该股票已显示过打字机效果 |
|
|
// 记录该股票已显示过打字机效果 |
|
|
stockTypewriterShown.value.set(stockCode, true); |
|
|
stockTypewriterShown.value.set(stockCode, true); |
|
|
} else { |
|
|
} else { |
|
|
console.log('该股票已显示过打字机效果,跳过'); |
|
|
|
|
|
|
|
|
console.log('该股票已显示过打字机效果,直接显示完整内容'); |
|
|
hasTriggeredTypewriter.value = true; |
|
|
hasTriggeredTypewriter.value = true; |
|
|
|
|
|
hasTriggeredAudio.value = true; |
|
|
|
|
|
|
|
|
|
|
|
// 直接显示完整内容,不使用打字机效果 |
|
|
|
|
|
const conclusion = parsedConclusion.value; |
|
|
|
|
|
displayedTexts.value = { |
|
|
|
|
|
one1: conclusion.one1 || '', |
|
|
|
|
|
one2: conclusion.one2 || '', |
|
|
|
|
|
two: conclusion.two || '', |
|
|
|
|
|
three: conclusion.three || '', |
|
|
|
|
|
four: conclusion.four || '', |
|
|
|
|
|
disclaimer: '该内容由AI内容生成,请注意甄别' |
|
|
|
|
|
}; |
|
|
|
|
|
displayedTitles.value = { |
|
|
|
|
|
one: 'L1: 情绪监控', |
|
|
|
|
|
two: 'L2: 情绪解码', |
|
|
|
|
|
three: 'L3: 情绪推演', |
|
|
|
|
|
four: 'L4: 情绪套利' |
|
|
|
|
|
}; |
|
|
|
|
|
// 显示所有有内容的模块 |
|
|
|
|
|
moduleVisibility.value = { |
|
|
|
|
|
one: !!(conclusion.one1 || conclusion.one2), |
|
|
|
|
|
two: !!conclusion.two, |
|
|
|
|
|
three: !!conclusion.three, |
|
|
|
|
|
four: !!conclusion.four, |
|
|
|
|
|
disclaimer: true |
|
|
|
|
|
}; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 触发音频播放 |
|
|
|
|
|
if (!hasTriggeredAudio.value && audioUrl.value && parsedConclusion.value) { |
|
|
|
|
|
console.log('自动触发场景应用音频播放'); |
|
|
|
|
|
hasTriggeredAudio.value = true; |
|
|
|
|
|
playAudio(audioUrl.value); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
}, |
|
|
}, |
|
@ -826,11 +1091,7 @@ onMounted(() => { |
|
|
// 等待DOM完全渲染后设置监听器 |
|
|
// 等待DOM完全渲染后设置监听器 |
|
|
nextTick(() => { |
|
|
nextTick(() => { |
|
|
setupIntersectionObserver(); |
|
|
setupIntersectionObserver(); |
|
|
|
|
|
|
|
|
// 页面加载完成后自动开始滚动 |
|
|
|
|
|
setTimeout(() => { |
|
|
|
|
|
triggerAutoScroll(); |
|
|
|
|
|
}, 1000); // 延迟1秒开始滚动,确保页面完全渲染 |
|
|
|
|
|
|
|
|
// 移除自动滚动触发,改为在第二个工作流接口调用时触发 |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|