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

- 更新 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

+300
View File
@@ -0,0 +1,300 @@
import { AnimatePresence, motion } from 'framer-motion'
import { useEffect, useState } from 'react'
import { Image } from '@/components'
import {
BeijingUnlockInteraction,
InputBg,
PianoUnlockInteraction,
PostcardUnlockInteraction,
SelectBg,
} from '../components'
import { getStaticImage } from '@/lib/utils'
import { LOBBY_COLUMNS, SCENE_TITLES, UNLOCK_TITLES } from './lobby.config'
import ContentDialog from './ContentDialog'
import UnlockHintDialog from './UnlockHintDialog'
import type { DialogState } from './useDialogReducer'
const starName = 'nieer'
const ANTHEM_LYRICS = [
{ value: '1', label: '起来!不愿做奴隶的人们!' },
{ value: '2', label: '把我们的血肉,筑成我们新的长城!' },
{ value: '3', label: '中华民族到了最危险的时候' },
{ value: '4', label: '每个人被迫着发出最后的吼声' },
{ value: '5', label: '起来!起来!起来!' },
{ value: '6', label: '我们万众一心' },
{ value: '7', label: '冒着敌人的炮火,前进!' },
{ value: '8', label: '前进!前进!进!' },
]
const SMEDLEY_QUOTES = [
{ value: '1', label: '建设一个强大的和自由的中国' },
{ value: '2', label: '为我的遗体唱一首歌,中国的国歌……"起来"' },
{ value: '3', label: '我的心灵在这个世界上除了中国的任何地方都未能找到安宁' },
]
interface DialogLayerProps {
state: DialogState
onEnterUnlock: () => void
onShare: (payload?: Record<string, unknown>) => void
onClose: () => void
}
const DialogLayer = ({ state, onEnterUnlock, onShare, onClose }: DialogLayerProps) => {
const [selectedLyric, setSelectedLyric] = useState<string>()
const [guizhouInput, setGuizhouInput] = useState<string>('')
const [pianoFinished, setPianoFinished] = useState(false)
const [selectedQuote, setSelectedQuote] = useState<string>()
const [postcardFinished, setPostcardFinished] = useState(false)
const [beijingFinished, setBeijingFinished] = useState(false)
const [postcardState, setPostcardState] = useState<{
hasPlane: boolean
hasText: boolean
postcardImagePath: string
}>({
hasPlane: false,
hasText: false,
postcardImagePath: 'nieer/reflection/lobby/content/场景5待交互',
})
const [beijingSelection, setBeijingSelection] = useState<{
paradeId?: string
paradeName?: string
paradeImagePath?: string
sloganId?: string
sloganImagePath?: string
previewImageDataUrl?: string
}>({})
const dialogNonce = state.mode === 'idle' ? 0 : state.nonce
// 每次点击卡片(nonce 变化)时,重置 unlock 交互状态
useEffect(() => {
setSelectedLyric(undefined)
setGuizhouInput('')
setPianoFinished(false)
setSelectedQuote(undefined)
setPostcardFinished(false)
setBeijingFinished(false)
setPostcardState({
hasPlane: false,
hasText: false,
postcardImagePath: 'nieer/reflection/lobby/content/场景5待交互',
})
setBeijingSelection({})
}, [dialogNonce])
if (state.mode === 'idle') return <AnimatePresence mode="sync" />
const scene = LOBBY_COLUMNS.find((s) => s.id === state.sceneId)
if (!scene) return null
let canShare = true
if (state.sceneId === 1) {
canShare = !!selectedLyric
} else if (state.sceneId === 2) {
canShare = guizhouInput.trim().length > 0
} else if (state.sceneId === 3) {
// 欧洲·亚洲:钢琴交互播放完成后才允许分享
canShare = pianoFinished
} else if (state.sceneId === 4) {
canShare = !!selectedQuote
} else if (state.sceneId === 5) {
canShare = postcardFinished
} else if (state.sceneId === 6) {
canShare = beijingFinished && typeof beijingSelection.previewImageDataUrl === 'string'
}
const handleShareClick = () => {
if (state.mode !== 'unlock') return
if (state.sceneId === 1) {
const selected = ANTHEM_LYRICS.find((item) => item.value === selectedLyric)
onShare({
sceneId: 1,
lyricValue: selectedLyric ?? '',
lyricLabel: selected?.label ?? '',
})
return
}
if (state.sceneId === 2) {
onShare({
sceneId: 2,
reflectionText: guizhouInput.trim(),
})
return
}
if (state.sceneId === 3) {
onShare({
sceneId: 3,
pianoCompleted: pianoFinished,
pianoImagePath: 'nieer/reflection/lobby/box/音符',
})
return
}
if (state.sceneId === 4) {
const selected = SMEDLEY_QUOTES.find((item) => item.value === selectedQuote)
onShare({
sceneId: 4,
quoteValue: selectedQuote ?? '',
quoteLabel: selected?.label ?? '',
})
return
}
if (state.sceneId === 5) {
onShare({
sceneId: 5,
...postcardState,
})
return
}
if (state.sceneId === 6) {
onShare({
sceneId: 6,
...beijingSelection,
})
return
}
onShare()
}
return (
<AnimatePresence mode="sync">
{state.mode === 'result' && (
<motion.div
key={`result-${state.sceneId}-${state.nonce}`}
className="absolute w-full h-full z-99"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
transition={{ duration: 0.25, ease: 'easeOut' }}
>
<div className="relative w-full h-full">
<Image
src={getStaticImage(
`${starName}/reflection/lobby/result/聂耳结束场景${state.sceneId}`
)}
alt="结果页"
/>
<Image
button
src={getStaticImage('components/关闭')}
className="absolute right-6 top-5 z-30"
alt="关闭"
onClick={onClose}
/>
</div>
</motion.div>
)}
{state.mode === 'unlock' && (
<motion.div
key={`unlock-${state.sceneId}-${state.hasShare}-${state.nonce}`}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
transition={{ duration: 0.25, ease: 'easeOut' }}
className="absolute inset-0 z-40"
>
<UnlockHintDialog
starName={starName}
title={UNLOCK_TITLES[state.sceneId]}
visible
onClose={onClose}
onShare={state.hasShare && canShare ? handleShareClick : undefined}
>
{state.sceneId === 1 && (
<div className="flex flex-col items-center justify-center mt-12">
<Image
className="mb-20"
src={getStaticImage(`${starName}/reflection/lobby/content/场景1交互`)}
alt="场景1交互"
/>
<SelectBg
options={ANTHEM_LYRICS}
value={selectedLyric || ''}
onChange={setSelectedLyric}
placeholder="请选择一句国歌歌词"
/>
</div>
)}
{state.sceneId === 2 && (
<div className="flex flex-col items-center justify-center mt-12">
<Image
className="mb-20"
src={getStaticImage(`${starName}/reflection/lobby/content/场景2交互`)}
alt="场景2交互"
/>
<InputBg
className="w-[875px]"
value={guizhouInput}
onChange={(value) => setGuizhouInput(value.slice(0, 10))}
placeholder="请输入你的爱国感悟(10字以内)"
maxLength={10}
/>
</div>
)}
{state.sceneId === 3 && (
<div className="flex flex-col items-center justify-center mt-12">
<PianoUnlockInteraction
onFinished={() => {
setPianoFinished(true)
}}
/>
</div>
)}
{state.sceneId === 4 && (
<div className="flex flex-col items-center justify-center mt-12">
<Image
className="mb-20"
src={getStaticImage(`${starName}/reflection/lobby/content/场景4交互`)}
alt="场景4交互"
/>
<SelectBg
options={SMEDLEY_QUOTES}
value={selectedQuote || ''}
onChange={setSelectedQuote}
placeholder="请选择一句最触动你的遗言"
/>
</div>
)}
{state.sceneId === 5 && (
<PostcardUnlockInteraction
onFinished={() => {
setPostcardFinished(true)
}}
onStateChange={setPostcardState}
/>
)}
{state.sceneId === 6 && (
<BeijingUnlockInteraction
onFinished={() => {
setBeijingFinished(true)
}}
onSelectionChange={setBeijingSelection}
/>
)}
</UnlockHintDialog>
</motion.div>
)}
{state.mode === 'content' && (
<ContentDialog
key={`content-${state.sceneId}-${state.nonce}`}
starName={starName}
title={SCENE_TITLES[state.sceneId]}
scene={scene}
onClose={onClose}
onEnterScene={onEnterUnlock}
/>
)}
</AnimatePresence>
)
}
export default DialogLayer