feat: 移除不再使用的配置文件和更新组件,提升代码整洁性

- 删除 .claude/settings.local.json 配置文件,清理不必要的代码。
- 更新多个组件以引入音频资源,增强用户互动体验。
- 调整引导对话文本,确保信息准确性和一致性。
- 更新图像资源,提升视觉内容的准确性和一致性。
This commit is contained in:
zhongyidian committed 2026-01-20 18:06:55 +08:00
1 parent cf64e7ec50
commit 8ba36a4a1a
132 files changed
+546 -454

No files matched your search

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

@@ -0,0 +1 @@
<svg id="图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 748.92 747.84"><defs><style>.cls-1{fill:#838383;}.cls-2{fill:#fff;}.cls-3{fill:#848484;}</style></defs><g id="_路径1_" data-name="&lt;路径1&gt;"><path class="cls-1" d="M131.45,183.48l-6.48,0c-5.55,0-9.19-1.14-11.45-3.5s-3.34-6.38-3.1-12.66a1.13,1.13,0,0,1,0-.18c.52-2.31,1.17-4.64,1.81-6.89,1.31-4.68,2.67-9.52,2.94-14.49a24.41,24.41,0,0,0-2.89-13.1c-2.55-5.06-5.78-8.54-9.87-10.66a25.68,25.68,0,0,0-11.88-3.1A24.72,24.72,0,0,0,75.4,124.4c-8.91,6.82-12.86,17.12-10.32,26.88a27.76,27.76,0,0,0,2.62,6.07A39.44,39.44,0,0,1,69.92,162c2.52,6.61,2.49,12.09-.09,15.85-2.45,3.57-7.13,5.46-13.54,5.49s-13.11,0-19.66,0l-24.75,0H7.05a8.8,8.8,0,0,1-4.44-.93l-.12-.08a3.6,3.6,0,0,1-1.39-3.28q0-84.87,0-169.84c0-.36,0-.73,0-1.09C1,5.86.89,3,4.12,1.52A5,5,0,0,1,7,.71c.43,0,.86,0,1.29.07s.75.06,1.12.06Q48,.81,86.6.81q46,0,92,0c2.59,0,4.21.78,4.9,2.3s.39,3.82.2,5.36c0,.39-.12.89-.11,1,1,12.9.85,33.06.76,43.9,0,3.9-.06,7.26,0,7.71a.66.66,0,0,1,0,.28A14.44,14.44,0,0,0,186.16,68a6.28,6.28,0,0,0,5.13,2.64h.25a20,20,0,0,0,3.43.26,29,29,0,0,0,11.3-2.24l1.58-.74c3.84-1.79,7.81-3.64,12.14-3.7h.6c9.58,0,16.41,3.57,19.75,10.31,2.9,5.85,3.44,12.75,3.53,15.45.16,4.25-2,10.69-5.25,15.64l-.08.1c-5.57,6.52-10.48,9.06-17.51,9.06a46.94,46.94,0,0,1-8.77-1l-.12,0a48.4,48.4,0,0,1-7.23-3.24c-1-.53-2.09-1.07-3.16-1.58a14.43,14.43,0,0,0-6.11-1.26,16.24,16.24,0,0,0-7.52,1.75c-3.34,1.84-4.08,7.4-4.11,11.74-.09,11.59.19,32.44.36,44.9.07,5.38.12,8.93.1,9.45-.17,6.51-1.41,7.65-8.28,7.68s-13.34.08-19.78.14C148,183.41,140,183.48,131.45,183.48Z" transform="translate(-0.05 0.29)"/><path class="cls-2" d="M7,1.71c.81,0,1.62.13,2.41.13h0l77.09,0q46.07,0,92.11,0c.76,0,3.2,0,4,1.71s-.06,5.27,0,6c1.43,18.24.51,50.8.77,51.72a15.38,15.38,0,0,0,1.93,7.2,7.26,7.26,0,0,0,6,3.09h.08a21.38,21.38,0,0,0,3.6.28,30.12,30.12,0,0,0,11.71-2.34c4.24-1.94,8.62-4.28,13.33-4.36h.58c9.43,0,15.77,3.52,18.86,9.76a39.11,39.11,0,0,1,3.42,15c.15,4-1.91,10.21-5.09,15.06-5.35,6.26-10,8.71-16.75,8.71a46.32,46.32,0,0,1-8.58-1c-3.65-1.19-6.82-3.14-10.27-4.77a15.39,15.39,0,0,0-6.53-1.36,17.26,17.26,0,0,0-8,1.87c-3.57,2-4.59,7.35-4.63,12.61-.13,16.61.51,52.67.46,54.33-.15,6.06-1.07,6.69-7.28,6.71-15.59.06-29.56.29-44.75.29l-6.47,0c-10.84-.05-14-4.2-13.55-15.12,1.61-7.09,4.38-14,4.78-21.55a25.46,25.46,0,0,0-3-13.6c-2.16-4.28-5.28-8.5-10.3-11.1a26.77,26.77,0,0,0-12.34-3.21,25.7,25.7,0,0,0-15.76,5.73c-8.75,6.7-13.49,17.15-10.69,27.93,1,4,3.4,7,4.88,10.82,4.65,12.19.29,19.94-12.71,20q-9.84,0-19.69,0L9,182.35h0l-1.87,0a7.83,7.83,0,0,1-3.95-.81,2.6,2.6,0,0,1-1-2.42q0-85,0-169.9c0-2.58-.64-5.44,2.52-6.85h0l.09,0-.06,0A3.88,3.88,0,0,1,7,1.71m0-2a6,6,0,0,0-3.4.93C-.13,2.43,0,5.89.08,8.18c0,.36,0,.71,0,1.06q0,84.92,0,169.84a4.57,4.57,0,0,0,1.78,4.07,1.32,1.32,0,0,0,.25.16,9.85,9.85,0,0,0,4.92,1.06h4.62l24.92,0q9.85,0,19.7,0c8.53,0,12.52-3.24,14.36-5.92,2.78-4.05,2.85-9.85.21-16.78a41.47,41.47,0,0,0-2.28-4.77A26.75,26.75,0,0,1,66.05,151c-2.44-9.36,1.38-19.26,10-25.83a23.7,23.7,0,0,1,14.55-5.32,24.88,24.88,0,0,1,11.42,3c3.89,2,7,5.37,9.44,10.23a23.51,23.51,0,0,1,2.78,12.6c-.26,4.86-1.6,9.64-2.91,14.27-.63,2.27-1.29,4.61-1.82,6.94a2.2,2.2,0,0,0,0,.37c-.25,6.56.79,10.69,3.37,13.38s6.33,3.78,12.17,3.81l6.48,0c8.55,0,16.53-.07,25-.15,6.36-.06,12.95-.12,19.76-.14s9.09-1.22,9.28-8.65c0-.52,0-3.44-.1-9.48-.17-12.46-.45-33.31-.36-44.89,0-5.76,1.32-9.62,3.6-10.88a15.33,15.33,0,0,1,7-1.62,13.23,13.23,0,0,1,5.67,1.17c1.05.49,2.11,1,3.13,1.56a48.57,48.57,0,0,0,7.38,3.3l.24.07a48.87,48.87,0,0,0,9,1c7.36,0,12.48-2.64,18.27-9.41l.15-.2c3.35-5.11,5.58-11.78,5.42-16.23-.09-2.76-.65-9.83-3.63-15.85-2.46-5-8-10.87-20.65-10.87H220c-4.55.06-8.62,2-12.55,3.8l-1.58.73A28.35,28.35,0,0,1,195,69.9a20.32,20.32,0,0,1-3.27-.25,1.84,1.84,0,0,0-.33,0h-.08a5.25,5.25,0,0,1-4.3-2.19,13.52,13.52,0,0,1-1.6-5.93,2.24,2.24,0,0,0,0-.52c-.05-.64,0-3.85,0-7.54.09-10.84.25-31-.76-43.94,0-.15.06-.56.1-.87.21-1.64.53-4.13-.28-5.89-.87-1.92-2.8-2.89-5.76-2.89-29.45,0-59.58,0-92.16,0Q48-.19,9.43-.16c-.32,0-.67,0-1-.06S7.5-.29,7-.29Z" transform="translate(-0.05 0.29)"/></g><g id="_路径2_" data-name="&lt;路径2&gt;"><path class="cls-1" d="M280.46,247.05c-5.67,0-11.16-2.2-16.32-6.53-8.29-7-11.6-15.61-9.84-25.73a42.36,42.36,0,0,1,3-8.95,38.08,38.08,0,0,0,2.9-9c.19-1,.4-1.94.61-2.84.53-2.35,1-4.58.62-6.08-1.28-4.57-7.12-5.55-11.81-5.56h-6.06c-7.18,0-14.33,0-21.49.06s-14.31.06-21.5.06h-5c-8.28,0-10.66-.41-10.68-9.2,0-7.82-.06-14.84-.11-21.78-.06-8.57-.12-16.67-.11-26.12,0-1.14.18-6.3.33-7.55.68-5.5,3.88-8.53,9-8.53a18.84,18.84,0,0,1,6,1.11,28.49,28.49,0,0,1,2.63,1.24,52,52,0,0,0,9.92,4.13,29.56,29.56,0,0,0,7,.83,24.8,24.8,0,0,0,5.89-.64,23.42,23.42,0,0,0,16.92-13.89c3.88-9,3.1-21.27-1.9-29.83a27.63,27.63,0,0,0-8.72-8.38,19,19,0,0,0-9.35-2.25,36.23,36.23,0,0,0-5.62.5c-3.23.52-6.78,2.31-9.91,3.89-.73.36-1.43.72-2.12,1-2.23,1.08-6.53,2.89-10.33,2.89a8.69,8.69,0,0,1-2.81-.42c-4.77-1.83-5.73-5.09-5.73-9.43q0-17.85,0-35.72V5.2a4.5,4.5,0,0,1,.62-2.43.9.9,0,0,1,.15-.19l1-1a1.33Line truncated

After

Width:  |  Height:  |  Size: 76 KiB

@@ -0,0 +1 @@
<svg id="图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 247.97 186.32"><defs><style>.cls-1{fill:#231815;stroke:#000;stroke-miterlimit:10;}</style></defs><path class="cls-1" d="M151.15,342.22v-181h184c3.1,1.63,4.23,4.06,4.18,7.67-.23,16.13-.15,32.27-.25,48.41-.06,10.84,6.05,14.8,16.28,10.68a83.68,83.68,0,0,1,12.74-4.2c11.69-2.67,21.62,2.25,26.64,13.14,1.51,3.27,2.29,6.87,3.4,10.32v9c-.47,1.22-1,2.43-1.41,3.67C392.55,272.25,382,280,369,279c-5.23-.44-10.5-2.83-15.32-5.22-8.24-4.09-13.91-1.26-14.44,7.84-.06,1.17,0,2.33,0,3.5q0,27,0,53.95c0,6.71-.71,7.4-7.57,7.39-18.64-.05-37.28-.08-55.92-.23-12.51-.09-18.4-8.37-14.61-20.41,1.35-4.27,3.05-8.44,4.27-12.74,3.25-11.36-.38-21.29-9.82-27.43a24.48,24.48,0,0,0-28.82,1.89c-8.38,6.82-10.72,16.56-6.59,27.49,1.46,3.87,3.19,7.65,4.37,11.6,3.26,11-3,19.49-14.37,19.59-17,.15-34,.07-50.93.33C155.49,346.55,152.86,345.51,151.15,342.22Z" transform="translate(-150.65 -160.68)"/></svg>

After

Width:  |  Height:  |  Size: 956 B

@@ -0,0 +1 @@
<svg id="图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 247.97 186.32"><defs><style>.cls-1{fill:none;stroke:#000;stroke-miterlimit:10;}</style></defs><path class="cls-1" d="M151.15,342.22v-181h184c3.1,1.63,4.23,4.06,4.18,7.67-.23,16.13-.15,32.27-.25,48.41-.06,10.84,6.05,14.8,16.28,10.68a83.68,83.68,0,0,1,12.74-4.2c11.69-2.67,21.62,2.25,26.64,13.14,1.51,3.27,2.29,6.87,3.4,10.32v9c-.47,1.22-1,2.43-1.41,3.67C392.55,272.25,382,280,369,279c-5.23-.44-10.5-2.83-15.32-5.22-8.24-4.09-13.91-1.26-14.44,7.84-.06,1.17,0,2.33,0,3.5q0,27,0,53.95c0,6.71-.71,7.4-7.57,7.39-18.64-.05-37.28-.08-55.92-.23-12.51-.09-18.4-8.37-14.61-20.41,1.35-4.27,3.05-8.44,4.27-12.74,3.25-11.36-.38-21.29-9.82-27.43a24.48,24.48,0,0,0-28.82,1.89c-8.38,6.82-10.72,16.56-6.59,27.49,1.46,3.87,3.19,7.65,4.37,11.6,3.26,11-3,19.49-14.37,19.59-17,.15-34,.07-50.93.33C155.49,346.55,152.86,345.51,151.15,342.22Z" transform="translate(-150.65 -160.68)"/></svg>

After

Width:  |  Height:  |  Size: 953 B

@@ -1,14 +1,19 @@
import { useEffect, useRef, useState, useCallback } from "react";
import { useEffect } from "react";
import { cn, getBackgroundImage } from "@/lib/utils";
import { AnimatePresence, motion } from "framer-motion";
import { useAudioManager } from "@/hooks/useAudioManager";
export interface ExhibitDescriptionNarrationProps {
/** 描述文本 */
text?: string;
/** 是否可见 */
visible?: boolean;
/** 是否自动播放语音 */
/** 是否自动播放音频(默认 true) */
autoPlay?: boolean;
/** 音频文件地址 */
audioSrc?: string;
/** 音频分组 id,相同 channel 内互斥,不同 channel 可同时播放;不传则全局互斥 */
audioChannelId?: string;
/** 额外的自定义类 */
className?: string;
}
@@ -16,114 +21,41 @@ export interface ExhibitDescriptionNarrationProps {
/**
* 展品描述播报组件
* - 显示展品描述文本
* - 支持文本转语音(TTS)播报
* - 提供播放/暂停控制
* - 支持音频文件播放(通过 useAudioManager)
* - 提供播放/停止控制
*/
const ExhibitDescriptionNarration = ({
text,
visible = true,
autoPlay = false,
autoPlay = true,
audioSrc,
audioChannelId,
className,
}: ExhibitDescriptionNarrationProps) => {
const [isPlaying, setIsPlaying] = useState(false);
const [isPaused, setIsPaused] = useState(false);
const utteranceRef = useRef<SpeechSynthesisUtterance | null>(null);
const synthRef = useRef<typeof window.speechSynthesis | null>(null);
// 音频管理:默认全局互斥,如果传了 audioChannelId 则按 channel 互斥
const { currentSrc, isPlaying, play, stop } = useAudioManager(
audioChannelId
? { mode: "channel", channelId: audioChannelId }
: { mode: "global" }
);
// 初始化语音合成
// 当前组件对应的音频是否正在播放
const isCurrentAudioPlaying =
audioSrc && currentSrc === audioSrc && isPlaying;
// 自动播放音频
useEffect(() => {
if (typeof window !== "undefined" && "speechSynthesis" in window) {
synthRef.current = window.speechSynthesis;
if (visible && autoPlay && audioSrc) {
play(audioSrc);
}
}, []);
}, [visible, autoPlay, audioSrc, play]);
// 播放语音
const playSpeech = useCallback(() => {
if (!text || !synthRef.current) return;
// 如果已有正在播放的语音,先停止
if (utteranceRef.current) {
synthRef.current.cancel();
}
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = "zh-CN";
utterance.rate = 0.9; // 语速稍慢
utterance.pitch = 1; // 音调
utterance.volume = 1; // 音量
utterance.onstart = () => {
setIsPlaying(true);
setIsPaused(false);
};
utterance.onend = () => {
setIsPlaying(false);
setIsPaused(false);
utteranceRef.current = null;
};
utterance.onerror = () => {
setIsPlaying(false);
setIsPaused(false);
utteranceRef.current = null;
};
utteranceRef.current = utterance;
synthRef.current.speak(utterance);
}, [text]);
// 暂停语音
const pauseSpeech = useCallback(() => {
if (synthRef.current && isPlaying) {
synthRef.current.pause();
setIsPaused(true);
}
}, [isPlaying]);
// 继续播放语音
const resumeSpeech = useCallback(() => {
if (synthRef.current && isPaused) {
synthRef.current.resume();
setIsPaused(false);
}
}, [isPaused]);
// 停止语音
const stopSpeech = useCallback(() => {
if (synthRef.current) {
synthRef.current.cancel();
setIsPlaying(false);
setIsPaused(false);
utteranceRef.current = null;
}
}, []);
// 自动播放
// 组件不可见时停止音频(仅当当前播放的是该组件的 audioSrc)
useEffect(() => {
if (visible && autoPlay && text && synthRef.current) {
// 延迟一下确保组件已渲染
const timer = setTimeout(() => {
playSpeech();
}, 300);
return () => {
clearTimeout(timer);
stopSpeech();
};
if (!visible && audioSrc && currentSrc === audioSrc) {
stop();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [visible, autoPlay, text]);
// 组件卸载或不可见时停止语音
useEffect(() => {
if (!visible) {
stopSpeech();
}
return () => {
stopSpeech();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [visible]);
}, [visible, audioSrc, currentSrc, stop]);
if (!text) return null;
@@ -166,12 +98,12 @@ const ExhibitDescriptionNarration = ({
{text}
</div>
{/* 控制按钮 */}
{synthRef.current && (
{/* 控制按钮 - 仅在提供了 audioSrc 时显示 */}
{audioSrc && (
<div className="flex items-center justify-end gap-2 flex-shrink-0">
{!isPlaying && !isPaused && (
{!isCurrentAudioPlaying && (
<button
onClick={playSpeech}
onClick={() => play(audioSrc)}
className="w-10 h-10 flex items-center justify-center rounded-full bg-white/30 hover:bg-white/50 transition-colors shadow-lg"
aria-label="播放"
>
@@ -184,39 +116,9 @@ const ExhibitDescriptionNarration = ({
</svg>
</button>
)}
{isPlaying && !isPaused && (
{isCurrentAudioPlaying && (
<button
onClick={pauseSpeech}
className="w-10 h-10 flex items-center justify-center rounded-full bg-white/30 hover:bg-white/50 transition-colors shadow-lg"
aria-label="暂停"
>
<svg
className="w-4 h-4 text-white"
fill="currentColor"
viewBox="0 0 20 20"
>
<path d="M5.75 3a.75.75 0 00-.75.75v12.5c0 .414.336.75.75.75h1.5a.75.75 0 00.75-.75V3.75A.75.75 0 007.25 3h-1.5zM12.75 3a.75.75 0 00-.75.75v12.5c0 .414.336.75.75.75h1.5a.75.75 0 00.75-.75V3.75a.75.75 0 00-.75-.75h-1.5z" />
</svg>
</button>
)}
{isPaused && (
<button
onClick={resumeSpeech}
className="w-10 h-10 flex items-center justify-center rounded-full bg-white/30 hover:bg-white/50 transition-colors shadow-lg"
aria-label="继续播放"
>
<svg
className="w-4 h-4 text-white"
fill="currentColor"
viewBox="0 0 20 20"
>
<path d="M6.3 2.841A1.5 1.5 0 004 4.11V15.89a1.5 1.5 0 002.3 1.269l9.344-5.89a1.5 1.5 0 000-2.538L6.3 2.84z" />
</svg>
</button>
)}
{(isPlaying || isPaused) && (
<button
onClick={stopSpeech}
onClick={stop}
className="w-10 h-10 flex items-center justify-center rounded-full bg-white/30 hover:bg-white/50 transition-colors shadow-lg"
aria-label="停止"
>
+9 -1
View File
@@ -26,6 +26,10 @@ export interface ExhibitOverlayProps {
description?: string;
/** 是否自动播放描述语音,默认 false */
autoPlayDescription?: boolean;
/** 展品描述对应的音频地址(可选) */
audioSrc?: string;
/** 音频分组 id,相同 channel 内互斥,不同 channel 可同时播放;不传则全局互斥 */
audioChannelId?: string;
/** 点击展示 */
onClick?: () => void;
/** 打开回调 */
@@ -48,7 +52,9 @@ const ExhibitOverlay = ({
visible = true,
className,
description,
autoPlayDescription = false,
autoPlayDescription = true,
audioSrc,
audioChannelId,
onClick,
onOpen,
}: ExhibitOverlayProps) => {
@@ -515,6 +521,8 @@ const ExhibitOverlay = ({
text={description}
visible={visible}
autoPlay={autoPlayDescription}
audioSrc={audioSrc}
audioChannelId={audioChannelId}
className="absolute left-20 top-1/2 -translate-y-1/2"
/>
)}
+1 -1
View File
@@ -152,7 +152,7 @@ export const StudyGuideModal = ({
"[text-shadow:_0px_0px_10px_rgba(193,85,64,0.4),_0px_0px_10px_rgba(193,85,64,0.4),_0px_0px_10px_rgba(193,85,64,0.4),_0px_0px_10px_rgba(193,85,64,0.4)]"
)}
style={{
marginTop: verticalAlign === "center" ? "100px" : "",
marginTop: verticalAlign === "center" ? "85px" : "",
}}
>
{children}
@@ -1,6 +1,7 @@
import { Image } from "@/components";
import { getStaticImage, getBackgroundImage } from "@/lib/utils";
import type { MouseEvent } from "react";
import { useEffect } from "react";
import { useAudioManager } from "@/hooks/useAudioManager";
export interface SuccessAlertProps {
/** 标题 */
@@ -9,6 +10,12 @@ export interface SuccessAlertProps {
content?: string;
/** 是否可见 */
visible?: boolean;
/** 成功提示音频地址(可选) */
audioSrc?: string;
/** 弹窗出现时是否自动播放音频(默认 true) */
autoPlayAudio?: boolean;
/** 音频分组 id,相同 channel 内互斥,不同 channel 可同时播放;不传则全局互斥 */
audioChannelId?: string;
/** 点击遮罩层回调 */
onMaskClick?: () => void;
/** 继续按钮回调 */
@@ -25,15 +32,44 @@ const SuccessAlert = ({
title = "恭喜你, 选择正确!",
content,
visible = true,
audioSrc,
autoPlayAudio = true,
audioChannelId,
onMaskClick,
onContinue,
onClose,
}: SuccessAlertProps) => {
const { currentSrc, play, stop } = useAudioManager(
audioChannelId
? { mode: "channel", channelId: audioChannelId }
: { mode: "global" }
);
// 弹窗显示时自动播放音频
useEffect(() => {
if (!visible) return;
if (!autoPlayAudio) return;
if (!audioSrc) return;
play(audioSrc);
}, [visible, autoPlayAudio, audioSrc, play]);
// 弹窗关闭时停止音频(仅当当前播放的是该弹窗的 audioSrc)
useEffect(() => {
if (visible) return;
if (audioSrc && currentSrc === audioSrc) {
stop();
}
}, [visible, audioSrc, currentSrc, stop]);
if (!visible) return null;
const handleContinue = (e: MouseEvent) => {
e.preventDefault();
e.stopPropagation();
// 点击继续时停止音频
if (audioSrc && currentSrc === audioSrc) {
stop();
}
// 只调用 onContinue,它会处理关闭逻辑
if (onContinue) {
onContinue();
+3 -40
View File
@@ -1,4 +1,4 @@
import { useEffect, useState, useRef } from "react";
import { useEffect, useState } from "react";
import { AnimatePresence, motion } from "framer-motion";
import { getStaticImage } from "@/lib/utils";
import { Image, Select } from "@/components";
@@ -30,9 +30,6 @@ const LetterToHuangyanpei = ({
// 是否已提交:提交后隐藏编辑信件,展示只读信件
const [submitted, setSubmitted] = useState(false);
// 用于跟踪是否已经自动填入过答案,避免重复填入
const autoFilledRef = useRef(false);
// 填空类型定义:0=填空1(限填),1=填空2(任填),2=填空3(任填),3=填空4(限填)
const BLANK_1_ANSWER = "手脑并用";
const BLANK_4_ANSWER = "有业者乐业";
@@ -40,6 +37,7 @@ const LetterToHuangyanpei = ({
// 填空1的选项
const BLANK_1_OPTIONS = [
"手脑并用",
"有业者乐业",
// "知行合一",
// "学以致用",
// "理论实践"
@@ -47,47 +45,13 @@ const LetterToHuangyanpei = ({
// 填空4的选项
const BLANK_4_OPTIONS = [
"手脑并用",
"有业者乐业",
// "有业者敬业",
// "有业者精业",
// "有业者成业",
];
// 自动浮现答案:填空1、4空白时自动填入正确答案
useEffect(() => {
// 只在组件可见且未自动填入过时检查
if (!visible) {
autoFilledRef.current = false;
return;
}
if (autoFilledRef.current) return;
setBlankValues((prevValues) => {
const nextValues = [...prevValues];
let changed = false;
// 填空1(索引0):如果为空,自动填入正确答案
if (!nextValues[0] || nextValues[0].trim() === "") {
nextValues[0] = BLANK_1_ANSWER;
changed = true;
}
// 填空4(索引3):如果为空,自动填入正确答案
if (!nextValues[3] || nextValues[3].trim() === "") {
nextValues[3] = BLANK_4_ANSWER;
changed = true;
}
if (changed || nextValues.length > 0) {
// 如果已经有值或已更改,标记为已处理,避免重复检查
autoFilledRef.current = true;
}
return nextValues;
});
}, [visible]);
// 校验输入:确保所有填空符合标准
const validateBlanks = () => {
// 计算占位总数,防止有些没被输入过而是 undefined
@@ -137,7 +101,6 @@ const LetterToHuangyanpei = ({
setSubmitted(false);
setBlankValues([]);
setBlankErrors([]);
autoFilledRef.current = false; // 重置自动填入标记
}
}, [visible]);
+3 -1
View File
@@ -36,11 +36,13 @@ const LetterToLuxun = ({
// 填空1的选项
const BLANK_1_OPTIONS = [
"首在立人",
"批判求真",
// 可以添加其他选项
];
// 填空2的选项
const BLANK_2_OPTIONS = [
"首在立人",
"批判求真",
// 可以添加其他选项
];
@@ -262,7 +264,7 @@ const LetterToLuxun = ({
// 任填:文本输入
<input
name={`blank-${currentIndex}`}
className={`inline-block w-[100px] h-[36px] mx-2 align-middle bg-transparent border-b-2 border-dashed [font-size:26px_!important] text-center outline-none ${
className={`inline-block w-[100px] h-[36px] mx-2 mb-2 align-middle bg-transparent border-b-2 border-dashed [font-size:26px_!important] text-center outline-none ${
blankErrors[currentIndex]
? "border-red-500"
: "border-[#4e402e]"
+28 -28
View File
@@ -39,7 +39,7 @@ const LetterToSunzhongshan = ({
const BLANK_1_ANSWER = "天下为公";
const BLANK_2_ANSWER = "与时俱进";
// 最后一个空的正确答案
const LAST_BLANK_ANSWER = "仍须努力";
const LAST_BLANK_ANSWER = "仍需努力";
// 前三个空的选项(所有空都用这三个选项)
const SELECT_OPTIONS = [
@@ -48,7 +48,7 @@ const LetterToSunzhongshan = ({
{ value: "与时俱进", label: "与时俱进" },
];
// 校验输入:前三个空必须选择正确答案,最后一个空必须是"仍须努力",其他空不能为空且不超过 10 个字符
// 校验输入:前三个空必须选择正确答案,最后一个空必须是"仍需努力",其他空不能为空且不超过 10 个字符
const validateBlanks = () => {
// 计算占位总数,防止有些没被输入过而是 undefined
const placeholderCount = (letterContent.match(/____/g) || []).length;
@@ -69,7 +69,7 @@ const LetterToSunzhongshan = ({
hasError = true;
}
} else if (i === placeholderCount - 1) {
// 最后一个空:必须是"仍须努力"
// 最后一个空:必须是"仍需努力"
if (!v || v !== LAST_BLANK_ANSWER) {
nextValues[i] = "";
nextErrors[i] = true;
@@ -251,31 +251,31 @@ const LetterToSunzhongshan = ({
}
// 立即验证选择是否正确
let correctAnswer = "";
if (currentIndex === 0) {
correctAnswer = BLANK_0_ANSWER;
} else if (currentIndex === 1) {
correctAnswer = BLANK_1_ANSWER;
} else if (currentIndex === 2) {
correctAnswer = BLANK_2_ANSWER;
}
// let correctAnswer = "";
// if (currentIndex === 0) {
// correctAnswer = BLANK_0_ANSWER;
// } else if (currentIndex === 1) {
// correctAnswer = BLANK_1_ANSWER;
// } else if (currentIndex === 2) {
// correctAnswer = BLANK_2_ANSWER;
// }
if (
correctAnswer &&
selectedValue !== correctAnswer
) {
// 选择错误,显示错误提示
setErrorAlertText(
"请选择正确答案"
);
setShowErrorAlert(true);
// 清空选择
next[currentIndex] = "";
setBlankValues(next);
const nextErrs = [...blankErrors];
nextErrs[currentIndex] = true;
setBlankErrors(nextErrs);
}
// if (
// correctAnswer &&
// selectedValue !== correctAnswer
// ) {
// // 选择错误,显示错误提示
// setErrorAlertText(
// "请选择正确答案"
// );
// setShowErrorAlert(true);
// // 清空选择
// next[currentIndex] = "";
// setBlankValues(next);
// const nextErrs = [...blankErrors];
// nextErrs[currentIndex] = true;
// setBlankErrors(nextErrs);
// }
}}
placeholder="请选择"
error={blankErrors[currentIndex]}
@@ -286,7 +286,7 @@ const LetterToSunzhongshan = ({
// 其他空:文本输入
<input
name={`blank-${currentIndex}`}
className={`inline-block w-[110px] h-[36px] mx-2 mb-2 align-middle bg-transparent border-b-2 border-dashed [font-size:26px_!important] text-center outline-none ${
className={`inline-block w-[110px] h-[36px] mx-2 bg-transparent border-b-2 border-dashed [font-size:26px_!important] text-center outline-none ${
blankErrors[currentIndex]
? "border-red-500"
: "border-[#4e402e]"
+1 -1
View File
@@ -126,7 +126,7 @@ export const EndingOverlay = ({
<div className="fixed inset-0 z-[10000] flex items-center justify-center bg-transparent">
{/* 文本内容层 */}
<div
className="relative z-10 flex items-center max-w-8xl py-50 px-24 h-[825px] bg-cover bg-center bg-no-repeat"
className="relative z-10 flex items-center max-w-full transition-all py-50 px-24 h-[825px] bg-cover bg-center bg-no-repeat"
style={{
backgroundImage: `url(${getStaticImage("components/overlay/bg")})`,
}}
+1
View File
@@ -322,6 +322,7 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(
width: `${
dropdownPosition.width > 0 ? dropdownPosition.width : "auto"
}px`,
minWidth: "80px",
zIndex: zIndex,
}}
role="listbox"
-45
View File
@@ -1,45 +0,0 @@
/**
* 展品与拼图碎片映射配置
* 定义每个展品对应解锁的碎片ID
*
* 格式:{ residence: { exhibitId: pieceId } }
* 示例:chenyun: { 1: 1 } 表示陈云故居的展品1对应碎片1
*/
export const puzzleExhibitMapping: Record<
string,
Record<number, number>
> = {
chenyun: {
// 展品1 对应 碎片1
1: 1,
// 展品2 对应 碎片2
2: 2,
// 后续可以继续添加其他展品的映射
// 3: 3,
// 4: 4,
},
// 后续可以添加其他故居的映射
// sunzhongshan: {
// 1: 1,
// },
};
/**
* 获取展品对应的碎片ID
* @param residence 故居名称
* @param exhibitId 展品ID
* @returns 碎片ID,如果不存在则返回 null
*/
export const getPuzzlePieceByExhibit = (
residence: string,
exhibitId: number
): number | null => {
const residenceMapping = puzzleExhibitMapping[residence];
if (!residenceMapping) {
return null;
}
return residenceMapping[exhibitId] ?? null;
};
-52
View File
@@ -1,52 +0,0 @@
/**
* 拼图碎片与页面路由映射配置
* 格式:故居名 -> 碎片ID -> 路由路径(相对于/:moduleName/)
*
* 示例:
* - 碎片1 对应 place1 页面
* - 碎片2 对应 place2 页面
* - 碎片3 对应 reflection 页面
* - 碎片4 对应 internalization 页面
*
* 注意:路由路径中的 :moduleName 会在使用时替换为实际的 moduleName
*/
export const puzzleMapping: Record<string, Record<number, string>> = {
chenyun: {
// 示例:碎片1 对应 place1 页面
1: "chenyun/place1",
// 后续可以继续添加其他碎片的映射
// 2: "chenyun/place2",
// 3: "chenyun/reflection",
// 4: "chenyun/internalization",
},
// 后续可以添加其他故居的映射
// sunzhongshan: {
// 1: "sunzhongshan/place1",
// },
};
/**
* 获取碎片对应的路由路径
* @param residence 故居名称
* @param pieceId 碎片ID
* @param moduleName 模块名称(用于替换路由中的 :moduleName)
* @returns 完整的路由路径,如果不存在则返回 null
*/
export const getPuzzleRoute = (
residence: string,
pieceId: number,
moduleName: string
): string | null => {
const residenceMapping = puzzleMapping[residence];
if (!residenceMapping) {
return null;
}
const route = residenceMapping[pieceId];
if (!route) {
return null;
}
return `/${moduleName}/${route}`;
};
+23 -12
View File
@@ -7,8 +7,8 @@ import {
type VideoRef,
type DragFillResult,
LetterToLuxun,
StudyGuideModal,
Narration,
EndingOverlay,
} from "@/components";
import { getBackgroundImage, getPublicPath, getStaticImage } from "@/lib/utils";
import { SuccessAlert, DragQuizPanel } from "@/components/layout";
@@ -454,7 +454,8 @@ const LuxunInternalization = () => {
onCanPlay={handleVideoCanPlay}
onEnded={() => {
setShowVideo(false);
setPostGuideVisible(true);
setShowLetter(true);
// setPostGuideVisible(true);
if (audioRef.current) {
audioRef.current.currentTime = 0;
audioRef.current.play().catch((error) => {
@@ -486,8 +487,11 @@ const LuxunInternalization = () => {
}}
showMask={false}
>
<GuideStepsItem speaker="guide">
关于"首在立人"走进上海鲁迅故居的红砖小楼,解锁了先生从绍兴到上海的文学足迹,我们读懂了他以笔为刃的战斗姿态,也见证了他与进步人士的革命情谊。
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_35.mp3")}
>
走进上海鲁迅故居的红砖小楼,解锁了先生从绍兴到上海的文学足迹,我们读懂了他以笔为刃的战斗姿态,也见证了他与进步人士的革命情谊。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
那些针砭时弊的杂文、借古讽今的篇章,还有藏在6平方米客房里的红色坚守,都在诉说着先生的热忱。
@@ -498,16 +502,22 @@ const LuxunInternalization = () => {
<GuideStepsItem speaker="guide">
先生认为,中华民族要屹立于世界民族之林,“首在立人”,“立人”思想从此贯穿在鲁迅先生的人生和作品中。”先生认为,中华民族要屹立于世界民族之林,“首在立人”,“立人”思想从此贯穿在鲁迅先生的人生和作品中。
</GuideStepsItem>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_36.mp3")}
>
小沪,关于"首在立人"这四个字我还是有些不理解,立人为什么这么重要?
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_37.mp3")}
>
"首在立人"这四个字,可以说是鲁迅先生一生的价值观。今天我们有幸邀请到了上海鲁迅纪念馆研究馆员施晓燕。关于你的问题,我想施老师会给你指点迷津!
</GuideStepsItem>
</GuideSteps>
{/* 视频结束后的引导 */}
<GuideSteps
{/* <GuideSteps
visible={postGuideVisible}
onVisibleChange={(visible) => {
setPostGuideVisible(visible);
@@ -521,7 +531,7 @@ const LuxunInternalization = () => {
<GuideStepsItem speaker="guide">
听完施老师的讲解,相信你对鲁迅先生的"立人"精神有了更深的理解。现在,就请你把这份感悟写下来,既总结参观的收获,也给自己未来许下一个承诺吧!
</GuideStepsItem>
</GuideSteps>
</GuideSteps> */}
{/* 信件组件 */}
<LetterToLuxun
@@ -533,8 +543,10 @@ const LuxunInternalization = () => {
}}
/>
<StudyGuideModal
<EndingOverlay
visible={showStudyGuide}
text="恭喜你已经顺利完成了鲁迅故居的学习,请继续前往下一个故居吧!"
animationSpeed={200}
onContinue={() => {
if (audioRef.current) {
audioRef.current.pause();
@@ -543,9 +555,8 @@ const LuxunInternalization = () => {
setShowStudyGuide(false);
navigate(`/${moduleName}/${starName}/home`);
}}
>
恭喜你已经顺利完成了鲁迅故居的学习,请继续前往下一个故居吧!
</StudyGuideModal>
audioSrc={getPublicPath("audio/luxun/mp3_luxun_38.mp3")}
/>
</div>
);
};
+45 -23
View File
@@ -8,7 +8,7 @@ import {
StudyGuideModal,
} from "@/components";
import { useNavigate, useParams } from "react-router-dom";
import { getBackgroundImage, getStaticImage } from "@/lib/utils";
import { getBackgroundImage, getPublicPath, getStaticImage } from "@/lib/utils";
import { useState, useEffect } from "react";
import { useCursorController } from "@/hooks/useCursorController";
@@ -36,11 +36,9 @@ const LuxunPlace1 = () => {
const [visible, setVisible] = useState(false);
const [visibleIndex, setVisibleIndex] = useState(0);
// 浏览完展品后的对话显示状态
// 打开展品2后的对话显示状态
const [showAfterExhibitDialog, setShowAfterExhibitDialog] = useState(false);
const [afterExhibitDialogShown, setAfterExhibitDialogShown] = useState(false);
// 对话结束后是否显示前进按钮
const [showForwardButton, setShowForwardButton] = useState(false);
const [afterExhibitDialogStep, setAfterExhibitDialogStep] = useState(0);
// 当前激活的展品 ID(只存 ID,不直接存路径,便于作为唯一标识)
const [activeExhibitId, setActiveExhibitId] = useState<1 | 2 | null>(null);
// 已浏览展品的 ID 集合
@@ -60,6 +58,11 @@ const LuxunPlace1 = () => {
setVisibleIndex(0);
setVisible(true);
}
if (index === 2) {
setAfterExhibitDialogStep(0);
setShowAfterExhibitDialog(true);
}
setActiveExhibitId(index);
};
@@ -68,12 +71,12 @@ const LuxunPlace1 = () => {
const completed = progressCount >= total;
// 当两个展品都浏览完成后,自动显示对话
useEffect(() => {
if (completed && !afterExhibitDialogShown) {
setShowAfterExhibitDialog(true);
setAfterExhibitDialogShown(true);
}
}, [completed, afterExhibitDialogShown]);
// useEffect(() => {
// if (completed && !afterExhibitDialogShown) {
// setShowAfterExhibitDialog(true);
// setAfterExhibitDialogShown(true);
// }
// }, [completed, afterExhibitDialogShown]);
const subtitle = completed
? "探索鲁迅故居一楼客厅(已完成)"
: `探索鲁迅故居一楼客厅(${progressCount}/${total})`;
@@ -123,7 +126,7 @@ const LuxunPlace1 = () => {
/>
{/* 继续前进 - 仅在完成所有展品且对话结束后显示 */}
{showForwardButton && (
{completed && (
<Image
className="absolute bottom-0 right-0"
src={getStaticImage("components/place/继续前进")}
@@ -165,6 +168,7 @@ const LuxunPlace1 = () => {
setShowAfterDialogGuide(true);
}
}}
audioSrc={getPublicPath("audio/luxun/mp3_luxun_01.mp3")}
>
{`同学你好,欢迎来到“走进故居·对话”学习模块!
此刻你正立在鲁迅故居的一楼客厅里,接下来请随旧时光的纹路,一同翻开先生思想长卷里的5段深刻轨迹:风雨前夕的思想淬炼、杂文集里的匕首与投枪、历史新编中的现实呐喊、为亡友编纂的精神丰碑、6平方米的红色庇护所。准备好共赴这场笔尖与灵魂的滚烫相遇了吗?`}
@@ -181,10 +185,16 @@ const LuxunPlace1 = () => {
showMask={false}
zIndex={10000}
>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_03.mp3")}
>
小沪!我知道鲁迅是1933年搬到大陆新村9号的,那在这之前他为什么会选择来到上海,又有哪些思想转变呢?
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_04.mp3")}
>
你问得很关键!从1892-1898年少年时期在绍兴三味书屋求学积淀,到1919-1923年于北京八道湾参加新文化运动、酝酿《阿Q正传》,再到1926年9-12月赴厦门大学任教期间聚焦底层创作杂文。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
@@ -200,6 +210,7 @@ const LuxunPlace1 = () => {
visible={showAfterDialogGuide}
onContinue={() => setShowAfterDialogGuide(false)}
ailgn="left"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_02.mp3")}
>
1.场景中出现动态光点的展品表示未浏览,可通过点击光点来了解展品背后的故事,通过滚轮可以放大展品,浏览展品后蓝色动态光点会变为已浏览状态。
{"\n"}
@@ -211,28 +222,39 @@ const LuxunPlace1 = () => {
{/* 浏览完展品后的对话 */}
<GuideSteps
visible={showAfterExhibitDialog}
currentStep={afterExhibitDialogStep}
onStepChange={setAfterExhibitDialogStep}
onVisibleChange={(nextVisible) => {
setShowAfterExhibitDialog(nextVisible);
if (!nextVisible) {
setShowForwardButton(true);
}
}}
showMask={true}
highlightSelectors={[]}
showMask={false}
zIndex={10000}
>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_05.mp3")}
>
这张照片也是鲁迅在上海期间的住所吗?
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_06.mp3")}
>
不错!1927年10月,鲁迅带着许广平来到上海,先住进了虹口区横浜路景云里。在景云里的两年半,他结识了冯雪峰等共产党员,参与发起中国左翼作家联盟,从"独战的英雄"转变为革命文艺战线的旗手。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
这段时间他写下《三闲集》《二心集》,表达他在当时历史条件下针对社会现象、文化问题和论战的看法与社会批判,此间还翻译了大量马列文艺理论著作,为此后的战斗打下了坚实思想基础。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_07.mp3")}
>
鲁迅在发起成立左联后,为躲避国民党当局监视,1930年5月,与许广平、周海婴迁居到拉摩斯公寓。1933年4月,受战火纷扰影响,为安全及健康计,同时也为了更方便开展革命文化工作,鲁迅以"内山书店职员"的名义,搬到了大陆新村9号。
</GuideStepsItem>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_08.mp3")}
>
这个过程的确十分曲折。
</GuideStepsItem>
</GuideSteps>
+25 -7
View File
@@ -7,7 +7,7 @@ import {
GuideStepsItem,
} from "@/components";
import { useNavigate, useParams } from "react-router-dom";
import { getBackgroundImage, getStaticImage } from "@/lib/utils";
import { getBackgroundImage, getPublicPath, getStaticImage } from "@/lib/utils";
import { useState } from "react";
import { useCursorController } from "@/hooks/useCursorController";
@@ -191,10 +191,16 @@ const LuxunPlace2 = () => {
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_09.mp3")}
>
小沪,这里的《且介亭杂文》就是鲁迅先生在大陆新村9号创作的吧,有人将先生的杂文评价为"匕首与投枪",《且介亭杂文》中有具体体现吗?
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_10.mp3")}
>
当然有!20世纪30年代,国民党"文化围剿"愈烈,进步思想遭严控,鲁迅在大陆新村9号创作出《且介亭杂文》等集子。这些杂文聚焦文化乱象与社会沉疴,篇幅精炼却直击要害,像匕首般刺穿虚伪表象,似投枪般直戳问题核心。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
@@ -211,10 +217,16 @@ const LuxunPlace2 = () => {
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_13.mp3")}
>
那《南腔北调集》这个名字又有什么深意呢?
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_14.mp3")}
>
这是鲁迅的自嘲,他曾说"我不会说绵软的苏白,不会打响亮的京腔,不入调,不入流",所以戏称自己的文章是"南腔北调"。这本书里的《〈守常全集》题记》,赞美了李大钊同志,说"他的遗文却将永驻,因为这是先驱者的遗产,革命史上的丰碑。"
</GuideStepsItem>
</GuideSteps>
@@ -228,10 +240,16 @@ const LuxunPlace2 = () => {
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_11.mp3")}
>
我们初中学的《中国人失掉自信力了吗》就是收录于《且介亭杂文》中的!
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_12.mp3")}
>
那你应该更有体会!这篇《中国人失掉自信力了吗》是将“匕首与投枪”的特质发挥到极致。当时国民党当局接连丧权辱国,一些文人却散布“中国人失掉自信力”的悲观论调,迎合反动派的卖国行径。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
+51 -18
View File
@@ -8,7 +8,7 @@ import {
SingleChoiceQuizPanel,
} from "@/components";
import { useNavigate, useParams } from "react-router-dom";
import { getBackgroundImage, getStaticImage } from "@/lib/utils";
import { getBackgroundImage, getPublicPath, getStaticImage } from "@/lib/utils";
import { useState } from "react";
import SuccessAlert from "@/components/layout/alert/success-alert";
import { useCursorController } from "@/hooks/useCursorController";
@@ -210,13 +210,16 @@ const LuxunPlace3 = () => {
<ExhibitOverlay
visible={activeExhibitId !== null}
src={activeExhibitId ? exhibitSrcMap[activeExhibitId] : ""}
// description={
// activeExhibitId === 2
// ? '本书里面还收录了《理水》,文章写的是大禹治水"三过家门而不入"的上古传说,实则藏着对现实的尖锐针砭。当时反动派统治腐朽,面对内忧外患只会粉饰太平,还有一群空谈理论、脱离实际的"学者"混淆视听。大禹带领民众跋山涉水、实地勘察,摒弃"堵"的旧法改用"疏"的良方,历经千辛万苦终平水患。这哪里是写古代治水?分明是在赞颂脚踏实地、敢破敢立的实干精神,讽刺那些庸碌无为的当权者与空谈家,用远古的正义之光映照现实的黑暗。'
// : activeExhibitId === 5
// ? "为了妥善出版亡友遗著,鲁迅亲自选稿、校对、设计封面。为了躲避反动派的审查,他还特意用“诸夏怀霜社”的名义出版,“怀霜”就是为了纪念瞿秋白。1936年《海上述林》上卷出版时,鲁迅的身体已经极度虚弱,但他仍一遍遍翻看样书,说“这是我对秋白最好的纪念”。"
// : undefined
// }
description={
activeExhibitId === 5
? "为了妥善出版亡友遗著,鲁迅亲自选稿、校对、设计封面。为了躲避反动派的审查,他还特意用“诸夏怀霜社”的名义出版,“怀霜”就是为了纪念瞿秋白。1936年《海上述林》上卷出版时,鲁迅的身体已经极度虚弱,但他仍一遍遍翻看样书,说“这是我对秋白最好的纪念”。"
: undefined
}
audioSrc={
activeExhibitId === 5
? getPublicPath("audio/luxun/mp3_luxun_23.mp3")
: undefined
}
onClose={() => {
if (activeExhibitId === 2) {
// 光标2:关闭展品框后显示题目
@@ -266,10 +269,16 @@ const LuxunPlace3 = () => {
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_15.mp3")}
>
小沪,书架上还有《故事新编》,这不是历史小说吗?鲁迅为什么要在这个时期写古代故事呢?
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_16.mp3")}
>
《故事新编》是鲁迅酝酿十几年的作品,最终在大陆新村9号完成定稿。表面上写的是神话传说和历史典故,实则句句都在影射现实。当时反动派妄图用复古思想麻痹民众,鲁迅就用"故事新编"的方式,借古讽今,唤醒民众。
</GuideStepsItem>
</GuideSteps>
@@ -284,10 +293,16 @@ const LuxunPlace3 = () => {
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_17.mp3")}
>
这本书里面还收录了《理水》,是大禹治水的故事吗?
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_18.mp3")}
>
不错,文章写的是大禹治水“三过家门而不入”的上古传说,实则藏着对现实的尖锐针砭。当时反动派统治腐朽,面对内忧外患只会粉饰太平,还有一群空谈理论、脱离实际的“学者”混淆视听。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
@@ -375,10 +390,16 @@ const LuxunPlace3 = () => {
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_19.mp3")}
>
我还看到书架上有很多翻译作品,这也是他在这儿完成的吗?
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_20.mp3")}
>
没错!鲁迅在这儿翻译了《死魂灵》《表》等大量外国作品。他说"没有拿来的,人不能自成为新人",就是想通过翻译,为中国文艺引进新思想、新方法,让更多人看到世界的真相,找到救国的道路。
</GuideStepsItem>
</GuideSteps>
@@ -403,10 +424,16 @@ const LuxunPlace3 = () => {
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_21.mp3")}
>
小沪,这两本书看起来格外厚重,它和鲁迅在这儿的经历有什么关联呢?
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_22.mp3")}
>
这不是鲁迅的著作,却是他在大陆新村9号倾注了全部心血的作品——为亡友瞿秋白编辑的译文集《海上述林》。1935年6月,瞿秋白被国民党反动派杀害,鲁迅悲痛万分,当即决定要为挚友出版译文集,让他的思想和精神流传下去。
</GuideStepsItem>
</GuideSteps>
@@ -471,10 +498,16 @@ const LuxunPlace3 = () => {
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_24.mp3")}
>
原来这本书不仅是译文集,更是两人革命友谊和共同信仰的见证!
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_25.mp3")}
>
太对了!鲁迅和瞿秋白的故事不仅在于《海上述林》的字里行间,这座小楼的三楼也藏着他们为了革命事业并肩战斗的初心。
</GuideStepsItem>
</GuideSteps>
+22 -7
View File
@@ -188,10 +188,16 @@ const LuxunPlace5 = () => {
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_26.mp3")}
>
你说的是这间小小的客房吗,这里为什么会被称为"红色庇护所"呢?
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_27.mp3")}
>
你可别小看这间仅6平方米的客房,在白色恐怖笼罩的1933年至1936年,这里是很多共产党人的"安全港湾"。1933年,瞿秋白夫妇第三次到鲁迅家避难,就秘密住进了这里。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
@@ -263,10 +269,16 @@ const LuxunPlace5 = () => {
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_28.mp3")}
>
除了瞿秋白,还有其他共产党人来过这里吗
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_29.mp3")}
>
当然!1936年4月,中共中央特派员冯雪峰到上海开展工作,也先住进了这间客房。冯雪峰把红军长征胜利、遵义会议召开的消息告诉鲁迅,还带来了毛泽东同志提出的抗日民族统一战线主张。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
@@ -302,11 +314,14 @@ const LuxunPlace5 = () => {
showMask={false}
zIndex={20010}
>
<GuideStepsItem speaker="guide">
我们已经了解了鲁迅居住在上海大陆新村9号时的重要事件。接下来,请你接过先生手中的“笔”,走出上海,跨越时空,追寻他从绍兴三味书屋到上海的创作足迹——解锁每个地标背后的故事,填充文字里的时代线索...
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_30.mp3")}
>
我们已经了解了鲁迅居住在上海大陆新村9号时的重要事件。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
在互动中,读懂先生笔墨间的温度与力量。
接下来,请你接过先生手中的“笔”,走出上海,跨越时空,追寻他从绍兴三味书屋到上海的创作足迹——解锁每个地标背后的故事,填充文字里的时代线索,在互动中,读懂先生笔墨间的温度与力量。
</GuideStepsItem>
</GuideSteps>
</div>
+2 -1
View File
@@ -2,7 +2,7 @@ import { useState, useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { Image, StudyGuideModal } from "@/components";
import SuccessAlert from "@/components/layout/alert/success-alert";
import { getBackgroundImage, getStaticImage } from "@/lib/utils";
import { getBackgroundImage, getPublicPath, getStaticImage } from "@/lib/utils";
const starName = "luxun";
@@ -340,6 +340,7 @@ const LuxunReflectionScene1 = () => {
onContinue={() => {
setShowGuideModal(false);
}}
audioSrc={getPublicPath("audio/luxun/mp3_luxun_31.mp3")}
>
{`同学你好,欢迎来到“跨越时空·体悟”学习模块!
请登陆“时空回溯・亲历时代”体验仓,下一站:1892年,绍兴三味书屋。
+11 -5
View File
@@ -4,7 +4,7 @@ import { Image, DragFill, GuideSteps, GuideStepsItem } from "@/components";
import type { DragFillResult } from "@/components";
import ErrorAlert from "@/components/layout/alert/error-alert";
import SuccessAlert from "@/components/layout/alert/success-alert";
import { getBackgroundImage, getStaticImage } from "@/lib/utils";
import { getBackgroundImage, getPublicPath, getStaticImage } from "@/lib/utils";
const starName = "luxun";
@@ -373,7 +373,7 @@ const LuxunReflectionScene2 = () => {
<img
src={getStaticImage(placedItem.imagePath)}
alt={placedItem.name}
className="w-20 h-20 object-contain"
className="w-23 h-23 object-contain"
/>
) : (
<div className="text-white text-sm text-center">
@@ -492,7 +492,7 @@ const LuxunReflectionScene2 = () => {
<img
src={getStaticImage(item.imagePath)}
alt={item.name}
className="w-16"
className="w-12"
draggable={false}
/>
);
@@ -585,7 +585,10 @@ const LuxunReflectionScene2 = () => {
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_32.mp3")}
>
这些线索藏在先生的文字里,长按卡片拖到对应地标旁,还原创作故事!
</GuideStepsItem>
</GuideSteps>
@@ -598,7 +601,10 @@ const LuxunReflectionScene2 = () => {
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_32_02.mp3")}
>
你读懂了鲁迅文字与土地的联结,再深入看看文字里的初心!
</GuideStepsItem>
</GuideSteps>
+29 -12
View File
@@ -1,7 +1,13 @@
import { getBackgroundImage, getStaticImage } from "@/lib/utils";
import { getBackgroundImage, getPublicPath, getStaticImage } from "@/lib/utils";
import { useState, useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { Image, GuideSteps, GuideStepsItem, EndingOverlay } from "@/components";
import {
Image,
GuideSteps,
GuideStepsItem,
StudyGuideModal,
EndingOverlay,
} from "@/components";
import { SingleChoiceQuizPanel } from "@/components/layout/quiz";
import SuccessAlert from "@/components/layout/alert/success-alert";
@@ -33,15 +39,14 @@ const LuxunReflectionScene3 = () => {
const [completedQuiz3, setCompletedQuiz3] = useState(false); // 题目3是否完成
const [showSummary, setShowSummary] = useState(false); // 是否显示总结
const [completionGuideVisible, setCompletionGuideVisible] = useState(false); // 通关提示对话是否显示
const [_completionGuideFinished, setCompletionGuideFinished] =
useState(false); // 通关提示对话是否已完成
const [showGuideModal, setShowGuideModal] = useState(true);
// 成功提示相关状态
const [showSuccessAlert, setShowSuccessAlert] = useState(false);
const [successMessage, setSuccessMessage] = useState("");
// 结束语覆盖层是否显示
const [showEndingOverlay, setShowEndingOverlay] = useState(false);
// 结束语覆盖层是否已关闭
const [endingOverlayFinished, setEndingOverlayFinished] = useState(false);
const [showEndingOverlay, setShowEndingOverlay] = useState(false);
const allCompleted = completedQuiz1 && completedQuiz2 && completedQuiz3;
const progress =
@@ -55,7 +60,7 @@ const LuxunReflectionScene3 = () => {
if (showSummary) {
const timer = setTimeout(() => {
setCompletionGuideVisible(true);
}, 800);
}, 500);
return () => clearTimeout(timer);
}
}, [showSummary]);
@@ -287,7 +292,7 @@ const LuxunReflectionScene3 = () => {
<div className="absolute top-[calc(2.4rem+204px)] left-0 w-[995px] h-[525px] pt-16 px-10">
<div className="flex items-center justify-between">
{/* 提示图标 - 鲁迅1左侧 */}
{hintPosition === "luxun1" && (
{hintPosition === "luxun1" && !showGuanzhu && (
<div className="absolute left-80 top-60 flex items-center z-10 pointer-events-none">
<img
className="animate-finger-bounce scale-x-[-1]"
@@ -438,8 +443,6 @@ const LuxunReflectionScene3 = () => {
onVisibleChange={(visible) => {
if (!visible) {
setCompletionGuideVisible(false);
setCompletionGuideFinished(true); // 对话关闭后标记为已完成
// 显示结束语覆盖层
setShowEndingOverlay(true);
}
}}
@@ -447,12 +450,14 @@ const LuxunReflectionScene3 = () => {
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_33_02.mp3")}
>
恭喜你读懂了鲁迅的文字与初心,愿这份精神能伴你前行!
</GuideStepsItem>
</GuideSteps>
{/* 结束语覆盖层 */}
<EndingOverlay
visible={showEndingOverlay}
text="愿你走过我的文学足迹,读懂文字的力量,心怀热望,直面生活!"
@@ -461,7 +466,19 @@ const LuxunReflectionScene3 = () => {
setShowEndingOverlay(false);
setEndingOverlayFinished(true);
}}
audioSrc={getPublicPath("audio/luxun/mp3_luxun_34.mp3")}
/>
<StudyGuideModal
visible={showGuideModal}
onContinue={() => {
setShowGuideModal(false);
}}
verticalAlign="center"
audioSrc={getPublicPath("audio/luxun/mp3_luxun_33.mp3")}
>
先生的每一步足迹、每一篇文字,都藏着不变的精神底色——拼出这份精神脉络,读懂先生的初心!
</StudyGuideModal>
</div>
);
};
+15 -4
View File
@@ -369,6 +369,7 @@ const ChenyunInternalization = () => {
<Narration
visible={showNarration}
text="从翠亨村的农家院落到影响中国的革命征程,孙中山先生以'亟拯斯民于水火,切扶大厦之将倾'的担当,在近代中国的黑暗中点燃了民主共和的火种。他留下的不仅是推翻帝制、建立共和的历史功绩,更有'振兴中华'的初心、'天下为公'的情怀、'与时俱进'的智慧。今天,我们站在故居的青砖黛瓦间,不仅要回望他为民族独立、人民幸福奋斗的一生,更要让这份精神穿越时空,成为新时代青年砥砺前行的力量。"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_55.mp3")}
onClose={() => {
setShowNarration(false);
}}
@@ -386,10 +387,16 @@ const ChenyunInternalization = () => {
}}
showMask={false}
>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_56.mp3")}
>
小沪,参观完故居,我对孙中山先生从"医人"到"医国"的转变、《实业计划》里的宏大构想有了很多感触,但总觉得这些历史和精神离我们的生活还有些远,不知道该怎么真正理解和传承。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_57.mp3")}
>
你的感受特别真实!孙中山先生的精神不是停留在书本里的文字,而是能指导我们当下行动的思想财富。今天我们特别邀请到了上海孙中山故居纪念馆研究部主任孙旻老师,让她带我们一起解读先生精神的时代意义,把这份传承落到实处。
</GuideStepsItem>
</GuideSteps>
@@ -407,7 +414,10 @@ const ChenyunInternalization = () => {
showMask={false}
zIndex={60}
>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_58.mp3")}
>
再看香山路7号这座小楼,相信你有很多心里话想对孙中山先生说。现在,就请你把这份感悟浓缩成几句话,既呼应先生的精神,也给自己未来许下一个承诺吧!
</GuideStepsItem>
</GuideSteps>
@@ -425,7 +435,8 @@ const ChenyunInternalization = () => {
<EndingOverlay
visible={showStudyGuide}
text="恭喜你已经顺利完成了黄炎培故居的学习,请继续前往下一个故居吧!"
text="恭喜你已经顺利完成了孙中山故居的学习,请继续前往下一个故居吧!"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_59.mp3")}
animationSpeed={200}
onContinue={() => {
if (audioRef.current) {
+86 -17
View File
@@ -255,6 +255,7 @@ const SunzhongshanPlace1 = () => {
setShowGuideModal(false);
setVisible(true);
}}
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_01.mp3")}
>
{`同学你好,欢迎来到“走进故居·对话”学习模块!
此刻你正置身于孙中山故居堂屋,接下来请随时光的脚步,一同走进他人生中四段激情燃烧的岁月:立志救国·推翻帝制、讨袁护法·捍卫共和、与时俱进·探索新路、抱病北上·鞠躬尽瘁。做好准备了吗?开始探索这段波澜壮阔的历史征程吧!`}
@@ -273,10 +274,16 @@ const SunzhongshanPlace1 = () => {
showMask={false}
highlightSelectors={[]}
>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_03.mp3")}
>
小沪,老师说过,孙中山先生早年学习的是医学。从治病救人到革命救国,他为什么会做出这种转变呢?
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_04.mp3")}
>
来看看这张孙中山先生青年时期的相片吧。
</GuideStepsItem>
</GuideSteps>
@@ -286,6 +293,7 @@ const SunzhongshanPlace1 = () => {
visible={showAfterDialogGuide}
onContinue={() => setShowAfterDialogGuide(false)}
ailgn="left"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_02.mp3")}
>
1.场景中出现动态光点的展品表示未浏览,可通过点击光点来了解展品背后的故事,通过滚轮可以放大展品,浏览展品后蓝色动态光点会变为已浏览状态。
{"\n"}
@@ -313,8 +321,16 @@ const SunzhongshanPlace1 = () => {
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem speaker="student">"革命四杰"?</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_05.mp3")}
>
"革命四杰"?
</GuideStepsItem>
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_06.mp3")}
>
是呀,这是孙中山先生在香港西医书院就读之时与同学的留影。孙先生1887年入学,其间广泛阅读西方自然科学和社会政治学说书籍。常与同学陈少白、尤列、杨鹤龄一起畅谈革命,抨击时政,被戏称为"四大寇"。
</GuideStepsItem>
</GuideSteps>
@@ -330,10 +346,16 @@ const SunzhongshanPlace1 = () => {
showMask={false}
zIndex={10000}
>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_07.mp3")}
>
原来孙先生早在青年时期就把国家前途命运放在了心上。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_08.mp3")}
>
不仅是关心,他还付诸了实际行动。看看这件展品。
</GuideStepsItem>
</GuideSteps>
@@ -357,12 +379,30 @@ const SunzhongshanPlace1 = () => {
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_09.mp3")}
>
这是郑观应写给盛宣怀的信。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_10.mp3")}
>
是的。1894年,孙中山先生回乡起草了"上李鸿章书",希望劝谏清政府改良救国。他多方寻找上书门径,这就是作为同乡和前辈的郑观应帮忙写给盛宣怀,希望他向李鸿章推荐孙先生的书信。
</GuideStepsItem>
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_11.mp3")}
>
那么,这次上书最后成功了吗?
</GuideStepsItem>
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_12.mp3")}
>
1894年春天,朝鲜农民起义爆发。孙中山先生的上书于这一年的6月投递,可惜当时甲午战争已经箭在弦上,李鸿章忙于备战,并未予以接见。这件事使得孙先生放弃了依靠清政府改良救国的幻想,转而投身革命,以求彻底推翻封建君主专制制度。
</GuideStepsItem>
</GuideSteps>
{/* 光标4的对话 */}
@@ -377,10 +417,16 @@ const SunzhongshanPlace1 = () => {
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_13.mp3")}
>
哇!这张《申报》好震撼,"中华民国万岁""孙大总统万岁",就连发行日期写的都是"中华民国元年元月二日"!
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_14.mp3")}
>
没错!资产阶级民主革命的尝试走过了风雨兼程的十余年,这一过程中孙中山先生一直在探寻救国道路,1905年孙先生在亲自撰写的《民报》发刊词中明确提出了三民主义,即民族、民权、民生这一资产阶级民主革命的政纲。
</GuideStepsItem>
</GuideSteps>
@@ -489,16 +535,25 @@ const SunzhongshanPlace1 = () => {
showMask={false}
zIndex={10000}
>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_15.mp3")}
>
这些尝试终于在武昌起义过后迎来了回响。1911年年末,湖南、广东等14个省和上海纷纷宣布脱离清政府独立。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
1912年1月1日上午,上海有一万多人聚集在沪宁车站,欢送孙先生前去南京就任临时大总统。当天夜里11点,孙先生在原清朝两江总督府宣誓就职,新的共和政体就此诞生。彼时仍属农历辛亥年,因此历史上将这一系列事件称为"辛亥革命"。
</GuideStepsItem>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_16.mp3")}
>
孙中山先生领导的辛亥革命推动了中国社会的变革,可是这张《民立报》让我有点不明白,孙先生刚当上临时大总统,为什么又很快宣布"解职"了呀?
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_17.mp3")}
>
中华民国的建立有个重要背景——"南北议和"。辛亥革命中宣告独立的省份大多集中在中国南方,负隅顽抗的清政府将最后希望寄托在主导北洋军阀势力的袁世凯身上。在列强支持下,袁世凯率军南下。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
@@ -518,14 +573,28 @@ const SunzhongshanPlace1 = () => {
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem speaker="student">这算不算一种"妥协"呀?</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_18.mp3")}
>
这算不算一种"妥协"呀?
</GuideStepsItem>
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_19.mp3")}
>
可以说,这是当时客观条件限制下的一种无奈妥协。因为革命党人的首要目标是推翻帝制,在武装力量不足以对抗北洋军阀的情况下,只能暂时信任袁世凯,希望他能维护共和。你看旁边的"南北议和"纪念章和纪念墨盒,就是当时这段历史的见证。
</GuideStepsItem>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_20.mp3")}
>
可后来袁世凯不是复辟当皇帝了吗?那孙中山先生是不是特别后悔呀?
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_21.mp3")}
>
的确,所以辛亥革命未能改变旧中国半殖民地半封建的社会性质和中国人民的悲惨境遇,也未能完成实现民族独立、人民解放的历史任务。孙中山先生十分痛心,但也更坚定了捍卫共和的决心。这也为他后来发起二次革命、护法运动埋下了伏笔。
</GuideStepsItem>
</GuideSteps>
+16 -4
View File
@@ -193,10 +193,16 @@ const SunzhongshanPlace2 = () => {
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_22.mp3")}
>
这张大大的路线图好复杂,上面画满了铁路线,这是孙中山先生规划的吗?
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_23.mp3")}
>
对!这是中国铁路总公司的路线图。孙中山先生辞去临时大总统后,致力于国家的实业建设。他认为"交通为实业之母,铁路又为交通之母"。要让国家强大,首先要发展实业,投身铁路规划与建设就是其中一项重要实践。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
@@ -237,10 +243,16 @@ const SunzhongshanPlace2 = () => {
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_24.mp3")}
>
真是一个宏伟的计划,可是在内忧外患的时局下,实现起来一定很艰难吧?
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_25.mp3")}
>
是的,1913年3月"宋教仁案"将国民党和袁世凯方面的矛盾推向高峰。不愿坐视袁世凯窃取辛亥革命果实后独揽大权,革命党人发动了"二次革命"。可惜革命失败,孙中山先生被迫流亡海外。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
+40 -10
View File
@@ -202,16 +202,28 @@ const SunzhongshanPlace3 = () => {
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_36.mp3")}
>
这张是李大钊先生的手迹啊,和孙中山先生有什么关系呢?
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_37.mp3")}
>
这封信的收件人是胡适。信里提到,孙中山先生经历了陈炯明叛变事件回到上海后,态度变得很冷静,不再坚持之前的护法主张,反而提出"收军权于中央""结合民主的联合战线,与反动派决裂"等。
</GuideStepsItem>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_38.mp3")}
>
孙先生与李大钊先生进行了深入的交谈。他在寻找新的合作力量吗?
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_39.mp3")}
>
这个力量就是新生的中国共产党。孙中山先生向共产党人释放了巨大的诚意。除了与李大钊的频繁接触,他还在上海会见了共产国际代表马林,明确表示愿意和苏俄合作;邀请鲍罗庭来中国担任国民党组织教练员;
</GuideStepsItem>
<GuideStepsItem speaker="guide">
@@ -231,10 +243,16 @@ const SunzhongshanPlace3 = () => {
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_40.mp3")}
>
我记得国共第一次合作正式形成的标志就是国民党一大,这就是当时的大会宣言吗?
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_41.mp3")}
>
完全正确!1924年1月20日,中国国民党第一次全国代表大会在广东高师礼堂开幕,孙中山先生以国民党总理的身份担任大会主席,主持开幕式。此次大会共有165名代表出席,其中共产党员代表超过20人,包括李大钊、毛泽东等中共早期领导人。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
@@ -336,10 +354,16 @@ const SunzhongshanPlace3 = () => {
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_34.mp3")}
>
这时候,工农群众的力量还没有正式登上历史舞台呢。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_35.mp3")}
>
旁边这本《星期评论》创刊于五四运动后。孙中山先生在五四运动中看到了群众的力量和工人、学生们的热情。更觉顺应时代潮流,传播新文化、介绍马克思主义、鼓励工人运动的重要性。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
@@ -359,10 +383,16 @@ const SunzhongshanPlace3 = () => {
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_32.mp3")}
>
这本书看起来很特别,《社会主义的精义》是孙中山先生写的吗?他也关注社会主义学说呀?
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_33.mp3")}
>
不仅关注,而且还很早。1912年10月14日-16日,孙中山先生连续三日在上海三马路(今汉口路)中华大戏院发表关于社会主义学说的演讲,吸引了众多听众。这就是演说后经由他本人修订出版的小册子。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
+20 -4
View File
@@ -178,6 +178,8 @@ const SunzhongshanPlace4 = () => {
return next;
});
}}
animationSpeed={200}
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_46.mp3")}
/>
{/* 关闭学习引导后显示的对话(图1) */}
@@ -192,10 +194,16 @@ const SunzhongshanPlace4 = () => {
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem speaker="student">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_42.mp3")}
>
革命终于有了可靠的盟友,统一战线建立;北伐战争在有条不紊地筹备中,国家统一在望,孙中山先生一定十分欣慰吧?
</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_43.mp3")}
>
是的。然而长期操劳国事使得孙中山先生身体抱恙。在生命最后的时间里,他仍在为国奔走,始终未曾停歇。看看这张拍摄于1924年11月17日的照片吧。
</GuideStepsItem>
</GuideSteps>
@@ -211,8 +219,16 @@ const SunzhongshanPlace4 = () => {
showMask={false}
zIndex={10000}
>
<GuideStepsItem speaker="student">这是……外滩?</GuideStepsItem>
<GuideStepsItem speaker="guide">
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_44.mp3")}
>
这是……外滩?
</GuideStepsItem>
<GuideStepsItem
speaker="guide"
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_45.mp3")}
>
没错。1924年10月,冯玉祥将军发动“北京政变”,推翻了曹锟政府,邀请孙中山先生北上共商国是,希望能实现国家和平统一。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
+6 -2
View File
@@ -1,7 +1,7 @@
import { useState, useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { Image, StudyGuideModal } from "@/components";
import { getBackgroundImage, getStaticImage } from "@/lib/utils";
import { getBackgroundImage, getPublicPath, getStaticImage } from "@/lib/utils";
const starName = "sunzhongshan";
const GUIDE_STORAGE_KEY = `${starName}-reflection-guide-shown`;
@@ -192,7 +192,11 @@ const SunzhongshanReflection = () => {
</div>
{/* 学习引导弹窗 */}
<StudyGuideModal visible={showGuide} onContinue={handleGuideClose}>
<StudyGuideModal
visible={showGuide}
onContinue={handleGuideClose}
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_47.mp3")}
>
同学你好,欢迎来到“跨越时空·体悟”学习模块!{"\n"}
请登陆“时空回溯・亲历时代”体验仓,下一站:二十世纪初,上海。{"\n"}
辛亥革命之后,民主共和的观念深入人心。面对中外反动势力,社会各界更广泛地行动起来,以各自的方式争取着民主自由和国家尊严。
+11 -3
View File
@@ -105,7 +105,7 @@ const SunzhongshanReflectionScene1 = () => {
const handleClueClick = (clueId: string, clueName: string) => {
// 如果已收集,不重复收集
if (isClueCollected(clueId)) {
setSuccessAlertContent("该线索已收集");
setSuccessAlertContent("该线索已收集!");
setShowSuccessAlert(true);
return;
}
@@ -125,7 +125,7 @@ const SunzhongshanReflectionScene1 = () => {
duration: 1200,
onComplete: () => {
// 动画完成后的回调
setSuccessAlertContent("获得该线索!快去线索簿查看详细信息吧!");
setSuccessAlertContent("获得1条线索,已获得线索可在线索簿中查看!");
setShowSuccessAlert(true);
},
});
@@ -139,7 +139,7 @@ const SunzhongshanReflectionScene1 = () => {
// 如果线索1已收集,显示提示
if (isClueCollected(clue1Id)) {
setSuccessAlertContent("该线索已收集");
setSuccessAlertContent("该线索已收集!");
setShowSuccessAlert(true);
return;
}
@@ -401,6 +401,11 @@ const SunzhongshanReflectionScene1 = () => {
visible={showSuccessAlert}
title="恭喜你!"
content={successAlertContent}
audioSrc={
successAlertContent === "获得1条线索,已获得线索可在线索簿中查看!"
? getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_49.mp3")
: undefined
}
onContinue={() => {
setShowSuccessAlert(false);
setSuccessAlertContent("");
@@ -438,6 +443,7 @@ const SunzhongshanReflectionScene1 = () => {
visible={showGuide}
onContinue={handleGuideClose}
zIndex={10000}
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_48.mp3")}
>
同学你好,欢迎来到二十世纪初的上海。
<br />
@@ -506,7 +512,9 @@ const SunzhongshanReflectionScene1 = () => {
<EndingOverlay
visible={showCompleteGuide}
text={`恭喜你完成场景一的探索!\n粤军总司令陈炯明与孙中山政见无法统一。1922年6月16日,陈炯明部将叶举率4000余人包围总统府,炮击孙中山住所粤秀楼。这一叛变行动使得北伐事业再次陷于停顿,第二次护法运动失败……\n请你走进场景二,继续探索吧!`}
animationSpeed={200}
onContinue={handleCompleteGuideClose}
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_50.mp3")}
/>
</div>
);
+11 -4
View File
@@ -115,7 +115,7 @@ const SunzhongshanReflectionScene2 = () => {
const handleClueClick = (clueId: string, clueName: string) => {
// 如果已收集,不重复收集
if (isClueCollected(clueId)) {
setSuccessAlertContent("该线索已收集");
setSuccessAlertContent("该线索已收集!");
setShowSuccessAlert(true);
return;
}
@@ -135,7 +135,7 @@ const SunzhongshanReflectionScene2 = () => {
duration: 1200,
onComplete: () => {
// 动画完成后的回调
setSuccessAlertContent("获得该线索!快去线索簿查看详细信息吧!");
setSuccessAlertContent("获得1条线索,已获得线索可在线索簿中查看!");
setShowSuccessAlert(true);
},
});
@@ -149,7 +149,7 @@ const SunzhongshanReflectionScene2 = () => {
// 如果线索1已收集,显示提示
if (isClueCollected(clue1Id)) {
setSuccessAlertContent("该线索已收集");
setSuccessAlertContent("该线索已收集!");
setShowSuccessAlert(true);
return;
}
@@ -169,7 +169,7 @@ const SunzhongshanReflectionScene2 = () => {
duration: 1200,
onComplete: () => {
// 动画完成后显示成功提示
setSuccessAlertContent("获得该线索!快去线索簿查看详细信息吧!");
setSuccessAlertContent("获得1条线索,已获得线索可在线索簿中查看!");
setShowSuccessAlert(true);
},
});
@@ -426,6 +426,11 @@ const SunzhongshanReflectionScene2 = () => {
visible={showSuccessAlert}
title="恭喜你!"
content={successAlertContent}
audioSrc={
successAlertContent === "获得1条线索,已获得线索可在线索簿中查看!"
? getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_49.mp3")
: undefined
}
onContinue={() => {
setShowSuccessAlert(false);
setSuccessAlertContent("");
@@ -463,6 +468,7 @@ const SunzhongshanReflectionScene2 = () => {
visible={showGuide}
onContinue={handleGuideClose}
zIndex={10000}
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_51.mp3")}
>
1921年7月,中国共产党第一次全国代表大会在上海召开,中国革命的面貌自此焕然一新。
<br />
@@ -533,6 +539,7 @@ const SunzhongshanReflectionScene2 = () => {
text={`恭喜你完成场景二的探索!\n1922年夏到1924年初,李大钊来到上海,在宣传革命思想的同时与孙中山进行了数次会面,为改组国民党和促成第一次国共合作作出了巨大贡献。\n1924年1月,中国国民党第一次全国代表大会在广州召开,孙中山重新解释了三民主义,事实上确立了联俄、联共、扶助农工三大政策。李大钊、毛泽东等10名共产党员当选为中国国民党第一届中央执行委员会委员或候补委员。国民党一大的召开标志着第一次国共合作的正式形成。\n请你走进场景三,继续探索吧!`}
animationSpeed={200}
onContinue={handleCompleteGuideClose}
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_52.mp3")}
/>
</div>
);
+11 -4
View File
@@ -112,7 +112,7 @@ const SunzhongshanReflectionScene3 = () => {
const handleClueClick = (clueId: string, clueName: string) => {
// 如果已收集,不重复收集
if (isClueCollected(clueId)) {
setSuccessAlertContent("该线索已收集");
setSuccessAlertContent("该线索已收集!");
setShowSuccessAlert(true);
return;
}
@@ -132,7 +132,7 @@ const SunzhongshanReflectionScene3 = () => {
duration: 1200,
onComplete: () => {
// 动画完成后的回调
setSuccessAlertContent("获得该线索!快去线索簿查看详细信息吧!");
setSuccessAlertContent("获得1条线索,已获得线索可在线索簿中查看!");
setShowSuccessAlert(true);
// 如果是在"窃窃私语"场景收集了线索1,显示 Cursor
@@ -162,7 +162,7 @@ const SunzhongshanReflectionScene3 = () => {
// 如果线索4已收集,显示提示
if (isClueCollected(clue4Id)) {
setSuccessAlertContent("该线索已收集");
setSuccessAlertContent("该线索已收集!");
setShowSuccessAlert(true);
return;
}
@@ -182,7 +182,7 @@ const SunzhongshanReflectionScene3 = () => {
duration: 1200,
onComplete: () => {
// 动画完成后显示成功提示
setSuccessAlertContent("获得该线索!快去线索簿查看详细信息吧!");
setSuccessAlertContent("获得1条线索,已获得线索可在线索簿中查看!");
setShowSuccessAlert(true);
},
});
@@ -472,6 +472,11 @@ const SunzhongshanReflectionScene3 = () => {
visible={showSuccessAlert}
title="恭喜你!"
content={successAlertContent}
audioSrc={
successAlertContent === "获得1条线索,已获得线索可在线索簿中查看!"
? getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_49.mp3")
: undefined
}
onContinue={() => {
setShowSuccessAlert(false);
setSuccessAlertContent("");
@@ -517,6 +522,7 @@ const SunzhongshanReflectionScene3 = () => {
<StudyGuideModal
visible={showGuide}
onContinue={handleGuideClose}
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_53.mp3")}
zIndex={10000}
>
后来,学校建立了中国社会主义青年团团支部,你积极靠拢,成为一名光荣的团员。
@@ -586,6 +592,7 @@ const SunzhongshanReflectionScene3 = () => {
<EndingOverlay
visible={showCompleteGuide}
text={`辛亥革命拉开了中国完全意义上的近代民族民主革命的序幕。北洋军阀首领袁世凯窃取革命成果后,各地军阀势力对内割据混战,对外出卖国家利益,在政治上谋求专制和独裁,俨然成为民族独立、民主政治、民生幸福之路上的头号绊脚石。\n为了改变这一时期的困局,孙中山尝试过高举护法旗帜,两度领导护法运动;与新生的中国共产党展开合作,与无产阶级工农群众共建起统一战线;为了团结一切可以团结的力量,调动一切可以调动的因素,在生命最后的日子里抱病北上,一路不遗余力地向社会各界宣传反帝反封建主张。他经历了太多挫折,却始终以'行'求'知'。`}
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_54.mp3")}
animationSpeed={200}
onContinue={handleCompleteGuideClose}
/>