feat: add puzzle residence configuration and reflection progress logic

- Introduced `puzzleResidences.ts` to define puzzle residence configurations including properties like display name, position, and puzzle availability.
- Added `reflectionProgress.ts` to manage badge unlocking logic based on place completion, including functions to determine unlocked badges and video sources for scenes.
This commit is contained in:
zhongyidian committed 2026-04-27 11:49:30 +08:00
1 parent e0fa47d21a
commit f5f36f75b5
153 files changed
+3846 -2352

No files matched your search

+48 -14
View File
@@ -1,9 +1,17 @@
import { useCallback, useMemo, useState } from 'react'
import { useNavigate, useParams } from 'react-router-dom'
import { motion } from 'framer-motion'
import { Image, Title, SceneHint, DragFill, ExhibitFlipOverlay } from '@/components'
import {
Image,
Title,
SceneHint,
DragFill,
ExhibitFlipOverlay,
FlipActionButton,
} from '@/components'
import type { DragFillResult } from '@/components'
import { cn, getBackgroundImage, getStaticImage } from '@/lib/utils'
import { downloadDataUrlImage } from '@/lib/utils/sensitiveWords'
import { useZhanglepingStore, saveZhanglepingStore } from '@/store/zhanglepingStore'
import { useSceneHintController } from './useSceneHintController'
@@ -65,8 +73,12 @@ const ZhanglepingReflectionScene5 = () => {
const [haloHovered, setHaloHovered] = useState(false)
const scene = 'scene5'
const total = 1
const { markReflectionCursorVisited, getVisitedReflectionCursors, drawArtworkDataUrl } =
useZhanglepingStore()
const {
markReflectionCursorVisited,
getVisitedReflectionCursors,
drawArtworkDataUrl,
hasDrawArtwork,
} = useZhanglepingStore()
// 根据 store 中碎片是否已解锁决定初始子场景
const fragmentAlreadyUnlocked = getVisitedReflectionCursors(scene).includes(1)
@@ -79,8 +91,9 @@ const ZhanglepingReflectionScene5 = () => {
const completed = progressCount >= total
const subtitle = completed ? '已完成' : `${Math.min(progressCount, total)}/${total}`
const hasSubmittedArtwork =
typeof drawArtworkDataUrl === 'string' &&
/^data:image\/[a-zA-Z]+;base64,/.test(drawArtworkDataUrl)
hasDrawArtwork ||
(typeof drawArtworkDataUrl === 'string' &&
/^data:image\/[a-zA-Z]+;base64,/.test(drawArtworkDataUrl))
// 子场景2:拖拽状态
const [dragFillValue, setDragFillValue] = useState<DragFillResult[]>([])
@@ -155,6 +168,16 @@ const ZhanglepingReflectionScene5 = () => {
setTimeout(() => setSubScene(3), 800)
}, [])
const handleDownloadArtwork = useCallback(async () => {
if (!hasSubmittedArtwork) return
try {
await downloadDataUrlImage(drawArtworkDataUrl, '三毛新时代图景.png')
} catch (error) {
console.error('Download artwork failed', error)
}
}, [drawArtworkDataUrl, hasSubmittedArtwork])
if (!moduleName) return null
const subSceneBg = { 1: '场景5', 2: '邮箱', 3: '画板' }[subScene]
@@ -372,15 +395,26 @@ const ZhanglepingReflectionScene5 = () => {
{/* 画板模块提交的画作(无画作时不显示空白画板) */}
{hasSubmittedArtwork && (
<div
className={cn(
'absolute left-40 top-178 w-[205px] h-[180px] transform-3d',
'bg-white flex justify-center items-center overflow-hidden'
)}
style={{ transform: 'rotateX(-15deg) rotateY(22deg) rotateZ(3deg)' }}
>
<Image objectFit="cover" src={drawArtworkDataUrl} />
</div>
<>
<div
className={cn(
'absolute left-40 top-178 w-[205px] h-[180px] transform-3d',
'bg-white flex justify-center items-center overflow-hidden'
)}
style={{ transform: 'rotateX(-15deg) rotateY(22deg) rotateZ(3deg)' }}
>
<Image objectFit="cover" src={drawArtworkDataUrl} />
</div>
<div
className="absolute left-62 top-[900px] z-20"
style={{ transform: 'rotateX(-15deg) rotateY(22deg) rotateZ(3deg)' }}
>
<FlipActionButton className="text-2xl px-5 py-2" onClick={handleDownloadArtwork}>
下载
</FlipActionButton>
</div>
</>
)}
</>
)}