feat: 添加新页面和更新导航逻辑,增强用户体验

- 在 Maozedong 组件中添加新的图像按钮,更新导航至不同的地点页面。
- 在路由配置中引入新的地点页面组件,支持用户访问不同的展览位置。
- 确保导航逻辑与新页面一致,提升用户交互体验。
This commit is contained in:
zhongyidian committed 2026-02-26 11:12:06 +08:00
1 parent 7bbeb10e57
commit 2c8bc73e57
24 files changed
+699 -3

No files matched your search

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1011 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1024 KiB

+11 -3
View File
@@ -28,13 +28,21 @@ const Maozedong = () => {
/> />
<div className="absolute right-20 top-75 flex flex-col space-y-10"> <div className="absolute right-20 top-75 flex flex-col space-y-10">
<Image button src={getStaticImage("star/一-解锁")} /> <Image
button
src={getStaticImage("star/一-解锁")}
onClick={() => navigate(`/${moduleName}/${starName}/place1`)}
/>
<Image <Image
button button
src={getStaticImage("star/二-解锁")} src={getStaticImage("star/二-解锁")}
onClick={() => navigate(`/${moduleName}/${starName}/demo`)} onClick={() => navigate(`/${moduleName}/${starName}/place2`)}
/>
<Image
button
src={getStaticImage("star/三-解锁")}
onClick={() => navigate(`/${moduleName}/${starName}/place3`)}
/> />
<Image button src={getStaticImage("star/三-解锁")} />
</div> </div>
</div> </div>
); );
+156
View File
@@ -0,0 +1,156 @@
import {
ExhibitOverlay,
GuideSteps,
GuideStepsItem,
Image,
Title,
} from "@/components";
import { useNavigate, useParams } from "react-router-dom";
import { getBackgroundImage, getStaticImage } from "@/lib/utils";
import { useState } from "react";
import Cursor from "@/components/cursor";
import { useCursorController } from "@/hooks/useCursorController";
// starName 固定为 maozedong
const starName = "maozedong";
// 展品 ID 与资源路径映射,ID 用来做唯一标识
const exhibitSrcMap: Record<1 | 2, string> = {
1: "maozedong/place1/场景1展品1",
2: "maozedong/place1/场景1展品2",
};
const MaozedongPlace1 = () => {
const navigate = useNavigate();
const { moduleName } = useParams<{
moduleName: string;
}>();
// 引导对话显示状态
const [visible, setVisible] = useState(false);
// 当前激活的展品 ID
const [activeExhibitId, setActiveExhibitId] = useState<1 | 2 | null>(null);
// 已访问的展品(本页内状态;如需持久化可接入 store)
const [visited, setVisited] = useState<Set<1 | 2>>(() => new Set());
// Cursor 顺序控制:1 -> 2
const { isEnabled } = useCursorController({
initialEnabled: [1],
dependencies: {
2: [1],
},
visited,
});
const handleOpenExhibit = (id: 1 | 2) => {
setActiveExhibitId(id);
};
const progressCount = visited.size;
const total = 2;
const completed = progressCount >= total;
const subtitle = completed
? "关于中共一大(已完成)"
: `关于中共一大(${progressCount}/${total})`;
if (!moduleName) return null;
return (
<div
className="w-full h-full bg-cover bg-center bg-no-repeat"
style={{
backgroundImage: getBackgroundImage(`${starName}/place1/场景1`),
}}
>
{/* 标题 */}
<Title
title="探索方向·确立信仰"
subtitle={subtitle}
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}/home`);
}
}}
/>
{/* 光标1 */}
<Cursor
className="absolute left-106 bottom-150"
onClick={() => handleOpenExhibit(1)}
read={visited.has(1)}
disabled={!isEnabled(1)}
/>
{/* 光标2 */}
<Cursor
className="absolute left-170 bottom-135"
onClick={() => handleOpenExhibit(2)}
read={visited.has(2)}
disabled={!isEnabled(2)}
/>
{/* 继续前进 - 仅在完成所有展品后显示 */}
{completed && (
<Image
className="absolute bottom-0 right-140"
src={getStaticImage(`${starName}/place1/场景1继续前进`)}
button
onClick={() => {
navigate(`/${moduleName}/${starName}/place2`);
}}
/>
)}
{/* 场景对话 */}
<GuideSteps
visible={visible}
onVisibleChange={(nextVisible) => {
setVisible(nextVisible);
}}
showMask={false}
>
<GuideStepsItem speaker="student">
小沪,毛泽东是如何确立马克思主义信仰的?
</GuideStepsItem>
<GuideStepsItem speaker="guide">
1918年,毛泽东第一次来到北京,在北大图书馆担任助理员期间,他接触到了马克思主义。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
1919年五四运动后,毛泽东回到湖南,创办《湘江评论》,开始宣传新思想。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
1920年,毛泽东第二次来到北京,阅读了《共产党宣言》等马克思主义著作,思想上发生了根本转变。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
1920年11月,毛泽东在长沙建立了共产主义小组,标志着他在思想上和组织上都确立了马克思主义信仰。
</GuideStepsItem>
</GuideSteps>
{/* 展品遮罩层 */}
<ExhibitOverlay
visible={activeExhibitId !== null}
src={activeExhibitId ? exhibitSrcMap[activeExhibitId] : ""}
onClose={() => {
if (activeExhibitId !== null) {
setVisited((prev) => {
const next = new Set(prev);
next.add(activeExhibitId);
return next;
});
}
setActiveExhibitId(null);
}}
/>
</div>
);
};
export default MaozedongPlace1;
+156
View File
@@ -0,0 +1,156 @@
import {
ExhibitOverlay,
GuideSteps,
GuideStepsItem,
Image,
Title,
} from "@/components";
import { useNavigate, useParams } from "react-router-dom";
import { getBackgroundImage, getStaticImage } from "@/lib/utils";
import { useState } from "react";
import Cursor from "@/components/cursor";
import { useCursorController } from "@/hooks/useCursorController";
// starName 固定为 maozedong
const starName = "maozedong";
// 展品 ID 与资源路径映射,ID 用来做唯一标识
const exhibitSrcMap: Record<1 | 2, string> = {
1: "maozedong/place2/场景2展品1",
2: "maozedong/place2/场景2展品2",
};
const MaozedongPlace2 = () => {
const navigate = useNavigate();
const { moduleName } = useParams<{
moduleName: string;
}>();
// 引导对话显示状态
const [visible, setVisible] = useState(false);
// 当前激活的展品 ID
const [activeExhibitId, setActiveExhibitId] = useState<1 | 2 | null>(null);
// 已访问的展品(本页内状态;如需持久化可接入 store)
const [visited, setVisited] = useState<Set<1 | 2>>(() => new Set());
// Cursor 顺序控制:1 -> 2
const { isEnabled } = useCursorController({
initialEnabled: [1],
dependencies: {
2: [1],
},
visited,
});
const handleOpenExhibit = (id: 1 | 2) => {
setActiveExhibitId(id);
};
const progressCount = visited.size;
const total = 2;
const completed = progressCount >= total;
const subtitle = completed
? "关于中共二大(已完成)"
: `关于中共二大(${progressCount}/${total})`;
if (!moduleName) return null;
return (
<div
className="w-full h-full bg-cover bg-center bg-no-repeat"
style={{
backgroundImage: getBackgroundImage(`${starName}/place2/场景2`),
}}
>
{/* 标题 */}
<Title
title="播撒红色火种"
subtitle={subtitle}
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}/home`);
}
}}
/>
{/* 光标1 */}
<Cursor
className="absolute left-217 bottom-145"
onClick={() => handleOpenExhibit(1)}
read={visited.has(1)}
disabled={!isEnabled(1)}
/>
{/* 光标2 */}
<Cursor
className="absolute right-92 bottom-105"
onClick={() => handleOpenExhibit(2)}
read={visited.has(2)}
disabled={!isEnabled(2)}
/>
{/* 继续前进 - 仅在完成所有展品后显示 */}
{completed && (
<Image
className="absolute bottom-0 right-0"
src={getStaticImage("components/place/继续前进")}
button
onClick={() => {
navigate(`/${moduleName}/${starName}/place3`);
}}
/>
)}
{/* 场景对话 */}
<GuideSteps
visible={visible}
onVisibleChange={(nextVisible) => {
setVisible(nextVisible);
}}
showMask={false}
>
<GuideStepsItem speaker="student">
小沪,毛泽东是如何传播马克思主义的?
</GuideStepsItem>
<GuideStepsItem speaker="guide">
1920年,毛泽东在长沙创办文化书社,销售进步书刊,传播新思想。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
他还在湖南第一师范学校任教,向学生宣传马克思主义理论。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
1921年7月,毛泽东作为湖南代表参加了中国共产党第一次全国代表大会,成为中国共产党的创始人之一。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
回到湖南后,毛泽东建立了中共湖南支部,并担任书记,开始在湖南各地发展党员,建立党组织。
</GuideStepsItem>
</GuideSteps>
{/* 展品遮罩层 */}
<ExhibitOverlay
visible={activeExhibitId !== null}
src={activeExhibitId ? exhibitSrcMap[activeExhibitId] : ""}
onClose={() => {
if (activeExhibitId !== null) {
setVisited((prev) => {
const next = new Set(prev);
next.add(activeExhibitId);
return next;
});
}
setActiveExhibitId(null);
}}
/>
</div>
);
};
export default MaozedongPlace2;
+167
View File
@@ -0,0 +1,167 @@
import {
ExhibitOverlay,
GuideSteps,
GuideStepsItem,
Image,
Title,
} from "@/components";
import { useNavigate, useParams } from "react-router-dom";
import { getBackgroundImage, getStaticImage } from "@/lib/utils";
import { useState } from "react";
import Cursor from "@/components/cursor";
import { useCursorController } from "@/hooks/useCursorController";
// starName 固定为 maozedong
const starName = "maozedong";
// 展品 ID 与资源路径映射,ID 用来做唯一标识
const exhibitSrcMap: Record<1 | 2 | 3, string> = {
1: "maozedong/place3/场景3展品1",
2: "maozedong/place3/场景3展品2",
3: "maozedong/place3/场景3展品3",
};
const MaozedongPlace3 = () => {
const navigate = useNavigate();
const { moduleName } = useParams<{
moduleName: string;
}>();
// 引导对话显示状态
const [visible, setVisible] = useState(false);
// 当前激活的展品 ID
const [activeExhibitId, setActiveExhibitId] = useState<
1 | 2 | 3 | null
>(null);
// 已访问的展品(本页内状态;如需持久化可接入 store)
const [visited, setVisited] = useState<Set<1 | 2 | 3>>(() => new Set());
// Cursor 顺序控制:1 -> 2 -> 3
const { isEnabled } = useCursorController({
initialEnabled: [1],
dependencies: {
2: [1],
3: [1, 2],
},
visited,
});
const handleOpenExhibit = (id: 1 | 2 | 3) => {
setActiveExhibitId(id);
};
const progressCount = visited.size;
const total = 3;
const completed = progressCount >= total;
const subtitle = completed
? "关于中共三大(已完成)"
: `关于中共三大(${progressCount}/${total})`;
if (!moduleName) return null;
return (
<div
className="w-full h-full bg-cover bg-center bg-no-repeat"
style={{
backgroundImage: getBackgroundImage(`${starName}/place3/场景3`),
}}
>
{/* 标题 */}
<Title
title="吹响合作号角"
subtitle={subtitle}
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}/home`);
}
}}
/>
{/* 光标1 */}
<Cursor
className="absolute left-162 bottom-140"
onClick={() => handleOpenExhibit(1)}
read={visited.has(1)}
disabled={!isEnabled(1)}
/>
{/* 光标2 */}
<Cursor
className="absolute left-200 bottom-140"
onClick={() => handleOpenExhibit(2)}
read={visited.has(2)}
disabled={!isEnabled(2)}
/>
{/* 光标3 */}
<Cursor
className="absolute right-155 bottom-90"
onClick={() => handleOpenExhibit(3)}
read={visited.has(3)}
disabled={!isEnabled(3)}
/>
{/* 继续前进 - 仅在完成所有展品后显示 */}
{completed && (
<Image
className="absolute bottom-0 right-0"
src={getStaticImage("components/place/继续前进")}
button
onClick={() => {
navigate(`/${moduleName}/${starName}/place4`);
}}
/>
)}
{/* 场景对话 */}
<GuideSteps
visible={visible}
onVisibleChange={(nextVisible) => {
setVisible(nextVisible);
}}
showMask={false}
>
<GuideStepsItem speaker="student">
小沪,毛泽东是如何推动国共合作的?
</GuideStepsItem>
<GuideStepsItem speaker="guide">
1923年6月,中共三大在广州召开,毛泽东参加了会议,会议决定与国民党合作,建立革命统一战线。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
1924年1月,国民党一大召开,标志着第一次国共合作正式形成。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
毛泽东在国民党内担任重要职务,积极推动两党合作,共同开展反帝反封建的革命斗争。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
在合作期间,毛泽东深入农村,开展农民运动,为革命积累了重要力量。
</GuideStepsItem>
</GuideSteps>
{/* 展品遮罩层 */}
<ExhibitOverlay
visible={activeExhibitId !== null}
src={activeExhibitId ? exhibitSrcMap[activeExhibitId] : ""}
onClose={() => {
if (activeExhibitId !== null) {
setVisited((prev) => {
const next = new Set(prev);
next.add(activeExhibitId);
return next;
});
}
setActiveExhibitId(null);
}}
/>
</div>
);
};
export default MaozedongPlace3;
+188
View File
@@ -0,0 +1,188 @@
import {
ExhibitOverlay,
GuideSteps,
GuideStepsItem,
Image,
Title,
} from "@/components";
import { useNavigate, useParams } from "react-router-dom";
import { getBackgroundImage, getStaticImage } from "@/lib/utils";
import { useState } from "react";
import Cursor from "@/components/cursor";
import { useCursorController } from "@/hooks/useCursorController";
// starName 固定为 maozedong
const starName = "maozedong";
// 展品 ID 与资源路径映射,ID 用来做唯一标识
const exhibitSrcMap: Record<1 | 2 | 3 | 4 | 5 | 6, string> = {
1: "maozedong/place4/场景4展品1",
2: "maozedong/place4/场景4展品2",
3: "maozedong/place4/场景4展品3",
4: "maozedong/place4/场景4展品4",
5: "maozedong/place4/场景4展品5",
6: "maozedong/place4/场景4展品6",
};
const MaozedongPlace4 = () => {
const navigate = useNavigate();
const { moduleName } = useParams<{
moduleName: string;
}>();
// 引导对话显示状态
const [visible, setVisible] = useState(false);
// 当前激活的展品 ID
const [activeExhibitId, setActiveExhibitId] = useState<
1 | 2 | 3 | 4 | 5 | 6 | null
>(null);
// 已访问的展品(本页内状态;如需持久化可接入 store)
const [visited, setVisited] = useState<Set<1 | 2 | 3 | 4 | 5 | 6>>(
() => new Set()
);
// Cursor 顺序控制:1 -> 2 -> 3 -> 4 -> 5 -> 6
const { isEnabled } = useCursorController({
initialEnabled: [1],
dependencies: {
2: [1],
3: [1, 2],
4: [1, 2, 3],
5: [1, 2, 3, 4],
6: [1, 2, 3, 4, 5],
},
visited,
});
const handleOpenExhibit = (id: 1 | 2 | 3 | 4 | 5 | 6) => {
setActiveExhibitId(id);
};
if (!moduleName) return null;
const progressCount = visited.size;
const total = 6;
const completed = progressCount >= total;
const subtitle = completed
? "关于中共四大(已完成)"
: `关于中共四大(${progressCount}/${total})`;
return (
<div
className="w-full h-full bg-cover bg-center bg-no-repeat"
style={{
backgroundImage: getBackgroundImage(`${starName}/place4/场景4`),
}}
>
{/* 标题 */}
<Title
title="唤起工农群众"
subtitle={subtitle}
className="absolute top-10 left-1/2 -translate-x-1/2"
/>
{completed && (<Image
className="absolute right-0 bottom-0"
src={getStaticImage("components/place/离开场景")}
button
onClick={() => {
if (moduleName && starName) {
navigate(`/${moduleName}/${starName}/home`);
}
}}
/>)}
{/* 光标1 */}
<Cursor
className="absolute left-105 bottom-158"
onClick={() => handleOpenExhibit(1)}
read={visited.has(1)}
disabled={!isEnabled(1)}
/>
{/* 光标2 */}
<Cursor
className="absolute left-179 bottom-163"
onClick={() => handleOpenExhibit(2)}
read={visited.has(2)}
disabled={!isEnabled(2)}
/>
{/* 光标3 */}
<Cursor
className="absolute left-215 bottom-163"
onClick={() => handleOpenExhibit(3)}
read={visited.has(3)}
disabled={!isEnabled(3)}
/>
{/* 光标4 */}
<Cursor
className="absolute left-212 bottom-123"
onClick={() => handleOpenExhibit(4)}
read={visited.has(4)}
disabled={!isEnabled(4)}
/>
{/* 光标5 */}
<Cursor
className="absolute right-186 bottom-162"
onClick={() => handleOpenExhibit(5)}
read={visited.has(5)}
disabled={!isEnabled(5)}
/>
{/* 光标6 */}
<Cursor
className="absolute right-153 bottom-123"
onClick={() => handleOpenExhibit(6)}
read={visited.has(6)}
disabled={!isEnabled(6)}
/>
{/* 场景对话 */}
<GuideSteps
visible={visible}
onVisibleChange={(nextVisible) => {
setVisible(nextVisible);
}}
showMask={false}
>
<GuideStepsItem speaker="student">
小沪,毛泽东是如何组织和发动工农群众的?
</GuideStepsItem>
<GuideStepsItem speaker="guide">
1925年,毛泽东回到湖南,深入农村调查研究,写出了《湖南农民运动考察报告》。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
他认识到农民是中国革命的主力军,只有发动农民,革命才能成功。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
毛泽东在各地建立农民协会,组织农民开展减租减息、反对封建压迫的斗争。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
同时,他也重视工人运动,在安源、长沙等地组织工人罢工,提高工人的政治觉悟。
</GuideStepsItem>
<GuideStepsItem speaker="guide">
通过这些工作,毛泽东为后来的革命斗争奠定了坚实的群众基础。
</GuideStepsItem>
</GuideSteps>
{/* 展品遮罩层 */}
<ExhibitOverlay
visible={activeExhibitId !== null}
src={activeExhibitId ? exhibitSrcMap[activeExhibitId] : ""}
onClose={() => {
if (activeExhibitId !== null) {
setVisited((prev) => {
const next = new Set(prev);
next.add(activeExhibitId);
return next;
});
}
setActiveExhibitId(null);
}}
/>
</div>
);
};
export default MaozedongPlace4;
+21
View File
@@ -5,6 +5,10 @@ import type { VideoRouteHandle } from "../types";
const DemoPage = lazy(() => import("@/pages/maozedong/demo")); const DemoPage = lazy(() => import("@/pages/maozedong/demo"));
const HomePage = lazy(() => import("@/pages/maozedong/home")); const HomePage = lazy(() => import("@/pages/maozedong/home"));
const MaozedongPlace1 = lazy(() => import("@/pages/maozedong/place/place1"));
const MaozedongPlace2 = lazy(() => import("@/pages/maozedong/place/place2"));
const MaozedongPlace3 = lazy(() => import("@/pages/maozedong/place/place3"));
const MaozedongPlace4 = lazy(() => import("@/pages/maozedong/place/place4"));
//毛泽东 router //毛泽东 router
// 路径格式:/:moduleName/:starName/:page // 路径格式:/:moduleName/:starName/:page
@@ -33,6 +37,23 @@ export const MaozedongRoutes: RouteObject[] = [
element: <DemoPage />, element: <DemoPage />,
// 没有 handle.videoSrc,直接渲染内容 // 没有 handle.videoSrc,直接渲染内容
}, },
// 对话模块
{
path: "place1",
element: <MaozedongPlace1 />,
},
{
path: "place2",
element: <MaozedongPlace2 />,
},
{
path: "place3",
element: <MaozedongPlace3 />,
},
{
path: "place4",
element: <MaozedongPlace4 />,
},
], ],
}, },
]; ];