diff --git a/src/app/(main)/risk-analysis-location/loading.tsx b/src/app/(main)/hydraulic-simulation/pipe-burst-analysis/loading.tsx similarity index 100% rename from src/app/(main)/risk-analysis-location/loading.tsx rename to src/app/(main)/hydraulic-simulation/pipe-burst-analysis/loading.tsx diff --git a/src/app/(main)/risk-analysis-location/page.tsx b/src/app/(main)/hydraulic-simulation/pipe-burst-analysis/page.tsx similarity index 100% rename from src/app/(main)/risk-analysis-location/page.tsx rename to src/app/(main)/hydraulic-simulation/pipe-burst-analysis/page.tsx diff --git a/src/app/(main)/hydraulic-simulation/pipe-flushing/loading.tsx b/src/app/(main)/hydraulic-simulation/pipe-flushing/loading.tsx new file mode 100644 index 0000000..2c57921 --- /dev/null +++ b/src/app/(main)/hydraulic-simulation/pipe-flushing/loading.tsx @@ -0,0 +1,5 @@ +import { MapSkeleton } from "@components/loading/MapSkeleton"; + +export default function Loading() { + return ; +} diff --git a/src/app/(main)/hydraulic-simulation/water-quality-simulation/page.tsx b/src/app/(main)/hydraulic-simulation/water-quality-simulation/page.tsx new file mode 100644 index 0000000..a3133fb --- /dev/null +++ b/src/app/(main)/hydraulic-simulation/water-quality-simulation/page.tsx @@ -0,0 +1,16 @@ +"use client"; + +import MapComponent from "@app/OlMap/MapComponent"; +import MapToolbar from "@app/OlMap/Controls/Toolbar"; +import WaterQualityPanel from "@/components/olmap/ContaminantSimulation/WaterQualityPanel"; + +export default function Home() { + return ( +
+ + + + +
+ ); +} diff --git a/src/app/_refine_context.tsx b/src/app/_refine_context.tsx index 1ac8bf6..d5366d1 100644 --- a/src/app/_refine_context.tsx +++ b/src/app/_refine_context.tsx @@ -21,6 +21,7 @@ import { LuReplace } from "react-icons/lu"; import { AiOutlineSecurityScan } from "react-icons/ai"; import { TbLocationPin } from "react-icons/tb"; import { AiOutlinePartition } from "react-icons/ai"; +import { MdWater, MdOutlineWaterDrop, MdCleaningServices } from "react-icons/md"; type RefineContextProps = { defaultMode?: string; @@ -154,11 +155,37 @@ const App = (props: React.PropsWithChildren) => { }, }, { - name: "风险分析定位", - list: "/risk-analysis-location", + name: "Hydraulic Simulation", meta: { + icon: , + label: "水力仿真", + }, + }, + { + name: "爆管分析定位", + list: "/hydraulic-simulation/pipe-burst-analysis", + meta: { + parent: "Hydraulic Simulation", icon: , - label: "风险分析定位", + label: "爆管分析定位", + }, + }, + { + name: "水质模拟", + list: "/hydraulic-simulation/water-quality-simulation", + meta: { + parent: "Hydraulic Simulation", + icon: , + label: "水质模拟", + }, + }, + { + name: "管道冲洗", + list: "/hydraulic-simulation/pipe-flushing", + meta: { + parent: "Hydraulic Simulation", + icon: , + label: "管道冲洗", }, }, { diff --git a/src/components/loading/MapSkeleton.tsx b/src/components/loading/MapSkeleton.tsx index 6cb2bb8..755d1ca 100644 --- a/src/components/loading/MapSkeleton.tsx +++ b/src/components/loading/MapSkeleton.tsx @@ -1,4 +1,4 @@ -import { Box, Skeleton } from "@mui/material"; +import { Box, Skeleton, CircularProgress } from "@mui/material"; /** * 地图页面骨架屏组件 @@ -26,7 +26,24 @@ export function MapSkeleton() { }} /> - {/* 左侧工具栏骨架 */} + {/* 中央加载指示器 */} + + + + + {/* 左侧工具栏骨架 (垂直) */} - {[1, 2, 3, 4, 5].map((i) => ( + {[1, 2, 3, 4].map((i) => ( ))} - {/* 右侧控制面板骨架 */} + {/* 右侧控制面板骨架 (抽屉式) */} - - - - - + + + {/* 面板内容区块 */} + + + + + + + {[1, 2, 3].map((i) => ( + + + + + + + + ))} + + - {/* 底部时间轴骨架 */} + {/* 底部时间轴/控制条骨架 */} - + + + - {/* 缩放控制骨架 */} + {/* 缩放控制骨架 (右下) */} - - - - - {/* 比例尺骨架 */} - - + + ); diff --git a/src/components/olmap/BurstPipeAnalysis/BurstPipeAnalysisPanel.tsx b/src/components/olmap/BurstPipeAnalysis/BurstPipeAnalysisPanel.tsx index 4b00334..6d204cc 100644 --- a/src/components/olmap/BurstPipeAnalysis/BurstPipeAnalysisPanel.tsx +++ b/src/components/olmap/BurstPipeAnalysis/BurstPipeAnalysisPanel.tsx @@ -22,13 +22,9 @@ import AnalysisParameters from "./AnalysisParameters"; import SchemeQuery from "./SchemeQuery"; import LocationResults from "./LocationResults"; import ValveIsolation from "./ValveIsolation"; -import ContaminantAnalysisParameters from "../ContaminantSimulation/AnalysisParameters"; -import ContaminantSchemeQuery from "../ContaminantSimulation/SchemeQuery"; -import ContaminantResultsPanel from "../ContaminantSimulation/ResultsPanel"; import axios from "axios"; import { config } from "@config/config"; import { useNotification } from "@refinedev/core"; -import { useData } from "@app/OlMap/MapComponent"; import { LocationResult, SchemeRecord, ValveIsolationResult } from "./types"; interface TabPanelProps { @@ -56,17 +52,12 @@ interface BurstPipeAnalysisPanelProps { onToggle?: () => void; } -type PanelMode = "burst" | "contaminant"; - const BurstPipeAnalysisPanel: React.FC = ({ open: controlledOpen, onToggle, }) => { const [internalOpen, setInternalOpen] = useState(true); const [currentTab, setCurrentTab] = useState(0); - const [panelMode, setPanelMode] = useState("burst"); - - const data = useData(); // 持久化方案查询结果 const [schemes, setSchemes] = useState([]); @@ -92,16 +83,6 @@ const BurstPipeAnalysisPanel: React.FC = ({ setCurrentTab(newValue); }; - const handleModeChange = (_event: React.SyntheticEvent, newMode: PanelMode) => { - setPanelMode(newMode); - // 切换模式时,如果当前标签索引超出新模式的标签数量,重置为第一个标签 - // 爆管分析有4个标签(0-3),水质模拟有3个标签(0-2) - const maxTabIndex = newMode === "burst" ? 3 : 2; - if (currentTab > maxTabIndex) { - setCurrentTab(0); - } - }; - const handleLocateScheme = async (scheme: SchemeRecord) => { try { const response = await axios.get( @@ -120,8 +101,7 @@ const BurstPipeAnalysisPanel: React.FC = ({ }; const drawerWidth = 520; - const isBurstMode = panelMode === "burst"; - const panelTitle = isBurstMode ? "爆管分析" : "水质模拟"; + const panelTitle = "爆管分析"; return ( <> @@ -197,32 +177,6 @@ const BurstPipeAnalysisPanel: React.FC = ({ - {/* Tabs 导航 */} - - - - - - = ({ } iconPosition="start" - label={isBurstMode ? "定位结果" : "模拟结果"} + label="定位结果" + /> + } + iconPosition="start" + label="关阀分析" /> - {isBurstMode && ( - } - iconPosition="start" - label="关阀分析" - /> - )} {/* Tab 内容 */} - {isBurstMode ? ( - - ) : ( - - )} + - {isBurstMode ? ( - - ) : ( - setCurrentTab(2)} /> - )} + - {isBurstMode ? ( - - ) : ( - - )} + - {isBurstMode && ( - - - - )} + + + diff --git a/src/components/olmap/ContaminantSimulation/WaterQualityPanel.tsx b/src/components/olmap/ContaminantSimulation/WaterQualityPanel.tsx new file mode 100644 index 0000000..9450e9f --- /dev/null +++ b/src/components/olmap/ContaminantSimulation/WaterQualityPanel.tsx @@ -0,0 +1,208 @@ +"use client"; + +import React, { useState } from "react"; +import { + Box, + Drawer, + Tabs, + Tab, + Typography, + IconButton, + Tooltip, +} from "@mui/material"; +import { + ChevronRight, + ChevronLeft, + Analytics as AnalyticsIcon, + Search as SearchIcon, + MyLocation as MyLocationIcon, +} from "@mui/icons-material"; +import ContaminantAnalysisParameters from "./AnalysisParameters"; +import ContaminantSchemeQuery from "./SchemeQuery"; +import ContaminantResultsPanel from "./ResultsPanel"; +import { useData } from "@app/OlMap/MapComponent"; + +interface WaterQualityPanelProps { + open?: boolean; + onToggle?: () => void; +} + +const WaterQualityPanel: React.FC = ({ + open: controlledOpen, + onToggle, +}) => { + const [internalOpen, setInternalOpen] = useState(true); + const [currentTab, setCurrentTab] = useState(0); + + const data = useData(); + + // 使用受控或非受控状态 + const isOpen = controlledOpen !== undefined ? controlledOpen : internalOpen; + const handleToggle = () => { + if (onToggle) { + onToggle(); + } else { + setInternalOpen(!internalOpen); + } + }; + + const handleTabChange = (_event: React.SyntheticEvent, newValue: number) => { + setCurrentTab(newValue); + }; + + const drawerWidth = 520; + const panelTitle = "水质模拟"; + + return ( + <> + {/* 收起时的触发按钮 */} + {!isOpen && ( + + + + + {panelTitle} + + + + + )} + + {/* 主面板 */} + + + {/* 头部 */} + + + + + {panelTitle} + + + + + + + + + + + + } + iconPosition="start" + label="分析要件" + /> + } + iconPosition="start" + label="方案查询" + /> + {/* } + iconPosition="start" + label="模拟结果" + /> */} + + + + {/* Tab 内容 */} + + + + + + setCurrentTab(2)} /> + + + + + + + + + ); +}; + +interface TabPanelProps { + children?: React.ReactNode; + index: number; + value: number; +} + +const TabPanel: React.FC = ({ children, value, index }) => { + return ( + + ); +}; + +export default WaterQualityPanel;