forked from zhongyidian/super_video
feat: 完善视频学习进度缓存
完善新手导航和三大创作模块的视频完成缓存、未完成提示与学习进度计算,确保进度按实际学习资源完成情况更新。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
1f8723aabd
commit
6f289959be
63 files changed
+3139
-1126
No files matched your search
@@ -1,7 +1,13 @@
|
||||
<template>
|
||||
<Transition name="step-navigation" appear>
|
||||
<Transition
|
||||
name="step-navigation"
|
||||
appear
|
||||
>
|
||||
<!-- 抽屉遮罩层 -->
|
||||
<div v-if="visible" class="fixed inset-0 z-50 bg-black/30 backdrop-blur-md">
|
||||
<div
|
||||
v-if="visible"
|
||||
class="fixed inset-0 z-50 bg-black/30 backdrop-blur-md"
|
||||
>
|
||||
<ImgButton
|
||||
class="absolute right-0 top-0 z-[10]"
|
||||
image="components/close"
|
||||
@@ -20,14 +26,24 @@
|
||||
@click.stop
|
||||
>
|
||||
<!-- 顶部分页箭头(上一页) -->
|
||||
<div class="relative z-10 flex items-center justify-center pointer-events-none">
|
||||
<div
|
||||
class="relative z-10 flex items-center justify-center pointer-events-none"
|
||||
>
|
||||
<button
|
||||
class="pointer-events-auto flex h-20 w-20 items-center justify-center border-none bg-transparent transition-all duration-200 disabled:cursor-not-allowed disabled:opacity-40 hover:enabled:scale-110"
|
||||
@click="goPrevPage"
|
||||
:disabled="!canPrevPage"
|
||||
>
|
||||
<svg width="64" height="64" viewBox="0 0 40 40" fill="none">
|
||||
<path d="M5 30L35 30L20 6Z" fill="white" />
|
||||
<svg
|
||||
width="64"
|
||||
height="64"
|
||||
viewBox="0 0 40 40"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
d="M5 30L35 30L20 6Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
@@ -41,20 +57,31 @@
|
||||
class="step-item-bg relative flex h-[122px] w-[831px] cursor-pointer items-center px-8 py-4 transition-all duration-300 hover:-translate-x-[23%]"
|
||||
:class="{
|
||||
'step-item-active-bg h-[230px] w-[1034px] pl-[180px] -translate-x-[23%]':
|
||||
currentPageStartIndex + index === currentStepIndex
|
||||
currentPageStartIndex + index === currentStepIndex,
|
||||
}"
|
||||
@click="handleStepClick(index)"
|
||||
>
|
||||
<!-- 步骤标签 -->
|
||||
<div
|
||||
class="mr-4 min-w-[5rem] rounded-full px-16 py-4 text-center text-2xl font-semibold shadow-md"
|
||||
class="mr-0 min-w-[5rem] rounded-full px-16 py-4 text-center text-2xl font-semibold shadow-md"
|
||||
:class="getTagColor(step.label)"
|
||||
>
|
||||
{{ step.label }}
|
||||
</div>
|
||||
|
||||
<!-- 步骤内容 -->
|
||||
<div class="mr-4 flex-1 text-3xl font-bold leading-[1.5]">
|
||||
<div
|
||||
class="mr-4 flex-1 text-3xl font-bold leading-[1.5] flex items-center"
|
||||
>
|
||||
<img
|
||||
class="pointer-events-none h-12 w-12 object-contain transition-all duration-300"
|
||||
:src="
|
||||
completedSteps?.[currentPageStartIndex + index]
|
||||
? completedIcon
|
||||
: incompleteIcon
|
||||
"
|
||||
alt=""
|
||||
/>
|
||||
{{ step.content }}
|
||||
</div>
|
||||
|
||||
@@ -63,7 +90,7 @@
|
||||
class="absolute right-[15%] z-[1] flex items-center justify-center text-6xl font-semibold text-white/30 transition-all duration-300"
|
||||
:class="{
|
||||
'mb-20 text-[150px] text-white drop-shadow-[5px_10px_5px_rgba(0,0,0,0.1)]':
|
||||
currentPageStartIndex + index === currentStepIndex
|
||||
currentPageStartIndex + index === currentStepIndex,
|
||||
}"
|
||||
>
|
||||
{{ currentPageStartIndex + index + 1 }}
|
||||
@@ -79,8 +106,16 @@
|
||||
@click="goNextPage"
|
||||
:disabled="!canNextPage"
|
||||
>
|
||||
<svg width="64" height="64" viewBox="0 0 40 40" fill="none">
|
||||
<path d="M5 10L35 10L20 34Z" fill="white" />
|
||||
<svg
|
||||
width="64"
|
||||
height="64"
|
||||
viewBox="0 0 40 40"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
d="M5 10L35 10L20 34Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
@@ -94,6 +129,8 @@
|
||||
import { ImgButton } from '@/components'
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { getTagColor } from '@/utils'
|
||||
import completedIcon from '@/assets/images/midterm/完成.png'
|
||||
import incompleteIcon from '@/assets/images/midterm/未完成.png'
|
||||
|
||||
export interface StepItem {
|
||||
id: string
|
||||
@@ -111,6 +148,8 @@ interface Props {
|
||||
steps: StepItem[]
|
||||
/** 当前步骤索引 */
|
||||
currentStepIndex?: number
|
||||
/** 步骤完成状态 */
|
||||
completedSteps?: boolean[]
|
||||
/** 是否显示关闭按钮 */
|
||||
showClose?: boolean
|
||||
}
|
||||
@@ -122,7 +161,7 @@ interface Emits {
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
currentStepIndex: 0,
|
||||
showClose: true
|
||||
showClose: true,
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emits>()
|
||||
@@ -131,10 +170,15 @@ const emit = defineEmits<Emits>()
|
||||
const pageSize = 5
|
||||
const currentPage = ref(0)
|
||||
|
||||
const totalPages = computed(() => Math.max(1, Math.ceil((props.steps?.length ?? 0) / pageSize)))
|
||||
const totalPages = computed(() =>
|
||||
Math.max(1, Math.ceil((props.steps?.length ?? 0) / pageSize)),
|
||||
)
|
||||
const currentPageStartIndex = computed(() => currentPage.value * pageSize)
|
||||
const pagedSteps = computed(() =>
|
||||
props.steps.slice(currentPageStartIndex.value, currentPageStartIndex.value + pageSize)
|
||||
props.steps.slice(
|
||||
currentPageStartIndex.value,
|
||||
currentPageStartIndex.value + pageSize,
|
||||
),
|
||||
)
|
||||
|
||||
const canPrevPage = computed(() => currentPage.value > 0)
|
||||
@@ -164,14 +208,17 @@ const handleClose = () => {
|
||||
// 当外部当前步骤改变时,自动定位到对应页
|
||||
watch(
|
||||
() => props.currentStepIndex,
|
||||
newVal => {
|
||||
(newVal) => {
|
||||
const idx = newVal ?? 0
|
||||
const targetPage = Math.floor(idx / pageSize)
|
||||
if (targetPage !== currentPage.value) {
|
||||
currentPage.value = Math.min(Math.max(targetPage, 0), totalPages.value - 1)
|
||||
currentPage.value = Math.min(
|
||||
Math.max(targetPage, 0),
|
||||
totalPages.value - 1,
|
||||
)
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
{ immediate: true },
|
||||
)
|
||||
</script>
|
||||
|
||||
|
||||
Reference in new issue
Block a user