|
@@ -1,13 +1,21 @@
|
|
|
<template>
|
|
<template>
|
|
|
<div class="dialog-content-wrapper">
|
|
<div class="dialog-content-wrapper">
|
|
|
|
|
+ <!-- 遮罩层 -->
|
|
|
|
|
+ <div v-if="showMask" class="mask-layer" ref="maskLayer">
|
|
|
|
|
+ <div class="play-button-container">
|
|
|
|
|
+ <button class="play-button" @click="startPlayback">
|
|
|
|
|
+ <el-icon class="play-icon"><VideoPlay /></el-icon>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
<!-- 标题 -->
|
|
<!-- 标题 -->
|
|
|
<div class="title-box">
|
|
<div class="title-box">
|
|
|
<!-- 返回 -->
|
|
<!-- 返回 -->
|
|
|
<div class="title-left">
|
|
<div class="title-left">
|
|
|
- <div class="back-icon-circle" @click="goBackToMain">
|
|
|
|
|
- <el-icon class="back-icon"><ArrowLeftBold /></el-icon>
|
|
|
|
|
|
|
+ <div class="box-icon" @click="goBackToMain">
|
|
|
|
|
+ <el-icon class="left-icon"><ArrowLeftBold /></el-icon>
|
|
|
|
|
+ {{ backText }}
|
|
|
</div>
|
|
</div>
|
|
|
- <span class="back-text" @click="goBackToMain">{{ backText }}</span>
|
|
|
|
|
</div>
|
|
</div>
|
|
|
<!-- 标题 -->
|
|
<!-- 标题 -->
|
|
|
<div class="title-center">
|
|
<div class="title-center">
|
|
@@ -99,7 +107,7 @@
|
|
|
<script setup>
|
|
<script setup>
|
|
|
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
|
|
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
|
|
|
import { useRouter } from 'vue-router'
|
|
import { useRouter } from 'vue-router'
|
|
|
-import { ArrowLeftBold, CaretLeft, CaretRight, Grid } from '@element-plus/icons-vue'
|
|
|
|
|
|
|
+import { ArrowLeftBold, CaretLeft, CaretRight, Grid, VideoPlay } from '@element-plus/icons-vue'
|
|
|
import VoiceInput from '@/components/ai/voice/VoiceInput.vue'
|
|
import VoiceInput from '@/components/ai/voice/VoiceInput.vue'
|
|
|
import { marked } from 'marked'
|
|
import { marked } from 'marked'
|
|
|
import {CreateDialogue, sendChatMessageStream} from "@/api/questions.js";
|
|
import {CreateDialogue, sendChatMessageStream} from "@/api/questions.js";
|
|
@@ -151,6 +159,8 @@ const recordingStartText = ref("")
|
|
|
const backgroundAudio = ref(null)
|
|
const backgroundAudio = ref(null)
|
|
|
// 对话音频
|
|
// 对话音频
|
|
|
const dialogueAudio = ref(null)
|
|
const dialogueAudio = ref(null)
|
|
|
|
|
+// 遮罩层显示状态
|
|
|
|
|
+const showMask = ref(true)
|
|
|
|
|
|
|
|
// 计算属性
|
|
// 计算属性
|
|
|
// 当前章节信息
|
|
// 当前章节信息
|
|
@@ -451,6 +461,21 @@ const goBackToMain = () => {
|
|
|
router.push('/ai-general-course')
|
|
router.push('/ai-general-course')
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// 开始播放
|
|
|
|
|
+const maskLayer = ref(null)
|
|
|
|
|
+const startPlayback = () => {
|
|
|
|
|
+ // 消失动画
|
|
|
|
|
+ if (maskLayer.value) {
|
|
|
|
|
+ maskLayer.value.classList.add('fade-out')
|
|
|
|
|
+ // 等待动画完成后隐藏遮罩层
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ showMask.value = false
|
|
|
|
|
+ }, 500)
|
|
|
|
|
+ }
|
|
|
|
|
+ // 播放当前对话语音
|
|
|
|
|
+ playDialogueAudio()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// 键盘事件处理,键盘左右箭头控制对话
|
|
// 键盘事件处理,键盘左右箭头控制对话
|
|
|
const handleKeydown = (event) => {
|
|
const handleKeydown = (event) => {
|
|
|
// 如果当前是用户输入对话,不处理键盘事件,让默认行为生效(在输入框中左右移动光标)
|
|
// 如果当前是用户输入对话,不处理键盘事件,让默认行为生效(在输入框中左右移动光标)
|
|
@@ -470,12 +495,12 @@ watch(currentSectionIndex, () => {
|
|
|
playBackgroundAudio()
|
|
playBackgroundAudio()
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
-// 监听对话变化
|
|
|
|
|
-watch(currentDialogue, () => {
|
|
|
|
|
- if (!isPlaying.value) {
|
|
|
|
|
- playDialogueAudio()
|
|
|
|
|
- }
|
|
|
|
|
-})
|
|
|
|
|
|
|
+// // 监听对话变化
|
|
|
|
|
+// watch(currentDialogue, () => {
|
|
|
|
|
+// if (!isPlaying.value) {
|
|
|
|
|
+// playDialogueAudio()
|
|
|
|
|
+// }
|
|
|
|
|
+// })
|
|
|
|
|
|
|
|
// 会话ID
|
|
// 会话ID
|
|
|
const activeConversationId = ref(null)
|
|
const activeConversationId = ref(null)
|
|
@@ -657,8 +682,8 @@ onMounted(() => {
|
|
|
window.addEventListener('keydown', handleKeydown)
|
|
window.addEventListener('keydown', handleKeydown)
|
|
|
// 播放背景音
|
|
// 播放背景音
|
|
|
playBackgroundAudio()
|
|
playBackgroundAudio()
|
|
|
- // 播放当前对话语音
|
|
|
|
|
- playDialogueAudio()
|
|
|
|
|
|
|
+ // // 播放当前对话语音
|
|
|
|
|
+ // playDialogueAudio()
|
|
|
// 设置音频播放完成回调
|
|
// 设置音频播放完成回调
|
|
|
setOnPlaybackComplete(handleAudioPlaybackComplete)
|
|
setOnPlaybackComplete(handleAudioPlaybackComplete)
|
|
|
})
|
|
})
|
|
@@ -706,37 +731,31 @@ onUnmounted(() => {
|
|
|
height: 100%;
|
|
height: 100%;
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
- justify-content: flex-start;
|
|
|
|
|
gap: rpx(5);
|
|
gap: rpx(5);
|
|
|
- .back-icon-circle {
|
|
|
|
|
- width: rpx(20);
|
|
|
|
|
- height: rpx(20);
|
|
|
|
|
- border-radius: 50%;
|
|
|
|
|
- border: rpx(1) solid rgba(0, 100, 192);
|
|
|
|
|
- background: linear-gradient(135deg, #A0DCF0, #50BEF0);
|
|
|
|
|
|
|
+ .box-icon {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
- justify-content: center;
|
|
|
|
|
|
|
+ color: #0064BE;
|
|
|
|
|
+ gap: rpx(5);
|
|
|
|
|
+ padding: rpx(5) rpx(10);
|
|
|
|
|
+ background: linear-gradient(135deg, #A0DCF0, #50BEF0);
|
|
|
|
|
+ border: rpx(1) solid rgba(0, 100, 192);
|
|
|
|
|
+ border-radius: rpx(30);
|
|
|
|
|
+ backdrop-filter: blur(10px);
|
|
|
cursor: pointer;
|
|
cursor: pointer;
|
|
|
transition: all 0.3s ease;
|
|
transition: all 0.3s ease;
|
|
|
|
|
+ font-size: rpx(9);
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ width: fit-content;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- .back-icon-circle:hover {
|
|
|
|
|
- transform: scale(1.1);
|
|
|
|
|
- box-shadow: 0 rpx(1) rpx(6) rgba(0, 0, 0, 0.3);
|
|
|
|
|
|
|
+ .box-icon:hover {
|
|
|
|
|
+ background-color: rgba(255, 255, 255, 90%);
|
|
|
|
|
+ transform: translateX(-3px);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- .back-icon-circle:active {
|
|
|
|
|
- transform: scale(0.95);
|
|
|
|
|
- }
|
|
|
|
|
- .back-icon {
|
|
|
|
|
- font-size: rpx(13);
|
|
|
|
|
- color: #0064BE;
|
|
|
|
|
- }
|
|
|
|
|
- .back-text {
|
|
|
|
|
|
|
+ .left-icon {
|
|
|
font-size: rpx(12);
|
|
font-size: rpx(12);
|
|
|
- color: #0064BE;
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -806,6 +825,61 @@ onUnmounted(() => {
|
|
|
position: relative;
|
|
position: relative;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+/* 遮罩层样式 */
|
|
|
|
|
+.mask-layer {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ top: 0;
|
|
|
|
|
+ left: 0;
|
|
|
|
|
+ right: 0;
|
|
|
|
|
+ bottom: 0;
|
|
|
|
|
+ background-color: rgba(0, 0, 0, 0.7);
|
|
|
|
|
+ z-index: 20;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ opacity: 1;
|
|
|
|
|
+ transition: all 0.5s ease-out;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.mask-layer.fade-out {
|
|
|
|
|
+ opacity: 0;
|
|
|
|
|
+ transform: scale(1.1);
|
|
|
|
|
+ z-index: -1;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.play-button-container {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.play-button {
|
|
|
|
|
+ width: rpx(80);
|
|
|
|
|
+ height: rpx(80);
|
|
|
|
|
+ border-radius: 50%;
|
|
|
|
|
+ border: none;
|
|
|
|
|
+ background: transparent;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ outline: none;
|
|
|
|
|
+ -webkit-tap-highlight-color: transparent;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.play-icon {
|
|
|
|
|
+ font-size: rpx(40);
|
|
|
|
|
+ color: #A0DCF0;
|
|
|
|
|
+ transition: all 0.3s ease;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.play-icon:hover {
|
|
|
|
|
+ transform: scale(1.2);
|
|
|
|
|
+ color: #50BEF0;
|
|
|
|
|
+ text-shadow: 0 0 rpx(10) rgba(64, 158, 255, 0.5);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
.content-box {
|
|
.content-box {
|
|
|
position: absolute;
|
|
position: absolute;
|
|
|
top: rpx(60);
|
|
top: rpx(60);
|