Files
shanghai-residences/src/pages/entrance/index.tsx
T
zhongyidianandClaude Opus 4.7 6d40e19346 feat: update residence learning flows
Commit non-media application and asset changes while leaving audio, video, and environment files out of version control.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 10:15:26 +08:00

201 lines
5.5 KiB
TypeScript

import { getBackgroundImage, getStaticImage } from '@/lib/utils'
import { Navigate, useNavigate, useParams } from 'react-router-dom'
import { Image } from '@/components'
import { isResidenceStarted, isResidenceUnlocked } from '@/config/progression'
import { usePuzzleStore } from '@/store/puzzleStore'
//英文命名 "革命先驱" | "文化名人" | "艺术名家" | "经济巨擘"
type ModuleName = 'revolutionary' | 'cultural' | 'artistic' | 'economic'
type EntranceActionState = 'locked' | 'unvisited' | 'visited'
type EntranceSide = 'left' | 'right'
interface Star {
name: string
starName: string
}
interface EntranceBackground {
backgroundImage: string
//Star1
Star: {
Star1: Star
Star2: Star
}
}
const entranceActionConfig: Record<
EntranceActionState,
{
image: string
position: Record<EntranceSide, string>
}
> = {
locked: {
image: 'entrance/暂未解锁',
position: {
left: 'absolute left-95 bottom-58',
right: 'absolute right-106 bottom-58',
},
},
unvisited: {
image: 'entrance/点击前往浏览',
position: {
left: 'absolute left-90.5 bottom-37',
right: 'absolute right-90.5 bottom-37',
},
},
visited: {
image: 'entrance/该故居已访问',
position: {
left: 'absolute left-95 bottom-58',
right: 'absolute right-102 bottom-58',
},
},
}
/**
* 故居入口页
* 根据模块名称显示不同的背景图
* 根据模块名称显示不同的进入按钮
* 革命先驱:毛泽东、孙中山
* 文化名人:鲁迅、黄炎培
* 艺术名家:聂耳、刘海栗
* 经济巨擘:荣德生、陈云
*
*/
const EntranceBackgroundMap: Record<ModuleName, EntranceBackground> = {
revolutionary: {
backgroundImage: getBackgroundImage('entrance/革命先驱'),
Star: {
Star1: {
name: '毛泽东',
starName: 'maozedong',
},
Star2: {
name: '孙中山',
starName: 'sunzhongshan',
},
},
},
cultural: {
backgroundImage: getBackgroundImage('entrance/文教名人'),
Star: {
Star1: {
name: '鲁迅',
starName: 'luxun',
},
Star2: {
name: '黄炎培',
starName: 'huangyanpei',
},
},
},
artistic: {
backgroundImage: getBackgroundImage('entrance/艺术名家'),
Star: {
Star1: {
name: '聂耳',
starName: 'nieer',
},
Star2: {
name: '张乐平',
starName: 'zhangleping',
},
},
},
economic: {
backgroundImage: getBackgroundImage('entrance/经济巨擎'),
Star: {
Star1: {
name: '荣德生',
starName: 'rongdesheng',
},
Star2: {
name: '陈云',
starName: 'chenyun',
},
},
},
}
const Entrance = () => {
const { moduleName } = useParams()
const navigate = useNavigate()
const puzzles = usePuzzleStore((state) => state.puzzles)
// const decodedModuleName = decodeURIComponent(moduleName || "");
const { backgroundImage, Star } = EntranceBackgroundMap[moduleName as ModuleName] || {}
if (!Star) {
return <Navigate to="/menu" replace />
}
const { Star1, Star2 } = Star
const isStar1Unlocked = isResidenceUnlocked(Star1.starName, puzzles)
const isStar2Unlocked = isResidenceUnlocked(Star2.starName, puzzles)
const isStar1Started = isResidenceStarted(Star1.starName, puzzles)
const isStar2Started = isResidenceStarted(Star2.starName, puzzles)
const getStarImage = (star: Star, isUnlocked: boolean) =>
getStaticImage(`entrance/${star.name}-${isUnlocked ? '解锁' : '未解锁'}`)
const getEntranceActionState = (isUnlocked: boolean, isStarted: boolean): EntranceActionState => {
if (!isUnlocked) return 'locked'
return isStarted ? 'visited' : 'unvisited'
}
const getEntranceAction = (side: EntranceSide, state: EntranceActionState) => {
const config = entranceActionConfig[state]
return {
className: config.position[side],
image: config.image,
}
}
const star1Action = getEntranceAction(
'left',
getEntranceActionState(isStar1Unlocked, isStar1Started)
)
const star2Action = getEntranceAction(
'right',
getEntranceActionState(isStar2Unlocked, isStar2Started)
)
return (
<div className="w-full h-full bg-cover bg-center bg-no-repeat" style={{ backgroundImage }}>
<Image
className="absolute bottom-5 right-5"
src={getStaticImage('entrance/入口-返回')}
button
onClick={() => navigate('/menu')}
/>
{/* 名人1 */}
<Image
className="absolute left-73 top-[calc(50%+38px)] -translate-y-1/2"
src={getStarImage(Star1, isStar1Unlocked)}
/>
<Image
className={star1Action.className}
src={getStaticImage(star1Action.image)}
button={isStar1Unlocked}
onClick={() => {
if (!isStar1Unlocked) return
navigate(`/${moduleName}/${Star1.starName}/home`)
}}
/>
{/* 名人2 */}
<Image
className="absolute right-77.5 top-[calc(50%+39px)] -translate-y-1/2"
src={getStarImage(Star2, isStar2Unlocked)}
/>
<Image
className={star2Action.className}
src={getStaticImage(star2Action.image)}
button={isStar2Unlocked}
onClick={() => {
if (!isStar2Unlocked) return
navigate(`/${moduleName}/${Star2.starName}/home`)
}}
/>
{/* <Image
className="absolute right-5 top-1/2 -translate-y-1/2"
src={Star2.image}
/> */}
</div>
)
}
export default Entrance