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

+179 -120
View File
@@ -2,33 +2,135 @@
defineOptions({
name: 'Action'
})
import { getBackgroundImage, IMAGE_PATHS, getStaticImage } from '@/utils'
import { getBackgroundImage, getPublicPath, IMAGE_PATHS, getStaticImage } from '@/utils'
import { ImgButton } from '@/components/Button'
import { ModuleSwitcher } from './components/ModuleSwitcher'
import { computed, ref, onMounted, onUnmounted } from 'vue'
import { useRouter } from 'vue-router'
import { useUserStore, useFilmStore, useVlogStore } from '@/stores'
import { GuideSteps, GuideStepsItem } from '@/components/Guide'
import { computed, ref, onMounted } from 'vue'
import { useRouter, type RouteRecordName } from 'vue-router'
import {
useUserStore,
useFilmStore,
useVlogStore,
useInterviewStore,
useBasicElementsStore
} from '@/stores'
import { GuideSteps } from '@/components/Guide'
import type { GuideStep } from '@/components/Guide/types'
//点击电视机按钮引导状态
const status = ref(false)
const status2 = ref(false)
const status3 = ref(false)
type GuideChildNode = {
id: number
label: string
icon: string
routeName: RouteRecordName
}
const stepIndex = ref(0)
type GuideNode = {
id: number
num: string
label: string
x: number
y: number
hasChildren: boolean
routeName?: RouteRecordName
children?: GuideChildNode[]
}
const handleComplete = () => {
status.value = false
const homeGuideShow = ref(true)
const unlockedHomeGuideShow = ref(false)
const elementGuideShow = ref(false)
const unlockedHomeGuideCompleted = ref(false)
const homeGuideSteps: GuideStep[] = [
{
id: 'home-guide-navigation',
content: '点击新手导航按钮,可以展开/收起新手导航面板!',
positionOffsetY: '50%',
highlightSelector: '.newUserGuide',
audioSrc: getPublicPath('audio/py_0_005.MP3')
},
{
id: 'home-guide-locked-modules',
content: '新手导航模块全部完成后才能解锁三大进阶模块哦~',
highlightSelector: '.newUserGuide',
positionOffsetY: '50%',
audioSrc: getPublicPath('audio/py_0_006.MP3')
},
{
id: 'home-guide-module-switcher',
content: '舞台划分为三大创作板块!点击 Vlog、访谈、电影的对应区域,右侧屏幕就会切换到对应板块!',
highlightSelector: '.module-switcher-guide',
positionOffsetY: '85%',
audioSrc: getPublicPath('audio/py_0_007.MP3')
},
{
id: 'home-guide-tv',
content: '再点击右侧屏幕就会开启对应的板块学习哦!',
highlightSelector: '.tvGuide',
positionOffsetY: '80%',
audioSrc: getPublicPath('audio/py_0_008.MP3')
},
{
id: 'home-guide-skill',
content: '点击技能树,可以一键回顾新手导航和三大创作板块所有的知识点哦!',
highlightSelector: '.skill-tree',
positionOffsetY: '50%',
audioSrc: getPublicPath('audio/py_0_009.MP3')
}
]
const unlockedHomeGuideSteps: GuideStep[] = [
{
id: 'home-guide-welcome',
content: '欢迎来到新手村!在这里,我会带着你解锁短视频创作的核心基本功!',
audioSrc: getPublicPath('audio/py_1_001.MP3')
},
{
id: 'home-guide-map',
content: '我们会读懂构图、用好光线、灵活运镜,了解基础拍摄技巧和剪映软件的基本操作。',
audioSrc: getPublicPath('audio/py_1_002.MP3')
},
{
id: 'home-guide-start-node',
content: '选择任意节点开始学习~',
highlightSelector: '.guide-content',
positionOffsetY: '20%',
audioSrc: getPublicPath('audio/py_1_003.MP3')
}
]
const elementGuideSteps: GuideStep[] = [
{
id: 'home-guide-start-element',
content: '选择任意要素开始学习~',
highlightSelector: '.btn',
positionOffsetY: '20%',
audioSrc: getPublicPath('audio/py_1_004.MP3')
}
]
const handleHomeGuideComplete = () => {
homeGuideShow.value = false
}
const handleUnlockedHomeGuideComplete = () => {
unlockedHomeGuideShow.value = false
}
const handleElementGuideComplete = () => {
elementGuideShow.value = false
unlockedHomeGuideCompleted.value = true
}
const router = useRouter()
const user = useUserStore()
const film = useFilmStore()
const vlog = useVlogStore()
const interview = useInterviewStore()
const basicElements = useBasicElementsStore()
// 响应式状态
const guideShow = ref(false)
const selectedModule = ref<'vlog' | 'film' | 'interview'>('film')
const selectedModule = ref<'vlog' | 'film' | 'interview'>('vlog')
const isImageLoading = ref(false)
// 预加载图片映射
@@ -39,7 +141,7 @@ const imageMap = {
}
//获取新手导航引导状态
const isGuideNewUser = computed(() => user.isGuideNewUser)
const isGuideNewUser = computed(() => basicElements.isGuideNewUser)
// 预加载所有图片
const preloadImages = () => {
@@ -52,19 +154,20 @@ const preloadImages = () => {
// 事件处理函数
const handleOpenGuide = () => {
status2.value = true
guideShow.value = true
if (!homeGuideShow.value && isGuideNewUser.value) {
unlockedHomeGuideShow.value = true
}
}
const handleCloseGuide = () => {
guideShow.value = false
if (isGuideNewUser.value) {
user.setIsGuideNewUser(false)
basicElements.setIsGuideNewUser(false)
}
}
const handleModuleChange = (module: 'vlog' | 'film' | 'interview') => {
status.value = true
if (selectedModule.value === module) return
isImageLoading.value = true
@@ -77,7 +180,7 @@ const handleModuleChange = (module: 'vlog' | 'film' | 'interview') => {
}
const handleClick = () => {
if (selectedModule.value === 'interview') return
// if (selectedModule.value === 'interview') return
// console.log(selectedModule.value.charAt(0).toUpperCase() + selectedModule.value.slice(1))
router.push({
//首字母大写
@@ -90,35 +193,28 @@ const selectedImage = computed(() => {
return getBackgroundImage(imageMap[selectedModule.value])
})
const hasFinishedAllHomeGuides = computed(() => unlockedHomeGuideCompleted.value)
const isSkillDisabled = computed(() => !hasFinishedAllHomeGuides.value)
const handleSkill = () => {
if (isSkillDisabled.value) return
router.push({
name: 'Skill'
})
}
// 清除所有与课程相关的缓存(Film / Vlog / User)
// 清除所有与课程相关的缓存
const handleClearCache = () => {
// 直接通过 $patch 重置各模块的状态,避免热更新导致的方法缺失问题
film.$patch({
isGuideFilmTask: true,
hasShownGuideFilmTask: false,
isFilmUnlock: false,
FilmUnlockProgress: 0
})
vlog.$patch({
isGuideVlogTask: true,
isVlogUnlock: false,
vlogUnlockProgress: 0
})
user.$patch({
isGuideNewUser: true
})
film.resetFilmState()
vlog.resetVlogState()
interview.resetInterviewState()
basicElements.resetBasicElementsState()
user.resetUserState()
}
// 新手导航数据
const nodes = [
const nodes: GuideNode[] = [
{
id: 1,
num: '1',
@@ -156,33 +252,31 @@ const nodes = [
const showChildrenNodeId = ref<number | null>(null)
// 处理节点点击
const handleNodeClick = (node: any, event?: MouseEvent) => {
if (event) {
event.stopPropagation()
}
const handleNodeClick = (node: GuideNode, event?: MouseEvent) => {
event?.stopPropagation()
if (node.hasChildren) {
// 如果点击的是有子节点的节点,切换显示状态
if (showChildrenNodeId.value === node.id) {
showChildrenNodeId.value = null
} else {
showChildrenNodeId.value = node.id
status3.value = true
}
} else {
// 如果点击的是没有子节点的节点,直接跳转
if (!node.hasChildren) {
if (node.routeName) {
router.push({ name: node.routeName })
}
return
}
if (showChildrenNodeId.value === node.id) {
showChildrenNodeId.value = null
return
}
showChildrenNodeId.value = node.id
if (!unlockedHomeGuideShow.value && !unlockedHomeGuideCompleted.value) {
elementGuideShow.value = true
}
}
// 处理子节点点击
const handleChildNodeClick = (childNode: any, event: MouseEvent) => {
const handleChildNodeClick = (childNode: GuideChildNode, event: MouseEvent) => {
event.stopPropagation()
if (childNode.routeName) {
router.push({ name: childNode.routeName })
}
router.push({ name: childNode.routeName })
}
// 点击外部关闭子节点
@@ -194,26 +288,28 @@ const handleClickOutside = () => {
onMounted(() => {
preloadImages()
})
onUnmounted(() => {
// 清理工作
})
</script>
<template>
<div class="w-full h-full relative">
<!-- 图片引导 -->
<div
class="absolute top-0 left-0 w-full h-full z-50"
v-if="stepIndex < 4 && isGuideNewUser"
@click="stepIndex++"
>
<img v-if="stepIndex === 0" :src="getStaticImage('home/1')" alt="" />
<img v-else-if="stepIndex === 1" :src="getStaticImage('home/2')" alt="" />
<img v-else-if="stepIndex === 2" :src="getStaticImage('home/3')" alt="" />
<img v-else-if="stepIndex === 3" :src="getStaticImage('home/4')" alt="" />
<div v-else class="absolute top-0 left-0 w-full h-full bg-black/50"></div>
</div>
<GuideSteps
v-if="isGuideNewUser && homeGuideShow"
:steps="homeGuideSteps"
position-offset-y="82%"
@complete="handleHomeGuideComplete"
/>
<GuideSteps
v-if="unlockedHomeGuideShow"
:steps="unlockedHomeGuideSteps"
position-offset-y="82%"
@complete="handleUnlockedHomeGuideComplete"
/>
<GuideSteps
v-if="elementGuideShow"
:steps="elementGuideSteps"
position-offset-y="82%"
@complete="handleElementGuideComplete"
/>
<!-- 背景图片 -->
<div
@@ -229,11 +325,15 @@ onUnmounted(() => {
class="w-10 cursor-pointer mx-2"
src="@/assets/svg/clear.svg"
@click="handleClearCache"
/>清空缓存。
/>
清空缓存。
</div>
<!-- 选择模块 -->
<div :class="['absolute left-0 top-[-140px] inset-0 z-[8] pointer-events-none']">
<div
class="module-switcher-guide absolute w-[1380px] h-[600px] top-[460px] left-[calc(50%-690px)] pointer-events-none"
></div>
<ModuleSwitcher v-model="selectedModule" @change="handleModuleChange" />
</div>
@@ -247,14 +347,17 @@ onUnmounted(() => {
<!-- 技能树 按钮 -->
<ImgButton
v-if="!isGuideNewUser"
@click="handleSkill"
:image="IMAGE_PATHS.ACTION.SKILLS_BTN"
class="absolute bottom-0 right-0 z-30"
:disabled="isSkillDisabled"
class="absolute bottom-0 right-0 z-30 skill-tree"
/>
<!-- 新手指导遮罩 -->
<div v-if="isGuideNewUser" class="absolute inset-0 bg-black/50 z-10"></div>
<div
v-if="isGuideNewUser"
:class="['absolute inset-0 z-10', homeGuideShow ? 'bg-black/0' : 'bg-black/50']"
></div>
<!-- 新手指导 -->
<div
@@ -281,7 +384,7 @@ onUnmounted(() => {
<!-- 新手导航内容 -->
<div class="relative w-[880px] h-[400px] mt-20">
<div class="w-full h-full relative" @click="handleClickOutside">
<div class="w-full h-full relative guide-content" @click="handleClickOutside">
<!-- 标题区域 -->
<div class="text-center mt-10 mb-8">
<h2 class="text-5xl font-bold mb-3 text-[#7bfffd] tracking-wider drop-shadow-lg">
@@ -395,49 +498,5 @@ onUnmounted(() => {
</div>
</div>
</div>
<!-- 引导与高亮 -->
<GuideSteps
v-if="status"
highlight-mode="gradient"
highlight-selector=".tvGuide"
position-offset-y="85%"
@complete="handleComplete"
>
<GuideStepsItem>
<div
class="flex items-center justify-start"
v-if="selectedModule === 'interview' || selectedModule === 'vlog'"
>
[{{
selectedModule == 'interview'
? '访谈-聆听与对话'
: selectedModule == 'vlog'
? 'vlog-创作与表达'
: ''
}}]模块建设中...
</div>
<div class="flex items-center justify-start" v-else>
请点击<img
class="w-20"
:src="getStaticImage(imageMap[selectedModule])"
alt=""
/>,继续学习[{{
selectedModule == 'film'
? '电影-创作与表达'
: selectedModule == 'vlog'
? 'vlog-创作与表达'
: ''
}}]模块!
</div>
</GuideStepsItem>
</GuideSteps>
<GuideSteps :visible="status2" highlight-selector=".btn-items" position-offset-y="20%">
<GuideStepsItem> 选择任意节点开始学习 </GuideStepsItem>
</GuideSteps>
<GuideSteps :visible="status3" highlight-selector=".btn" position-offset-y="20%">
<GuideStepsItem> 选择任意要素开始学习 </GuideStepsItem>
</GuideSteps>
</div>
</template>