|
@@ -68,6 +68,19 @@
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
+ <!-- 视频显示区域 -->
|
|
|
|
|
+ <div v-if="currentDialogue && currentDialogue.type === 'video'" class="video-display">
|
|
|
|
|
+ <video
|
|
|
|
|
+ :src="currentDialogue.videoUrl"
|
|
|
|
|
+ class="dialogue-video"
|
|
|
|
|
+ controls
|
|
|
|
|
+ autoplay
|
|
|
|
|
+ @ended="handleVideoEnded"
|
|
|
|
|
+ >
|
|
|
|
|
+ 您的浏览器不支持视频播放
|
|
|
|
|
+ </video>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
<!-- 用户输入卡片 -->
|
|
<!-- 用户输入卡片 -->
|
|
|
<div
|
|
<div
|
|
|
v-if="currentDialogue.type === 'user'"
|
|
v-if="currentDialogue.type === 'user'"
|
|
@@ -116,7 +129,12 @@
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- <img :src="currentBackgroundImage" alt="背景图" class="background-image">
|
|
|
|
|
|
|
+ <!-- 背景图 -->
|
|
|
|
|
+ <img v-if="currentBackgroundType === 'imageAudio'" :src="currentBackgroundImage" alt="背景图" class="background-image">
|
|
|
|
|
+ <!-- 背景视频 -->
|
|
|
|
|
+ <video v-else-if="currentBackgroundType === 'video'" ref="backgroundVideoRef" :src="currentBackgroundVideo" class="background-video" loop muted playsinline>
|
|
|
|
|
+ 您的浏览器不支持视频播放
|
|
|
|
|
+ </video>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
@@ -182,8 +200,12 @@ const currentPoemContent = ref('')
|
|
|
const backgroundAudio = ref(null)
|
|
const backgroundAudio = ref(null)
|
|
|
// 对话音频
|
|
// 对话音频
|
|
|
const dialogueAudio = ref(null)
|
|
const dialogueAudio = ref(null)
|
|
|
|
|
+// 背景视频
|
|
|
|
|
+const backgroundVideoRef = ref(null)
|
|
|
// 遮罩层显示状态
|
|
// 遮罩层显示状态
|
|
|
const showMask = ref(true)
|
|
const showMask = ref(true)
|
|
|
|
|
+// 是否开始播放
|
|
|
|
|
+const isPlaybackStarted = ref(false)
|
|
|
|
|
|
|
|
// 计算属性
|
|
// 计算属性
|
|
|
// 当前章节信息
|
|
// 当前章节信息
|
|
@@ -203,6 +225,19 @@ const currentBackgroundImage = computed(() => {
|
|
|
return currentSection.value.backgroundImage.url
|
|
return currentSection.value.backgroundImage.url
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
|
|
+// 当前背景类型
|
|
|
|
|
+const currentBackgroundType = computed(() => {
|
|
|
|
|
+ return currentSection.value?.backgroundType || 'imageAudio'
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+// 当前背景视频
|
|
|
|
|
+const currentBackgroundVideo = computed(() => {
|
|
|
|
|
+ if (!currentSection.value || !currentSection.value.backgroundVideo || !currentSection.value.backgroundVideo.url) {
|
|
|
|
|
+ return ''
|
|
|
|
|
+ }
|
|
|
|
|
+ return currentSection.value.backgroundVideo.url
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
// 当前对话缓存
|
|
// 当前对话缓存
|
|
|
const currentDialogueCache = ref(null)
|
|
const currentDialogueCache = ref(null)
|
|
|
|
|
|
|
@@ -296,6 +331,9 @@ const stopAllAudio = () => {
|
|
|
dialogueAudio.value.pause()
|
|
dialogueAudio.value.pause()
|
|
|
dialogueAudio.value.currentTime = 0
|
|
dialogueAudio.value.currentTime = 0
|
|
|
}
|
|
}
|
|
|
|
|
+ if (backgroundVideoRef.value) {
|
|
|
|
|
+ backgroundVideoRef.value.pause()
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const playBackgroundAudio = () => {
|
|
const playBackgroundAudio = () => {
|
|
@@ -305,13 +343,24 @@ const playBackgroundAudio = () => {
|
|
|
backgroundAudio.value.currentTime = 0
|
|
backgroundAudio.value.currentTime = 0
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 播放当前环节的背景音
|
|
|
|
|
- if (currentSection.value?.backgroundAudio?.url && isPlaying.value) {
|
|
|
|
|
|
|
+ // 只有当背景类型为 imageAudio 时才播放背景音频
|
|
|
|
|
+ if (currentBackgroundType.value === 'imageAudio' && currentSection.value?.backgroundAudio?.url && isPlaying.value) {
|
|
|
backgroundAudio.value = new Audio(currentSection.value.backgroundAudio.url)
|
|
backgroundAudio.value = new Audio(currentSection.value.backgroundAudio.url)
|
|
|
backgroundAudio.value.loop = true
|
|
backgroundAudio.value.loop = true
|
|
|
backgroundAudio.value.volume = 1
|
|
backgroundAudio.value.volume = 1
|
|
|
backgroundAudio.value.play().catch(e => console.error('背景音播放失败:', e))
|
|
backgroundAudio.value.play().catch(e => console.error('背景音播放失败:', e))
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // 处理背景视频
|
|
|
|
|
+ if (currentBackgroundType.value === 'video' && isPlaybackStarted.value && isPlaying.value) {
|
|
|
|
|
+ // 视频已经在模板中渲染,这里只需要确保它在播放状态
|
|
|
|
|
+ if (backgroundVideoRef.value) {
|
|
|
|
|
+ backgroundVideoRef.value.play().catch(e => console.error('背景视频播放失败:', e))
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (backgroundVideoRef.value) {
|
|
|
|
|
+ // 暂停视频
|
|
|
|
|
+ backgroundVideoRef.value.pause()
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const playDialogueAudio = (isAutoPlay = false) => {
|
|
const playDialogueAudio = (isAutoPlay = false) => {
|
|
@@ -321,11 +370,6 @@ const playDialogueAudio = (isAutoPlay = false) => {
|
|
|
dialogueAudio.value.currentTime = 0
|
|
dialogueAudio.value.currentTime = 0
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 如果是诗词类型,不播放音频
|
|
|
|
|
- if (currentDialogue.value?.type === 'poem') {
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
// 播放当前对话的语音
|
|
// 播放当前对话的语音
|
|
|
if (currentDialogue.value?.voiceoverUrl) {
|
|
if (currentDialogue.value?.voiceoverUrl) {
|
|
|
const audio = new Audio(currentDialogue.value.voiceoverUrl)
|
|
const audio = new Audio(currentDialogue.value.voiceoverUrl)
|
|
@@ -414,16 +458,36 @@ const togglePlay = () => {
|
|
|
if (isPlaying.value) {
|
|
if (isPlaying.value) {
|
|
|
// 播放背景音
|
|
// 播放背景音
|
|
|
playBackgroundAudio()
|
|
playBackgroundAudio()
|
|
|
|
|
+ // 继续播放背景视频
|
|
|
|
|
+ if (backgroundVideoRef.value) {
|
|
|
|
|
+ backgroundVideoRef.value.play().catch(e => console.error('背景视频播放失败:', e))
|
|
|
|
|
+ }
|
|
|
if(!getIsPlaying() && !conversationInProgress.value){
|
|
if(!getIsPlaying() && !conversationInProgress.value){
|
|
|
// 开始播放序列
|
|
// 开始播放序列
|
|
|
playSequence()
|
|
playSequence()
|
|
|
}
|
|
}
|
|
|
} else {
|
|
} else {
|
|
|
- // 暂停所有音频
|
|
|
|
|
|
|
+ // 暂停所有音频和视频
|
|
|
stopAllAudio()
|
|
stopAllAudio()
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// 处理视频结束事件
|
|
|
|
|
+const handleVideoEnded = () => {
|
|
|
|
|
+ // 视频播放完毕后,继续播放下一条对话
|
|
|
|
|
+ if (isPlaying.value) {
|
|
|
|
|
+ if (!playNext(true)) {
|
|
|
|
|
+ // 播放完毕,检查是否是最后一个对话
|
|
|
|
|
+ if (isAtLastDialogue()) {
|
|
|
|
|
+ console.log('视频序列:已到达最后一个对话,触发 dialogueEnded 事件');
|
|
|
|
|
+ emit('dialogueEnded', props.isLastCourse);
|
|
|
|
|
+ }
|
|
|
|
|
+ isPlaying.value = false
|
|
|
|
|
+ stopAllAudio()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// 自动播放序列
|
|
// 自动播放序列
|
|
|
const playSequence = () => {
|
|
const playSequence = () => {
|
|
|
if (!isPlaying.value) return
|
|
if (!isPlaying.value) return
|
|
@@ -436,18 +500,26 @@ const playSequence = () => {
|
|
|
// 显示诗词并替换内容为最新的诗词
|
|
// 显示诗词并替换内容为最新的诗词
|
|
|
showPoem.value = true
|
|
showPoem.value = true
|
|
|
currentPoemContent.value = currentDialogue.value.content
|
|
currentPoemContent.value = currentDialogue.value.content
|
|
|
- // 自动切换到下一条对话
|
|
|
|
|
- setTimeout(() => {
|
|
|
|
|
- if (!playNext(true)) {
|
|
|
|
|
- // 播放完毕,检查是否是最后一个对话
|
|
|
|
|
- if (isAtLastDialogue()) {
|
|
|
|
|
- console.log('诗词序列:已到达最后一个对话,触发 dialogueEnded 事件');
|
|
|
|
|
- emit('dialogueEnded', props.isLastCourse);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 检查是否有语音
|
|
|
|
|
+ if (currentDialogue.value?.voiceoverUrl) {
|
|
|
|
|
+ // 播放诗词语音
|
|
|
|
|
+ playDialogueAudio(true)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 没有语音,直接切换到下一条对话
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ if (!playNext(true)) {
|
|
|
|
|
+ // 播放完毕,检查是否是最后一个对话
|
|
|
|
|
+ if (isAtLastDialogue()) {
|
|
|
|
|
+ console.log('诗词序列:已到达最后一个对话,触发 dialogueEnded 事件');
|
|
|
|
|
+ emit('dialogueEnded', props.isLastCourse);
|
|
|
|
|
+ }
|
|
|
|
|
+ // 播放完毕
|
|
|
|
|
+ isPlaying.value = false
|
|
|
|
|
+ stopAllAudio()
|
|
|
}
|
|
}
|
|
|
- isPlaying.value = false
|
|
|
|
|
- stopAllAudio()
|
|
|
|
|
- }
|
|
|
|
|
- }, 500)
|
|
|
|
|
|
|
+ }, 500)
|
|
|
|
|
+ }
|
|
|
} else {
|
|
} else {
|
|
|
// 播放当前对话语音,传递isAutoPlay参数
|
|
// 播放当前对话语音,传递isAutoPlay参数
|
|
|
playDialogueAudio(true)
|
|
playDialogueAudio(true)
|
|
@@ -486,6 +558,10 @@ const playPrevious = () => {
|
|
|
// 显示诗词并替换内容为最新的诗词
|
|
// 显示诗词并替换内容为最新的诗词
|
|
|
showPoem.value = true
|
|
showPoem.value = true
|
|
|
currentPoemContent.value = currentDialogue.value.content
|
|
currentPoemContent.value = currentDialogue.value.content
|
|
|
|
|
+ } else if (currentDialogue.value?.type === 'video') {
|
|
|
|
|
+ // 视频类型对话,隐藏诗词
|
|
|
|
|
+ showPoem.value = false
|
|
|
|
|
+ currentPoemContent.value = ""
|
|
|
} else {
|
|
} else {
|
|
|
showPoem.value = false
|
|
showPoem.value = false
|
|
|
currentPoemContent.value = ""
|
|
currentPoemContent.value = ""
|
|
@@ -539,10 +615,26 @@ const playNext = (isAutoPlay = false) => {
|
|
|
// 显示诗词并替换内容为最新的诗词
|
|
// 显示诗词并替换内容为最新的诗词
|
|
|
showPoem.value = true
|
|
showPoem.value = true
|
|
|
currentPoemContent.value = currentDialogue.value.content
|
|
currentPoemContent.value = currentDialogue.value.content
|
|
|
- // 自动切换到下一条对话,不需要播放音频
|
|
|
|
|
- setTimeout(() => {
|
|
|
|
|
- playNext(isAutoPlay)
|
|
|
|
|
- }, 500)
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 检查是否有语音
|
|
|
|
|
+ if (currentDialogue.value?.voiceoverUrl) {
|
|
|
|
|
+ // 播放诗词语音
|
|
|
|
|
+ if (isPlaying.value) {
|
|
|
|
|
+ playDialogueAudio(true)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ playDialogueAudio()
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 没有语音,直接切换到下一条对话
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ playNext(isAutoPlay)
|
|
|
|
|
+ }, 500)
|
|
|
|
|
+ }
|
|
|
|
|
+ return true
|
|
|
|
|
+ } else if (currentDialogue.value?.type === 'video') {
|
|
|
|
|
+ // 视频类型对话,隐藏诗词,不自动播放下一条
|
|
|
|
|
+ showPoem.value = false
|
|
|
|
|
+ currentPoemContent.value = ''
|
|
|
return true
|
|
return true
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -566,10 +658,25 @@ const playNext = (isAutoPlay = false) => {
|
|
|
// 显示诗词
|
|
// 显示诗词
|
|
|
showPoem.value = true
|
|
showPoem.value = true
|
|
|
currentPoemContent.value = currentDialogue.value.content
|
|
currentPoemContent.value = currentDialogue.value.content
|
|
|
- // 自动切换到下一条对话,不需要播放音频
|
|
|
|
|
- setTimeout(() => {
|
|
|
|
|
- playNext(isAutoPlay)
|
|
|
|
|
- }, 500)
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 检查是否有语音
|
|
|
|
|
+ if (currentDialogue.value?.voiceoverUrl) {
|
|
|
|
|
+ // 播放诗词语音
|
|
|
|
|
+ if (isPlaying.value) {
|
|
|
|
|
+ playDialogueAudio(true)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ playDialogueAudio()
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 没有语音,直接切换到下一条对话
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ playNext(isAutoPlay)
|
|
|
|
|
+ }, 500)
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (currentDialogue.value?.type === 'video') {
|
|
|
|
|
+ // 视频类型对话,隐藏诗词
|
|
|
|
|
+ showPoem.value = false
|
|
|
|
|
+ currentPoemContent.value = ''
|
|
|
} else {
|
|
} else {
|
|
|
// 根据是否为自动播放状态决定如何播放语音
|
|
// 根据是否为自动播放状态决定如何播放语音
|
|
|
if (isPlaying.value) {
|
|
if (isPlaying.value) {
|
|
@@ -594,6 +701,14 @@ const goBackToMain = () => {
|
|
|
// 开始播放
|
|
// 开始播放
|
|
|
const maskLayer = ref(null)
|
|
const maskLayer = ref(null)
|
|
|
const startPlayback = () => {
|
|
const startPlayback = () => {
|
|
|
|
|
+ // 设置开始播放状态
|
|
|
|
|
+ isPlaybackStarted.value = true
|
|
|
|
|
+
|
|
|
|
|
+ // 开始播放背景视频
|
|
|
|
|
+ if (backgroundVideoRef.value) {
|
|
|
|
|
+ backgroundVideoRef.value.play().catch(e => console.error('背景视频播放失败:', e))
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 消失动画
|
|
// 消失动画
|
|
|
if (maskLayer.value) {
|
|
if (maskLayer.value) {
|
|
|
maskLayer.value.classList.add('fade-out')
|
|
maskLayer.value.classList.add('fade-out')
|
|
@@ -608,10 +723,21 @@ const startPlayback = () => {
|
|
|
// 显示诗词
|
|
// 显示诗词
|
|
|
showPoem.value = true
|
|
showPoem.value = true
|
|
|
currentPoemContent.value = currentDialogue.value.content
|
|
currentPoemContent.value = currentDialogue.value.content
|
|
|
- // 自动切换到下一条对话
|
|
|
|
|
- setTimeout(() => {
|
|
|
|
|
- playNext()
|
|
|
|
|
- }, 500)
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 检查是否有语音
|
|
|
|
|
+ if (currentDialogue.value?.voiceoverUrl) {
|
|
|
|
|
+ // 播放诗词语音
|
|
|
|
|
+ playDialogueAudio()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 没有语音,直接切换到下一条对话
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ playNext()
|
|
|
|
|
+ }, 500)
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (currentDialogue.value?.type === 'video') {
|
|
|
|
|
+ // 视频类型对话,隐藏诗词
|
|
|
|
|
+ showPoem.value = false
|
|
|
|
|
+ currentPoemContent.value = ''
|
|
|
} else {
|
|
} else {
|
|
|
// 播放当前对话语音
|
|
// 播放当前对话语音
|
|
|
playDialogueAudio()
|
|
playDialogueAudio()
|
|
@@ -1059,6 +1185,14 @@ onUnmounted(() => {
|
|
|
position: relative;
|
|
position: relative;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+.background-video {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ object-fit: cover;
|
|
|
|
|
+ z-index: 1;
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
/* 遮罩层样式 */
|
|
/* 遮罩层样式 */
|
|
|
.mask-layer {
|
|
.mask-layer {
|
|
|
position: absolute;
|
|
position: absolute;
|
|
@@ -1732,7 +1866,7 @@ onUnmounted(() => {
|
|
|
background-size: 100%;
|
|
background-size: 100%;
|
|
|
background-repeat: no-repeat;
|
|
background-repeat: no-repeat;
|
|
|
background-position: center;
|
|
background-position: center;
|
|
|
- opacity: 0.5;
|
|
|
|
|
|
|
+ opacity: 0.8;
|
|
|
z-index: -1;
|
|
z-index: -1;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1755,7 +1889,8 @@ onUnmounted(() => {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
justify-content: center;
|
|
|
- flex-direction: column;
|
|
|
|
|
|
|
+ //flex-direction: column;
|
|
|
|
|
+ text-align: center;
|
|
|
transition: all 0.5s ease-in-out;
|
|
transition: all 0.5s ease-in-out;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1775,6 +1910,22 @@ onUnmounted(() => {
|
|
|
background: rgba(0, 0, 0, 0.5);
|
|
background: rgba(0, 0, 0, 0.5);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+.video-display {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ top: 45%;
|
|
|
|
|
+ left: 50%;
|
|
|
|
|
+ transform: translate(-50%, -50%);
|
|
|
|
|
+ z-index: 5;
|
|
|
|
|
+ width: 65%;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.dialogue-video {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ border-radius: rpx(10);
|
|
|
|
|
+ box-shadow: 0 rpx(5) rpx(15) rgba(0, 0, 0, 0.2);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
.poem-text::before {
|
|
.poem-text::before {
|
|
|
content: '';
|
|
content: '';
|
|
|
position: absolute;
|
|
position: absolute;
|