"use client"; import React, { useEffect, useMemo, useState } from "react"; import ReactDOM from "react-dom"; import { Box, Button, Card, CardContent, Checkbox, Chip, Collapse, FormControlLabel, IconButton, Link, Tooltip, Typography, } 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/locale/zh-cn"; import dayjs, { Dayjs } from "dayjs"; import axios from "axios"; import moment from "moment"; import { useNotification } from "@refinedev/core"; import { config, NETWORK_NAME } from "@config/config"; 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 { Style, Icon, Circle, Fill, Stroke } from "ol/style"; import Feature from "ol/Feature"; import { bbox, featureCollection } from "@turf/turf"; import Timeline from "@app/OlMap/Controls/Timeline"; import { ContaminantSchemaItem, ContaminantSchemeRecord } from "./types"; interface SchemeQueryProps { schemes?: ContaminantSchemeRecord[]; onSchemesChange?: (schemes: ContaminantSchemeRecord[]) => void; onViewResults?: () => void; network?: string; } const SCHEME_TYPE = "contaminant_analysis"; const SchemeQuery: React.FC = ({ schemes: externalSchemes, onSchemesChange, onViewResults, 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< ContaminantSchemeRecord[] >([]); const [loading, setLoading] = useState(false); const [expandedId, setExpandedId] = useState(null); const [mapContainer, setMapContainer] = useState(null); const { open } = useNotification(); const map = useMap(); const data = useData(); const { schemeName, setSchemeName } = data || {}; const schemes = externalSchemes !== undefined ? externalSchemes : internalSchemes; const setSchemes = onSchemesChange || setInternalSchemes; useEffect(() => { if (!map) return; const target = map.getTargetElement(); if (target) { setMapContainer(target); } }, [map]); // 初始化高亮图层 useEffect(() => { if (!map) return; const themeColor = "rgba(3, 168, 107"; // #03a86b const sourceStyle = [ // 外层扩散光圈 new Style({ image: new Circle({ radius: 12, fill: new Fill({ color: `${themeColor}, 0.2)`, }), }), }), // 中层扩散背景 new Style({ image: new Circle({ radius: 8, stroke: new Stroke({ color: `${themeColor}, 0.5)`, width: 2, }), fill: new Fill({ color: `${themeColor}, 0.3)`, }), }), }), // 上层图标 new Style({ image: new Icon({ src: "/icons/contaminant_source.svg", scale: 0.2, anchor: [0.5, 1], }), }), ]; const highlightLayer = new VectorLayer({ source: new VectorSource(), style: sourceStyle, maxZoom: 24, minZoom: 12, properties: { name: "污染源高亮", value: "contaminant_source_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, highlightLayer]); const formatTime = (timeStr: string) => { const time = moment(timeStr); return time.format("MM-DD"); }; const filteredSchemes = useMemo(() => { return schemes.filter((scheme) => scheme.type === SCHEME_TYPE); }, [schemes]); const handleQuery = async () => { if (!queryAll && !queryDate) return; setLoading(true); try { const response = await axios.get( `${config.BACKEND_URL}/api/v1/getallschemes/?network=${network}`, ); let filteredResults = response.data; if (!queryAll) { const formattedDate = queryDate!.format("YYYY-MM-DD"); filteredResults = response.data.filter( (item: ContaminantSchemaItem) => { const itemDate = moment(item.create_time).format("YYYY-MM-DD"); return itemDate === formattedDate; }, ); } setSchemes( filteredResults.map((item: ContaminantSchemaItem) => ({ 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 handleLocateSource = (sourceIds: string[]) => { if (sourceIds.length > 0) { queryFeaturesByIds(sourceIds, "geo_junctions_mat").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) => { const scheme = filteredSchemes.find((s) => s.id === id); if (!scheme) return; setShowTimeline(true); const schemeDate = scheme.startTime ? new Date(scheme.startTime) : undefined; if (scheme.startTime && scheme.schemeDetail?.duration) { const start = new Date(scheme.startTime); const end = new Date( start.getTime() + scheme.schemeDetail.duration * 1000, ); setSelectedDate(schemeDate); setTimeRange({ start, end }); } setSchemeName?.(scheme.schemeName); if (scheme.schemeDetail?.source) { handleLocateSource([scheme.schemeDetail.source]); } }; return ( <> {showTimeline && mapContainer && ReactDOM.createPortal( , mapContainer, )} 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 }, }, }} /> {filteredSchemes.length === 0 ? ( 总共 0 条 No data ) : ( 共 {filteredSchemes.length} 条记录 {filteredSchemes.map((scheme) => ( {scheme.schemeName} ID: {scheme.id} · 日期:{" "} {formatTime(scheme.create_time)} setExpandedId( expandedId === scheme.id ? null : scheme.id, ) } color="primary" className="p-1" > {/* scheme.schemeDetail?.source && handleLocateSource([scheme.schemeDetail.source]) } color="primary" className="p-1" > */} 污染源: {scheme.schemeDetail?.source ? ( { e.preventDefault(); handleLocateSource([ scheme.schemeDetail!.source!, ]); }} > {scheme.schemeDetail.source} ) : ( N/A )} 浓度: {scheme.schemeDetail?.concentration ?? "N/A"}{" "} mg/L 持续时间: {scheme.schemeDetail?.duration ?? "N/A"} 秒 模式: {scheme.schemeDetail?.pattern || "无"} 用户: {scheme.user} 创建时间: {moment(scheme.create_time).format( "YYYY-MM-DD HH:mm", )} 开始时间: {moment(scheme.startTime).format( "YYYY-MM-DD HH:mm", )} ))} )} ); }; export default SchemeQuery;