完善新手导航和三大创作模块的视频完成缓存、未完成提示与学习进度计算,确保进度按实际学习资源完成情况更新。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
218 lines
5.7 KiB
Vue
218 lines
5.7 KiB
Vue
<script lang="tsx" name="Work" setup>
|
|
defineOptions({ name: 'Work' })
|
|
import { ref, computed } from 'vue'
|
|
import {
|
|
getBackgroundImage,
|
|
IMAGE_PATHS,
|
|
getStaticImage,
|
|
getPublicPath,
|
|
} from '@/utils'
|
|
import { ImgButton } from '@/components/Button'
|
|
import WorkSideSwitcher from './components/WorkSideSwitcher.vue'
|
|
import { GuideSteps } from '@/components/Guide'
|
|
import { useRouter } from 'vue-router'
|
|
import { useScale } from '@/composables/useScale'
|
|
import { ImagePreview } from '@/components'
|
|
import { useInterviewStore } from '@/stores'
|
|
|
|
const router = useRouter()
|
|
|
|
const interview = useInterviewStore()
|
|
|
|
type WorkTab = 'vlog' | 'case' | 'profession' | 'idea'
|
|
|
|
const selected = computed({
|
|
get: () => interview.workSelectedTab as WorkTab,
|
|
set: (value: WorkTab) => interview.setWorkSelectedTab(value),
|
|
})
|
|
const workSideSwitcherRef = ref<InstanceType<typeof WorkSideSwitcher>>()
|
|
|
|
const isGuideInterviewTask = computed(() => interview.isGuideInterviewTask)
|
|
const isInterviewUnlock = computed(() => interview.isInterviewUnlock)
|
|
|
|
//引导步骤
|
|
const steps = [
|
|
{
|
|
id: 'step1',
|
|
audioSrc: getPublicPath('audio/py_3_001.MP3'),
|
|
content: '我们常常会思考,未来自己想成为什么样的人呢?',
|
|
highlightSelector: '.img-switch-item--case',
|
|
},
|
|
{
|
|
id: 'step2',
|
|
audioSrc: getPublicPath('audio/py_3_002.MP3'),
|
|
content: '如果把这份思考勇敢地说出来,被镜头记录下来,你会如何表达呢?',
|
|
highlightSelector: '.work-border',
|
|
},
|
|
]
|
|
|
|
//切换图片
|
|
const sideSwitcherItems = ref([
|
|
{
|
|
key: 'vlog',
|
|
description: '我的职业蓝图',
|
|
image: 'interview/work/案件赏析小屏',
|
|
},
|
|
{
|
|
key: 'case',
|
|
description: '我的职业蓝图',
|
|
image: 'interview/work/案件赏析小屏',
|
|
},
|
|
{
|
|
key: 'profession',
|
|
description: '我的职业蓝图',
|
|
image: 'interview/work/案件赏析小屏',
|
|
},
|
|
{
|
|
key: 'idea',
|
|
description: '我的职业蓝图',
|
|
image: 'interview/work/思路提点小屏',
|
|
},
|
|
])
|
|
|
|
//当前选中的item
|
|
const currentItem = computed(() => {
|
|
return sideSwitcherItems.value.find((item) => item.key === selected.value)
|
|
})
|
|
|
|
const handleSideSwitcherChange = (value: WorkTab) => {
|
|
if (selected.value === value) return
|
|
}
|
|
|
|
const handleImageClick = () => {
|
|
router.push({
|
|
name: 'InterviewVideo',
|
|
query: {
|
|
image:
|
|
workSideSwitcherRef.value?.items.find(
|
|
(item) => item.key === selected.value,
|
|
)?.activeImage || '',
|
|
key: selected.value,
|
|
},
|
|
})
|
|
}
|
|
|
|
const handleClose = () => {
|
|
router.push({ name: 'Interview' })
|
|
}
|
|
|
|
const handleWorkClick = () => {
|
|
if (!isInterviewUnlock.value) return
|
|
openImages([getStaticImage('work/桂花酒酿小圆子')])
|
|
}
|
|
const handleGuideComplete = () => {
|
|
interview.setIsGuideInterviewTask(false)
|
|
}
|
|
|
|
const handleOpenGuide = () => {
|
|
interview.setIsGuideInterviewTask(true)
|
|
}
|
|
|
|
const { scale, offsetX, offsetY } = useScale()
|
|
|
|
function openImages(urls: string[], startIndex = 0) {
|
|
ImagePreview({
|
|
images: urls,
|
|
startIndex,
|
|
maskClosable: true,
|
|
getScaleMetrics: () => ({
|
|
scale: scale.value,
|
|
offsetX: offsetX.value,
|
|
offsetY: offsetY.value,
|
|
}),
|
|
})
|
|
}
|
|
</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/work/任务取景框背景'),
|
|
}"
|
|
></div>
|
|
|
|
<!-- 关闭 -->
|
|
<ImgButton
|
|
class="absolute right-0 top-0"
|
|
:image="IMAGE_PATHS.COMPONENTS.CLOSE"
|
|
@click="handleClose"
|
|
/>
|
|
|
|
<!-- border图片 -->
|
|
<div
|
|
class="work-border absolute top-[140px] left-24 inset-0 bg-cover bg-center bg-no-repeat z-10 w-[1360px] h-[800px] p-10"
|
|
:style="{ backgroundImage: getBackgroundImage(IMAGE_PATHS.WORK.BORDER) }"
|
|
>
|
|
<!-- 手 -->
|
|
<div
|
|
class="absolute left-[46%] top-[45%] -translate-x-1/2 -translate-y-1/2 z-10 animate-bounce pointer-events-none"
|
|
>
|
|
<img
|
|
class="w-40"
|
|
:src="getStaticImage('work/小手')"
|
|
alt=""
|
|
/>
|
|
<!-- <span class="text-white text-2xl font-bold ml-5">点击进入</span> -->
|
|
</div>
|
|
<div
|
|
class="flex items-center pl-16 w-[1357px] h-[265px] absolute -bottom-16 left-0 bg-cover bg-center bg-no-repeat z-20"
|
|
:style="{ backgroundImage: getBackgroundImage(IMAGE_PATHS.WORK.TITLE) }"
|
|
>
|
|
<span
|
|
class="mt-24 text-white font-bold text-6xl [text-shadow:0_2px_4px_rgba(0,0,0,0.2),0_0_6px_rgba(0,207,255,0.9),0_0_14px_rgba(0,207,255,0.7)]"
|
|
>
|
|
{{ currentItem?.description }}
|
|
</span>
|
|
</div>
|
|
<img
|
|
@click="handleImageClick"
|
|
class="rounded-[62px] cursor-pointer"
|
|
:src="getStaticImage(currentItem?.image || '')"
|
|
alt=""
|
|
/>
|
|
</div>
|
|
|
|
<!-- 左侧选项切换 -->
|
|
<div class="absolute right-4 top-56 z-20">
|
|
<WorkSideSwitcher
|
|
ref="workSideSwitcherRef"
|
|
v-model="selected"
|
|
@change="handleSideSwitcherChange"
|
|
:item-class="'w-[359px] h-[96px]'"
|
|
/>
|
|
</div>
|
|
|
|
<!-- 任务取景按钮 -->
|
|
<ImgButton
|
|
class="absolute right-14 bottom-10"
|
|
:image="
|
|
isInterviewUnlock
|
|
? IMAGE_PATHS.WORK.TASK_BTN_OK
|
|
: IMAGE_PATHS.WORK.TASK_BTN
|
|
"
|
|
@click="handleWorkClick"
|
|
/>
|
|
|
|
<!-- 引导与高亮 -->
|
|
<GuideSteps
|
|
v-if="isGuideInterviewTask"
|
|
:steps="steps"
|
|
:highlight-padding="12"
|
|
:highlight-radius="20"
|
|
@complete="handleGuideComplete"
|
|
@skip="handleGuideComplete"
|
|
position-offset-y="85%"
|
|
/>
|
|
|
|
<!-- 引导词按钮 -->
|
|
<ImgButton
|
|
class="absolute w-[120px] bottom-0 right-0 z-10"
|
|
image="components/long"
|
|
@click="handleOpenGuide"
|
|
/>
|
|
</div>
|
|
</template>
|