Files
shanghai-residences/src/pages/chenyun/place/place5.tsx
T
zhongyidian b1d55b9464 feat: 添加断点续读功能,优化用户体验
- 在 chenyunStore 中引入 lastVisitedPlace 和 setLastVisitedPlace 方法,支持记录用户上次访问的场景。
- 在各个场景组件中使用 useEffect 设置 lastVisitedPlace,确保用户在返回时能继续上次访问的地点。
- 更新相关组件,确保在导航时能够正确使用 lastVisitedPlace,提升用户交互体验。
- 在 puzzleStore 中添加保存用户定义的功能,增强数据持久性。
2026-03-06 16:33:44 +08:00

268 lines
9.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState, useEffect } from 'react'
import {
ExhibitOverlay,
Title,
Image,
Cursor,
Video,
GuideSteps,
GuideStepsItem,
MultipleChoiceQuizPanel,
} from '@/components'
import { getBackgroundImage, getStaticImage, getPublicPath } from '@/lib/utils'
import { useNavigate, useParams } from 'react-router-dom'
import { useChenyunStore, saveChenyunStore } from '@/store/chenyunStore'
import { usePuzzleAnimation } from '@/contexts/PuzzleAnimationContext'
import { useCursorController } from '@/hooks/useCursorController'
import SuccessAlert from '@/components/layout/alert/success-alert'
import ScaledPortal from '@/components/layout/ScaledPortal'
// starName 固定为 chenyun
const starName = 'chenyun'
// 展品 ID 与资源路径映射
const exhibitSrcMap: Record<1 | 2 | 3, string> = {
1: 'chenyun/place5/场景5展品1',
2: 'chenyun/place5/场景5展品2',
3: 'chenyun/place5/场景5展品3',
}
// 展品描述映射(目前未使用,后续如需在展品弹窗中显示可恢复)
// const exhibitDescriptionMap: Record<1 | 2 | 3, string> = {
// 1: "",
// 2: "延安时期因为国民党的封锁,陕甘宁边区经济困难。陈云转战陕甘宁边区主持财经工作,他遇到了很多经济难题。1944年新春伊始,陈云开始担任西北财经办事处副主任,工作转到财经方面,这是陈云以主要精力从事领导经济工作的开始。当时,陕甘宁边区在国民党严密封锁下,财经状况严峻,边区物价猛涨,老百姓日常生活必需的布匹和粮食十分缺乏。国民党顽固派封锁边区,企图“不让一粒粮、一尺布进入边区”,同时千方百计阻止边区食盐出口。",
// 3: "",
// };
const ChenyunPlace5 = () => {
const navigate = useNavigate()
const { moduleName } = useParams<{
moduleName: string
}>()
// 获取碎片动画触发方法
const { triggerPuzzleAnimation } = usePuzzleAnimation()
const [activeExhibitId, setActiveExhibitId] = useState<1 | 2 | 3 | null>(null)
// 使用持久化的访问状态
const place = 'place5'
const { markExhibitVisited, getVisitedExhibits, setLastVisitedPlace } = useChenyunStore()
useEffect(() => {
setLastVisitedPlace(place)
}, [setLastVisitedPlace])
// 从 store 获取已访问的展品,转换为 Set 用于兼容现有代码
const visitedExhibits = getVisitedExhibits(place)
const visited = new Set(visitedExhibits)
const { isEnabled } = useCursorController({
initialEnabled: [1, 2],
dependencies: { 3: [1, 2] },
visited,
})
const [videoVisible3, setVideoVisible3] = useState(false)
const [quiz3Visible, setQuiz3Visible] = useState(false)
const [successAlert3Visible, setSuccessAlert3Visible] = useState(false)
// 展品1引导对话
const [guideVisible, setGuideVisible] = useState(false)
const [guideStep, setGuideStep] = useState(0)
const handleOpenExhibit = (index: 1 | 2 | 3) => {
if (index === 3) {
// 展品3:播放视频,不打开展品
setVideoVisible3(true)
return
}
if (index === 2) {
setGuideVisible(true)
setGuideStep(0)
}
setActiveExhibitId(index)
}
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(`chenyun/place5/场景5`),
}}
>
{/* 标题 */}
<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}/place4`)
}
}}
/>
{/* 展品1 */}
<Cursor
className="absolute left-30 top-70"
onClick={() => handleOpenExhibit(1)}
read={visited.has(1)}
disabled={!isEnabled(1)}
/>
{/* 展品2 */}
<Cursor
className="absolute left-107 top-72"
onClick={() => handleOpenExhibit(2)}
read={visited.has(2)}
disabled={!isEnabled(2)}
/>
{/* 展品3 */}
<Cursor
className="absolute right-170 top-84"
onClick={() => handleOpenExhibit(3)}
read={visited.has(3)}
disabled={!isEnabled(3)}
/>
{/* 继续前进 - 仅在完成所有展品后显示 */}
{completed && (
<Image
className="absolute bottom-5 right-20"
src={getStaticImage('chenyun/place5/继续前进4')}
button
onClick={() => {
saveChenyunStore()
navigate(`/${moduleName}/${starName}/place6`)
}}
/>
)}
{/* 展品1的引导对话 */}
<GuideSteps
visible={guideVisible}
onVisibleChange={setGuideVisible}
currentStep={guideStep}
onStepChange={(step) => setGuideStep(step)}
showMask={false}
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath('audio/chenyun/mp3_chenyun_15.mp3')}
>
小沪,延安时期因为国民党的封锁,陕甘宁边区经济困难。陈云转战陕甘宁边区主持财经工作,他一定遇到了很多经济难题吧。
</GuideStepsItem>
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath('audio/chenyun/mp3_chenyun_16.mp3')}
>
1944年新春伊始,陈云开始担任西北财经办事处副主任,工作转到财经方面,这是陈云以主要精力从事领导经济工作的开始。当时,陕甘宁边区在国民党严密封锁下,财经状况严峻,边区物价猛涨,老百姓日常生活必需的布匹和粮食十分缺乏。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
国民党顽固派封锁边区,企图“不让一粒粮、一尺布进入边区”,同时千方百计阻止边区食盐出口。
</GuideStepsItem>
</GuideSteps>
{/* 展品3视频播放(全屏) */}
{videoVisible3 && (
<Video
src={getPublicPath('video/chenyun/chenyun_07.mp4')}
className="w-full h-full"
autoPlay
playsInline
showSkipButton
onEnded={() => {
setVideoVisible3(false)
setQuiz3Visible(true)
}}
/>
)}
{/* 展品3视频后的题目 + 成功提示(使用 ScaledPortal) */}
<ScaledPortal enabled={quiz3Visible || successAlert3Visible} zIndex={10050}>
<>
<MultipleChoiceQuizPanel
visible={quiz3Visible}
title="【多选】陈云主持陕甘宁边区财经工作期间,提出的解决当时经济困难的应对之策包括:"
options={[
{ value: 'A', label: 'A.实行贸易统一' },
{
value: 'B',
label: 'B.事先准确估计情况,预测市场商情,以争取主动',
},
{
value: 'C',
label: 'C.不断总结封锁状态下对外贸易的经验,摸索实现等价交换的途径',
},
{
value: 'D',
label: 'D.贸易公司对边区内部贸易也要采取市场上做生意的办法',
},
{ value: 'E', label: 'E.做贸易工作的干部要精通业务' },
]}
correctAnswer={['A', 'B', 'C', 'D', 'E']}
minSelections={1}
maxSelections={5}
errorMessage="你的选择还不够全面哦~请你再想想吧!"
onSubmit={(_values, isCorrect) => {
if (isCorrect) {
setQuiz3Visible(false)
setSuccessAlert3Visible(true)
}
}}
zIndex={10000}
/>
<SuccessAlert
visible={successAlert3Visible}
onContinue={() => {
setSuccessAlert3Visible(false)
markExhibitVisited(place, 3)
triggerPuzzleAnimation(8)
}}
/>
</>
</ScaledPortal>
{/* 遮罩层 */}
<ExhibitOverlay
visible={activeExhibitId !== null}
src={activeExhibitId ? exhibitSrcMap[activeExhibitId] : ''}
// description={
// activeExhibitId ? exhibitDescriptionMap[activeExhibitId] : undefined
// }
autoPlayDescription={activeExhibitId === 2}
onOpen={() => {
if (activeExhibitId === 2) {
triggerPuzzleAnimation(7)
}
}}
onClose={() => {
if (activeExhibitId !== null) {
markExhibitVisited(place, activeExhibitId)
}
setActiveExhibitId(null)
}}
/>
</div>
)
}
export default ChenyunPlace5