feat: 完善访谈与vlog模块页面,重组设备与图片公共资源,新增音频管理与一班对接工具

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
zhongyidianandClaude Opus 4.7 committed 2026-05-05 15:41:41 +08:00
1 parent c4c9be9d35
commit fc9633f371
159 files changed
+4823 -2535

No files matched your search

+173 -22
View File
@@ -1,19 +1,135 @@
<script lang="tsx" setup>
import { getBackgroundImage, IMAGE_PATHS } from '@/utils'
import { getBackgroundImage, getPublicPath, IMAGE_PATHS } from '@/utils'
import { ImgButton } from '@/components/Button'
import { computed, onMounted, ref, watch } 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 { useVlogStore } from '@/stores'
import VlogMenu from './components/menu.vue'
const route = useRoute()
const router = useRouter()
const vlog = useVlogStore()
const visible = ref(false)
const visible = computed({
get: () => vlog.vlogMenuVisible,
set: value => vlog.setVlogMenuVisible(value)
})
const currentStep = computed(() => vlog.vlogUnlockProgress)
const progress = computed(() => vlog.progressPercent)
const showReturnGuide = ref(route.query.guide === 'return' && vlog.isGuideVlogReturn)
const returnSteps: GuideStep[] = [
{
id: 'return-step1',
content: '通过前面的案例赏析,相信你已经初步了解了 Vlog 的表达方式。',
audioSrc: getPublicPath('audio/py_2_006.MP3')
},
{
id: 'return-step2',
content: '接下来,我们将一起完成一支真实主题作品——《我的志愿者手记》。',
audioSrc: getPublicPath('audio/py_2_007.MP3')
},
{
id: 'return-step3',
content:
'在记录社区志愿活动的过程中,学习 Vlog 从前期策划、中期拍摄到后期剪辑的完整流程,同时传递热心助人、服务社会的价值力量。',
audioSrc: getPublicPath('audio/py_2_008.MP3')
}
]
const handleReturnGuideComplete = () => {
showReturnGuide.value = false
vlog.setIsGuideVlogReturn(false)
router.replace({ name: 'Vlog' })
}
// 三个阶段菜单的一次性引导
const stageGuideMap = {
1: {
flagKey: 'isGuideVlogStage1',
setter: 'setIsGuideVlogStage1',
steps: [
{
id: 'stage1-step1',
content: '一次顺利的拍摄,往往从充分准备开始。',
audioSrc: getPublicPath('audio/py_2_009.MP3')
},
{
id: 'stage1-step2',
content:
'前期阶段,我们需要完成四项关键任务:【资源筹备】【创意构想】【脚本撰写】【分镜制作】',
audioSrc: getPublicPath('audio/py_2_010.MP3')
},
{
id: 'stage1-step3',
content: '画面中高亮的按钮是我们可以展开学习的内容。点击进入学习吧!',
audioSrc: getPublicPath('audio/py_2_011.MP3')
}
]
},
2: {
flagKey: 'isGuideVlogStage2',
setter: 'setIsGuideVlogStage2',
steps: [
{
id: 'stage2-step1',
content:
'拍摄过程可分为三个核心步骤:【设备准备】【实地取景】【现场拍摄】。稳妥的准备是顺利拍摄的前提。',
audioSrc: getPublicPath('audio/py_2_012.MP3')
}
]
},
3: {
flagKey: 'isGuideVlogStage3',
setter: 'setIsGuideVlogStage3',
steps: [
{
id: 'stage3-step1',
content: '经过一系列的准备与拍摄,现在我们来到后期剪辑阶段。',
audioSrc: getPublicPath('audio/py_2_016.MP3')
},
{
id: 'stage3-step2',
content: '这个阶段需要做好四件事:【素材整理】【剪辑】【调色】【导出发布】。',
audioSrc: getPublicPath('audio/py_2_017.MP3')
},
{
id: 'stage3-step3',
content: '画面中高亮的按钮,是可以展开深入学习的内容,点击就能开启学习啦!',
audioSrc: getPublicPath('audio/py_2_018.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 !== 1 && step !== 2 && step !== 3) return
const stage = step as StageKey
const config = stageGuideMap[stage]
if (!(vlog as any)[config.flagKey]) return
stageGuideStep.value = stage
}
const handleStageGuideComplete = () => {
if (!stageGuideStep.value) return
const config = stageGuideMap[stageGuideStep.value]
;(vlog as any)[config.setter](false)
stageGuideStep.value = null
}
const handleComplete = () => {
// console.log('完成前置条件')
// 处理完成逻辑
@@ -27,7 +143,7 @@ const handleStepChange = (step: number) => {
}
const handleWorkClick = () => {
console.log('任务取景按钮点击')
// console.log('任务取景按钮点击')
// 处理任务取景按钮点击逻辑
router.push({ name: 'VlogWork' })
}
@@ -36,20 +152,15 @@ const handleClose = () => {
router.push({ name: 'Action' })
}
// watch(
// () => currentStep.value,
// newVal => {
// if (newVal !== 0 && newVal !== 4) {
// visible.value = true
// }
// }
// )
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>
@@ -60,6 +171,16 @@ onMounted(() => {
:style="{ backgroundImage: getBackgroundImage(IMAGE_PATHS.VLOG.BG_CENTER) }"
></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"
@@ -89,8 +210,12 @@ 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">
<!-- 关闭按钮 -->
@@ -105,16 +230,42 @@ onMounted(() => {
<!-- 进度条 -->
<div class="absolute left-0 bottom-0 w-full">
<ProgressBar :current-step="currentStep" @step-change="handleStepChange" />
<ProgressBar
module-name="vlog"
:current-step="currentStep"
:completed-step="vlog.completedStep"
@complete="handleComplete"
@step-change="handleStepChange"
/>
</div>
</Overlay>
<div class="absolute bottom-0 w-full">
<ProgressBar
module-name="vlog"
:current-step="currentStep"
:completed-step="vlog.completedStep"
@complete="handleComplete"
@step-change="handleStepChange"
/>
</div>
<!-- 返回 Vlog 首页后的一次性引导 -->
<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>