feat: 完善课程学习与技能树体验

完善课程资源、学习进度与技能树解锁体验,统一视频播放与引导相关交互。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
zhongyidianandClaude Opus 4.7 committed 2026-05-24 10:30:18 +08:00
1 parent 6f289959be
commit 3e258b87d9
314 files changed
+7020 -3386

No files matched your search

+78 -14
View File
@@ -1,4 +1,4 @@
import { computed, defineComponent, ref } from 'vue'
import { computed, defineComponent, onUnmounted, ref } from 'vue'
import {
getBackgroundImage,
getPublicPath,
@@ -6,7 +6,8 @@ import {
IMAGE_PATHS,
} from '@/utils'
import { ImgButton } from '@/components/Button'
import { GuideSteps, ImagePreview, Video } from '@/components'
import { CourseGuidePopup, CourseFrameworkPopup, GuideSteps, Video } from '@/components'
import Loading from './components/Loading.vue'
import { useScale } from '@/composables/useScale'
import { useBasicElementsStore } from '@/stores/modules/basic-elements'
@@ -89,10 +90,7 @@ export default defineComponent({
const { scale, offsetX, offsetY } = useScale()
// 首次进入的视频播放序列
const introVideos = computed(() => [
getPublicPath('video/campus life.mp4'),
getPublicPath('video/new/xsdh_CapCut_Tutorial.mp4'),
])
const introVideos = computed(() => [getPublicPath('video/start.mp4')])
const introVideoIndex = ref(0)
const showIntroVideo = ref(isFirstVisit)
const currentIntroSrc = computed(
@@ -116,10 +114,64 @@ export default defineComponent({
}
}
// Loading 加载状态
const showLoading = ref(isFirstVisit)
const loadingProgress = ref(0)
let progressTimer: ReturnType<typeof setInterval> | null = null
let loadingFallbackTimer: ReturnType<typeof setTimeout> | null = null
if (isFirstVisit) {
progressTimer = setInterval(() => {
if (loadingProgress.value < 99) {
loadingProgress.value += 1
}
}, 60)
loadingFallbackTimer = setTimeout(() => {
finishLoading()
}, 8000)
}
onUnmounted(() => {
if (progressTimer) clearInterval(progressTimer)
if (loadingFallbackTimer) clearTimeout(loadingFallbackTimer)
})
const finishLoading = () => {
if (!showLoading.value) return
if (progressTimer) {
clearInterval(progressTimer)
progressTimer = null
}
if (loadingFallbackTimer) {
clearTimeout(loadingFallbackTimer)
loadingFallbackTimer = null
}
loadingProgress.value = 100
setTimeout(() => {
showLoading.value = false
}, 200)
}
const handleIntroVideoPlay = () => {
finishLoading()
}
const handleIntroVideoLoaded = () => {
finishLoading()
}
const openMindMap = () => {
ImagePreview({
images: [getStaticImage('maps/dt')],
maskClosable: true,
CourseFrameworkPopup({
getScaleMetrics: () => ({
scale: scale.value,
offsetX: offsetX.value,
offsetY: offsetY.value,
}),
})
}
const openCourseGuide = () => {
CourseGuidePopup({
getScaleMetrics: () => ({
scale: scale.value,
offsetX: offsetX.value,
@@ -142,19 +194,25 @@ export default defineComponent({
/>
</div>
<div class="absolute right-20 bottom-36 kecheng-wrapper">
<div class="absolute right-20 bottom-10 space-y-3">
<ImgButton
class={['kecheng-wrapper']}
image={IMAGE_PATHS.BUTTONS.COURSE_FRAME}
onClick={openMindMap}
/>
</div>
<div class="absolute right-20 bottom-10 zhiying-wrapper">
<ImgButton image={IMAGE_PATHS.BUTTONS.OPERATE_GUIDE} />
<ImgButton
class={['zhiying-wrapper']}
image={IMAGE_PATHS.BUTTONS.OPERATE_GUIDE}
onClick={openCourseGuide}
/>
</div>
{showIntroVideo.value && (
<div class="absolute inset-0 z-[10000] bg-black flex items-center justify-center">
<Loading
visible={showLoading.value}
progress={loadingProgress.value}
/>
<Video
src={currentIntroSrc.value}
autoplay={true}
@@ -162,6 +220,12 @@ export default defineComponent({
controls={true}
volume={1}
fit="contain"
onPlay={handleIntroVideoPlay}
// onLoadedmetadata={handleIntroVideoLoaded}
// onLoadeddata={handleIntroVideoLoaded}
// onCanplay={handleIntroVideoLoaded}
onCanplaythrough={handleIntroVideoLoaded}
onPlayfailed={finishLoading}
onEnded={handleIntroVideoEnded}
/>
</div>