feat:黄炎培对话对接中

This commit is contained in:
zhongyidian committed 2026-01-07 21:08:38 +08:00
1 parent 3611a0857a
commit 6ab184b328
125 files changed
+6617 -2391

No files matched your search

+134 -39
View File
@@ -2,7 +2,8 @@ import { useState, useEffect, useMemo, useCallback } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { Image, DragFill } from "@/components";
import type { DragFillResult } from "@/components";
import SuccessAlert from "@/components/layout/success-alert";
import SuccessAlert from "@/components/layout/alert/success-alert";
import ErrorAlert from "@/components/layout/alert/error-alert";
import { getBackgroundImage, getStaticImage } from "@/lib/utils";
const starName = "luxun";
@@ -14,48 +15,56 @@ const dragItems = [
name: "闰土",
imagePath: "luxun/reflection/scene2/拖拽-润土",
targetBlankId: "city-1-label-0", // 绍兴-人物
errorHint: "闰土的故事和鲁迅的少年绍兴时光有关哦",
},
{
id: "baicaoyuan",
name: "《从百草园到三味书屋》",
imagePath: "luxun/reflection/scene2/拖拽-百草园到三味书屋",
targetBlankId: "city-1-label-1", // 绍兴-作品
errorHint: "这个作品写的是鲁迅在绍兴的故事哦",
},
{
id: "aqzhengzhuan",
name: "《阿Q正传》",
imagePath: "luxun/reflection/scene2/拖拽-阿Q正传",
targetBlankId: "city-2-label-0", // 北京-作品
errorHint: "这部经典小说和鲁迅在北京的创作有关哦",
},
{
id: "jingshenshengli",
name: "“精神胜利法”",
name: '"精神胜利法"',
imagePath: "luxun/reflection/scene2/拖拽-精神胜利法",
targetBlankId: "city-2-label-1", // 北京-核心主题
errorHint: "这个主题是《阿Q正传》的核心,和北京创作",
},
{
id: "guanzhudiceng",
name: "“关注底层”",
name: '"关注底层"',
imagePath: "luxun/reflection/scene2/关注底层",
targetBlankId: "city-3-label-0", // 厦门-创作方向
errorHint: "这个创作方向是鲁迅在厦门的核心追求哦",
},
{
id: "zhaohuaxishi",
name: "《朝花夕拾》",
imagePath: "luxun/reflection/scene2/拖拽-朝花夕拾",
targetBlankId: "city-4-label-0", // 广州-作品集
errorHint: "这个作品集和鲁迅在广州的旅居时光有关哦",
},
{
id: "gushixinbian",
name: "《故事新编》",
imagePath: "luxun/reflection/scene2/拖拽-故事新编",
targetBlankId: "city-5-label-0", // 上海-作品
errorHint: "再试试~这个作品和上海的创作故事更相关哦",
},
{
id: "biweidaoqiang",
name: "“笔为刀枪”",
name: '"笔为刀枪"',
imagePath: "luxun/reflection/scene2/笔为刀枪",
targetBlankId: "city-5-label-1", // 上海-创作中心
errorHint: "这个初心是鲁迅在上海创作的核心哦",
},
];
@@ -67,10 +76,10 @@ const cityConfig = [
name: "绍兴",
location: "绍兴三味书屋",
imagePath: "luxun/reflection/scene2/绍兴",
position: { top: "58%", left: "60%" }, // 中间
position: { top: "65%", left: "65%" }, // 中间
labels: [
{ text: "人物", containerPosition: { top: "0%", left: "-20%" } },
{ text: "作品", containerPosition: { top: "0%", left: "20%" } },
{ text: "人物", containerPosition: { top: "50%", left: "20%" } },
{ text: "作品", containerPosition: { top: "50%", left: "50%" } },
],
time: "1892年-1898年",
description: "少年鲁迅在此求学,为《从百草园到三味书屋》埋下创作种子。",
@@ -80,10 +89,10 @@ const cityConfig = [
name: "北京",
location: "北京八道湾胡同",
imagePath: "luxun/reflection/scene2/北京",
position: { top: "14%", left: "50%" }, // 顶部
position: { top: "14%", left: "62%" }, // 顶部
labels: [
{ text: "作品", containerPosition: { top: "0%", left: "-20%" } },
{ text: "核心主题", containerPosition: { top: "0%", left: "20%" } },
{ text: "作品", containerPosition: { top: "72%", left: "30%" } },
{ text: "核心主题", containerPosition: { top: "72%", left: "70%" } },
],
time: "1919-1923年",
description:
@@ -94,9 +103,9 @@ const cityConfig = [
name: "厦门",
location: "厦门大学",
imagePath: "luxun/reflection/scene2/厦门",
position: { top: "80%", left: "70%" }, // 右下
position: { top: "83%", left: "78%" }, // 右下
labels: [
{ text: "创作方向", containerPosition: { top: "0%", left: "0%" } },
{ text: "创作方向", containerPosition: { top: "52%", left: "60%" } },
],
time: "1926年9-12月",
description:
@@ -107,8 +116,10 @@ const cityConfig = [
name: "广州",
location: "广州白云楼",
imagePath: "luxun/reflection/scene2/广州",
position: { top: "80%", left: "30%" }, // 左下
labels: [{ text: "作品集", containerPosition: { top: "0%", left: "0%" } }],
position: { top: "88%", left: "35%" }, // 左下
labels: [
{ text: "作品集", containerPosition: { top: "38%", left: "50%" } },
],
time: "1927年3-9月",
description:
"先生在此整理旧作,思考民族觉醒之路,《朝花夕拾》部分篇章定稿于此。",
@@ -118,10 +129,10 @@ const cityConfig = [
name: "上海",
location: "上海鲁迅故居",
imagePath: "luxun/reflection/scene2/上海",
position: { top: "40%", left: "60%" }, // 中上
position: { top: "48%", left: "70%" }, // 中上
labels: [
{ text: "作品", containerPosition: { top: "0%", left: "-20%" } },
{ text: "创作中心", containerPosition: { top: "0%", left: "20%" } },
{ text: "作品", containerPosition: { top: "50%", left: "20%" } },
{ text: "创作中心", containerPosition: { top: "50%", left: "50%" } },
],
time: "1933-1936年",
description:
@@ -142,6 +153,9 @@ const LuxunReflectionScene2 = () => {
const [showSuccessAlert, setShowSuccessAlert] = useState(false);
// 是否全部完成
const [allCompleted, setAllCompleted] = useState(false);
// 错误提示相关状态
const [showErrorAlert, setShowErrorAlert] = useState(false);
const [errorHint, setErrorHint] = useState("");
// 转换为 DragFill 的 options
const options = useMemo(
@@ -169,18 +183,58 @@ const LuxunReflectionScene2 = () => {
}, []);
// 处理拖拽结果变化 - 验证是否正确放置
const handleValueChange = useCallback((newValue: DragFillResult[]) => {
// 验证每个元素是否放置到正确的标签区域
const filteredValue = newValue.filter((result) => {
const itemId = result.optionValue;
const blankId = result.blankId;
const handleValueChange = useCallback(
(newValue: DragFillResult[]) => {
// 找出新添加或位置改变的拖拽结果(与当前值比较)
const previousValue = dragFillValue;
const changedResults = newValue.filter(
(newResult) =>
!previousValue.some(
(prevResult) =>
prevResult.optionValue === newResult.optionValue &&
prevResult.blankId === newResult.blankId
)
);
const item = dragItems.find((item) => item.id === itemId);
return item && item.targetBlankId === blankId;
});
// 检查改变的结果是否有错误
let hasError = false;
let errorMessage = "";
setDragFillValue(filteredValue);
}, []);
for (const result of changedResults) {
const itemId = result.optionValue;
const blankId = result.blankId;
const item = dragItems.find((item) => item.id === itemId);
// 如果放置位置不正确,显示错误提示
if (item && item.targetBlankId !== blankId) {
hasError = true;
errorMessage = item.errorHint || "放置位置不正确,请再试试~";
break; // 只显示第一个错误的提示
}
}
if (hasError) {
// 显示错误提示
setErrorHint(errorMessage);
setShowErrorAlert(true);
// 不更新拖拽值,保持之前正确的状态
// DragFill 是受控组件,不更新 value 会保持之前的状态
return;
}
// 验证每个元素是否放置到正确的标签区域,只保留正确的放置
const filteredValue = newValue.filter((result) => {
const itemId = result.optionValue;
const blankId = result.blankId;
const item = dragItems.find((item) => item.id === itemId);
return item && item.targetBlankId === blankId;
});
setDragFillValue(filteredValue);
},
[dragFillValue]
);
const progress = dragFillValue.length;
const totalTasks = dragItems.length; // 8个拖拽元素
@@ -197,6 +251,11 @@ const LuxunReflectionScene2 = () => {
setShowSuccessAlert(false);
};
const handleErrorAlertClose = () => {
setShowErrorAlert(false);
setErrorHint("");
};
// 获取每个标签区域已放置的元素
const getLabelPlacedItem = (cityId: number, labelIndex: number) => {
const blankId = `city-${cityId}-label-${labelIndex}`;
@@ -207,7 +266,7 @@ const LuxunReflectionScene2 = () => {
return (
<div
className="flex justify-between w-full h-full bg-cover bg-center bg-no-repeat gap-10 relative"
className="select-none flex justify-between w-full h-full bg-cover bg-center bg-no-repeat gap-10 relative"
style={{
backgroundImage: getBackgroundImage(
`${starName}/reflection/scene2/底图`
@@ -221,7 +280,7 @@ const LuxunReflectionScene2 = () => {
button
onClick={() => {
if (moduleName && starName) {
navigate(`/${moduleName}/${starName}/home`);
navigate(`/${moduleName}/${starName}/reflection`);
}
}}
/>
@@ -262,10 +321,10 @@ const LuxunReflectionScene2 = () => {
<div
key={index}
data-blank-id={blankId}
className={`absolute border-2 border-dashed rounded-lg transition-all min-w-[120px] min-h-[120px] flex flex-col items-center justify-center ${
className={`absolute border-2 border-dashed rounded-lg transition-all min-w-[80px] min-h-[80px] flex flex-col items-center justify-center ${
isFilled
? "border-green-500 bg-green-500/20"
: "border-white/60 bg-white/10 hover:border-blue-400 hover:bg-blue-400/20"
: "border-white/60 bg-black/60 hover:border-blue-400 hover:bg-blue-400/20"
}`}
style={{
top: containerPosition.top,
@@ -281,7 +340,7 @@ const LuxunReflectionScene2 = () => {
className="w-20 h-20 object-contain"
/>
) : (
<div className="text-white/30 text-sm text-center">
<div className="text-white text-sm text-center">
{labelText}
</div>
)}
@@ -296,6 +355,34 @@ const LuxunReflectionScene2 = () => {
</div>
</div>
{/* 导航栏 */}
<div className="absolute top-20 left-[37%] w-80 space-y-4 z-99">
{/* 任务2漫集美学 */}
<div className="text-4xl font-bold h-16 cursor-pointer relative"></div>
{/* 任务3学习精神 */}
<div
className="text-4xl font-bold h-16 cursor-pointer relative"
onClick={() => {
if (moduleName && allCompleted) {
navigate(`/${moduleName}/${starName}/reflection3`);
}
}}
>
{/* 任务完成后显示点一点图标 */}
{allCompleted && (
<div className="absolute -right-20 top-1/2 flex items-center gap-2 z-10 pointer-events-none">
<img
className="animate-finger-bounce scale-x-[-1]"
src={getStaticImage(
`${starName}/reflection/scene1/鲁迅体悟点一点图标`
)}
alt="点一点图标"
/>
</div>
)}
</div>
</div>
{/* 右侧区域 */}
<div className="flex-1 relative">
{/* 任务进度 */}
@@ -309,7 +396,7 @@ const LuxunReflectionScene2 = () => {
{/* 鲁迅画廊 */}
<div className="absolute top-[calc(2.4rem+204px)] left-0 w-[995px] h-[467px] pt-16 pl-70 pr-10">
{/* 提示图标 */}
<div className="absolute left-10 top-30 flex items-center z-10 pointer-events-none">
<div className="absolute left-14 top-35 flex items-center z-10 pointer-events-none">
<img
src={getStaticImage(
`${starName}/reflection/scene2/鲁迅体悟拖一拖`
@@ -364,7 +451,7 @@ const LuxunReflectionScene2 = () => {
<img
src={getStaticImage(item.imagePath)}
alt={item.name}
className="w-20 h-20 object-contain shadow-lg"
className="w-16"
draggable={false}
/>
);
@@ -390,11 +477,11 @@ const LuxunReflectionScene2 = () => {
<div className="space-y-8">
{cityConfig.map((city) => (
<div key={city.id} className="space-y-4">
<div className="text-red-600 text-6xl font-bold">
{city.location}
</div>
<div className="text-gray-600 text-4xl font-normal">
{city.time}
<div className="text-red-600 text-5xl font-bold flex items-end gap-6">
<div>{city.location}</div>
<div className="text-gray-600 text-4xl font-normal">
{city.time}
</div>
</div>
<div className="text-black text-4xl font-normal leading-relaxed">
{city.description}
@@ -438,6 +525,14 @@ const LuxunReflectionScene2 = () => {
onClose={handleSuccessAlertClose}
onContinue={handleSuccessAlertClose}
/>
{/* 错误提示弹窗 */}
<ErrorAlert
visible={showErrorAlert}
text={errorHint}
onClose={handleErrorAlertClose}
onMaskClick={handleErrorAlertClose}
/>
</div>
);
};