更新依赖项及历史人物相关内容资源

- 更新 package.json 和 package-lock.json 中的依赖项
- 新增/更新陈云、黄炎培、鲁迅、孙中山的音频文件
- 新增场景和组件的图片资源
- 清理未使用的音频和图片文件
- 优化多个组件和页面的实现逻辑
This commit is contained in:
zhongyidian committed 2026-04-20 13:27:18 +08:00
1 parent b1d55b9464
commit e0fa47d21a
629 files changed
+24390 -2991

No files matched your search

+101 -33
View File
@@ -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}>
<>