重命名HistoryDataPanel为PredictDataPanel
This commit is contained in:
237
src/components/olmap/HealthRiskAnalysis/PredictDataPanel.tsx
Normal file
237
src/components/olmap/HealthRiskAnalysis/PredictDataPanel.tsx
Normal file
@@ -0,0 +1,237 @@
|
||||
"use client";
|
||||
|
||||
import React, { useMemo, useRef } from "react";
|
||||
import Draggable from "react-draggable";
|
||||
|
||||
import { Box, Chip, Stack, Typography } from "@mui/material";
|
||||
import { ShowChart } from "@mui/icons-material";
|
||||
import ReactECharts from "echarts-for-react";
|
||||
import "dayjs/locale/zh-cn";
|
||||
import { useHealthRisk } from "./HealthRiskContext";
|
||||
|
||||
export interface PredictDataPanelProps {
|
||||
/** 选中的要素信息列表,格式为 [[id, type], [id, type]] */
|
||||
featureInfos: [string, string][];
|
||||
/** Y 轴数值的小数位数 */
|
||||
fractionDigits?: number;
|
||||
}
|
||||
|
||||
const PredictDataPanel: React.FC<PredictDataPanelProps> = ({
|
||||
featureInfos,
|
||||
fractionDigits = 4,
|
||||
}) => {
|
||||
const { predictionResults } = useHealthRisk();
|
||||
const draggableRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// 提取选中的设备 ID
|
||||
const selectedIds = useMemo(
|
||||
() => featureInfos.map(([id]) => id),
|
||||
[featureInfos]
|
||||
);
|
||||
|
||||
// 过滤出选中管道的预测结果
|
||||
const filteredResults = useMemo(() => {
|
||||
return predictionResults.filter((res) => selectedIds.includes(res.link_id));
|
||||
}, [predictionResults, selectedIds]);
|
||||
|
||||
const hasData = filteredResults.length > 0;
|
||||
|
||||
// 构建图表所需的数据集
|
||||
const dataset = useMemo(() => {
|
||||
if (filteredResults.length === 0) return [];
|
||||
|
||||
// 获取所有唯一的时间点并排序
|
||||
const allX = Array.from(
|
||||
new Set(filteredResults.flatMap((res) => res.survival_function.x))
|
||||
).sort((a, b) => a - b);
|
||||
|
||||
return allX.map((x) => {
|
||||
const row: any = { x, label: `${x}年` };
|
||||
filteredResults.forEach((res) => {
|
||||
const index = res.survival_function.x.indexOf(x);
|
||||
if (index !== -1) {
|
||||
row[res.link_id] = res.survival_function.y[index];
|
||||
}
|
||||
});
|
||||
return row;
|
||||
});
|
||||
}, [filteredResults]);
|
||||
|
||||
const renderEmpty = () => (
|
||||
<Box
|
||||
sx={{
|
||||
flex: 1,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
py: 8,
|
||||
color: "text.secondary",
|
||||
height: "100%",
|
||||
}}
|
||||
>
|
||||
<ShowChart sx={{ fontSize: 64, mb: 2, opacity: 0.3 }} />
|
||||
<Typography variant="h6" gutterBottom sx={{ fontWeight: 500 }}>
|
||||
暂无预测数据
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
请在地图上选择已分析的管道
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
|
||||
const renderChart = () => {
|
||||
if (!hasData) return renderEmpty();
|
||||
|
||||
const colors = [
|
||||
"#1976d2",
|
||||
"#dc004e",
|
||||
"#ff9800",
|
||||
"#4caf50",
|
||||
"#9c27b0",
|
||||
"#00bcd4",
|
||||
"#f44336",
|
||||
"#8bc34a",
|
||||
"#ff5722",
|
||||
"#3f51b5",
|
||||
];
|
||||
|
||||
const xData = dataset.map((item) => item.x);
|
||||
|
||||
const series = filteredResults.map((res, index) => ({
|
||||
name: `管道 ${res.link_id}`,
|
||||
type: "line",
|
||||
smooth: true,
|
||||
symbol: "circle",
|
||||
symbolSize: 6,
|
||||
itemStyle: {
|
||||
color: colors[index % colors.length],
|
||||
},
|
||||
data: res.survival_function.y,
|
||||
}));
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: "axis",
|
||||
formatter: (params: any) => {
|
||||
let res = `${params[0].name}年<br/>`;
|
||||
params.forEach((item: any) => {
|
||||
res += `${item.marker} ${item.seriesName}: ${item.value.toFixed(
|
||||
fractionDigits
|
||||
)}<br/>`;
|
||||
});
|
||||
return res;
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
top: "top",
|
||||
type: "scroll",
|
||||
},
|
||||
grid: {
|
||||
left: "5%",
|
||||
right: "5%",
|
||||
bottom: "10%",
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
type: "category",
|
||||
name: "年",
|
||||
boundaryGap: false,
|
||||
data: xData,
|
||||
},
|
||||
yAxis: {
|
||||
type: "value",
|
||||
name: "生存概率",
|
||||
min: 0,
|
||||
max: 1,
|
||||
},
|
||||
series,
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ width: "100%", height: "100%" }}>
|
||||
<ReactECharts
|
||||
option={option}
|
||||
style={{ height: "100%", width: "100%" }}
|
||||
notMerge={true}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Draggable nodeRef={draggableRef}>
|
||||
<Box
|
||||
ref={draggableRef}
|
||||
sx={{
|
||||
position: "absolute",
|
||||
right: "1rem",
|
||||
top: "1rem",
|
||||
width: "min(920px, calc(100vw - 2rem))",
|
||||
maxWidth: "100vw",
|
||||
height: "40vh",
|
||||
maxHeight: "calc(100vh - 2rem)",
|
||||
boxSizing: "border-box",
|
||||
borderRadius: "12px",
|
||||
boxShadow:
|
||||
"0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)",
|
||||
backdropFilter: "blur(8px)",
|
||||
opacity: 0.95,
|
||||
transition: "opacity 0.3s ease-in-out",
|
||||
border: "none",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
zIndex: 1300,
|
||||
backgroundColor: "white",
|
||||
overflow: "hidden",
|
||||
"&:hover": {
|
||||
opacity: 1,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
className="flex flex-col h-full rounded-xl"
|
||||
sx={{ height: "100%", width: "100%" }}
|
||||
>
|
||||
{/* Header */}
|
||||
<Box
|
||||
sx={{
|
||||
p: 2,
|
||||
borderBottom: 1,
|
||||
borderColor: "divider",
|
||||
backgroundColor: "primary.main",
|
||||
color: "primary.contrastText",
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<Stack direction="row" spacing={1} alignItems="center">
|
||||
<ShowChart fontSize="small" />
|
||||
<Typography variant="h6" sx={{ fontWeight: "bold" }}>
|
||||
健康预测曲线
|
||||
</Typography>
|
||||
<Chip
|
||||
size="small"
|
||||
label={`${filteredResults.length}`}
|
||||
sx={{
|
||||
backgroundColor: "rgba(255,255,255,0.2)",
|
||||
color: "primary.contrastText",
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
{/* Content */}
|
||||
<Box sx={{ flex: 1, p: 2, overflow: "hidden" }}>{renderChart()}</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Draggable>
|
||||
);
|
||||
};
|
||||
|
||||
export default PredictDataPanel;
|
||||
Reference in New Issue
Block a user