diff --git a/src/components/olmap/BurstPipeAnalysis/SchemeQuery.tsx b/src/components/olmap/BurstPipeAnalysis/SchemeQuery.tsx
index 7f7f491..a10d40f 100644
--- a/src/components/olmap/BurstPipeAnalysis/SchemeQuery.tsx
+++ b/src/components/olmap/BurstPipeAnalysis/SchemeQuery.tsx
@@ -1,6 +1,8 @@
"use client";
import React, { useEffect, useState } from "react";
+import ReactDOM from "react-dom"; // 添加这行
+
import {
Box,
Button,
@@ -35,9 +37,12 @@ import * as turf from "@turf/turf";
import { GeoJSON } from "ol/format";
import VectorLayer from "ol/layer/Vector";
import VectorSource from "ol/source/Vector";
-import { Stroke, Style } from "ol/style";
-import Feature from "ol/Feature";
-import { set } from "ol/transform";
+import { Stroke, Style, Icon } from "ol/style";
+import Feature, { FeatureLike } from "ol/Feature";
+import { along, lineString, length, toMercator } from "@turf/turf";
+import { Point } from "ol/geom";
+import { toLonLat } from "ol/proj";
+import Timeline from "@app/OlMap/Controls/Timeline";
interface SchemeDetail {
burst_ID: string[];
@@ -72,7 +77,6 @@ interface SchemaItem {
interface SchemeQueryProps {
schemes?: SchemeRecord[];
onSchemesChange?: (schemes: SchemeRecord[]) => void;
- onViewDetails?: (id: number) => void;
onLocate?: (id: number) => void;
network?: string;
}
@@ -80,20 +84,29 @@ interface SchemeQueryProps {
const SchemeQuery: React.FC
= ({
schemes: externalSchemes,
onSchemesChange,
- onViewDetails,
onLocate,
network = NETWORK_NAME,
}) => {
const [queryAll, setQueryAll] = useState(true);
const [queryDate, setQueryDate] = useState(dayjs(new Date()));
- const [internalSchemes, setInternalSchemes] = useState([]);
- const [loading, setLoading] = useState(false);
- const [expandedId, setExpandedId] = useState(null);
- const { open } = useNotification();
-
const [highlightLayer, setHighlightLayer] =
useState | null>(null);
const [highlightFeatures, setHighlightFeatures] = useState([]);
+
+ // 时间轴相关状态
+ const [showTimeline, setShowTimeline] = useState(false);
+ const [selectedDate, setSelectedDate] = useState(undefined);
+ const [timeRange, setTimeRange] = useState<
+ { start: Date; end: Date } | undefined
+ >();
+ const [internalSchemes, setInternalSchemes] = useState([]);
+ const [schemeName, setSchemeName] = useState("");
+ const [loading, setLoading] = useState(false);
+ const [expandedId, setExpandedId] = useState(null);
+ const [mapContainer, setMapContainer] = useState(null); // 地图容器元素
+
+ const { open } = useNotification();
+
const map = useMap();
// 使用外部提供的 schemes 或内部状态
@@ -181,38 +194,95 @@ const SchemeQuery: React.FC = ({
});
}
};
+
+ // 内部的方案查询函数
+ const handleViewDetails = (id: number) => {
+ setShowTimeline(true);
+ // 计算时间范围
+ const scheme = schemes.find((s) => s.id === id);
+ const burstPipeIds = scheme?.schemeDetail?.burst_ID || [];
+ const schemeDate = scheme?.startTime
+ ? new Date(scheme.startTime)
+ : undefined;
+ if (scheme?.startTime && scheme.schemeDetail?.modify_total_duration) {
+ const start = new Date(scheme.startTime);
+ const end = new Date(
+ start.getTime() + scheme.schemeDetail.modify_total_duration * 1000
+ );
+ setSelectedDate(schemeDate);
+ setTimeRange({ start, end });
+ setSchemeName(scheme.schemeName);
+ handleLocatePipes(burstPipeIds);
+ }
+ };
+
// 初始化管道图层和高亮图层
useEffect(() => {
if (!map) return;
-
- // 创建高亮图层 - 爆管管段标识样式
- const highlightLayer = new VectorLayer({
- source: new VectorSource(),
- style: [
- // 外层发光效果(底层)
+ // 获取地图的目标容器
+ const target = map.getTargetElement();
+ if (target) {
+ setMapContainer(target);
+ }
+ const burstPipeStyle = function (feature: FeatureLike) {
+ const styles = [];
+ // 线条样式(底层发光,主线条,内层高亮线)
+ styles.push(
new Style({
stroke: new Stroke({
color: "rgba(255, 0, 0, 0.3)",
width: 12,
}),
}),
- // 主线条 - 使用虚线表示爆管
new Style({
stroke: new Stroke({
- color: "#ff0000",
+ color: "rgba(255, 0, 0, 1)",
width: 6,
- lineDash: [15, 10], // 虚线样式,表示管道损坏/爆管
- }),
- }),
- // 内层高亮线
- new Style({
- stroke: new Stroke({
- color: "#ff6666",
- width: 3,
lineDash: [15, 10],
}),
}),
- ],
+ new Style({
+ stroke: new Stroke({
+ color: "rgba(255, 102, 102, 1)",
+ width: 3,
+ lineDash: [15, 10],
+ }),
+ })
+ );
+ const geometry = feature.getGeometry();
+ const lineCoords =
+ geometry?.getType() === "LineString"
+ ? (geometry as any).getCoordinates()
+ : null;
+ if (geometry) {
+ const lineCoordsWGS84 = lineCoords.map((coord: []) => {
+ const [lon, lat] = toLonLat(coord);
+ return [lon, lat];
+ });
+ // 计算中点
+ const lineStringFeature = lineString(lineCoordsWGS84);
+ const lineLength = length(lineStringFeature);
+ const midPoint = along(lineStringFeature, lineLength / 2).geometry
+ .coordinates;
+ // 在中点添加 icon 样式
+ const midPointMercator = toMercator(midPoint);
+ styles.push(
+ new Style({
+ geometry: new Point(midPointMercator),
+ image: new Icon({
+ src: "/icons/burst_pipe_icon.svg",
+ scale: 0.2,
+ anchor: [0.5, 1],
+ }),
+ })
+ );
+ }
+ return styles;
+ };
+ // 创建高亮图层 - 爆管管段标识样式
+ const highlightLayer = new VectorLayer({
+ source: new VectorSource(),
+ style: burstPipeStyle,
properties: {
name: "爆管管段高亮",
value: "burst_pipe_highlight",
@@ -247,348 +317,366 @@ const SchemeQuery: React.FC = ({
}, [highlightFeatures]);
return (
-
- {/* 查询条件 - 单行布局 */}
-
-
-
- setQueryAll(e.target.checked)}
- size="small"
- />
- }
- label={查询全部}
- className="m-0"
- />
-
-
- value && dayjs.isDayjs(value) && setQueryDate(value)
+ <>
+ {/* 将时间轴渲染到地图容器中 */}
+ {showTimeline &&
+ mapContainer &&
+ ReactDOM.createPortal(
+ ,
+ mapContainer // 渲染到地图容器中,而不是 body
+ )}
+
+ {/* 查询条件 - 单行布局 */}
+
+
+
+ setQueryAll(e.target.checked)}
+ size="small"
+ />
}
- format="YYYY-MM-DD"
- disabled={queryAll}
- slotProps={{
- textField: {
- size: "small",
- sx: { width: 200 },
- },
- }}
+ label={查询全部}
+ className="m-0"
/>
-
-
-
-
-
-
- {/* 结果列表 */}
-
- {schemes.length === 0 ? (
-
-
-
+
- 总共 0 条
-
- No data
-
+
- ) : (
-
-
- 共 {schemes.length} 条记录
-
- {schemes.map((scheme) => (
-
-
- {/* 主要信息行 */}
-
-
-
-
- {scheme.schemeName}
-
-
-
-
- ID: {scheme.id} · 日期: {formatTime(scheme.create_time)}
-
-
- {/* 操作按钮 */}
-
-
-
- setExpandedId(
- expandedId === scheme.id ? null : scheme.id
- )
- }
- color="primary"
- className="p-1"
- >
-
-
-
-
- onLocate?.(scheme.id)}
- color="primary"
- className="p-1"
- >
-
-
-
-
-
+
- {/* 可折叠的详细信息 */}
-
-
- {/* 信息网格布局 */}
-
- {/* 爆管详情列 */}
-
-
-
-
- 管段ID:
-
-
- {scheme.schemeDetail?.burst_ID?.length ? (
- scheme.schemeDetail.burst_ID.map(
- (pipeId, index) => (
- {
- e.preventDefault();
- handleLocatePipes?.([pipeId]);
- }}
- >
- {pipeId}
-
+ {/* 结果列表 */}
+
+ {schemes.length === 0 ? (
+
+
+
+
+ 总共 0 条
+
+ No data
+
+
+ ) : (
+
+
+ 共 {schemes.length} 条记录
+
+ {schemes.map((scheme) => (
+
+
+ {/* 主要信息行 */}
+
+
+
+
+ {scheme.schemeName}
+
+
+
+
+ ID: {scheme.id} · 日期:{" "}
+ {formatTime(scheme.create_time)}
+
+
+ {/* 操作按钮 */}
+
+
+
+ setExpandedId(
+ expandedId === scheme.id ? null : scheme.id
+ )
+ }
+ color="primary"
+ className="p-1"
+ >
+
+
+
+
+ onLocate?.(scheme.id)}
+ color="primary"
+ className="p-1"
+ >
+
+
+
+
+
+
+ {/* 可折叠的详细信息 */}
+
+
+ {/* 信息网格布局 */}
+
+ {/* 爆管详情列 */}
+
+
+
+
+ 管段ID:
+
+
+ {scheme.schemeDetail?.burst_ID?.length ? (
+ scheme.schemeDetail.burst_ID.map(
+ (pipeId, index) => (
+ {
+ e.preventDefault();
+ handleLocatePipes?.([pipeId]);
+ }}
+ >
+ {pipeId}
+
+ )
)
- )
- ) : (
-
- N/A
-
- )}
+ ) : (
+
+ N/A
+
+ )}
+
+
+
+
+ 管径:
+
+
+ 560 mm
+
+
+
+
+ 爆管面积:
+
+
+ {scheme.schemeDetail?.burst_size?.[0] ||
+ "N/A"}{" "}
+ cm²
+
+
+
+
+ 持续时间:
+
+
+ {scheme.schemeDetail?.modify_total_duration ||
+ "N/A"}{" "}
+ 秒
+
-
-
- 管径:
-
-
- 560 mm
-
-
-
-
- 爆管面积:
-
-
- {scheme.schemeDetail?.burst_size?.[0] || "N/A"}{" "}
- cm²
-
-
-
-
- 持续时间:
-
-
- {scheme.schemeDetail?.modify_total_duration ||
- "N/A"}{" "}
- 秒
-
+
+
+ {/* 方案信息列 */}
+
+
+
+
+ 用户:
+
+
+ {scheme.user}
+
+
+
+
+ 创建时间:
+
+
+ {moment(scheme.create_time).format(
+ "YYYY-MM-DD HH:mm"
+ )}
+
+
+
+
+ 开始时间:
+
+
+ {moment(scheme.startTime).format(
+ "YYYY-MM-DD HH:mm"
+ )}
+
+
- {/* 方案信息列 */}
-
-
-
-
- 用户:
-
-
- {scheme.user}
-
-
-
-
- 创建时间:
-
-
- {moment(scheme.create_time).format(
- "YYYY-MM-DD HH:mm"
- )}
-
-
-
-
- 开始时间:
-
-
- {moment(scheme.startTime).format(
- "YYYY-MM-DD HH:mm"
- )}
-
-
-
-
-
-
- {/* 操作按钮区域 */}
-
- {scheme.schemeDetail?.burst_ID?.length ? (
+ {/* 操作按钮区域 */}
+
+ {scheme.schemeDetail?.burst_ID?.length ? (
+
+ ) : null}
- ) : null}
-
+
-
-
-
-
- ))}
-
- )}
+
+
+
+ ))}
+
+ )}
+
-
+ >
);
};
diff --git a/src/components/olmap/BurstPipeAnalysisPanel.tsx b/src/components/olmap/BurstPipeAnalysisPanel.tsx
index 952dffd..5321b05 100644
--- a/src/components/olmap/BurstPipeAnalysisPanel.tsx
+++ b/src/components/olmap/BurstPipeAnalysisPanel.tsx
@@ -12,7 +12,6 @@ import {
import AnalysisParameters from "./BurstPipeAnalysis/AnalysisParameters";
import SchemeQuery from "./BurstPipeAnalysis/SchemeQuery";
import LocationResults from "./BurstPipeAnalysis/LocationResults";
-
interface SchemeDetail {
burst_ID: string[];
burst_size: number[];
@@ -81,7 +80,6 @@ const BurstPipeAnalysisPanel: React.FC = ({
const handleTabChange = (_event: React.SyntheticEvent, newValue: number) => {
setCurrentTab(newValue);
};
-
const drawerWidth = 520;
return (
@@ -89,7 +87,7 @@ const BurstPipeAnalysisPanel: React.FC = ({
{/* 收起时的触发按钮 */}
{!isOpen && (
@@ -205,10 +203,6 @@ const BurstPipeAnalysisPanel: React.FC = ({
{
- console.log("查看详情:", id);
- // TODO: 显示方案详情
- }}
onLocate={(id) => {
console.log("定位方案:", id);
// TODO: 在地图上定位
diff --git a/src/components/olmap/SCADADataPanel.tsx b/src/components/olmap/SCADADataPanel.tsx
index 81a96c0..73ff8c5 100644
--- a/src/components/olmap/SCADADataPanel.tsx
+++ b/src/components/olmap/SCADADataPanel.tsx
@@ -356,7 +356,7 @@ const SCADADataPanel: React.FC = ({
{/* Header */}