Browse Source

小财神(情绪大模型)场景应用渐出效果;音频播放完成。

ds_hxl
宋杰 2 weeks ago
parent
commit
715cb49429
  1. 359
      src/views/AiEmotion.vue

359
src/views/AiEmotion.vue

@ -123,25 +123,25 @@
<img src="@/assets/img/AiEmotion/场景应用.png" alt="场景应用标题">
<div class="bk-image">
<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.one2">{{ displayedTexts.one2 }}</p>
</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>
</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>
</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>
</div>
<!-- AI生成内容免责声明 -->
<div class="disclaimer-item" v-if="parsedConclusion">
<div class="disclaimer-item" v-if="parsedConclusion && moduleVisibility.disclaimer">
<p class="disclaimer-text">{{ displayedTexts.disclaimer }}</p>
</div>
</div>
@ -196,7 +196,7 @@ const hasTriggeredAudio = ref(false); // 是否已触发音频播放
const hasTriggeredTypewriter = ref(false); //
const intersectionObserver = ref(null); // observer
//
//
const displayedTexts = ref({
one1: '',
one2: '',
@ -205,9 +205,28 @@ const displayedTexts = ref({
four: '',
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 stockTypewriterShown = ref(new Map());
//
const stockAudioPlayed = ref(new Map());
//
const audioUrl = ref('');
@ -261,7 +280,7 @@ watch(currentStock, (newStock) => {
//
if (stockCode && stockTypewriterShown.value.has(stockCode)) {
//
//
if (newStock.conclusionData) {
try {
const conclusion = JSON.parse(newStock.conclusionData);
@ -273,6 +292,20 @@ watch(currentStock, (newStock) => {
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
};
} catch (error) {
console.error('解析结论数据失败:', error);
}
@ -287,10 +320,88 @@ watch(currentStock, (newStock) => {
four: '',
disclaimer: ''
};
displayedTitles.value = {
one: '',
two: '',
three: '',
four: ''
};
moduleVisibility.value = {
one: false,
two: false,
three: false,
four: false,
disclaimer: false
};
}
nextTick(() => {
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 {
isPageLoaded.value = false;
@ -321,7 +432,36 @@ watch(parsedConclusion, (newConclusion) => {
if (voiceUrl && voiceUrl.startsWith('http')) {
console.log('找到并清理后的语音URL:', 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 {
console.log('未找到有效的语音URL,原始URL:', newConclusion.url);
console.log('结论数据中的所有字段:', Object.keys(newConclusion));
@ -331,11 +471,20 @@ watch(parsedConclusion, (newConclusion) => {
//
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 = [];
//
//
displayedTexts.value = {
one1: '',
one2: '',
@ -345,40 +494,116 @@ function startTypewriterEffect(conclusion) {
disclaimer: ''
};
//
const typeSpeed = 250;
let totalDelay = 0;
displayedTitles.value = {
one: '',
two: '',
three: '',
four: ''
};
//
const textKeys = ['one1', 'one2', 'two', 'three', 'four'];
moduleVisibility.value = {
one: false,
two: false,
three: false,
four: false,
disclaimer: false
};
textKeys.forEach((key) => {
if (conclusion[key]) {
const text = conclusion[key];
const startDelay = totalDelay;
//
const typeSpeed = 200;
let totalDelay = 0;
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 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++) {
const timer = setTimeout(() => {
displayedTexts.value.disclaimer = disclaimerText.substring(0, i);
}, disclaimerStartDelay + i * typeSpeed);
}, totalDelay + i * typeSpeed);
typewriterTimers.value.push(timer);
}
}
@ -521,6 +746,10 @@ async function handleSendMessage(input) {
};
console.log('======================================')
//
console.log('第二个工作流接口开始调用,立即开始缓慢滚动');
startAutoScroll();
// fetchData
const [conclusionResult, fetchDataResult] = await Promise.all([
getConclusionAPI(conclusionParams),
@ -766,27 +995,63 @@ function setupIntersectionObserver() {
//
const stockCode = currentStock.value?.stockInfo?.code || currentStock.value?.stockInfo?.symbol;
//
//
if (!hasTriggeredTypewriter.value && parsedConclusion.value && stockCode) {
//
if (!stockTypewriterShown.value.has(stockCode)) {
console.log('开始场景应用打字机效果');
//
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('该股票已显示过打字机效果,跳过');
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
};
}
}
//
if (!hasTriggeredAudio.value && audioUrl.value && parsedConclusion.value) {
console.log('自动触发场景应用音频播放');
hasTriggeredAudio.value = true;
playAudio(audioUrl.value);
}
}
});
},
@ -826,11 +1091,7 @@ onMounted(() => {
// DOM
nextTick(() => {
setupIntersectionObserver();
//
setTimeout(() => {
triggerAutoScroll();
}, 1000); // 1
//
});
});

Loading…
Cancel
Save