"use client"; import React, { useEffect, useState } from "react"; import ReactDOM from "react-dom"; // 添加这行 import { Box, Button, Typography, Checkbox, FormControlLabel, IconButton, Card, CardContent, Chip, Tooltip, Collapse, Link, } from "@mui/material"; import { Info as InfoIcon, LocationOn as LocationIcon, } from "@mui/icons-material"; import { DatePicker } from "@mui/x-date-pickers/DatePicker"; import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider"; import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; import dayjs, { Dayjs } from "dayjs"; import "dayjs/locale/zh-cn"; import axios from "axios"; import moment from "moment"; import { config, NETWORK_NAME } from "@config/config"; import { useNotification } from "@refinedev/core"; import { queryFeaturesByIds } from "@/utils/mapQueryService"; import { useData, useMap } from "@app/OlMap/MapComponent"; import { GeoJSON } from "ol/format"; import VectorLayer from "ol/layer/Vector"; import VectorSource from "ol/source/Vector"; import { Stroke, Style, Icon } from "ol/style"; import Feature, { FeatureLike } from "ol/Feature"; import { along, lineString, length, toMercator, bbox, featureCollection, } 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[]; burst_size: number[]; modify_total_duration: number; modify_fixed_pump_pattern: any; modify_valve_opening: any; modify_variable_pump_pattern: any; } interface SchemeRecord { id: number; schemeName: string; type: string; user: string; create_time: string; startTime: string; // 详情信息 schemeDetail?: SchemeDetail; } interface SchemaItem { scheme_id: number; scheme_name: string; scheme_type: string; username: string; create_time: string; scheme_start_time: string; scheme_detail?: SchemeDetail; } interface SchemeQueryProps { schemes?: SchemeRecord[]; onSchemesChange?: (schemes: SchemeRecord[]) => void; onLocate?: (id: number) => void; network?: string; } const SchemeQuery: React.FC = ({ schemes: externalSchemes, onSchemesChange, onLocate, network = NETWORK_NAME, }) => { const [queryAll, setQueryAll] = useState(true); const [queryDate, setQueryDate] = useState(dayjs(new Date())); 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 [loading, setLoading] = useState(false); const [expandedId, setExpandedId] = useState(null); const [mapContainer, setMapContainer] = useState(null); // 地图容器元素 const { open } = useNotification(); const map = useMap(); const data = useData(); if (!data) return null; const { schemeName, setSchemeName } = data; // 使用外部提供的 schemes 或内部状态 const schemes = externalSchemes !== undefined ? externalSchemes : internalSchemes; const setSchemes = onSchemesChange || setInternalSchemes; // 格式化日期为简短格式 const formatTime = (timeStr: string) => { const time = moment(timeStr); return time.format("MM-DD"); }; const handleQuery = async () => { if (!queryAll && !queryDate) return; setLoading(true); try { const response = await axios.get( `${config.backendUrl}/getallschemes/?network=${network}` ); let filteredResults = response.data; if (!queryAll) { const formattedDate = queryDate!.format("YYYY-MM-DD"); filteredResults = response.data.filter((item: SchemaItem) => { const itemDate = moment(item.create_time).format("YYYY-MM-DD"); return itemDate === formattedDate; }); } setSchemes( filteredResults.map((item: SchemaItem) => ({ id: item.scheme_id, schemeName: item.scheme_name, type: item.scheme_type, user: item.username, create_time: item.create_time, startTime: item.scheme_start_time, schemeDetail: item.scheme_detail, })) ); if (filteredResults.length === 0) { open?.({ type: "error", message: "查询结果", description: queryAll ? "没有找到任何方案" : `${queryDate!.format("YYYY-MM-DD")} 没有找到相关方案`, }); } } catch (error) { console.error("查询请求失败:", error); open?.({ type: "error", message: "查询失败", description: "获取方案列表失败,请稍后重试", }); } finally { setLoading(false); } }; const handleLocatePipes = (pipeIds: string[]) => { if (pipeIds.length > 0) { queryFeaturesByIds(pipeIds).then((features) => { if (features.length > 0) { // 设置高亮要素 setHighlightFeatures(features); // 将 OpenLayers Feature 转换为 GeoJSON Feature const geojsonFormat = new GeoJSON(); const geojsonFeatures = features.map((feature) => geojsonFormat.writeFeatureObject(feature) ); const extent = bbox(featureCollection(geojsonFeatures as any)); if (extent) { map?.getView().fit(extent, { maxZoom: 18, duration: 1000 }); } } }); } }; // 内部的方案查询函数 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 }); if (setSchemeName) { setSchemeName(scheme.schemeName); } handleLocatePipes(burstPipeIds); } }; // 初始化管道图层和高亮图层 useEffect(() => { if (!map) return; // 获取地图的目标容器 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: "rgba(255, 0, 0, 1)", width: 6, 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.svg", scale: 0.2, anchor: [0.5, 1], }), }) ); } return styles; }; // 创建高亮图层 const highlightLayer = new VectorLayer({ source: new VectorSource(), style: burstPipeStyle, maxZoom: 24, minZoom: 12, properties: { name: "爆管管段高亮", value: "burst_pipe_highlight", }, }); map.addLayer(highlightLayer); setHighlightLayer(highlightLayer); return () => { map.removeLayer(highlightLayer); }; }, [map]); // 高亮要素的函数 useEffect(() => { if (!highlightLayer) { return; } const source = highlightLayer.getSource(); if (!source) { return; } // 清除之前的高亮 source.clear(); // 添加新的高亮要素 highlightFeatures.forEach((feature) => { if (feature instanceof Feature) { source.addFeature(feature); } }); }, [highlightFeatures]); return ( <> {/* 将时间轴渲染到地图容器中 */} {showTimeline && mapContainer && ReactDOM.createPortal( , mapContainer // 渲染到地图容器中,而不是 body )} {/* 查询条件 - 单行布局 */} setQueryAll(e.target.checked)} size="small" /> } label={查询全部} className="m-0" /> value && dayjs.isDayjs(value) && setQueryDate(value) } format="YYYY-MM-DD" disabled={queryAll} slotProps={{ textField: { size: "small", sx: { width: 200 }, }, }} /> {/* 结果列表 */} {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 )} 管径: 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.schemeDetail?.burst_ID?.length ? ( ) : null} ))} )} ); }; export default SchemeQuery;