feat: 完善课程学习与技能树体验

完善课程资源、学习进度与技能树解锁体验,统一视频播放与引导相关交互。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
zhongyidianandClaude Opus 4.7 committed 2026-05-24 10:30:18 +08:00
1 parent 6f289959be
commit 3e258b87d9
314 files changed
+7020 -3386

No files matched your search

+87 -20
View File
@@ -14,6 +14,13 @@ import { useFilmStore } from '@/stores'
const router = useRouter()
const film = useFilmStore()
const isEditingUnlocked = computed(
() => film.editingVideosWatched.start === true,
)
const isEditingItemLocked = (videoKey: string) =>
videoKey !== 'start' && !isEditingUnlocked.value
const completionIcon = (completed: boolean) =>
getStaticImage(`midterm/${completed ? '完成' : '未完成'}.png`)
@@ -22,40 +29,46 @@ const editingItems = computed(() => [
image: 'film/after-editing/剪辑初始',
videoKey: 'start',
onClick: goStart,
iconClass: 'right-14 top-9',
class:
'absolute left-[0px] bottom-[42%] translate-y-1/2 bg-cover bg-center bg-no-repeat',
},
{
image: 'film/after-editing/节奏型剪辑',
image: 'film/after-editing/img/节奏型剪辑',
textImage: 'film/after-editing/text/节奏型剪辑',
videoKey: 'rhythm',
onClick: goRhythm,
class: 'absolute right-[0px] top-[160px] bg-cover bg-center bg-no-repeat',
class: 'absolute right-[215px] top-[170px] bg-cover bg-center bg-no-repeat',
},
{
image: 'film/after-editing/声音转场',
image: 'film/after-editing/img/声音转场',
textImage: 'film/after-editing/text/声音转场',
videoKey: 'soundTransition',
onClick: goSoundTransition,
class: 'absolute right-[42px] top-[410px] bg-cover bg-center bg-no-repeat',
class: 'absolute right-[215px] top-[420px] bg-cover bg-center bg-no-repeat',
},
{
image: 'film/after-editing/快切',
image: 'film/after-editing/img/快切',
textImage: 'film/after-editing/text/快切',
videoKey: 'cut',
onClick: goCut,
class: 'absolute right-[520px] top-[580px] bg-cover bg-center bg-no-repeat',
class: 'absolute right-[620px] top-[560px] bg-cover bg-center bg-no-repeat',
},
{
image: 'film/after-editing/关联剪辑',
image: 'film/after-editing/img/关联剪辑',
textImage: 'film/after-editing/text/关联剪辑',
videoKey: 'association',
onClick: goAssociation,
class:
'absolute right-[42px] bottom-[270px] bg-cover bg-center bg-no-repeat',
'absolute right-[215px] bottom-[290px] bg-cover bg-center bg-no-repeat',
},
{
image: 'film/after-editing/连续剪辑',
image: 'film/after-editing/img/连续剪辑',
textImage: 'film/after-editing/text/连续剪辑',
videoKey: 'continuity',
onClick: goContinuity,
class:
'absolute right-[42px] bottom-[16px] bg-cover bg-center bg-no-repeat',
'absolute right-[215px] bottom-[35px] bg-cover bg-center bg-no-repeat',
},
])
@@ -87,6 +100,11 @@ const goContinuity = () => {
router.push({ name: 'FilmEditingContinuity' })
}
const goEditingItem = (item: (typeof editingItems.value)[number]) => {
if (isEditingItemLocked(item.videoKey)) return
item.onClick()
}
// 6 个剪辑子视频学习完成后的一次性引导
const editingCompleteSteps: GuideStep[] = [
{
@@ -121,7 +139,11 @@ const handleEditingCompleteGuideDone = () => {
<div
class="absolute inset-0 bg-cover bg-center bg-no-repeat"
:style="{
backgroundImage: getBackgroundImage('film/after-editing/剪辑底图'),
backgroundImage: getBackgroundImage(
isEditingUnlocked
? 'film/after-editing/剪辑底图亮'
: 'film/after-editing/剪辑底图灰',
),
}"
></div>
@@ -137,15 +159,45 @@ const handleEditingCompleteGuideDone = () => {
:key="item.videoKey"
:class="item.class"
>
<ImgButton
:image="item.image"
@click="item.onClick"
/>
<img
class="pointer-events-none absolute left-0 top-0 z-10 h-12 w-12"
:src="completionIcon(film.editingVideosWatched[item.videoKey] === true)"
alt=""
/>
<div
:class="[
'relative transition-all duration-200',
isEditingItemLocked(item.videoKey)
? 'opacity-40 pointer-events-none'
: 'hover:scale-105 hover:cursor-pointer hover:brightness-125',
]"
@click="goEditingItem(item)"
>
<!-- img -->
<img :src="getStaticImage(item.image)" />
<!-- 点击提示 -->
<img
v-if="item.videoKey === 'start' && !isEditingUnlocked"
class="pointer-events-none absolute right-6 -bottom-40 z-20 start-click-hint"
:src="getStaticImage('film/after-editing/点击此处开始')"
alt=""
/>
<!-- 文本 -->
<img
class="absolute right-0 bottom-6 translate-x-[64%] translate-y-1/2 z-10"
:src="getStaticImage(item.textImage ?? '')"
alt=""
/>
<!-- 完成图标 -->
<img
:class="[
'pointer-events-none absolute z-10 w-16',
item.iconClass ?? '-right-6 -top-6',
]"
:src="
completionIcon(film.editingVideosWatched[item.videoKey] === true)
"
alt=""
/>
</div>
</div>
<!-- 关闭 -->
@@ -167,6 +219,21 @@ const handleEditingCompleteGuideDone = () => {
</template>
<style scoped>
.start-click-hint {
animation: start-click-hint 1.2s ease-in-out infinite;
}
@keyframes start-click-hint {
0%,
100% {
transform: translateY(0) scale(1);
}
50% {
transform: translateY(-14px) scale(1.05);
}
}
.onsite-btn {
@apply relative flex flex-col items-start backdrop-blur-md rounded-2xl px-10 py-8 text-left bg-white/5 hover:bg-white/10 border border-white/10 hover:border-white/30 transition-all duration-200 cursor-pointer;
}