更新依赖项及历史人物相关内容资源
- 更新 package.json 和 package-lock.json 中的依赖项 - 新增/更新陈云、黄炎培、鲁迅、孙中山的音频文件 - 新增场景和组件的图片资源 - 清理未使用的音频和图片文件 - 优化多个组件和页面的实现逻辑
This commit is contained in:
1 parent
b1d55b9464
commit
e0fa47d21a
629 files changed
+24390
-2991
No files matched your search
@@ -10,11 +10,12 @@ import {
|
||||
} from '@/components'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
import { getBackgroundImage, getStaticImage, getPublicPath } from '@/lib/utils'
|
||||
import { useRef, useState } from 'react'
|
||||
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 { saveZhanglepingStore, useZhanglepingStore } from '@/store/zhanglepingStore'
|
||||
|
||||
// starName 固定为 zhangleping
|
||||
const starName = 'zhangleping'
|
||||
@@ -34,10 +35,22 @@ const ZhanglepingPlace2 = () => {
|
||||
}>()
|
||||
|
||||
const [activeExhibitId, setActiveExhibitId] = useState<1 | 2 | 3 | null>(null)
|
||||
const [visited, setVisited] = useState<Set<1 | 2 | 3>>(() => new Set())
|
||||
// 持久化接入
|
||||
const markVisited = useZhanglepingStore((s) => s.markExhibitVisited)
|
||||
const getVisited = useZhanglepingStore((s) => s.getVisitedExhibits)
|
||||
const setLastVisitedPlace = useZhanglepingStore((s) => s.setLastVisitedPlace)
|
||||
const persistedVisited = useMemo(
|
||||
() => new Set(getVisited('place2') as (1 | 2 | 3)[]),
|
||||
[getVisited]
|
||||
)
|
||||
const [visited, setVisited] = useState<Set<1 | 2 | 3>>(() => new Set(persistedVisited))
|
||||
|
||||
const [guideVisible2, setGuideVisible2] = useState(false)
|
||||
const [guideStep2, setGuideStep2] = useState(0)
|
||||
const [guideVisible1, setGuideVisible1] = useState(false)
|
||||
const [guideStep1, setGuideStep1] = useState(0)
|
||||
|
||||
// 展品2目前无引导对话(保留 setter 以便未来扩展)
|
||||
const [_guideVisible2, _setGuideVisible2] = useState(false)
|
||||
const [_guideStep2, _setGuideStep2] = useState(0)
|
||||
|
||||
const [guideVisible3, setGuideVisible3] = useState(false)
|
||||
const [guideStep3, setGuideStep3] = useState(0)
|
||||
@@ -51,6 +64,10 @@ const ZhanglepingPlace2 = () => {
|
||||
const [showVideo1, setShowVideo1] = useState(false)
|
||||
const video1Ref = useRef<VideoRef>(null)
|
||||
|
||||
// 展品2:关闭后播放的视频
|
||||
const [showVideo2, setShowVideo2] = useState(false)
|
||||
const video2Ref = useRef<VideoRef>(null)
|
||||
|
||||
// Cursor 顺序控制:先学习展品3,再依次解锁展品2、展品1
|
||||
const { isEnabled } = useCursorController({
|
||||
initialEnabled: [3],
|
||||
@@ -66,17 +83,16 @@ const ZhanglepingPlace2 = () => {
|
||||
const total = 3
|
||||
const progressCount = visited.size
|
||||
const completed = progressCount >= total
|
||||
const subtitle = completed
|
||||
? '搜索该阶段相关资料(已完成)'
|
||||
: `搜索该阶段相关资料(${progressCount}/${total})`
|
||||
const subtitle = completed ? '已完成' : `${progressCount}/${total}`
|
||||
|
||||
const handleOpenExhibit = (id: 1 | 2 | 3) => {
|
||||
setActiveExhibitId(id)
|
||||
if (id === 1) {
|
||||
// 展品1仅展示大图,无引导对话
|
||||
// 展品1:打开弹窗时显示引导对话
|
||||
setGuideVisible1(true)
|
||||
setGuideStep1(0)
|
||||
} else if (id === 2) {
|
||||
setGuideVisible2(true)
|
||||
setGuideStep2(0)
|
||||
// 展品2:无引导对话
|
||||
} else {
|
||||
setGuideVisible3(true)
|
||||
setGuideStep3(0)
|
||||
@@ -142,30 +158,37 @@ const ZhanglepingPlace2 = () => {
|
||||
src={getStaticImage('components/place/继续前进')}
|
||||
button
|
||||
onClick={() => {
|
||||
setLastVisitedPlace('place3')
|
||||
saveZhanglepingStore()
|
||||
navigate(`/${moduleName}/${starName}/place3`)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 展品1:仅图片,无对话,引导通过关闭后的视频讲解 */}
|
||||
{/* 展品2:代表作品与历史背景 */}
|
||||
{/* 展品1:代表作品与历史背景(打开展品1弹窗时显示对话) */}
|
||||
<GuideSteps
|
||||
visible={guideVisible2}
|
||||
onVisibleChange={setGuideVisible2}
|
||||
currentStep={guideStep2}
|
||||
onStepChange={setGuideStep2}
|
||||
visible={guideVisible1}
|
||||
onVisibleChange={setGuideVisible1}
|
||||
currentStep={guideStep1}
|
||||
onStepChange={setGuideStep1}
|
||||
showMask={false}
|
||||
highlightSelectors={[]}
|
||||
zIndex={10001}
|
||||
>
|
||||
<GuideStepsItem speaker="student">
|
||||
张先生还在街头做过画,这样的画作能直面民众,唤起国人的抗战热情,应该就是你说的画笔如“刀枪”吧!
|
||||
<GuideStepsItem
|
||||
speaker="student"
|
||||
audioSrc={getPublicPath('audio/zhangleping/mp3_zhangleping_08.mp3')}
|
||||
>
|
||||
这幅发表在《抗战漫画》第12期上的《侵略者的坟墓》与刚才那幅《穷凶极恶的结果》一样,当时的国人看起来肯定特别提振精神!
|
||||
</GuideStepsItem>
|
||||
<GuideStepsItem
|
||||
speaker="guide"
|
||||
audioSrc={getPublicPath('audio/zhangleping/mp3_zhangleping_09.mp3')}
|
||||
>
|
||||
这就是张乐平先生画作在当时发挥的最重要的作用!他的幅幅作品犹如刺刀钢枪,揭露日军暴行,抨击汉奸敌伪的卖国嘴脸,直至抗战的最后胜利。那时他常说“漫画是流动的宣传队”。
|
||||
</GuideStepsItem>
|
||||
<GuideStepsItem speaker="guide">
|
||||
不错。你看到的正是抗战时期的场景,也是张乐平先生艺术生涯中最具历史使命感和时代感的时期。1937年震惊中外的“七七事变”爆发,日本侵略行径激起了全中国人民的强烈愤慨。
|
||||
</GuideStepsItem>
|
||||
<GuideStepsItem speaker="guide">
|
||||
淞沪会战当月,张先生以抗日救亡漫画宣传队副领队身份北上宣传抗日,后任队长,他带领宣传队足迹遍及沪、苏、皖、鄂、浙、湘、桂、赣、闽、粤等地,配合抗战形势创作了大量的抗日漫画。
|
||||
他的漫画还被译成多国文字,印成传单,空投敌占区,宣传正义,呼唤良知,这正是“笔铸刀枪”的力量!
|
||||
</GuideStepsItem>
|
||||
</GuideSteps>
|
||||
|
||||
@@ -185,10 +208,16 @@ const ZhanglepingPlace2 = () => {
|
||||
highlightSelectors={[]}
|
||||
zIndex={10001}
|
||||
>
|
||||
<GuideStepsItem speaker="student">
|
||||
<GuideStepsItem
|
||||
speaker="student"
|
||||
audioSrc={getPublicPath('audio/zhangleping/mp3_zhangleping_06.mp3')}
|
||||
>
|
||||
这幅“穷凶极恶的结果”的漫画太形象了,小沪,聂耳先生用音乐唤醒大家的爱国情怀,那张乐平先生的画笔在不同年代里,是不是也为大家带来了不一样的力量呀?
|
||||
</GuideStepsItem>
|
||||
<GuideStepsItem speaker="guide">
|
||||
<GuideStepsItem
|
||||
speaker="guide"
|
||||
audioSrc={getPublicPath('audio/zhangleping/mp3_zhangleping_07.mp3')}
|
||||
>
|
||||
这是个很好的问题!张乐平先生的画笔在不同的历史时期发挥着不同作用,比如抗日战争时是“刀枪”,揭露日寇暴行;解放战争时是“镜子”,映照社会现实;新中国成立后是“园丁”,为万千儿童种下真善美的种子。
|
||||
</GuideStepsItem>
|
||||
<GuideStepsItem speaker="guide">
|
||||
@@ -201,10 +230,23 @@ const ZhanglepingPlace2 = () => {
|
||||
src={activeExhibitId ? exhibitSrcMap[activeExhibitId] : ''}
|
||||
onClose={() => {
|
||||
if (activeExhibitId !== null) {
|
||||
// 展品1:关闭后播放一段视频,视频结束再标记已访问
|
||||
// 展品1:关闭弹窗时(对话可能还在进行),先不关闭弹窗,等待对话完全结束
|
||||
if (activeExhibitId === 1) {
|
||||
// 如果对话还在显示,先关闭对话
|
||||
if (guideVisible1) {
|
||||
setGuideVisible1(false)
|
||||
} else {
|
||||
// 对话已经结束,关闭弹窗并播放视频
|
||||
setActiveExhibitId(null)
|
||||
setShowVideo1(true)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 展品2:关闭后播放一段视频,视频结束再标记已访问
|
||||
if (activeExhibitId === 2) {
|
||||
setActiveExhibitId(null)
|
||||
setShowVideo1(true)
|
||||
setShowVideo2(true)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -214,16 +256,17 @@ const ZhanglepingPlace2 = () => {
|
||||
setVisited((prev) => {
|
||||
const next = new Set(prev)
|
||||
next.add(3)
|
||||
markVisited('place2', 3)
|
||||
return next
|
||||
})
|
||||
}
|
||||
} else {
|
||||
setVisited((prev) => {
|
||||
const next = new Set(prev)
|
||||
next.add(activeExhibitId)
|
||||
return next
|
||||
})
|
||||
}
|
||||
// setVisited((prev) => {
|
||||
// const next = new Set(prev)
|
||||
// next.add(activeExhibitId)
|
||||
// markVisited('place2', activeExhibitId)
|
||||
// return next
|
||||
// })
|
||||
}
|
||||
setActiveExhibitId(null)
|
||||
}}
|
||||
@@ -233,7 +276,7 @@ const ZhanglepingPlace2 = () => {
|
||||
{showVideo1 && (
|
||||
<Video
|
||||
ref={video1Ref}
|
||||
src={getPublicPath('video/sunzhongshan/sunzhongshan_03.mp4')}
|
||||
src={getPublicPath('video/zhangleping/zhangleping_3.mp4')}
|
||||
className="fixed inset-0 z-20000"
|
||||
autoPlay
|
||||
playsInline
|
||||
@@ -243,6 +286,7 @@ const ZhanglepingPlace2 = () => {
|
||||
setVisited((prev) => {
|
||||
const next = new Set(prev)
|
||||
next.add(1)
|
||||
markVisited('place2', 1)
|
||||
return next
|
||||
})
|
||||
}}
|
||||
@@ -252,6 +296,30 @@ const ZhanglepingPlace2 = () => {
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 展品2关闭后的讲解视频 */}
|
||||
{showVideo2 && (
|
||||
<Video
|
||||
ref={video2Ref}
|
||||
src={getPublicPath('video/sunzhongshan/sunzhongshan_03.mp4')}
|
||||
className="fixed inset-0 z-20000"
|
||||
autoPlay
|
||||
playsInline
|
||||
showSkipButton
|
||||
onEnded={() => {
|
||||
setShowVideo2(false)
|
||||
setVisited((prev) => {
|
||||
const next = new Set(prev)
|
||||
next.add(2)
|
||||
markVisited('place2', 2)
|
||||
return next
|
||||
})
|
||||
}}
|
||||
onSkip={() => {
|
||||
setShowVideo2(false)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 展品3对话结束后的连线题 + 成功提示(使用 ScaledPortal) */}
|
||||
<ScaledPortal enabled={quizVisible3 || successAlert3Visible} zIndex={10050}>
|
||||
<>
|
||||
|
||||
Reference in new issue
Block a user