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:
1 parent
e0fa47d21a
commit
f5f36f75b5
153 files changed
+3846
-2352
No files matched your search
@@ -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变换和缩放 */}
|
||||
|
||||
Reference in new issue
Block a user