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>
182 lines
6.6 KiB
TypeScript
182 lines
6.6 KiB
TypeScript
import {
|
|
ExhibitOverlay,
|
|
GuideSteps,
|
|
GuideStepsItem,
|
|
Image,
|
|
Title,
|
|
StudyGuideModal,
|
|
} from '@/components'
|
|
import { useNavigate, useParams } from 'react-router-dom'
|
|
import { getBackgroundImage, getPublicPath, getStaticImage } from '@/lib/utils'
|
|
import { useState } from 'react'
|
|
import Cursor from '@/components/cursor'
|
|
import { usePuzzleAnimation } from '@/contexts/PuzzleAnimationContext'
|
|
import { useHuangyanpeiStore, saveHuangyanpeiStore } from '@/store/huangyanpeiStore'
|
|
|
|
// starName 固定为 huangyanpei
|
|
const starName = 'huangyanpei'
|
|
|
|
// 展品 ID 与资源路径映射,ID 用来做唯一标识
|
|
const exhibitSrcMap: Record<1, string> = {
|
|
1: 'huangyanpei/place1/3_003',
|
|
}
|
|
|
|
const HuangyanpeiPlace1 = () => {
|
|
const navigate = useNavigate()
|
|
const { moduleName } = useParams<{
|
|
moduleName: string
|
|
}>()
|
|
|
|
const { triggerPuzzleAnimation } = usePuzzleAnimation()
|
|
|
|
// 学习引导弹窗
|
|
const [showGuideModal, setShowGuideModal] = useState(true)
|
|
// 温馨提示弹窗
|
|
const [showAfterDialogGuide, setShowAfterDialogGuide] = useState(false)
|
|
// 温馨提示关闭后的引导对话显示状态
|
|
const [visible, setVisible] = useState(false)
|
|
// 当前激活的展品 ID(只存 ID,不直接存路径,便于作为唯一标识)
|
|
const [activeExhibitId, setActiveExhibitId] = useState<1 | null>(null)
|
|
|
|
// 使用持久化的访问状态
|
|
const place = 'place1'
|
|
const { markExhibitVisited, getVisitedExhibits, setLastVisitedPlace } = useHuangyanpeiStore()
|
|
|
|
// 从 store 获取已访问的展品,转换为 Set 用于兼容现有代码
|
|
const visitedExhibits = getVisitedExhibits(place)
|
|
const visited = new Set(visitedExhibits)
|
|
|
|
const handleOpenExhibit = (index: 1 | null) => {
|
|
setActiveExhibitId(index)
|
|
}
|
|
|
|
const progressCount = visited.size
|
|
const total = 1
|
|
const completed = progressCount >= total
|
|
const subtitle = completed ? '已完成' : `${progressCount}/${total}`
|
|
const subtitleClassName = completed
|
|
? 'text-[#fff998] [text-shadow:_0_8px_8px_rgba(0,0,0,0.35),_0_0_16px_rgba(0,0,0,0.25)]'
|
|
: undefined
|
|
|
|
return (
|
|
<div
|
|
className="w-full h-full bg-cover bg-center bg-no-repeat"
|
|
style={{
|
|
backgroundImage: getBackgroundImage(`huangyanpei/place1/3_001`),
|
|
}}
|
|
>
|
|
{/* 标题 */}
|
|
<Title
|
|
title="思想启蒙"
|
|
subtitle={subtitle}
|
|
subtitleClassName={subtitleClassName}
|
|
className="absolute top-10 left-1/2 -translate-x-1/2"
|
|
/>
|
|
|
|
<Image
|
|
className="absolute right-5 top-5"
|
|
src={getStaticImage('components/离开')}
|
|
button
|
|
onClick={() => {
|
|
if (moduleName && starName) {
|
|
navigate(`/${moduleName}/${starName}/home`)
|
|
}
|
|
}}
|
|
/>
|
|
|
|
{/* 展品1 */}
|
|
<Cursor
|
|
className="absolute left-[calc(50%-8px)] bottom-100"
|
|
onClick={() => handleOpenExhibit(1)}
|
|
read={visited.has(1)}
|
|
/>
|
|
|
|
{/* 继续前进 - 仅在完成所有展品后显示 */}
|
|
{completed && (
|
|
<Image
|
|
className="absolute bottom-0 right-0"
|
|
src={getStaticImage('components/place/继续前进')}
|
|
button
|
|
onClick={() => {
|
|
setLastVisitedPlace('place2')
|
|
saveHuangyanpeiStore()
|
|
navigate(`/${moduleName}/${starName}/place2`)
|
|
}}
|
|
/>
|
|
)}
|
|
|
|
{/* 遮罩层 */}
|
|
<ExhibitOverlay
|
|
visible={activeExhibitId !== null}
|
|
src={activeExhibitId ? exhibitSrcMap[activeExhibitId] : ''}
|
|
onOpen={() => {
|
|
if (activeExhibitId === 1) {
|
|
triggerPuzzleAnimation(1)
|
|
}
|
|
}}
|
|
onClose={() => {
|
|
if (activeExhibitId !== null) {
|
|
markExhibitVisited(place, activeExhibitId)
|
|
}
|
|
setActiveExhibitId(null)
|
|
}}
|
|
/>
|
|
|
|
{/* 学习引导 */}
|
|
<StudyGuideModal
|
|
visible={showGuideModal}
|
|
onContinue={() => {
|
|
setShowGuideModal(false)
|
|
setShowAfterDialogGuide(true)
|
|
}}
|
|
audioSrc={getPublicPath('audio/huangyanpei/mp3_huangyanpei_02.mp3')}
|
|
ailgn="left"
|
|
>
|
|
1.场景中出现动态光点的展品表示未浏览,可通过点击光点来了解展品背后的故事,通过滚轮可以放大展品,浏览展品后蓝色动态光点会变为已浏览状态。
|
|
{'\n'}
|
|
2.在学习过程中会掉落拼图碎片,拼图碎片会自动收入拼图盒中,拼图盒可以点开查看当前拼图收集情况,点击单个拼图可以回到获得拼图的场景再次学习。
|
|
{'\n'}
|
|
3.所有动态光点的展品全部学习后才能进入下一个场景学习。
|
|
</StudyGuideModal>
|
|
|
|
{/* 温馨提示关闭后的引导对话 */}
|
|
<GuideSteps visible={visible} onVisibleChange={setVisible} showMask={false}>
|
|
<GuideStepsItem
|
|
speaker="student"
|
|
audioSrc={getPublicPath('audio/huangyanpei/mp3_huangyanpei_03.mp3')}
|
|
>
|
|
小沪,这栋古色古香的江南大院,就是黄炎培先生从小生活、读书的地方吗?
|
|
</GuideStepsItem>
|
|
<GuideStepsItem
|
|
speaker="guide"
|
|
audioSrc={getPublicPath('audio/huangyanpei/mp3_huangyanpei_04_1.mp3')}
|
|
>
|
|
没错,1878年10月1日,黄炎培先生就出生在当时江苏省川沙县城关镇(现上海市浦东新区川沙镇)的这处“内史第”,宅子原是他外祖父沈树镛家,也是他后来所说的“生于斯、读于斯、长于斯”之地。
|
|
</GuideStepsItem>
|
|
<GuideStepsItem
|
|
speaker="guide"
|
|
audioSrc={getPublicPath('audio/huangyanpei/mp3_huangyanpei_04_2.mp3')}
|
|
>
|
|
1890年,川沙创办“观澜书院”,12岁的黄炎培以第一名的成绩考入,每天提着母亲亲手缝制的布书包,往返十里路求学,一点都不叫苦。1893年他去上海应县考,松江府复试名列前茅;1899年,在松江府试中更是以第一名的成绩中了秀才。
|
|
</GuideStepsItem>
|
|
</GuideSteps>
|
|
|
|
{/* 温馨提示 */}
|
|
<StudyGuideModal
|
|
visible={showAfterDialogGuide}
|
|
onContinue={() => {
|
|
setShowAfterDialogGuide(false)
|
|
setVisible(true)
|
|
}}
|
|
audioSrc={getPublicPath('audio/huangyanpei/mp3_huangyanpei_01.mp3')}
|
|
firstLineIndent="2em"
|
|
>
|
|
同学你好,欢迎来到“走进故居·对话”学习模块!{'\n'}
|
|
此刻你正站在黄炎培先生故居门前,接下来就跟着时光的脉络,拆解黄先生人生里5段滚烫的旅程:思想启蒙、教育实践、创立职教、延安对话、职教传薪。准备好一起走进这段炽热的岁月了吗?
|
|
</StudyGuideModal>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default HuangyanpeiPlace1
|