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

+63 -4
View File
@@ -4,6 +4,7 @@ import { cn, getBackgroundImage, getStaticImage } from '@/lib/utils'
import { usePuzzleStore } from '@/store/puzzleStore'
import { getPuzzlePieceData } from '@/config/puzzleData'
import { getPuzzlePiecePath, getPuzzleViewBox } from '@/config/puzzlePaths'
import { PUZZLE_RESIDENCES } from '@/config/puzzleResidences'
import { Image } from '@/components'
/**
@@ -16,6 +17,9 @@ const getResidenceDisplayName = (residence: string): string => {
luxun: '鲁迅',
sunzhongshan: '孙中山',
maozedong: '毛泽东',
zhangleping: '张乐平',
nieer: '聂耳',
rongdesheng: '荣德生',
}
return nameMap[residence] || residence
@@ -28,6 +32,10 @@ export interface PuzzleSystemProps {
onClose: () => void
/** 故居名称,如 'chenyun' */
residence: string
/** 是否显示菜单入口的故居导航 */
showResidenceNav?: boolean
/** 切换故居 */
onResidenceChange?: (residence: string) => void
}
/**
@@ -143,7 +151,13 @@ const PuzzlePieceSvg = ({
* - 已解锁显示正常图片,未解锁显示遮罩
* - 点击碎片可跳转到对应页面
*/
const PuzzleSystem = ({ visible, onClose, residence }: PuzzleSystemProps) => {
const PuzzleSystem = ({
visible,
onClose,
residence,
showResidenceNav = false,
onResidenceChange,
}: PuzzleSystemProps) => {
const navigate = useNavigate()
const { moduleName } = useParams<{ moduleName: string }>()
const { isPuzzleUnlocked, getPuzzleCollectProgress } = usePuzzleStore()
@@ -154,9 +168,15 @@ const PuzzleSystem = ({ visible, onClose, residence }: PuzzleSystemProps) => {
useEffect(() => {
if (!visible) {
setSelectedPiece(null)
setHoveredPiece(null)
}
}, [visible])
useEffect(() => {
setSelectedPiece(null)
setHoveredPiece(null)
}, [residence])
if (!visible) return null
// 背景图片
@@ -177,13 +197,13 @@ const PuzzleSystem = ({ visible, onClose, residence }: PuzzleSystemProps) => {
// 处理前往按钮点击
const handleGoToPage = () => {
if (moduleName && selectedPiece) {
if (activeModuleName && selectedPiece) {
// console.log(residence, selectedPiece, moduleName);
// const route = getPuzzleRoute(residence, selectedPiece, moduleName);
// console.log(route);
const route = selectedPieceData?.route
if (route) {
navigate(`/${moduleName}/${residence}/${route}`)
navigate(`/${activeModuleName}/${residence}/${route}`)
onClose()
}
}
@@ -200,6 +220,12 @@ const PuzzleSystem = ({ visible, onClose, residence }: PuzzleSystemProps) => {
// 获取收集进度
const collectProgress = getPuzzleCollectProgress(residence)
const activeResidenceConfig = PUZZLE_RESIDENCES.find((item) => item.residence === residence)
const activeModuleName = moduleName || activeResidenceConfig?.moduleName
const residenceNavItems = PUZZLE_RESIDENCES.filter((item) => item.hasPuzzle).map((item) => ({
...item,
progress: getPuzzleCollectProgress(item.residence),
}))
return (
<div
@@ -221,6 +247,37 @@ const PuzzleSystem = ({ visible, onClose, residence }: PuzzleSystemProps) => {
backgroundImage: `url(${backgroundImage})`,
}}
>
{showResidenceNav && (
<div className="absolute left-0 top-0 z-10 h-full w-[360px] pt-10 flex flex-col gap-3 pointer-events-auto">
{residenceNavItems.map((item) => {
const isActive = item.residence === residence
const progress = Math.round(item.progress)
return (
<button
key={item.residence}
type="button"
className={cn(
'relative w-[341px] h-[112px] bg-contain bg-center bg-no-repeat text-left transition-all hover:brightness-110',
isActive ? 'translate-x-3 scale-105' : ''
)}
style={{
backgroundImage: `url(${getStaticImage(
isActive ? 'puzzle/选中' : 'puzzle/未选中'
)})`,
}}
onClick={() => onResidenceChange?.(item.residence)}
>
<div className="relative h-full pl-14 pr-5 flex items-center justify-between text-white font-bold">
<span className="text-2xl drop-shadow">{item.displayName}</span>
<span className="text-xl drop-shadow">{progress}%</span>
</div>
</button>
)
})}
</div>
)}
{/* 关闭按钮 */}
<Image
className="absolute right-8 top-8 z-10"
@@ -259,7 +316,9 @@ const PuzzleSystem = ({ visible, onClose, residence }: PuzzleSystemProps) => {
</div>
{/* 完整拼图底图 - 作为背景显示所有碎片拼好的样子 */}
<div className="absolute inset-0 flex justify-end">
<div
className={cn('absolute inset-0 flex justify-end', showResidenceNav ? 'pl-[360px]' : '')}
>
<div className="flex items-center gap-4 px-6 pt-30">
<div className="w-200 h-200 perspective-[1000px]">
{/* SVG拼图容器 - 使用3D变换和缩放 */}