Files
super_video/src/views/interview/index.vue
T
zhongyidianandClaude Opus 4.7 6f289959be feat: 完善视频学习进度缓存
完善新手导航和三大创作模块的视频完成缓存、未完成提示与学习进度计算,确保进度按实际学习资源完成情况更新。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 09:05:29 +08:00

276 lines
7.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script lang="tsx" name="Interview" setup>
defineOptions({ name: 'Interview' })
import { getBackgroundImage, getPublicPath, IMAGE_PATHS } from '@/utils'
import { ImgButton } from '@/components/Button'
import { computed, ref, watch, type Ref } from 'vue'
import { ProgressBar, Overlay } from '@/components'
import { GuideSteps, GuideStepsItem, 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 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'),
},
]
const handleReturnGuideComplete = () => {
showReturnGuide.value = false
interview.setIsGuideInterviewReturn(false)
router.replace({ name: 'Interview' })
}
const handlePracticeGuideComplete = () => {
interview.markGuideShown(practiceGuideKey)
}
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)
const handleComplete = () => {
// console.log('完成前置条件')
// 处理完成逻辑
router.push({ name: 'InterviewPractice' })
}
const handleStepChange = (step: number) => {
interview.setInterviewUnlockProgress(step)
visible.value = true
}
const handleWorkClick = () => {
console.log('任务取景按钮点击')
// 处理任务取景按钮点击逻辑
router.push({ name: 'InterviewWork' })
}
const handleClose = () => {
router.push({ name: 'Action' })
}
watch(
showPracticeGuide,
(val) => {
if (val) {
interview.markGuideShown(practiceGuideKey)
}
},
{ immediate: true },
)
watch(
[visible, currentStep],
([isVisible, step]) => {
if (isVisible && step < 4) {
triggerStageGuide(step)
}
},
{ immediate: true },
)
</script>
<template>
<div class="w-full h-full relative">
<!-- 背景图片-center -->
<div
class="absolute inset-0 bg-cover bg-center bg-no-repeat"
:style="{ backgroundImage: getBackgroundImage('interview/访谈背景.png') }"
></div>
<!-- 关闭 -->
<div class="absolute top-[140px] left-[calc(50%)] -translate-x-1/2">
<div
class="flex items-center justify-center text-white font-[1000] [text-shadow:_6px_6px_4px_#2b80c1,_6px_6px_4px_#2b80c1,_6px_6px_4px_#2b80c1,_6px_6px_4px_#2b80c1]"
>
<span class="text-6xl">学习进度:</span>
<span class="text-7xl">{{ progress }}%</span>
</div>
</div>
<!-- 关闭 -->
<ImgButton
class="absolute right-0 top-0 z-20"
:image="IMAGE_PATHS.COMPONENTS.CLOSE"
@click="handleClose"
/>
<!-- 背景图片 -->
<div
class="absolute inset-0 bg-cover bg-center bg-no-repeat"
:style="{ backgroundImage: getBackgroundImage(IMAGE_PATHS.VLOG.BG) }"
></div>
<!-- 标题 -->
<div class="absolute top-2 left-1/2 -translate-x-1/2">
<ImgButton :image="'interview/访谈-聆听与对话.png'" />
</div>
<!-- 任务取景按钮 -->
<div
class="absolute left-24 top-24"
:style="{
backgroundImage: getBackgroundImage(IMAGE_PATHS.VLOG.TASK_CAMERA),
}"
>
<ImgButton
: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"
>
<!-- 关闭按钮 -->
<ImgButton
class="absolute right-0 top-0 z-20"
:image="IMAGE_PATHS.COMPONENTS.CLOSE"
@click="visible = false"
/>
<!-- 菜单 -->
<InterviewMenu :current-step="currentStep" />
<!-- 进度条 -->
<div class="absolute left-0 bottom-0 w-full">
<ProgressBar
module-name="interview"
:current-step="currentStep"
:completed-step="interview.completedStep"
@step-change="handleStepChange"
/>
</div>
</Overlay>
<div class="absolute bottom-0 w-full">
<ProgressBar
module-name="interview"
:current-step="currentStep"
:completed-step="interview.completedStep"
@complete="handleComplete"
@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>