feat: 完善模块引导与学习进度体验

统一引导提示的持久化控制,补充学习进度入口与模块资源,避免用户返回页面时重复触发提示。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
zhongyidianandClaude Opus 4.7 committed 2026-05-08 09:44:59 +08:00
1 parent fc9633f371
commit 6f8252472f
114 files changed
+2758 -1798

No files matched your search

+110 -10
View File
@@ -1,17 +1,91 @@
<script lang="tsx" name="Interview" setup>
defineOptions({ name: 'Interview' })
import { getBackgroundImage, IMAGE_PATHS } from '@/utils'
import { getBackgroundImage, getPublicPath, IMAGE_PATHS } from '@/utils'
import { ImgButton } from '@/components/Button'
import { computed, onMounted, ref } from 'vue'
import { computed, ref, watch, type Ref } from 'vue'
import { ProgressBar, Overlay } from '@/components'
import { useRouter } from 'vue-router'
import { GuideSteps, type GuideStep } from '@/components/Guide'
import { useRoute, useRouter } from 'vue-router'
import { useInterviewStore } from '@/stores'
import InterviewMenu from './components/menu.vue'
const route = useRoute()
const router = useRouter()
const interview = useInterviewStore()
const visible = ref(false)
const showReturnGuide = ref(route.query.guide === 'return' && interview.isGuideInterviewReturn)
const returnSteps: GuideStep[] = [
{
id: 'return-step1',
content: '接下来,我们将进入真实项目流程,体验一条人物访谈作品是如何一步步完成的。',
audioSrc: getPublicPath('audio/py_3_007.MP3')
}
]
const handleReturnGuideComplete = () => {
showReturnGuide.value = false
interview.setIsGuideInterviewReturn(false)
router.replace({ name: 'Interview' })
}
const stageGuideMap = {
2: {
flagKey: 'isGuideInterviewStage2',
setter: 'setIsGuideInterviewStage2',
steps: [
{
id: 'stage2-step1',
content: '前期准备阶段,我们已经完成了主题确认、脚本结构与镜头规划。',
audioSrc: getPublicPath('audio/py_3_010.MP3')
},
{
id: 'stage2-step2',
content: '进入中期拍摄后就要按照既定方案,把后期需要用的素材尽可能拍完整。',
audioSrc: getPublicPath('audio/py_3_011.MP3')
}
]
},
3: {
flagKey: 'isGuideInterviewStage3',
setter: 'setIsGuideInterviewStage3',
steps: [
{
id: 'stage3-step1',
content: '拍摄完成啦!现在我们正式进入剪辑环节。',
audioSrc: getPublicPath('audio/py_3_012.MP3')
}
]
}
} as const
type StageKey = keyof typeof stageGuideMap
const stageGuideStep = ref<StageKey | null>(null)
const stageGuideSteps = computed(() =>
stageGuideStep.value ? stageGuideMap[stageGuideStep.value].steps : []
) as Ref<GuideStep[]>
const triggerStageGuide = (step: number) => {
if (step !== 2 && step !== 3) return
const stage = step as StageKey
const config = stageGuideMap[stage]
if (!(interview as any)[config.flagKey]) return
stageGuideStep.value = stage
}
const handleStageGuideComplete = () => {
if (!stageGuideStep.value) return
const config = stageGuideMap[stageGuideStep.value]
;(interview as any)[config.setter](false)
stageGuideStep.value = null
}
const visible = computed({
get: () => interview.interviewMenuVisible,
set: value => interview.setInterviewMenuVisible(value)
})
const currentStep = computed(() => interview.InterviewUnlockProgress)
const progress = computed(() => interview.progressPercent)
@@ -37,11 +111,15 @@ const handleClose = () => {
router.push({ name: 'Action' })
}
onMounted(() => {
if (currentStep.value !== 0 && currentStep.value !== 4) {
visible.value = true
}
})
watch(
[visible, currentStep],
([isVisible, step]) => {
if (isVisible && step < 4) {
triggerStageGuide(step)
}
},
{ immediate: true }
)
</script>
<template>
@@ -92,7 +170,11 @@ onMounted(() => {
</div>
<!-- 引导任务 -->
<ImgButton class="absolute right-24 bottom-24" :image="IMAGE_PATHS.VLOG.TASK_PERSON" />
<ImgButton
v-show="!showReturnGuide"
class="absolute right-24 bottom-24"
:image="IMAGE_PATHS.VLOG.TASK_PERSON"
/>
<Overlay content-class="w-full h-full" :visible="visible && currentStep < 4">
<!-- 关闭按钮 -->
@@ -125,5 +207,23 @@ onMounted(() => {
@step-change="handleStepChange"
/>
</div>
<!-- 思路提点完成回首页的一次性引导 -->
<GuideSteps
v-if="showReturnGuide"
:steps="returnSteps"
position-offset-y="85%"
@complete="handleReturnGuideComplete"
@skip="handleReturnGuideComplete"
/>
<!-- 阶段菜单一次性引导 -->
<GuideSteps
v-if="stageGuideStep"
:steps="stageGuideSteps"
position-offset-y="85%"
@complete="handleStageGuideComplete"
@skip="handleStageGuideComplete"
/>
</div>
</template>