Files
shanghai-residences/src/pages/luxun/place/place4.tsx
T
2026-04-30 09:17:31 +08:00

400 lines
14 KiB
TypeScript

import {
ExhibitOverlay,
GuideSteps,
GuideStepsItem,
Image,
Title,
Cursor,
SingleChoiceQuizPanel,
Video,
type VideoAnchor,
} from '@/components'
import type { VideoRef } from '@/components/video'
import { useNavigate, useParams } from 'react-router-dom'
import { getBackgroundImage, getPublicPath, getStaticImage } from '@/lib/utils'
import { useState, useRef, useMemo } from 'react'
import SuccessAlert from '@/components/layout/alert/success-alert'
import { useCursorController } from '@/hooks/useCursorController'
import { usePuzzleAnimation } from '@/contexts/PuzzleAnimationContext'
import { useLuxunStore, saveLuxunStore } from '@/store/luxunStore'
import { setCourseTimeProgress } from '@/lib/utils/api-common'
// starName 固定为 luxun
const starName = 'luxun'
// 展品 ID 与资源路径映射
const exhibitSrcMap: Record<1 | 2 | 3, string> = {
1: 'luxun/place4/展品11',
2: 'luxun/place4/展品12',
3: 'luxun/place4/展品13',
}
const quizDefaultError = '请结合刚刚的旁白内容,再思考一下吧!'
const quizExplanations: Record<string, string> = {
// A: "陈独秀与鲁迅有思想交往,但他并未在这间“红色庇护所”中避难、与鲁迅夜谈革命议题。",
// B: "恭喜你,选对啦。",
// C: "李大钊于1927年牺牲,时间上不符合这间客房被用作“红色庇护所”的背景。",
// D: "周恩来虽长期在上海从事革命活动,但并未在这里避难并与鲁迅夜谈。",
A: '请你结合刚刚学习的内容,再思考一下吧!',
B: '恭喜你,选择正确!',
C: '请你结合刚刚学习的内容,再思考一下吧!',
D: '请你结合刚刚学习的内容,再思考一下吧!',
}
const LuxunPlace5 = () => {
const navigate = useNavigate()
const { moduleName } = useParams<{
moduleName: string
}>()
//碎片动画触发方法
const { triggerPuzzleAnimation } = usePuzzleAnimation()
const [introGuideVisible, setIntroGuideVisible] = useState(true)
const [introGuideStep, setIntroGuideStep] = useState(0)
const [activeExhibitId, setActiveExhibitId] = useState<1 | 2 | 3 | null>(null)
// 使用持久化的访问状态
const place = 'place4'
const { markExhibitVisited, getVisitedExhibits, setLastVisitedPlace } = useLuxunStore()
// 从 store 获取已访问的展品,转换为 Set 用于兼容现有代码
const visitedExhibits = getVisitedExhibits(place)
const visited = new Set(visitedExhibits)
// Cursor 顺序控制:1 -> 2 -> 3
const { isEnabled } = useCursorController({
initialEnabled: [1],
dependencies: {
2: [1],
3: [2],
},
visited: visited,
})
// 光标2的题目状态
const [quizVisible, setQuizVisible] = useState(false)
const [quizError, setQuizError] = useState(quizDefaultError)
const [successAlertVisible, setSuccessAlertVisible] = useState(false)
// 光标3的引导对话状态
const [guide3Visible, setGuide3Visible] = useState(false)
const [guide3Step, setGuide3Step] = useState(0)
// 结尾视频与对话
const [showEndingVideo, setShowEndingVideo] = useState(false)
const [endingGuideVisible, setEndingGuideVisible] = useState(false)
// 视频锚点相关状态
const [activeVideoSuccess, setActiveVideoSuccess] = useState(false)
const [resumeVideoCallback, setResumeVideoCallback] = useState<(() => void) | null>(null)
const videoRef = useRef<VideoRef>(null)
const handleOpenExhibit = (index: 1 | 2 | 3) => {
if (index === 3) {
setActiveExhibitId(3)
setGuide3Visible(true)
setGuide3Step(0)
return
}
setActiveExhibitId(index)
}
// 视频锚点配置
const videoAnchors: VideoAnchor[] = useMemo(
() => [
{
id: 'quiz-anchor',
time: 41,
once: true,
afterResume: 'remove',
render: ({ resume }) => (
<>
<SingleChoiceQuizPanel
visible={!activeVideoSuccess}
title='【单选】毛泽东主席称赞鲁迅先生为“最伟大和最英勇的___”。'
options={[
{ value: 'A', label: 'A. 旗手' },
{ value: 'B', label: 'B. 作家' },
{ value: 'C', label: 'C. 战士' },
]}
correctAnswer="A"
onWrongSelect={(value: string) => {
switch (value) {
case 'B':
return '这不是毛泽东主席的原话。请你再想想吧!'
case 'C':
return '这不是毛泽东主席的原话。请你再想想吧!'
default:
return '请你再想想吧!'
}
}}
onSubmit={(value: string, isCorrect: boolean) => {
if (isCorrect) {
setActiveVideoSuccess(true)
setResumeVideoCallback(() => resume)
}
}}
zIndex={20000}
/>
<SuccessAlert
visible={activeVideoSuccess}
onContinue={() => {
setActiveVideoSuccess(false)
const cb = resumeVideoCallback
setResumeVideoCallback(null)
cb?.()
}}
onClose={() => {
setActiveVideoSuccess(false)
const cb = resumeVideoCallback
setResumeVideoCallback(null)
cb?.()
}}
/>
</>
),
},
],
[activeVideoSuccess, resumeVideoCallback]
)
const progressCount = visited.size
const total = 3
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(`luxun/place4/背景`),
}}
>
<Title
title="6平方米的红色庇护所"
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}/place3`)
}
}}
/>
{/* 展品 */}
<Cursor
className="absolute left-167 bottom-30"
onClick={() => handleOpenExhibit(1)}
read={visited.has(1)}
disabled={!isEnabled(1)}
/>
<Cursor
className="absolute left-228 bottom-30"
onClick={() => handleOpenExhibit(2)}
read={visited.has(2)}
disabled={!isEnabled(2)}
/>
<Cursor
className="absolute right-115 bottom-18 z-20"
onClick={() => handleOpenExhibit(3)}
read={visited.has(3)}
disabled={!isEnabled(3)}
/>
{completed && (
<Image
className="absolute bottom-0 right-0"
src={getStaticImage('components/place/继续前进')}
button
onClick={() => {
// 完成后先播放视频,不立即跳转页面
saveLuxunStore()
setShowEndingVideo(true)
}}
/>
)}
<ExhibitOverlay
visible={activeExhibitId !== null}
src={activeExhibitId ? exhibitSrcMap[activeExhibitId] : ''}
onOpen={() => {
if (activeExhibitId === 1) {
triggerPuzzleAnimation(11)
}
if (activeExhibitId === 2) {
triggerPuzzleAnimation(12)
}
if (activeExhibitId === 3) {
triggerPuzzleAnimation(13)
}
}}
description={
activeExhibitId === 1
? '瞿秋白同志曾说,“鲁迅是莱谟斯,是野兽的奶汁所喂养大的,是封建宗法社会的逆子,是绅士阶级的贰臣,而同时也是一些浪漫谛克的革命家的诤友!”'
: activeExhibitId === 2
? '鲁迅先生曾经抄录清朝诗人何瓦琴的对联赠给瞿秋白,“人生得一知己足矣,斯世当以同怀视之。”'
: undefined
}
onClose={() => {
if (activeExhibitId === 2) {
setActiveExhibitId(null)
setQuizVisible(true)
return
}
if (activeExhibitId === 3) {
// 对话关闭时再标记已读
setActiveExhibitId(null)
return
}
if (activeExhibitId !== null) {
markExhibitVisited(place, activeExhibitId)
}
setActiveExhibitId(null)
}}
/>
{/* 学习引导结束后的对话 */}
<GuideSteps
visible={introGuideVisible}
onVisibleChange={setIntroGuideVisible}
currentStep={introGuideStep}
onStepChange={setIntroGuideStep}
showMask={false}
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem speaker="student" audioSrc={getPublicPath('audio/luxun/mp3_luxun_26.mp3')}>
你说的是这间小小的客房吗,这里为什么会被称为“红色庇护所”呢?
</GuideStepsItem>
<GuideStepsItem speaker="guide" audioSrc={getPublicPath('audio/luxun/mp3_luxun_27.mp3')}>
你可别小看这间仅6平方米的客房,在白色恐怖笼罩的1933年至1936年,这里是很多共产党人的"安全港湾"。1933年,瞿秋白夫妇第三次到鲁迅家避难,就秘密住进了这里。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
当时反动派到处搜捕共产党人,鲁迅冒着生命危险掩护他们,两人常彻夜长谈。瞿秋白的夫人杨之华回忆道:“秋白和鲁迅在一起,往往感觉夜太短。深刻的友谊的交谈,从政治到文艺,从实际到理论,从希腊到莫斯科,轻松,愉快,活泼。”
</GuideStepsItem>
</GuideSteps>
{/* 光标2的题目 */}
<SingleChoiceQuizPanel
visible={quizVisible}
title="【单选】上海鲁迅故居三楼客房被称为“红色庇护所”,以下哪位共产党人曾在此避难并与鲁迅探讨革命议题?"
options={[
{ value: 'A', label: 'A.陈独秀' },
{ value: 'B', label: 'B.瞿秋白' },
{ value: 'C', label: 'C.李大钊' },
{ value: 'D', label: 'D.周恩来' },
]}
correctAnswer="B"
errorMessage={quizError}
onSubmit={(value, isCorrect) => {
if (isCorrect) {
setQuizError(quizDefaultError)
setQuizVisible(false)
setSuccessAlertVisible(true)
}
}}
onWrongSelect={(value) => {
const explanation = quizExplanations[value]
if (explanation) {
setQuizError(explanation)
return explanation
}
setQuizError(quizDefaultError)
return quizDefaultError
}}
zIndex={10001}
/>
{/* 光标2的成功提示 */}
<SuccessAlert
visible={successAlertVisible}
content="1933年,瞿秋白夫妇第三次到鲁迅家避难,就住进了这间仅6平方米的客房,与鲁迅常常彻夜长谈革命与文学。"
onContinue={() => {
setSuccessAlertVisible(false)
markExhibitVisited(place, 2)
}}
/>
{/* 光标3的引导对话 */}
<GuideSteps
visible={guide3Visible}
onVisibleChange={(visible) => {
setGuide3Visible(visible)
if (!visible) {
markExhibitVisited(place, 3)
}
}}
currentStep={guide3Step}
onStepChange={setGuide3Step}
showMask={false}
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem speaker="student" audioSrc={getPublicPath('audio/luxun/mp3_luxun_28.mp3')}>
除了瞿秋白,还有其他共产党人来过这里吗
</GuideStepsItem>
<GuideStepsItem speaker="guide" audioSrc={getPublicPath('audio/luxun/mp3_luxun_29.mp3')}>
当然!1936年4月,中共中央特派员冯雪峰到上海开展工作,也先住进了这间客房。冯雪峰把红军长征胜利、遵义会议召开的消息告诉鲁迅,还带来了毛泽东同志提出的抗日民族统一战线主张。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
鲁迅听后格外振奋,激动地说“我做一个小兵还是胜任的,用笔!”,这句话也成了他晚年投身抗日救亡运动的誓言。
</GuideStepsItem>
</GuideSteps>
{/* 结尾视频播放 */}
{showEndingVideo && (
<Video
ref={videoRef}
src={getPublicPath('video/luxun/luxun_06.mp4')}
className="fixed inset-0 z-20000"
controls
autoPlay
playsInline
showSkipButton
anchors={videoAnchors}
onEnded={() => {
setEndingGuideVisible(true)
}}
/>
)}
{/* 视频结束后的结束语对话 */}
<GuideSteps
visible={endingGuideVisible}
onVisibleChange={(visible) => {
setShowEndingVideo(false)
setEndingGuideVisible(visible)
if (!visible && moduleName) {
// 对话结束后返回鲁迅首页
setCourseTimeProgress(7)
navigate(`/${moduleName}/${starName}/home`)
}
}}
showMask={false}
zIndex={20010}
>
<GuideStepsItem speaker="guide" audioSrc={getPublicPath('audio/luxun/mp3_luxun_30.mp3')}>
我们已经了解了鲁迅居住在上海大陆新村9号时的重要事件。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
接下来,请你接过先生手中的“笔”,走出上海,跨越时空,追寻他从绍兴三味书屋到上海的创作足迹——解锁每个地标背后的故事,填充文字里的时代线索,在互动中,读懂先生笔墨间的温度与力量。
</GuideStepsItem>
</GuideSteps>
</div>
)
}
export default LuxunPlace5