diff --git a/src/components/olmap/HealthRiskAnalysis/HealthRiskPieChart.tsx b/src/components/olmap/HealthRiskAnalysis/HealthRiskPieChart.tsx index 8547ede..abb5a6f 100644 --- a/src/components/olmap/HealthRiskAnalysis/HealthRiskPieChart.tsx +++ b/src/components/olmap/HealthRiskAnalysis/HealthRiskPieChart.tsx @@ -2,7 +2,17 @@ import React, { useMemo } from "react"; import ReactECharts from "echarts-for-react"; -import { Paper, Typography, Box, Chip, IconButton, Tooltip, Stack } from "@mui/material"; +import { + Paper, + Typography, + Box, + Chip, + IconButton, + Tooltip, + Stack, + Slide, + Fade, +} from "@mui/material"; import { ChevronLeft, ChevronRight, PieChart } from "@mui/icons-material"; import { RAINBOW_COLORS, RISK_BREAKS, RISK_LABELS } from "./types"; import { useHealthRisk } from "./HealthRiskContext"; @@ -29,12 +39,9 @@ const HealthRiskPieChart: React.FC = () => { const index = year - 4; if (index >= 0 && index < y.length) { const probability = y[index]; - if ( - probability >= RISK_BREAKS[riskIdx] && - probability < RISK_BREAKS[riskIdx + 1] - ) { - count++; - } else if (riskIdx === 9 && probability === 1.0) { + const lowerBound = riskIdx === 0 ? -1 : RISK_BREAKS[riskIdx - 1]; + const upperBound = RISK_BREAKS[riskIdx]; + if (probability > lowerBound && probability <= upperBound) { count++; } } @@ -145,56 +152,92 @@ const HealthRiskPieChart: React.FC = () => { } return ( -
- {/* 头部 */} -
-
- - - - -

管道健康风险分布

- -
-
+ <> + {/* 收起时的触发按钮 */} + 0}> + setIsExpanded(true)} + sx={{ zIndex: 1300 }} + > + + + + 风险分布 + + + + + - {/* 内容区域 */} -
- { - chartRef.current = e; - }} - option={option} - onEvents={onEvents} - style={{ height: "100%", width: "100%" }} - opts={{ renderer: "canvas" }} - /> -
-
+ +
+ {/* 头部 */} +
+
+ + + + +

管道健康风险分布

+ +
+ + + setIsExpanded(false)} + sx={{ color: "white" }} + > + + + + +
+ + {/* 内容区域 */} +
+ { + chartRef.current = e; + }} + option={option} + onEvents={onEvents} + style={{ height: "100%", width: "100%" }} + opts={{ renderer: "canvas" }} + /> +
+
+
+ ); }; diff --git a/src/components/olmap/HealthRiskAnalysis/Timeline.tsx b/src/components/olmap/HealthRiskAnalysis/Timeline.tsx index 0751fa8..68973e0 100644 --- a/src/components/olmap/HealthRiskAnalysis/Timeline.tsx +++ b/src/components/olmap/HealthRiskAnalysis/Timeline.tsx @@ -3,7 +3,6 @@ import React, { useState, useEffect, useRef, useCallback } from "react"; import { useNotification } from "@refinedev/core"; import WebGLVectorTileLayer from "ol/layer/WebGLVectorTile"; -import { parseColor } from "@/utils/parseColor"; import { Box, @@ -40,6 +39,20 @@ import { const backendUrl = config.BACKEND_URL; +// 辅助函数:将日期向下取整到最近的15分钟 +const getRoundedDate = (date: Date): Date => { + const minutes = date.getHours() * 60 + date.getMinutes(); + const roundedMinutes = Math.floor(minutes / 15) * 15; + const roundedDate = new Date(date); + roundedDate.setHours( + Math.floor(roundedMinutes / 60), + roundedMinutes % 60, + 0, + 0 + ); + return roundedDate; +}; + interface TimelineProps { schemeDate?: Date; timeRange?: { start: Date; end: Date }; @@ -64,7 +77,7 @@ const Timeline: React.FC = ({ const [selectedDateTime, setSelectedDateTime] = useState(new Date()); const [isPlaying, setIsPlaying] = useState(false); - const [playInterval, setPlayInterval] = useState(15000); // 毫秒 + const [playInterval, setPlayInterval] = useState(5000); // 毫秒 const [isPredicting, setIsPredicting] = useState(false); const [pipeLayer, setPipeLayer] = useState(null); @@ -89,10 +102,9 @@ const Timeline: React.FC = ({ // 播放时间间隔选项 const intervalOptions = [ + { value: 2000, label: "2秒" }, { value: 5000, label: "5秒" }, { value: 10000, label: "10秒" }, - { value: 15000, label: "15秒" }, - { value: 20000, label: "20秒" }, ]; // 处理时间轴滑动 @@ -139,7 +151,7 @@ const Timeline: React.FC = ({ const handleStop = useCallback(() => { setIsPlaying(false); - setSelectedDateTime(new Date()); + setSelectedDateTime(getRoundedDate(new Date())); if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null; @@ -180,18 +192,7 @@ const Timeline: React.FC = ({ // 日期时间选择处理 const handleDateTimeChange = useCallback((newDate: Date | null) => { if (newDate) { - // 将时间向下取整到最近的15分钟 - const minutes = newDate.getHours() * 60 + newDate.getMinutes(); - const roundedMinutes = Math.floor(minutes / 15) * 15; - const roundedDate = new Date(newDate); - roundedDate.setHours( - Math.floor(roundedMinutes / 60), - roundedMinutes % 60, - 0, - 0 - ); - - setSelectedDateTime(roundedDate); + setSelectedDateTime(getRoundedDate(newDate)); } }, []); @@ -218,18 +219,7 @@ const Timeline: React.FC = ({ // 组件加载时设置初始时间为当前时间的最近15分钟 useEffect(() => { - const now = new Date(); - const minutes = now.getHours() * 60 + now.getMinutes(); - // 向下取整到最近的15分钟刻度 - const roundedMinutes = Math.floor(minutes / 15) * 15; - const roundedDate = new Date(now); - roundedDate.setHours( - Math.floor(roundedMinutes / 60), - roundedMinutes % 60, - 0, - 0 - ); - setSelectedDateTime(roundedDate); + setSelectedDateTime(getRoundedDate(new Date())); return () => { if (intervalRef.current) { @@ -295,19 +285,11 @@ const Timeline: React.FC = ({ if (!renderFeatures || renderFeatures.length === 0) return; const healthData = healthDataRef.current; - let i = 0; renderFeatures.forEach((renderFeature: any) => { const featureId = renderFeature.get("id"); const value = healthData.get(featureId); if (value !== undefined) { renderFeature.properties_["healthRisk"] = value; - // 输出前10个点的信息以供调试 - if (i < 10) { - console.log( - `瓦片加载 - 设置特征 ${featureId} 的 healthRisk 为 ${value}` - ); - i++; - } } }); }; @@ -337,11 +319,6 @@ const Timeline: React.FC = ({ // 更新 ref 数据 healthDataRef.current = pipeHealthData; - // 输出前 10 条数据以供调试 - console.log( - `更新健康数据,年份: ${currentYear}`, - Array.from(pipeHealthData.entries()).slice(0, 10) - ); // 更新图层数据 updatePipeHealthData(pipeHealthData); @@ -361,35 +338,23 @@ const Timeline: React.FC = ({ const widthCases: any[] = []; breaks.forEach((breakValue, index) => { - if (index < breaks.length - 1) { - const colorIndex = Math.floor( - (index / (breaks.length - 1)) * (colors.length - 1) - ); - const color = parseColor(colors[colorIndex]); + const colorStr = colors[index]; + // 线宽根据健康风险调整:低生存概率(高风险)用粗线 + const width = 2 + (1 - index / (breaks.length - 1)) * 4; - // 线宽根据健康风险调整:低生存概率(高风险)用粗线 - const width = 2 + (1 - index / (breaks.length - 1)) * 4; - - colorCases.push( - ["between", ["get", "healthRisk"], breakValue, breaks[index + 1]], - [color.r / 255, color.g / 255, color.b / 255, 1] - ); - widthCases.push( - ["between", ["get", "healthRisk"], breakValue, breaks[index + 1]], - width - ); - } + colorCases.push(["<=", ["get", "healthRisk"], breakValue], colorStr); + widthCases.push(["<=", ["get", "healthRisk"], breakValue], width); }); - + console.log( + `应用健康风险样式,年份: ${currentYear}, 分段: ${breaks.length}` + ); + console.log("颜色表达式:", colorCases); + console.log("宽度表达式:", widthCases); // 应用样式到图层 pipeLayer.setStyle({ - "stroke-color": ["case", ...colorCases, [0.5, 0.5, 0.5, 1]], + "stroke-color": ["case", ...colorCases, "rgba(128, 128, 128, 1)"], "stroke-width": ["case", ...widthCases, 2], }); - - console.log( - `已应用健康风险样式,年份: ${currentYear}, 分段: ${breaks.length}` - ); }, [ pipeLayer, predictionResults, @@ -466,7 +431,6 @@ const Timeline: React.FC = ({ if (response.ok) { const results: PredictionResult[] = await response.json(); setPredictionResults(results); - console.log("预测结果:", results[0], results[1], results.length); open?.({ type: "success", message: `模拟预测完成,获取到 ${results.length} 条管道数据`, diff --git a/src/components/olmap/HealthRiskAnalysis/types.ts b/src/components/olmap/HealthRiskAnalysis/types.ts index 6a3f58b..1df3014 100644 --- a/src/components/olmap/HealthRiskAnalysis/types.ts +++ b/src/components/olmap/HealthRiskAnalysis/types.ts @@ -26,7 +26,7 @@ export const RAINBOW_COLORS = [ "rgba(142, 68, 173, 0.9)", // 紫 (0.9 - 1.0) - 低风险 ]; -export const RISK_BREAKS = [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]; +export const RISK_BREAKS = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]; export const RISK_LABELS = [ "0.0 - 0.1 (极高风险)",