diff --git a/src/components/olmap/HealthRiskAnalysis/HistoryDataPanel.tsx b/src/components/olmap/HealthRiskAnalysis/HistoryDataPanel.tsx
new file mode 100644
index 0000000..7f73e8d
--- /dev/null
+++ b/src/components/olmap/HealthRiskAnalysis/HistoryDataPanel.tsx
@@ -0,0 +1,313 @@
+"use client";
+
+import React, { useMemo, useRef, useState } from "react";
+import Draggable from "react-draggable";
+
+import { Box, Chip, Stack, Tab, Tabs, Typography } from "@mui/material";
+import { ShowChart, TableChart } from "@mui/icons-material";
+import { DataGrid, GridColDef } from "@mui/x-data-grid";
+import { zhCN } from "@mui/x-data-grid/locales";
+import ReactECharts from "echarts-for-react";
+import "dayjs/locale/zh-cn";
+import { useHealthRisk } from "./HealthRiskContext";
+
+export interface HistoryDataPanelProps {
+ /** 选中的要素信息列表,格式为 [[id, type], [id, type]] */
+ featureInfos: [string, string][];
+ /** 数据类型: realtime-查询模拟值和监测值, none-仅查询监测值, scheme-查询策略模拟值和监测值 */
+ type?: "realtime" | "scheme" | "none";
+ /** 策略类型 */
+ scheme_type?: string;
+ /** 策略名称 */
+ scheme_name?: string;
+ /** 默认展示的选项卡 */
+ defaultTab?: "chart" | "table";
+ /** Y 轴数值的小数位数 */
+ fractionDigits?: number;
+}
+
+type PanelTab = "chart" | "table";
+
+const HistoryDataPanel: React.FC
= ({
+ featureInfos,
+ defaultTab = "chart",
+ fractionDigits = 4,
+}) => {
+ const { predictionResults } = useHealthRisk();
+ const [activeTab, setActiveTab] = useState(defaultTab);
+ const draggableRef = useRef(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 columns: GridColDef[] = useMemo(() => {
+ const base: GridColDef[] = [
+ {
+ field: "label",
+ headerName: "预测时长",
+ minWidth: 120,
+ flex: 1,
+ },
+ ];
+
+ const dynamic = filteredResults.map((res) => ({
+ field: res.link_id,
+ headerName: `管道 ${res.link_id}`,
+ minWidth: 150,
+ flex: 1,
+ valueFormatter: (value: any) => {
+ if (value === null || value === undefined) return "--";
+ return Number(value).toFixed(fractionDigits);
+ },
+ }));
+
+ return [...base, ...dynamic];
+ }, [filteredResults, fractionDigits]);
+
+ const rows = useMemo(
+ () =>
+ dataset.map((item, index) => ({
+ id: index,
+ ...item,
+ })),
+ [dataset]
+ );
+
+ const renderEmpty = () => (
+
+
+
+ 暂无预测数据
+
+
+ 请在地图上选择已分析的管道
+
+
+ );
+
+ 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}年
`;
+ params.forEach((item: any) => {
+ res += `${item.marker} ${item.seriesName}: ${item.value.toFixed(
+ fractionDigits
+ )}
`;
+ });
+ 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 (
+
+
+
+ );
+ };
+
+ const renderTable = () => {
+ if (!hasData) return renderEmpty();
+
+ return (
+
+ );
+ };
+
+ return (
+
+
+ {/* Header */}
+
+
+
+
+
+ 健康预测曲线
+
+
+
+
+
+
+ {/* Tabs */}
+
+ setActiveTab(value)}
+ variant="fullWidth"
+ >
+ }
+ iconPosition="start"
+ label="预测曲线"
+ />
+ }
+ iconPosition="start"
+ label="数据表格"
+ />
+
+
+
+ {/* Content */}
+
+ {activeTab === "chart" ? renderChart() : renderTable()}
+
+
+
+ );
+};
+
+export default HistoryDataPanel;