- 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.
348 lines
12 KiB
TypeScript
348 lines
12 KiB
TypeScript
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
|