feat: remove LuxunReflection component and update routing for new reflection scenes
- Deleted the LuxunReflection component. - Updated routing to include new reflection scenes: reflection2 and reflection3.
No files matched your search
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 488 KiB |
|
After Width: | Height: | Size: 491 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 499 KiB |
|
After Width: | Height: | Size: 498 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 135 KiB |
|
After Width: | Height: | Size: 194 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
@@ -1,38 +0,0 @@
|
|||||||
import { useNavigate, useParams } from "react-router-dom";
|
|
||||||
import { Image } from "@/components";
|
|
||||||
import { getBackgroundImage, getStaticImage } from "@/lib/utils";
|
|
||||||
|
|
||||||
const starName = "luxun";
|
|
||||||
|
|
||||||
const LuxunReflection = () => {
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const { moduleName } = useParams<{
|
|
||||||
moduleName: string;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="w-full h-full bg-cover bg-center bg-no-repeat"
|
|
||||||
style={{
|
|
||||||
backgroundImage: getBackgroundImage(`${starName}/reflection/底图`),
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
className="absolute right-5 top-5"
|
|
||||||
src={getStaticImage("components/离开")}
|
|
||||||
button
|
|
||||||
onClick={() => {
|
|
||||||
if (moduleName && starName) {
|
|
||||||
navigate(`/${moduleName}/${starName}/home`);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<div className="flex items-center justify-center h-full">
|
|
||||||
<div className="text-white text-4xl">鲁迅故居-体悟模块(待完善)</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LuxunReflection;
|
|
||||||
|
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
|
import { Image } from "@/components";
|
||||||
|
import { getBackgroundImage, getStaticImage } from "@/lib/utils";
|
||||||
|
|
||||||
|
const starName = "luxun";
|
||||||
|
|
||||||
|
const cityMap: Record<1 | 2 | 3 | 4 | 5, string> = {
|
||||||
|
1: "luxun/reflection/scene1/绍兴",
|
||||||
|
2: "luxun/reflection/scene1/北京",
|
||||||
|
3: "luxun/reflection/scene1/厦门",
|
||||||
|
4: "luxun/reflection/scene1/广州",
|
||||||
|
5: "luxun/reflection/scene1/上海",
|
||||||
|
};
|
||||||
|
|
||||||
|
const LuxunReflectionScene1 = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { moduleName } = useParams<{
|
||||||
|
moduleName: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="flex justify-between w-full h-full bg-cover bg-center bg-no-repeat gap-8"
|
||||||
|
style={{
|
||||||
|
backgroundImage: getBackgroundImage(
|
||||||
|
`${starName}/reflection/scene1/底图`
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
className="absolute right-5 top-5"
|
||||||
|
src={getStaticImage(`${starName}/reflection/scene1/离开`)}
|
||||||
|
button
|
||||||
|
onClick={() => {
|
||||||
|
if (moduleName && starName) {
|
||||||
|
navigate(`/${moduleName}/${starName}/home`);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 左侧 */}
|
||||||
|
<div className="w-[45%] relative">
|
||||||
|
<div className="absolute">
|
||||||
|
<img
|
||||||
|
className="absolute inset-0"
|
||||||
|
src={getStaticImage(`${starName}/reflection/scene1/北京`)}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
{/* 两个拖拽接收区域 */}
|
||||||
|
<div className=""></div>
|
||||||
|
<div className="border border-red-500"></div>
|
||||||
|
</div>
|
||||||
|
<div className="relative">
|
||||||
|
<img
|
||||||
|
className="absolute inset-0"
|
||||||
|
src={getStaticImage(`${starName}/reflection/scene1/上海`)}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<img
|
||||||
|
src={getStaticImage(`${starName}/reflection/scene1/绍兴`)}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<img
|
||||||
|
src={getStaticImage(`${starName}/reflection/scene1/厦门`)}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<img
|
||||||
|
src={getStaticImage(`${starName}/reflection/scene1/广州`)}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* 右侧 */}
|
||||||
|
<div className="flex-1">
|
||||||
|
{/* 任务进度 */}
|
||||||
|
<div>
|
||||||
|
<span className="text-white text-2xl font-bold [text-shadow:_6px_6px_4px_#b37921,_6px_6px_4px_#b37921,_6px_6px_4px_#b37921,_6px_6px_4px_#b37921]">
|
||||||
|
任务进度:(0/8)
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{/* 鲁迅画廊-拖拽区域 */}
|
||||||
|
<div></div>
|
||||||
|
{/* 任务展示窗口 */}
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<div>任务展示窗口</div>
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LuxunReflectionScene1;
|
||||||
@@ -0,0 +1,265 @@
|
|||||||
|
import { useState, useCallback, useMemo, useEffect, useRef } from "react";
|
||||||
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
|
import { createPortal } from "react-dom";
|
||||||
|
import { AnimatePresence, motion } from "framer-motion";
|
||||||
|
import { Title, Image, Cursor, DragFill } from "@/components";
|
||||||
|
import type { DragFillResult } from "@/components";
|
||||||
|
import { cn, getBackgroundImage, getStaticImage } from "@/lib/utils";
|
||||||
|
import { useScale } from "@/contexts/ScaleContext";
|
||||||
|
|
||||||
|
const starName = "luxun";
|
||||||
|
|
||||||
|
// 文学作品拖拽选项
|
||||||
|
const literatureOptions = [
|
||||||
|
{ value: "朝花夕拾", label: "朝花夕拾", image: "luxun/reflection/scene2/拖拽-朝花夕拾" },
|
||||||
|
{ value: "阿Q正传", label: "阿Q正传", image: "luxun/reflection/scene2/拖拽-阿Q正传" },
|
||||||
|
{ value: "故事新编", label: "故事新编", image: "luxun/reflection/scene2/拖拽-故事新编" },
|
||||||
|
{ value: "百草园到三味书屋", label: "百草园到三味书屋", image: "luxun/reflection/scene2/拖拽-百草园到三味书屋" },
|
||||||
|
{ value: "精神胜利法", label: "精神胜利法", image: "luxun/reflection/scene2/拖拽-精神胜利法" },
|
||||||
|
{ value: "润土", label: "润土", image: "luxun/reflection/scene2/拖拽-润土" },
|
||||||
|
];
|
||||||
|
|
||||||
|
// 目标位置
|
||||||
|
const targetBlanks = [
|
||||||
|
{ id: "blank1", label: "位置1" },
|
||||||
|
{ id: "blank2", label: "位置2" },
|
||||||
|
{ id: "blank3", label: "位置3" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const LuxunReflectionScene2 = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { moduleName } = useParams<{
|
||||||
|
moduleName: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const [showDialog, setShowDialog] = useState(false);
|
||||||
|
const [visited, setVisited] = useState<Set<number>>(new Set());
|
||||||
|
const [dragValue, setDragValue] = useState<DragFillResult[]>([]);
|
||||||
|
const completedRef = useRef(false);
|
||||||
|
const { scale, offsetX, offsetY, DESIGN_WIDTH, DESIGN_HEIGHT } = useScale();
|
||||||
|
const [mounted, setMounted] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMounted(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// 重置游戏状态
|
||||||
|
useEffect(() => {
|
||||||
|
if (showDialog) {
|
||||||
|
setDragValue([]);
|
||||||
|
completedRef.current = false;
|
||||||
|
}
|
||||||
|
}, [showDialog]);
|
||||||
|
|
||||||
|
// 检查是否完成
|
||||||
|
useEffect(() => {
|
||||||
|
if (dragValue.length === 3 && !completedRef.current) {
|
||||||
|
completedRef.current = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
setVisited((prev) => {
|
||||||
|
const next = new Set(prev);
|
||||||
|
next.add(1);
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
setShowDialog(false);
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
}, [dragValue]);
|
||||||
|
|
||||||
|
const progressCount = visited.size;
|
||||||
|
const total = 1;
|
||||||
|
const completed = progressCount >= total;
|
||||||
|
const subtitle = completed
|
||||||
|
? "探索文学作品·精神传承(已完成)"
|
||||||
|
: `探索文学作品·精神传承(${progressCount}/${total})`;
|
||||||
|
const subtitleClassName = completed
|
||||||
|
? "text-[#fff998] [text-shadow:_0_8px_8px_rgba(0,0,0,0.35),_0_0_16px_rgba(0,0,0,0.25)]"
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const handleValueChange = useCallback((newValue: DragFillResult[]) => {
|
||||||
|
if (newValue.length <= 3) {
|
||||||
|
setDragValue(newValue);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const maskSrc = getStaticImage("components/蒙版遮罩");
|
||||||
|
const backSrc = getStaticImage("components/关闭");
|
||||||
|
|
||||||
|
const overlayStyle = useMemo<React.CSSProperties>(
|
||||||
|
() => ({
|
||||||
|
position: "fixed",
|
||||||
|
inset: 0,
|
||||||
|
zIndex: 9999,
|
||||||
|
pointerEvents: "none",
|
||||||
|
}),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const scaledContainerStyle = useMemo<React.CSSProperties>(
|
||||||
|
() => ({
|
||||||
|
position: "absolute",
|
||||||
|
width: `${DESIGN_WIDTH}px`,
|
||||||
|
height: `${DESIGN_HEIGHT}px`,
|
||||||
|
left: `${offsetX}px`,
|
||||||
|
top: `${offsetY}px`,
|
||||||
|
transform: `scale(${scale})`,
|
||||||
|
transformOrigin: "top left",
|
||||||
|
overflow: "hidden",
|
||||||
|
}),
|
||||||
|
[scale, offsetX, offsetY, DESIGN_WIDTH, DESIGN_HEIGHT]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="w-full h-full bg-cover bg-center bg-no-repeat"
|
||||||
|
style={{
|
||||||
|
backgroundImage: getBackgroundImage(`${starName}/reflection/scene2/底图`),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Title
|
||||||
|
title="跨越时空"
|
||||||
|
subtitle={subtitle}
|
||||||
|
subtitleClassName={subtitleClassName}
|
||||||
|
className="absolute top-10 left-1/2 -translate-x-1/2"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Image
|
||||||
|
className="absolute right-5 top-5"
|
||||||
|
src={getStaticImage(`${starName}/reflection/scene2/离开`)}
|
||||||
|
button
|
||||||
|
onClick={() => {
|
||||||
|
if (moduleName && starName) {
|
||||||
|
navigate(`/${moduleName}/${starName}/home`);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 点击区域 - 打开拖拽弹窗 */}
|
||||||
|
<Cursor
|
||||||
|
className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2"
|
||||||
|
onClick={() => setShowDialog(true)}
|
||||||
|
read={visited.has(1)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{completed && (
|
||||||
|
<Image
|
||||||
|
className="absolute bottom-0 right-0"
|
||||||
|
src={getStaticImage("components/place/继续前进")}
|
||||||
|
button
|
||||||
|
onClick={() => {
|
||||||
|
if (moduleName && starName) {
|
||||||
|
navigate(`/${moduleName}/${starName}/reflection3`);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 拖拽弹窗 */}
|
||||||
|
{mounted &&
|
||||||
|
createPortal(
|
||||||
|
<AnimatePresence>
|
||||||
|
{showDialog && (
|
||||||
|
<motion.div
|
||||||
|
key="literature-drag-dialog"
|
||||||
|
style={overlayStyle}
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
transition={{ duration: 0.3, ease: "easeInOut" }}
|
||||||
|
>
|
||||||
|
<div style={scaledContainerStyle}>
|
||||||
|
<div
|
||||||
|
className={cn("absolute inset-0 flex flex-col", "bg-black/70")}
|
||||||
|
style={{ pointerEvents: "auto" }}
|
||||||
|
>
|
||||||
|
{/* 背景蒙版 */}
|
||||||
|
{maskSrc && (
|
||||||
|
<Image
|
||||||
|
src={maskSrc}
|
||||||
|
className="absolute inset-0 w-full h-full object-cover select-none pointer-events-none"
|
||||||
|
aria-hidden
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 返回按钮 */}
|
||||||
|
<Image
|
||||||
|
src={backSrc}
|
||||||
|
onClick={() => setShowDialog(false)}
|
||||||
|
alt="返回"
|
||||||
|
className="absolute top-5 right-5 z-20"
|
||||||
|
button
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 内容区域 */}
|
||||||
|
<div className="relative z-10 w-full h-full flex items-center justify-center px-30 py-16 pt-30">
|
||||||
|
<div className="w-full max-w-6xl">
|
||||||
|
<div className="text-5xl font-bold text-white mb-8 text-center">
|
||||||
|
将文学作品拖拽到对应位置
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DragFill
|
||||||
|
options={literatureOptions.map((opt) => ({
|
||||||
|
value: opt.value,
|
||||||
|
label: opt.label,
|
||||||
|
}))}
|
||||||
|
blanks={targetBlanks}
|
||||||
|
value={dragValue}
|
||||||
|
onChange={handleValueChange}
|
||||||
|
allowDuplicate={false}
|
||||||
|
renderOption={(option) => {
|
||||||
|
const opt = literatureOptions.find(
|
||||||
|
(o) => o.value === option.value
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center gap-2">
|
||||||
|
<Image
|
||||||
|
src={getStaticImage(opt?.image || "")}
|
||||||
|
className="w-32 h-32 object-contain"
|
||||||
|
/>
|
||||||
|
<span className="text-2xl text-white">{option.label}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
renderBlank={(blank, filledValue, isHovered) => {
|
||||||
|
const filledOption = filledValue
|
||||||
|
? literatureOptions.find((opt) => opt.value === filledValue)
|
||||||
|
: null;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"w-48 h-48 border-2 rounded-lg flex flex-col items-center justify-center gap-2 transition-all",
|
||||||
|
filledValue
|
||||||
|
? "border-white bg-white/20"
|
||||||
|
: "border-dashed border-white/50 bg-white/10",
|
||||||
|
isHovered && "border-white bg-white/30"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{filledOption ? (
|
||||||
|
<>
|
||||||
|
<Image
|
||||||
|
src={getStaticImage(filledOption.image)}
|
||||||
|
className="w-24 h-24 object-contain"
|
||||||
|
/>
|
||||||
|
<span className="text-white text-lg">{filledOption.label}</span>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<span className="text-white text-xl">{blank.label}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>,
|
||||||
|
document.body
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LuxunReflectionScene2;
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
|
import { Title, Image, Cursor, ExhibitOverlay } from "@/components";
|
||||||
|
import { getBackgroundImage, getStaticImage } from "@/lib/utils";
|
||||||
|
|
||||||
|
const starName = "luxun";
|
||||||
|
|
||||||
|
const routeMap: Record<1 | 2 | 3 | 4, string> = {
|
||||||
|
1: "luxun/reflection/scene3/绍兴到北京",
|
||||||
|
2: "luxun/reflection/scene3/北京到厦门",
|
||||||
|
3: "luxun/reflection/scene3/厦门广州",
|
||||||
|
4: "luxun/reflection/scene3/广州上海",
|
||||||
|
};
|
||||||
|
|
||||||
|
const themeMap: Record<5 | 6, string> = {
|
||||||
|
5: "luxun/reflection/scene3/家国情怀",
|
||||||
|
6: "luxun/reflection/scene3/批判现实",
|
||||||
|
};
|
||||||
|
|
||||||
|
const LuxunReflectionScene3 = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { moduleName } = useParams<{
|
||||||
|
moduleName: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const [activeRouteId, setActiveRouteId] = useState<1 | 2 | 3 | 4 | null>(null);
|
||||||
|
const [activeThemeId, setActiveThemeId] = useState<5 | 6 | null>(null);
|
||||||
|
const [visited, setVisited] = useState<Set<number>>(new Set());
|
||||||
|
|
||||||
|
const handleOpenRoute = (routeId: 1 | 2 | 3 | 4) => {
|
||||||
|
setActiveRouteId(routeId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOpenTheme = (themeId: 5 | 6) => {
|
||||||
|
setActiveThemeId(themeId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const progressCount = visited.size;
|
||||||
|
const total = 6;
|
||||||
|
const completed = progressCount >= total;
|
||||||
|
const subtitle = completed
|
||||||
|
? "探索精神传承·思想之光(已完成)"
|
||||||
|
: `探索精神传承·思想之光(${progressCount}/${total})`;
|
||||||
|
const subtitleClassName = completed
|
||||||
|
? "text-[#fff998] [text-shadow:_0_8px_8px_rgba(0,0,0,0.35),_0_0_16px_rgba(0,0,0,0.25)]"
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="w-full h-full bg-cover bg-center bg-no-repeat"
|
||||||
|
style={{
|
||||||
|
backgroundImage: getBackgroundImage(`${starName}/reflection/scene3/底图`),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Title
|
||||||
|
title="跨越时空"
|
||||||
|
subtitle={subtitle}
|
||||||
|
subtitleClassName={subtitleClassName}
|
||||||
|
className="absolute top-10 left-1/2 -translate-x-1/2"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Image
|
||||||
|
className="absolute right-5 top-5"
|
||||||
|
src={getStaticImage(`${starName}/reflection/scene3/离开`)}
|
||||||
|
button
|
||||||
|
onClick={() => {
|
||||||
|
if (moduleName && starName) {
|
||||||
|
navigate(`/${moduleName}/${starName}/home`);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 路线点击区域 - 根据实际位置调整 */}
|
||||||
|
<Cursor
|
||||||
|
className="absolute left-20 top-30"
|
||||||
|
onClick={() => handleOpenRoute(1)}
|
||||||
|
read={visited.has(1)}
|
||||||
|
/>
|
||||||
|
<Cursor
|
||||||
|
className="absolute left-40 top-35"
|
||||||
|
onClick={() => handleOpenRoute(2)}
|
||||||
|
read={visited.has(2)}
|
||||||
|
/>
|
||||||
|
<Cursor
|
||||||
|
className="absolute left-60 top-40"
|
||||||
|
onClick={() => handleOpenRoute(3)}
|
||||||
|
read={visited.has(3)}
|
||||||
|
/>
|
||||||
|
<Cursor
|
||||||
|
className="absolute left-50 top-50"
|
||||||
|
onClick={() => handleOpenRoute(4)}
|
||||||
|
read={visited.has(4)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 主题点击区域 */}
|
||||||
|
<Cursor
|
||||||
|
className="absolute left-30 top-60"
|
||||||
|
onClick={() => handleOpenTheme(5)}
|
||||||
|
read={visited.has(5)}
|
||||||
|
/>
|
||||||
|
<Cursor
|
||||||
|
className="absolute left-70 top-55"
|
||||||
|
onClick={() => handleOpenTheme(6)}
|
||||||
|
read={visited.has(6)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 路线展品弹窗 */}
|
||||||
|
<ExhibitOverlay
|
||||||
|
visible={activeRouteId !== null}
|
||||||
|
src={activeRouteId ? routeMap[activeRouteId] : ""}
|
||||||
|
onClose={() => {
|
||||||
|
if (activeRouteId !== null) {
|
||||||
|
setVisited((prev) => {
|
||||||
|
const next = new Set(prev);
|
||||||
|
next.add(activeRouteId);
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setActiveRouteId(null);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 主题展品弹窗 */}
|
||||||
|
<ExhibitOverlay
|
||||||
|
visible={activeThemeId !== null}
|
||||||
|
src={activeThemeId ? themeMap[activeThemeId] : ""}
|
||||||
|
onClose={() => {
|
||||||
|
if (activeThemeId !== null) {
|
||||||
|
setVisited((prev) => {
|
||||||
|
const next = new Set(prev);
|
||||||
|
next.add(activeThemeId);
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setActiveThemeId(null);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LuxunReflectionScene3;
|
||||||
@@ -8,7 +8,13 @@ const LuxunPlace2 = lazy(() => import("@/pages/luxun/place/place2"));
|
|||||||
const LuxunPlace3 = lazy(() => import("@/pages/luxun/place/place3"));
|
const LuxunPlace3 = lazy(() => import("@/pages/luxun/place/place3"));
|
||||||
const LuxunPlace4 = lazy(() => import("@/pages/luxun/place/place4"));
|
const LuxunPlace4 = lazy(() => import("@/pages/luxun/place/place4"));
|
||||||
const LuxunPlace5 = lazy(() => import("@/pages/luxun/place/place5"));
|
const LuxunPlace5 = lazy(() => import("@/pages/luxun/place/place5"));
|
||||||
const LuxunReflection = lazy(() => import("@/pages/luxun/reflection"));
|
const LuxunReflection = lazy(() => import("@/pages/luxun/reflection/scene1"));
|
||||||
|
const LuxunReflection2 = lazy(
|
||||||
|
() => import("@/pages/luxun/reflection/scene2")
|
||||||
|
);
|
||||||
|
const LuxunReflection3 = lazy(
|
||||||
|
() => import("@/pages/luxun/reflection/scene3")
|
||||||
|
);
|
||||||
const LuxunInternalization = lazy(
|
const LuxunInternalization = lazy(
|
||||||
() => import("@/pages/luxun/internalization")
|
() => import("@/pages/luxun/internalization")
|
||||||
);
|
);
|
||||||
@@ -73,6 +79,14 @@ export const LuxunRoutes: RouteObject[] = [
|
|||||||
path: "reflection",
|
path: "reflection",
|
||||||
element: <LuxunReflection />,
|
element: <LuxunReflection />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "reflection2",
|
||||||
|
element: <LuxunReflection2 />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "reflection3",
|
||||||
|
element: <LuxunReflection3 />,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "internalization",
|
path: "internalization",
|
||||||
element: <LuxunInternalization />,
|
element: <LuxunInternalization />,
|
||||||
|
|||||||