feat:黄炎培对话对接中
This commit is contained in:
1 parent
3611a0857a
commit
6ab184b328
125 files changed
+6617
-2391
No files matched your search
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user