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>
361 lines
15 KiB
TypeScript
361 lines
15 KiB
TypeScript
import {
|
|
ExhibitOverlay,
|
|
GuideSteps,
|
|
GuideStepsItem,
|
|
Image,
|
|
Title,
|
|
Cursor,
|
|
StudyGuideModal,
|
|
} from '@/components'
|
|
import { MatchQuizPanel, MultipleChoiceQuizPanel } from '@/components/layout/quiz'
|
|
import { SuccessAlert } from '@/components/layout'
|
|
import { useNavigate, useParams } from 'react-router-dom'
|
|
import { getBackgroundImage, getPublicPath, getStaticImage } from '@/lib/utils'
|
|
import { useState } from 'react'
|
|
import { useCursorController } from '@/hooks/useCursorController'
|
|
import { usePuzzleAnimation } from '@/contexts/PuzzleAnimationContext'
|
|
import { useLuxunStore, saveLuxunStore } from '@/store/luxunStore'
|
|
|
|
// starName 固定为 luxun
|
|
const starName = 'luxun'
|
|
|
|
// 展品 ID 与资源路径映射,ID 用来做唯一标识
|
|
const exhibitSrcMap: Record<1 | 2, string> = {
|
|
1: 'luxun/place1/展品1',
|
|
2: 'luxun/place1/展品2',
|
|
}
|
|
|
|
const LuxunPlace1 = () => {
|
|
const navigate = useNavigate()
|
|
const { moduleName } = useParams<{
|
|
moduleName: string
|
|
}>()
|
|
|
|
//碎片动画触发方法
|
|
const { triggerPuzzleAnimation } = usePuzzleAnimation()
|
|
|
|
// 学习引导弹窗
|
|
const [showGuideModal, setShowGuideModal] = useState(true)
|
|
// 对话结束后的学习引导弹窗
|
|
const [showAfterDialogGuide, setShowAfterDialogGuide] = useState(false)
|
|
const [afterDialogGuideShown, setAfterDialogGuideShown] = useState(false)
|
|
// 引导对话显示状态
|
|
const [visible, setVisible] = useState(false)
|
|
const [visibleIndex, setVisibleIndex] = useState(0)
|
|
|
|
// 打开展品2后的对话显示状态
|
|
const [showAfterExhibitDialog, setShowAfterExhibitDialog] = useState(false)
|
|
const [afterExhibitDialogStep, setAfterExhibitDialogStep] = useState(0)
|
|
// 当前激活的展品 ID(只存 ID,不直接存路径,便于作为唯一标识)
|
|
const [activeExhibitId, setActiveExhibitId] = useState<1 | 2 | null>(null)
|
|
// 使用持久化的访问状态
|
|
const place = 'place1'
|
|
const { markExhibitVisited, getVisitedExhibits, setLastVisitedPlace } = useLuxunStore()
|
|
|
|
// 从 store 获取已访问的展品,转换为 Set 用于兼容现有代码
|
|
const visitedExhibits = getVisitedExhibits(place)
|
|
const visited = new Set(visitedExhibits)
|
|
// 连线题显示状态(展品1关闭后)
|
|
const [showMatchQuiz, setShowMatchQuiz] = useState(false)
|
|
// 连线题成功提示状态
|
|
const [showMatchQuizSuccess, setShowMatchQuizSuccess] = useState(false)
|
|
const [showExhibit2Quiz, setShowExhibit2Quiz] = useState(false)
|
|
const [showExhibit2QuizSuccess, setShowExhibit2QuizSuccess] = useState(false)
|
|
|
|
// Cursor 顺序控制:1 -> 2
|
|
const { isEnabled } = useCursorController({
|
|
initialEnabled: [1],
|
|
dependencies: {
|
|
2: [1],
|
|
},
|
|
visited: visited,
|
|
})
|
|
|
|
const handleOpenExhibit = (index: 1 | 2) => {
|
|
if (index === 1) {
|
|
setVisibleIndex(0)
|
|
setVisible(true)
|
|
}
|
|
|
|
if (index === 2) {
|
|
setAfterExhibitDialogStep(0)
|
|
setShowAfterExhibitDialog(true)
|
|
}
|
|
setActiveExhibitId(index)
|
|
}
|
|
|
|
const progressCount = visited.size
|
|
const total = 2
|
|
const completed = progressCount >= total
|
|
|
|
// 当两个展品都浏览完成后,自动显示对话
|
|
// useEffect(() => {
|
|
// if (completed && !afterExhibitDialogShown) {
|
|
// setShowAfterExhibitDialog(true);
|
|
// setAfterExhibitDialogShown(true);
|
|
// }
|
|
// }, [completed, afterExhibitDialogShown]);
|
|
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(`luxun/place1/背景`),
|
|
}}
|
|
>
|
|
{/* 标题 */}
|
|
<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%-48px)] top-80"
|
|
onClick={() => handleOpenExhibit(1)}
|
|
read={visited.has(1)}
|
|
disabled={!isEnabled(1)}
|
|
/>
|
|
{/* 展品2 */}
|
|
<Cursor
|
|
className="absolute left-[calc(50%)] bottom-60"
|
|
onClick={() => handleOpenExhibit(2)}
|
|
read={visited.has(2)}
|
|
disabled={!isEnabled(2)}
|
|
/>
|
|
|
|
{/* 继续前进 - 仅在完成所有展品且对话结束后显示 */}
|
|
{completed && (
|
|
<Image
|
|
className="absolute bottom-0 right-0"
|
|
src={getStaticImage('components/place/继续前进')}
|
|
button
|
|
onClick={() => {
|
|
setLastVisitedPlace('place2')
|
|
saveLuxunStore()
|
|
navigate(`/${moduleName}/${starName}/place2`)
|
|
}}
|
|
/>
|
|
)}
|
|
|
|
{/* 遮罩层 */}
|
|
<ExhibitOverlay
|
|
visible={activeExhibitId !== null}
|
|
src={activeExhibitId ? exhibitSrcMap[activeExhibitId] : ''}
|
|
onOpen={() => {
|
|
if (activeExhibitId === 1) {
|
|
triggerPuzzleAnimation(1)
|
|
}
|
|
if (activeExhibitId === 2) {
|
|
triggerPuzzleAnimation(2)
|
|
}
|
|
}}
|
|
// description={
|
|
// activeExhibitId == 1
|
|
// ? `1892-1898年,少年时期在绍兴三味书屋求学积淀;\n1919-1923年,于北京八道湾参加新文化运动、酝酿《阿Q正传》;\n1926年9-12月,赴厦门大学任教期间聚焦底层创作杂文;\n1927年3-9月,广州白云楼整理旧作定稿《朝花夕拾》部分篇章。`
|
|
// : undefined
|
|
// }
|
|
onClose={() => {
|
|
if (activeExhibitId !== null) {
|
|
// 如果是展品1关闭,显示连线题
|
|
if (activeExhibitId === 1) {
|
|
setShowMatchQuiz(true)
|
|
} else {
|
|
setShowExhibit2Quiz(true)
|
|
}
|
|
}
|
|
setActiveExhibitId(null)
|
|
}}
|
|
/>
|
|
|
|
{/* 学习引导词 */}
|
|
<StudyGuideModal
|
|
visible={showGuideModal}
|
|
onContinue={() => {
|
|
setShowGuideModal(false)
|
|
if (!visible && !afterDialogGuideShown) {
|
|
setAfterDialogGuideShown(true)
|
|
setShowAfterDialogGuide(true)
|
|
}
|
|
}}
|
|
audioSrc={getPublicPath('audio/luxun/mp3_luxun_02.mp3')}
|
|
ailgn="left"
|
|
>
|
|
1.场景中出现动态光点的展品表示未浏览,可通过点击光点来了解展品背后的故事,通过滚轮可以放大展品,浏览展品后蓝色动态光点会变为已浏览状态。
|
|
{'\n'}
|
|
2.在学习过程中会掉落拼图碎片,拼图碎片会自动收入拼图盒中,拼图盒可以点开查看当前拼图收集情况,点击单个拼图可以回到获得拼图的场景再次学习。
|
|
{'\n'}
|
|
3.所有动态光点的展品全部学习后才能进入下一个场景学习。
|
|
</StudyGuideModal>
|
|
|
|
{/* 引导 */}
|
|
<GuideSteps
|
|
visible={visible}
|
|
currentStep={visibleIndex}
|
|
onStepChange={setVisibleIndex}
|
|
onVisibleChange={(nextVisible) => {
|
|
setVisible(nextVisible)
|
|
}}
|
|
showMask={false}
|
|
zIndex={10000}
|
|
>
|
|
<GuideStepsItem speaker="student" audioSrc={getPublicPath('audio/luxun/mp3_luxun_03.mp3')}>
|
|
小沪,我知道鲁迅是1933年搬到大陆新村9号的,那在这之前他为什么会选择来到上海,又有哪些思想转变呢?
|
|
</GuideStepsItem>
|
|
<GuideStepsItem speaker="guide" audioSrc={getPublicPath('audio/luxun/mp3_luxun_04_1.mp3')}>
|
|
你问得很关键!从1892-1898年少年时期在绍兴三味书屋求学积淀,到1919-1923年于北京八道湾参加新文化运动、酝酿《阿Q正传》,再到1926年9-12月赴厦门大学任教期间聚焦底层创作杂文。
|
|
</GuideStepsItem>
|
|
<GuideStepsItem speaker="guide" audioSrc={getPublicPath('audio/luxun/mp3_luxun_04_2.mp3')}>
|
|
1927年3-9月广州白云楼整理旧作定稿《朝花夕拾》部分篇章,鲁迅的创作足迹跨越南北。
|
|
</GuideStepsItem>
|
|
<GuideStepsItem speaker="guide" audioSrc={getPublicPath('audio/luxun/mp3_luxun_04_3.mp3')}>
|
|
而1927年是鲁迅思想的重要转折点。这一年他在广州亲历了“四一二”反革命政变,亲眼看见进步青年和革命志士倒在血泊中,曾经对“温和改革”的幻想彻底破灭。
|
|
</GuideStepsItem>
|
|
<GuideStepsItem speaker="guide" audioSrc={getPublicPath('audio/luxun/mp3_luxun_04_4.mp3')}>
|
|
此前他写下《狂人日记》《阿Q正传》批判封建礼教,参加语丝社针砭时弊,但始终在“彷徨”中摸索救国之路。广州的血雨腥风,让他认清了反动派的真面目。
|
|
</GuideStepsItem>
|
|
</GuideSteps>
|
|
|
|
{/* 对话结束后的学习引导 */}
|
|
<StudyGuideModal
|
|
visible={showAfterDialogGuide}
|
|
onContinue={() => setShowAfterDialogGuide(false)}
|
|
audioSrc={getPublicPath('audio/luxun/mp3_luxun_01.mp3')}
|
|
firstLineIndent="2em"
|
|
>
|
|
{`同学你好,欢迎来到“走进故居·对话”学习模块!
|
|
此刻你正立在鲁迅故居的一楼客厅里,接下来请随旧时光的纹路,一同翻开先生思想长卷里的5段深刻轨迹:风雨前夕的思想淬炼、杂文集里的匕首与投枪、历史新编中的现实呐喊、为亡友编纂的精神丰碑、6平方米的红色庇护所。准备好共赴这场笔尖与灵魂的滚烫相遇了吗?`}
|
|
</StudyGuideModal>
|
|
|
|
{/* 浏览完展品后的对话 */}
|
|
<GuideSteps
|
|
visible={showAfterExhibitDialog}
|
|
currentStep={afterExhibitDialogStep}
|
|
onStepChange={setAfterExhibitDialogStep}
|
|
onVisibleChange={(nextVisible) => {
|
|
setShowAfterExhibitDialog(nextVisible)
|
|
}}
|
|
showMask={false}
|
|
zIndex={10000}
|
|
>
|
|
<GuideStepsItem speaker="student" audioSrc={getPublicPath('audio/luxun/mp3_luxun_05.mp3')}>
|
|
这张照片也是鲁迅在上海期间的住所吗?
|
|
</GuideStepsItem>
|
|
<GuideStepsItem speaker="guide" audioSrc={getPublicPath('audio/luxun/mp3_luxun_06_1.mp3')}>
|
|
不错!1927年10月,鲁迅带着许广平来到上海,先住进了虹口区横浜路景云里。在景云里的两年半,他结识了冯雪峰等共产党员,参与发起中国左翼作家联盟,从“独战的英雄”转变为革命文艺战线的旗手。
|
|
</GuideStepsItem>
|
|
<GuideStepsItem speaker="guide" audioSrc={getPublicPath('audio/luxun/mp3_luxun_06_2.mp3')}>
|
|
这段时间他写下《三闲集》《二心集》,表达他在当时历史条件下针对社会现象、文化问题和论战的看法与社会批判,此间还翻译了大量马列文艺理论著作,为此后的战斗打下了坚实思想基础。
|
|
</GuideStepsItem>
|
|
<GuideStepsItem speaker="guide" audioSrc={getPublicPath('audio/luxun/mp3_luxun_07.mp3')}>
|
|
鲁迅在发起成立左联后,为躲避国民党当局监视,1930年5月,与许广平、周海婴迁居到拉摩斯公寓。1933年4月,受战火纷扰影响,为安全及健康计,同时也为了更方便开展革命文化工作,鲁迅以“内山书店职员”的名义,搬到了大陆新村9号。
|
|
</GuideStepsItem>
|
|
<GuideStepsItem speaker="student" audioSrc={getPublicPath('audio/luxun/mp3_luxun_08.mp3')}>
|
|
这个过程的确十分曲折。
|
|
</GuideStepsItem>
|
|
</GuideSteps>
|
|
|
|
{/* 连线题(展品1关闭后) */}
|
|
<MatchQuizPanel
|
|
visible={showMatchQuiz}
|
|
title="【连线】请将左侧的日期与右侧对应的事件进行连线:"
|
|
leftItems={[
|
|
{ id: '1', label: '1892-1898年' },
|
|
{ id: '2', label: '1919-1923年' },
|
|
{ id: '3', label: '1926年9-12月' },
|
|
{ id: '4', label: '1927年3-9月' },
|
|
]}
|
|
rightItems={[
|
|
{ id: 'b', label: '于北京八道湾参加新文化运动、酝酿《阿Q正传》' },
|
|
{ id: 'a', label: '在绍兴三味书屋求学积淀' },
|
|
{ id: 'd', label: '在广州白云楼整理旧作定稿《朝花夕拾》部分篇章' },
|
|
{ id: 'c', label: '赴厦门大学任教,聚焦底层创作杂文' },
|
|
]}
|
|
correctMatches={[
|
|
{ leftId: '1', rightId: 'a' },
|
|
{ leftId: '2', rightId: 'b' },
|
|
{ leftId: '3', rightId: 'c' },
|
|
{ leftId: '4', rightId: 'd' },
|
|
]}
|
|
errorMessage="连线不正确,请重新尝试!"
|
|
onSubmit={(matches, isCorrect) => {
|
|
if (isCorrect) {
|
|
setShowMatchQuiz(false)
|
|
setShowMatchQuizSuccess(true)
|
|
}
|
|
}}
|
|
zIndex={10001}
|
|
/>
|
|
|
|
{/* 连线题成功提示 */}
|
|
<SuccessAlert
|
|
title="恭喜你,匹配正确!"
|
|
visible={showMatchQuizSuccess}
|
|
onClose={() => {
|
|
setShowMatchQuizSuccess(false)
|
|
// 标记展品1为已访问
|
|
markExhibitVisited(place, 1)
|
|
}}
|
|
/>
|
|
|
|
<MultipleChoiceQuizPanel
|
|
visible={showExhibit2Quiz}
|
|
title="【多选】鲁迅先生居住在上海景云里的两年半,写下的作品包括:"
|
|
options={[
|
|
{ value: 'A', label: 'A. 阿 Q 正传' },
|
|
{ value: 'B', label: 'B. 三闲集' },
|
|
{ value: 'C', label: 'C. 二心集' },
|
|
{ value: 'D', label: 'D. 朝花夕拾' },
|
|
]}
|
|
correctAnswer={['B', 'C']}
|
|
errorMessage={(selectedValues) => {
|
|
const picked = new Set(selectedValues)
|
|
|
|
if (picked.has('A')) {
|
|
return '《阿 Q 正传》创作于 1921—1922 年,最初发表于《晨报副刊》,早于鲁迅居住在上海景云里时期。请你再想想吧!'
|
|
}
|
|
if (picked.has('D')) {
|
|
return '《朝花夕拾》创作于 1926 年,最初以《旧事重提》为总题发表,早于鲁迅居住在上海景云里时期。请你再想想吧!'
|
|
}
|
|
return '请你再想想吧!'
|
|
}}
|
|
onSubmit={(_values, isCorrect) => {
|
|
if (isCorrect) {
|
|
setShowExhibit2Quiz(false)
|
|
setShowExhibit2QuizSuccess(true)
|
|
}
|
|
}}
|
|
zIndex={10001}
|
|
/>
|
|
|
|
<SuccessAlert
|
|
title="恭喜你,选择正确!"
|
|
visible={showExhibit2QuizSuccess}
|
|
onClose={() => {
|
|
setShowExhibit2QuizSuccess(false)
|
|
markExhibitVisited(place, 2)
|
|
}}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default LuxunPlace1
|