import { ExhibitOverlay, GuideSteps, GuideStepsItem, Image, StudyGuideModal, Title, SelectFillQuizPanel, Video, type VideoRef, } from '@/components' import { useNavigate, useParams } from 'react-router-dom' import { getBackgroundImage, getPublicPath, getStaticImage } from '@/lib/utils' import { useMemo, useRef, useState } from 'react' import Cursor from '@/components/cursor' import { useCursorController } from '@/hooks/useCursorController' import SuccessAlert from '@/components/layout/alert/success-alert' import ScaledPortal from '@/components/layout/ScaledPortal' import type { SelectFillResult } from '@/components/layout/quiz' import { saveMaozedongStore, useMaozedongStore } from '@/store/maozedongStore' import { usePuzzleAnimation } from '@/contexts/PuzzleAnimationContext' // starName 固定为 maozedong const starName = 'maozedong' // 展品 ID 与资源路径映射,ID 用来做唯一标识 const exhibitSrcMap: Record<1 | 2, string> = { 1: 'maozedong/place1/场景1展品1', 2: 'maozedong/place1/场景1展品2', } const MaozedongPlace1 = () => { const navigate = useNavigate() const { moduleName } = useParams<{ moduleName: string }>() // 获取碎片动画触发方法 const { triggerPuzzleAnimation } = usePuzzleAnimation() // 接入持久化:记录本场景 place1 的已访问展品与断点 const markVisited = useMaozedongStore((s) => s.markExhibitVisited) const getVisited = useMaozedongStore((s) => s.getVisitedExhibits) const setLastVisitedPlace = useMaozedongStore((s) => s.setLastVisitedPlace) const persistedVisited = useMemo(() => new Set(getVisited('place1') as (1 | 2)[]), [getVisited]) // 学习引导弹窗 const [showGuideModal, setShowGuideModal] = useState(true) // 对话结束后的学习引导弹窗 const [showAfterDialogGuide, setShowAfterDialogGuide] = useState(false) // 展品2的对话引导状态 const [guideVisible2, setGuideVisible2] = useState(false) const [guideStep2, setGuideStep2] = useState(0) // 当前激活的展品 ID const [activeExhibitId, setActiveExhibitId] = useState<1 | 2 | null>(null) // 已访问的展品(初始化自 store;随后每次关闭展品时写回) const [visited, setVisited] = useState>(() => new Set(persistedVisited)) // 展品2关闭后播放的视频 + 选择填空题 const [showVideo2, setShowVideo2] = useState(false) const video2Ref = useRef(null) const [quizVisible, setQuizVisible] = useState(false) const [successAlertVisible, setSuccessAlertVisible] = useState(false) // Cursor 顺序控制:1 -> 2 const { isEnabled } = useCursorController({ initialEnabled: [2], dependencies: { 1: [2], }, visited, }) const handleOpenExhibit = (id: 1 | 2) => { setActiveExhibitId(id) // 根据展品ID显示对应的对话 if (id === 1) { setGuideVisible2(true) setGuideStep2(0) } } // 监听展品2对话引导步骤变化 const handleGuideStepChange2 = (step: number) => { setGuideStep2(step) } // 监听展品2对话引导完成 const handleGuideVisibleChange2 = (visible: boolean) => { setGuideVisible2(visible) } const progressCount = visited.size const total = 2 const completed = progressCount >= total const subtitle = completed ? '已完成' : `${progressCount}/${total}` if (!moduleName) return null return (
{/* 标题 */} <Image className="absolute right-5 top-5" src={getStaticImage('components/离开')} button onClick={() => { if (moduleName && starName) { navigate(`/${moduleName}/${starName}/home`) } }} /> {/* 光标1 */} <Cursor className="absolute left-106 bottom-150" onClick={() => handleOpenExhibit(1)} read={visited.has(1)} disabled={!isEnabled(1)} /> {/* 光标2 */} <Cursor className="absolute left-170 bottom-135" onClick={() => handleOpenExhibit(2)} read={visited.has(2)} disabled={!isEnabled(2)} /> {/* 继续前进 - 仅在完成所有展品后显示 */} {completed && ( <Image className="absolute bottom-0 right-140" src={getStaticImage(`${starName}/place1/场景1继续前进`)} button onClick={() => { setLastVisitedPlace('place2') saveMaozedongStore() navigate(`/${moduleName}/${starName}/place2`) }} /> )} {/* 展品2的对话引导 */} <GuideSteps visible={guideVisible2} onVisibleChange={handleGuideVisibleChange2} currentStep={guideStep2} onStepChange={handleGuideStepChange2} showMask={false} highlightSelectors={[]} zIndex={10001} > <GuideStepsItem speaker="student" audioSrc={getPublicPath('audio/maozedong/mp3_mzd_03.mp3')} > 那参加中共一大时,毛主席对未来有什么想法吗? </GuideStepsItem> <GuideStepsItem speaker="guide" audioSrc={getPublicPath('audio/maozedong/mp3_mzd_04.mp3')}> 当时全国只有50多个党员,毛主席所在的湖南也才几个。但他觉得,重要的是把马克思主义和中国的实际情况结合起来。就像种田,种子再好,也得看土壤——中国的 “土壤”就是农民占大多数,这是和苏联不一样的地方。 </GuideStepsItem> </GuideSteps> {/* 展品遮罩层 */} <ExhibitOverlay visible={activeExhibitId !== null} src={activeExhibitId ? exhibitSrcMap[activeExhibitId] : ''} onOpen={() => { if (activeExhibitId === 2) { triggerPuzzleAnimation(1) } if (activeExhibitId === 1) { triggerPuzzleAnimation(2) } }} onClose={() => { if (activeExhibitId === 2) { setActiveExhibitId(null) setShowVideo2(true) return } if (activeExhibitId !== null) { setVisited((prev) => { const next = new Set(prev) next.add(activeExhibitId) // 写回持久化 markVisited('place1', activeExhibitId) return next }) } setActiveExhibitId(null) }} /> {/* 展品2关闭后播放的视频 */} {showVideo2 && ( <Video ref={video2Ref} src={getPublicPath('video/maozedong/mzd_00.mp4')} className="fixed inset-0 z-20000" autoPlay playsInline showSkipButton skipKey="maozedong_place1_video" showReadonlyProgress onEnded={() => { setShowVideo2(false) setQuizVisible(true) }} onSkip={() => { setShowVideo2(false) setQuizVisible(true) }} /> )} {/* 展品2对话结束后的选择填空题 + 成功提示(使用 ScaledPortal) */} <ScaledPortal enabled={quizVisible || successAlertVisible} zIndex={10050}> <> <SelectFillQuizPanel visible={quizVisible} titlePrefix="【填空】毛泽东主席认为,到了" titleSuffix="年夏天,在理论上,而且在某种程度的行动上,他已成为一个马克思主义者了。" blanks={[{ id: 'd1' }, { id: 'd2' }, { id: 'd3' }, { id: 'd4' }]} options={[ { value: '0', label: '0' }, { value: '1', label: '1' }, { value: '2', label: '2' }, { value: '3', label: '3' }, { value: '4', label: '4' }, { value: '5', label: '5' }, { value: '6', label: '6' }, { value: '7', label: '7' }, { value: '8', label: '8' }, { value: '9', label: '9' }, ]} correctAnswers={{ d1: '1', d2: '9', d3: '2', d4: '0', }} errorMessage="1920年5月5日到7月初,在上海居住的2个月期间,毛主席的思想完成了向马克思主义者的转变。请你再想想吧!" onSubmit={(_results: SelectFillResult[], isCorrect: boolean) => { if (isCorrect) { setQuizVisible(false) setSuccessAlertVisible(true) } }} onError={(_error: string) => { // 错误时仅提示,再让学生重做 }} submitText="确定" zIndex={10000} /> <SuccessAlert visible={successAlertVisible} onContinue={() => { setSuccessAlertVisible(false) setVisited((prev) => { const next = new Set(prev) next.add(2) markVisited('place1', 2) return next }) }} /> </> </ScaledPortal> {/* 学习引导词 */} <StudyGuideModal visible={showGuideModal} audioSrc={getPublicPath('audio/maozedong/mp3_mzd_02.mp3')} onContinue={() => { setShowGuideModal(false) setShowAfterDialogGuide(true) }} ailgn="left" > 1.场景中出现动态光点的展品表示未浏览,可通过点击光点来了解展品背后的故事,通过滚轮可以放大展品,浏览展品后蓝色动态光点会变为已浏览状态。 {'\n'} 2.在学习过程中会掉落拼图碎片,拼图碎片会自动收入拼图盒中,拼图盒可以点开查看当前拼图收集情况,点击单个拼图可以回到获得拼图的场景再次学习。 {'\n'} 3.所有动态光点的展品全部学习后才能进入下一个场景学习。 </StudyGuideModal> {/* 对话结束后的学习引导 */} <StudyGuideModal visible={showAfterDialogGuide} audioSrc={getPublicPath('audio/maozedong/mp3_mzd_01.mp3')} onContinue={() => setShowAfterDialogGuide(false)} firstLineIndent="2em" > {`同学你好,欢迎来到“走进故居·对话”学习模块! 此刻你正站在上海毛泽东旧居陈列馆的门口,接下来请跟着时光的脚步,一同回望毛泽东主席人生中4段风云激荡的岁月:摸索方向·确立信仰、播撒红色火种、吹响合作号角、唤起工农群众。准备好推开这扇门,走进那段滚烫的峥嵘岁月了吗?`} </StudyGuideModal> </div> ) } export default MaozedongPlace1