Files
shanghai-residences/src/pages/rongdesheng/place/place4.tsx
T
zhongyidian 284302341c feat: add progression logic for puzzle residences
- Implemented functions to manage puzzle progress including checking if residences are started, complete, and unlocked.
- Added utility functions to retrieve piece IDs based on residence and route.
- Introduced a new TypeScript file for progression management.
- Integrated puzzle data configuration and residence data for seamless progression tracking.
2026-05-01 21:19:25 +08:00

348 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
EndingOverlay,
ExhibitOverlay,
GuideSteps,
GuideStepsItem,
Image,
Title,
Video,
} from '@/components'
import Cursor from '@/components/cursor'
import { useCursorController } from '@/hooks/useCursorController'
import { getBackgroundImage, getPublicPath, getStaticImage } from '@/lib/utils'
import { useMemo, useState } from 'react'
import { useNavigate, useParams } from 'react-router-dom'
import { saveRongdeshengStore, useRongdeshengStore } from '@/store/rongdeshengStore'
const starName = 'rongdesheng'
const place = 'place4'
const RongdeshengPlace4 = () => {
const navigate = useNavigate()
const { moduleName } = useParams<{ moduleName: string }>()
const { markExhibitVisited, getVisitedExhibits, setLastVisitedPlace } = useRongdeshengStore()
const persistedVisited = useMemo(
() => new Set(getVisitedExhibits(place) as (1 | 2 | 3 | 4)[]),
[getVisitedExhibits]
)
const [visited, setVisited] = useState<Set<1 | 2 | 3 | 4>>(() => new Set(persistedVisited))
const total = 4
const progressCount = visited.size
const completed = progressCount >= total
const subtitle = completed ? '已完成' : `${progressCount}/${total}`
// cursor:1 -> 2 -> 3 -> 4
const { isEnabled } = useCursorController({
initialEnabled: [1],
dependencies: {
2: [1],
3: [2],
4: [3],
},
visited,
})
const [activeExhibitId, setActiveExhibitId] = useState<1 | 2 | 3 | 4 | null>(null)
const [showVideo, setShowVideo] = useState(false)
const [showEndingVideo, setShowEndingVideo] = useState(false)
const [guide1Visible, setGuide1Visible] = useState(false)
const [guide1Step, setGuide1Step] = useState(0)
const [guide1AfterVideoVisible, setGuide1AfterVideoVisible] = useState(false)
const [guide1AfterVideoStep, setGuide1AfterVideoStep] = useState(0)
const [guide2Visible, setGuide2Visible] = useState(false)
const [guide2Step, setGuide2Step] = useState(0)
const [guide3Visible, setGuide3Visible] = useState(false)
const [guide3Step, setGuide3Step] = useState(0)
const [guide4Visible, setGuide4Visible] = useState(false)
const [guide4Step, setGuide4Step] = useState(0)
const [endingGuideVisible, setEndingGuideVisible] = useState(false)
if (!moduleName) return null
const exhibitSrcMap: Record<1 | 2 | 3 | 4, string> = {
1: 'rongdesheng/place4/场景4展品1',
2: 'rongdesheng/place4/场景4展品2',
3: 'rongdesheng/place4/场景4展品3',
4: 'rongdesheng/place4/场景4展品4',
}
const handleOpenExhibit = (id: 1 | 2 | 3 | 4) => {
setActiveExhibitId(id)
if (id === 1) {
setGuide1Step(0)
setGuide1Visible(true)
}
if (id === 2) {
setGuide2Step(0)
setGuide2Visible(true)
}
if (id === 3) {
setGuide3Step(0)
setGuide3Visible(true)
}
if (id === 4) {
setGuide4Step(0)
setGuide4Visible(true)
}
}
const handleVideoEnded = () => {
setShowVideo(false)
setGuide1AfterVideoStep(0)
setGuide1AfterVideoVisible(true)
}
const handleEndingVideoEnded = () => {
setShowEndingVideo(false)
setEndingGuideVisible(true)
}
return (
<div
className="w-full h-full bg-cover bg-center bg-no-repeat"
style={{
backgroundImage: getBackgroundImage(`${starName}/${place}/场景4`),
}}
>
<Title
title="风雨坚守·初心不改"
subtitle={subtitle}
className="absolute top-10 left-1/2 -translate-x-1/2"
/>
<Image
className="absolute right-5 top-5"
src={getStaticImage('components/离开')}
button
onClick={() => navigate(`/${moduleName}/${starName}/place3`)}
/>
<Cursor
className="absolute left-73 bottom-145"
onClick={() => handleOpenExhibit(1)}
read={visited.has(1)}
disabled={!isEnabled(1)}
/>
<Cursor
className="absolute left-120 bottom-145"
onClick={() => handleOpenExhibit(2)}
read={visited.has(2)}
disabled={!isEnabled(2)}
/>
<Cursor
className="absolute left-165 bottom-145"
onClick={() => handleOpenExhibit(3)}
read={visited.has(3)}
disabled={!isEnabled(3)}
/>
{/* 展品 */}
<Cursor
className="absolute left-175 bottom-70"
onClick={() => handleOpenExhibit(4)}
read={visited.has(4)}
disabled={!isEnabled(4)}
/>
{completed && (
<Image
className="absolute bottom-0 right-0"
src={getStaticImage('components/place/离开场景')}
button
onClick={() => {
setLastVisitedPlace('place4')
saveRongdeshengStore()
navigate(`/${moduleName}/${starName}/home`)
}}
/>
)}
<ExhibitOverlay
visible={activeExhibitId !== null}
src={activeExhibitId ? exhibitSrcMap[activeExhibitId] : ''}
onClose={() => {
if (activeExhibitId === null) return
const closingId = activeExhibitId
setActiveExhibitId(null)
// 展品1在视频后对话结束再改状态;2/3/4关闭展品即改状态
if (closingId === 2 || closingId === 3 || closingId === 4) {
setVisited((prev) => {
const next = new Set(prev)
next.add(closingId)
markExhibitVisited(place, closingId)
saveRongdeshengStore()
return next
})
if (closingId === 4) {
setShowEndingVideo(true)
}
}
if (closingId === 1) {
setShowVideo(true)
}
}}
/>
{showVideo && (
<Video
src={getPublicPath('video/rongdesheng/rongdesheng_10.mp4')}
className="fixed inset-0 z-20000"
autoPlay
playsInline
showSkipButton
onEnded={handleVideoEnded}
onSkip={handleVideoEnded}
/>
)}
{showEndingVideo && (
<Video
src={getPublicPath('video/rongdesheng/rongdesheng_11.mp4')}
className="fixed inset-0 z-20000"
autoPlay
playsInline
showSkipButton
onEnded={handleEndingVideoEnded}
onSkip={handleEndingVideoEnded}
/>
)}
<GuideSteps
visible={guide1Visible}
onVisibleChange={setGuide1Visible}
currentStep={guide1Step}
onStepChange={setGuide1Step}
showMask={false}
highlightSelectors={[]}
zIndex={10001}
>
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_22.mp3')}
>
荣德生先生竟然遭遇过绑架?这是怎么回事?他是怎么度过这次危机的?
</GuideStepsItem>
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_23.mp3')}
>
1946年,荣德生先生在上海高恩路(现高安路)住所门前遭到绑架,绑匪勒索巨额款项,将他囚禁了35天。这是当时的新闻报道。面对突如其来的变故,荣氏企业内部沉着应对,在保障人身安全的前提下,积极与绑匪谈判,最终筹集款项将他赎回。
</GuideStepsItem>
</GuideSteps>
<GuideSteps
visible={guide1AfterVideoVisible}
onVisibleChange={(v) => {
setGuide1AfterVideoVisible(v)
if (!v) {
setVisited((prev) => {
const next = new Set(prev)
if (!next.has(1)) {
next.add(1)
markExhibitVisited(place, 1)
saveRongdeshengStore()
}
return next
})
}
}}
currentStep={guide1AfterVideoStep}
onStepChange={setGuide1AfterVideoStep}
showMask={false}
highlightSelectors={[]}
zIndex={10001}
>
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_24.mp3')}
>
这次绑架事件让荣先生身心受到巨大创伤,但也让他更加看清了当时社会的动荡不安,坚定了他期盼国家安定、民族工业能平稳发展的愿望。
</GuideStepsItem>
</GuideSteps>
<GuideSteps
visible={guide2Visible}
onVisibleChange={setGuide2Visible}
currentStep={guide2Step}
onStepChange={setGuide2Step}
showMask={false}
highlightSelectors={[]}
zIndex={10001}
>
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_25.mp3')}
>
“荣氏工业机构,绝不迁赴国外”,当时荣德生先生为什么阻止企业迁往国外啊?
</GuideStepsItem>
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_26.mp3')}
>
1948年,解放战争接近尾声,很多实业家纷纷撤离大陆,前往台湾或海外。但荣德生先生专程从无锡来到上海,发表声明,明确表态绝不离开大陆。他在给海外亲友的信中写道:“民族工业的根基在大陆,离开这片土地,实业救国便成空谈。”
</GuideStepsItem>
<GuideStepsItem speaker="guide">
他不仅自己留下,还力排众议,阻止了荣氏企业迁台的计划,保住了大量的工厂设备、技术人才和生产资料。这份坚守,为新中国成立后的民族工业留存了重要力量,也体现了一位民族实业家的家国情怀。
</GuideStepsItem>
</GuideSteps>
<GuideSteps
visible={guide3Visible}
onVisibleChange={setGuide3Visible}
currentStep={guide3Step}
onStepChange={setGuide3Step}
showMask={false}
highlightSelectors={[]}
zIndex={10001}
>
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_27.mp3')}
>
新中国成立后,荣氏的企业很快就复工了吗?政府对他的贡献给了肯定吗?
</GuideStepsItem>
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_28.mp3')}
>
是的!新中国成立后,在政府的支持下,荣氏企业迅速恢复生产。荣德生先生一生致力于实业救国,为民族工业的发展作出了巨大贡献,政府也对他给予了高度肯定。
</GuideStepsItem>
</GuideSteps>
<GuideSteps
visible={guide4Visible}
onVisibleChange={setGuide4Visible}
currentStep={guide4Step}
onStepChange={setGuide4Step}
showMask={false}
highlightSelectors={[]}
zIndex={10001}
>
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_29.mp3')}
>
晚年的荣先生依然关注民族工业的发展,积极建言献策,他创办的企业后来成为新中国轻工业的重要骨干,为国家的经济建设贡献了力量。
</GuideStepsItem>
</GuideSteps>
<EndingOverlay
visible={endingGuideVisible}
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_30.mp3')}
text="接下来,请你跨越时空,走进1915年的申新纺织厂,参与工厂的创办与运营中,在模拟决策中感受民族工业在风雨中崛起的力量。让我们带着这份感动与启发,传承爱国情怀,续写新时代的实业篇章。"
animationSpeed={200}
onContinue={() => setEndingGuideVisible(false)}
/>
</div>
)
}
export default RongdeshengPlace4