forked from zhongyidian/super_video
feat: 完善视频学习进度缓存
完善新手导航和三大创作模块的视频完成缓存、未完成提示与学习进度计算,确保进度按实际学习资源完成情况更新。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
1f8723aabd
commit
6f289959be
63 files changed
+3139
-1126
No files matched your search
@@ -5,7 +5,7 @@ import { ImgButton } from '@/components/Button'
|
||||
|
||||
import { computed, ref, watch, type Ref } from 'vue'
|
||||
import { ProgressBar, Overlay } from '@/components'
|
||||
import { GuideSteps, type GuideStep } from '@/components/Guide'
|
||||
import { GuideSteps, GuideStepsItem, type GuideStep } from '@/components/Guide'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useInterviewStore } from '@/stores'
|
||||
import InterviewMenu from './components/menu.vue'
|
||||
@@ -14,14 +14,26 @@ const route = useRoute()
|
||||
const router = useRouter()
|
||||
const interview = useInterviewStore()
|
||||
|
||||
const showReturnGuide = ref(route.query.guide === 'return' && interview.isGuideInterviewReturn)
|
||||
const showReturnGuide = ref(
|
||||
route.query.guide === 'return' && interview.isGuideInterviewReturn,
|
||||
)
|
||||
|
||||
// 实战操作台一次性引导(currentStep === 4 时解锁)
|
||||
const practiceGuideKey = 'interview-practice-guide'
|
||||
const hasShownPracticeGuide = computed(
|
||||
() => interview.guideShown[practiceGuideKey] === true,
|
||||
)
|
||||
const showPracticeGuide = computed(
|
||||
() => currentStep.value === 4 && !hasShownPracticeGuide.value,
|
||||
)
|
||||
|
||||
const returnSteps: GuideStep[] = [
|
||||
{
|
||||
id: 'return-step1',
|
||||
content: '接下来,我们将进入真实项目流程,体验一条人物访谈作品是如何一步步完成的。',
|
||||
audioSrc: getPublicPath('audio/py_3_007.MP3')
|
||||
}
|
||||
content:
|
||||
'接下来,我们将进入真实项目流程,体验一条人物访谈作品是如何一步步完成的。',
|
||||
audioSrc: getPublicPath('audio/py_3_007.MP3'),
|
||||
},
|
||||
]
|
||||
|
||||
const handleReturnGuideComplete = () => {
|
||||
@@ -30,6 +42,10 @@ const handleReturnGuideComplete = () => {
|
||||
router.replace({ name: 'Interview' })
|
||||
}
|
||||
|
||||
const handlePracticeGuideComplete = () => {
|
||||
interview.markGuideShown(practiceGuideKey)
|
||||
}
|
||||
|
||||
const stageGuideMap = {
|
||||
2: {
|
||||
flagKey: 'isGuideInterviewStage2',
|
||||
@@ -38,14 +54,15 @@ const stageGuideMap = {
|
||||
{
|
||||
id: 'stage2-step1',
|
||||
content: '前期准备阶段,我们已经完成了主题确认、脚本结构与镜头规划。',
|
||||
audioSrc: getPublicPath('audio/py_3_010.MP3')
|
||||
audioSrc: getPublicPath('audio/py_3_010.MP3'),
|
||||
},
|
||||
{
|
||||
id: 'stage2-step2',
|
||||
content: '进入中期拍摄后就要按照既定方案,把后期需要用的素材尽可能拍完整。',
|
||||
audioSrc: getPublicPath('audio/py_3_011.MP3')
|
||||
}
|
||||
]
|
||||
content:
|
||||
'进入中期拍摄后就要按照既定方案,把后期需要用的素材尽可能拍完整。',
|
||||
audioSrc: getPublicPath('audio/py_3_011.MP3'),
|
||||
},
|
||||
],
|
||||
},
|
||||
3: {
|
||||
flagKey: 'isGuideInterviewStage3',
|
||||
@@ -54,17 +71,17 @@ const stageGuideMap = {
|
||||
{
|
||||
id: 'stage3-step1',
|
||||
content: '拍摄完成啦!现在我们正式进入剪辑环节。',
|
||||
audioSrc: getPublicPath('audio/py_3_012.MP3')
|
||||
}
|
||||
]
|
||||
}
|
||||
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 : []
|
||||
stageGuideStep.value ? stageGuideMap[stageGuideStep.value].steps : [],
|
||||
) as Ref<GuideStep[]>
|
||||
|
||||
const triggerStageGuide = (step: number) => {
|
||||
@@ -84,7 +101,7 @@ const handleStageGuideComplete = () => {
|
||||
|
||||
const visible = computed({
|
||||
get: () => interview.interviewMenuVisible,
|
||||
set: value => interview.setInterviewMenuVisible(value)
|
||||
set: (value) => interview.setInterviewMenuVisible(value),
|
||||
})
|
||||
|
||||
const currentStep = computed(() => interview.InterviewUnlockProgress)
|
||||
@@ -111,6 +128,16 @@ const handleClose = () => {
|
||||
router.push({ name: 'Action' })
|
||||
}
|
||||
|
||||
watch(
|
||||
showPracticeGuide,
|
||||
(val) => {
|
||||
if (val) {
|
||||
interview.markGuideShown(practiceGuideKey)
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
watch(
|
||||
[visible, currentStep],
|
||||
([isVisible, step]) => {
|
||||
@@ -118,7 +145,7 @@ watch(
|
||||
triggerStageGuide(step)
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
{ immediate: true },
|
||||
)
|
||||
</script>
|
||||
|
||||
@@ -161,22 +188,41 @@ watch(
|
||||
<!-- 任务取景按钮 -->
|
||||
<div
|
||||
class="absolute left-24 top-24"
|
||||
:style="{ backgroundImage: getBackgroundImage(IMAGE_PATHS.VLOG.TASK_CAMERA) }"
|
||||
:style="{
|
||||
backgroundImage: getBackgroundImage(IMAGE_PATHS.VLOG.TASK_CAMERA),
|
||||
}"
|
||||
>
|
||||
<ImgButton
|
||||
:image="currentStep === 4 ? IMAGE_PATHS.VLOG.TASK_BTN_OK : IMAGE_PATHS.VLOG.TASK_BTN"
|
||||
:image="
|
||||
currentStep > 0
|
||||
? IMAGE_PATHS.VLOG.TASK_BTN_OK
|
||||
: IMAGE_PATHS.VLOG.TASK_BTN
|
||||
"
|
||||
@click="handleWorkClick"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<GuideSteps
|
||||
:visible="showPracticeGuide"
|
||||
highlight-selector=".right-section"
|
||||
position-offset-y="50%"
|
||||
@complete="handlePracticeGuideComplete"
|
||||
>
|
||||
<GuideStepsItem>点击【实战操作台】进入实战操作!</GuideStepsItem>
|
||||
</GuideSteps>
|
||||
|
||||
<!-- 引导任务 -->
|
||||
<ImgButton
|
||||
v-if="currentStep !== 4"
|
||||
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">
|
||||
<Overlay
|
||||
content-class="w-full h-full"
|
||||
:visible="visible && currentStep < 4"
|
||||
>
|
||||
<!-- 关闭按钮 -->
|
||||
<ImgButton
|
||||
class="absolute right-0 top-0 z-20"
|
||||
|
||||
Reference in new issue
Block a user