"use client"; import React, { useEffect, useMemo, useState } from "react"; import { Box, Button, Typography, Checkbox, FormControlLabel, IconButton, Card, CardContent, Chip, Tooltip, Collapse, Link, } from "@mui/material"; import { Info as InfoIcon, EditLocationAlt as EditIcon, } 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 { api } from "@/lib/api"; import moment from "moment"; import { config, NETWORK_NAME } from "@config/config"; import { useNotification } from "@refinedev/core"; import { useMap } from "@components/olmap/core/MapComponent"; import { queryFeaturesByIds } from "@/utils/mapQueryService"; import { useControllableObjectState } from "@components/olmap/core/useControllableState"; 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, { FeatureLike } from "ol/Feature"; import { bbox, featureCollection } from "@turf/turf"; import type { SchemeRecord } from "./types"; import { useSchemeCreatorName } from "@components/olmap/core/useSchemeCreatorName"; import { SchemeQueryEmptyState } from "@components/olmap/common/PanelEmptyState"; interface SchemaItem { id: number; scheme_name: string; sensor_number: number; min_diameter: number; username: string; create_time: string; sensor_location?: string[]; } interface SchemeQueryProps { schemes?: SchemeRecord[]; onSchemesChange?: (schemes: SchemeRecord[]) => void; onEdit?: (id: number) => void; network?: string; state?: MonitoringSchemeQueryState; onStateChange?: (state: MonitoringSchemeQueryState) => void; } export interface MonitoringSchemeQueryState { queryAll: boolean; queryDate: Dayjs | null; expandedId: number | null; hasQueried: boolean; } export const createMonitoringSchemeQueryState = (): MonitoringSchemeQueryState => ({ queryAll: true, queryDate: dayjs(new Date()), expandedId: null, hasQueried: false, }); const SchemeQuery: React.FC = ({ schemes: externalSchemes, onSchemesChange, onEdit, network = NETWORK_NAME, state, onStateChange, }) => { const [queryState, , setQueryField] = useControllableObjectState( state, onStateChange, createMonitoringSchemeQueryState(), ); const { queryAll, queryDate, expandedId, hasQueried } = queryState; const [internalSchemes, setInternalSchemes] = useState([]); const [loading, setLoading] = useState(false); const creatorName = useSchemeCreatorName(); const { open } = useNotification(); const map = useMap(); const [highlightLayer, setHighlightLayer] = useState | null>(null); const [highlightFeatures, setHighlightFeatures] = useState([]); // 使用外部提供的 schemes 或内部状态 const schemes = externalSchemes !== undefined ? externalSchemes : internalSchemes; const setSchemes = onSchemesChange || setInternalSchemes; const sortedSchemes = useMemo( () => schemes .slice() .sort( (a, b) => moment(b.create_time).valueOf() - moment(a.create_time).valueOf(), ), [schemes], ); // 格式化简短日期 const formatShortDate = (timeStr: string) => { const time = moment(timeStr); return time.format("MM-DD"); }; // 初始化管道图层和高亮图层 useEffect(() => { if (!map) return; // 定义传感器样式 const sensorStyle = new Style({ image: new Icon({ src: "/icons/sensor.svg", scale: 0.2, anchor: [0.5, 1], }), }); // 创建高亮图层 const highlightLayer = new VectorLayer({ source: new VectorSource(), style: sensorStyle, maxZoom: 24, minZoom: 12, properties: { name: "传感器高亮", value: "sensor_highlight", queryable: false, }, }); 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 handleQuery = async () => { if (!queryAll && !queryDate) return; setLoading(true); try { const response = await api.get( `${config.BACKEND_URL}/api/v1/sensor-placement-schemes`, ); let filteredResults = response.data; // 按日期过滤 if (!queryAll && queryDate) { const formattedDate = queryDate.format("YYYY-MM-DD"); filteredResults = filteredResults.filter((item: SchemaItem) => { const itemDate = moment(item.create_time).format("YYYY-MM-DD"); return itemDate === formattedDate; }); } const nextSchemes = filteredResults.map((item: SchemaItem) => ({ id: item.id, schemeName: item.scheme_name, sensorNumber: item.sensor_number, minDiameter: item.min_diameter, username: item.username, create_time: item.create_time, sensorLocation: item.sensor_location, })); setSchemes(nextSchemes); setQueryField("hasQueried", true); if (filteredResults.length === 0) { open?.({ type: "success", message: "查询结果", description: queryAll ? "没有找到任何方案" : `${queryDate?.format("YYYY-MM-DD")} 没有找到相关方案`, }); } else { open?.({ type: "success", message: "查询成功", description: `共找到 ${filteredResults.length} 条方案记录`, }); } } catch (error) { console.error("查询请求失败:", error); open?.({ type: "error", message: "查询失败", description: "获取方案列表失败,请稍后重试", }); } finally { setLoading(false); } }; // 定位传感器 const handleLocateSensors = (sensorIds: string[]) => { if (!map) { open?.({ type: "error", message: "地图未加载", }); return; } if (sensorIds.length > 0) { queryFeaturesByIds(sensorIds, "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) => { setQueryField("expandedId", expandedId === id ? null : id); }; return ( {/* 查询条件 - 单行布局 */} { setQueryField("queryAll", e.target.checked); setQueryField("hasQueried", false); }} size="small" /> } label={查询全部日期} className="m-0" /> { if (value && dayjs.isDayjs(value)) { setQueryField("queryDate", value); setQueryField("hasQueried", false); } }} format="YYYY-MM-DD" disabled={queryAll} slotProps={{ textField: { size: "small", sx: { width: 200 }, }, }} /> {/* 结果列表 */} {sortedSchemes.length === 0 ? ( ) : ( 共 {sortedSchemes.length} 条记录 {sortedSchemes.map((scheme) => ( {/* 主要信息行 */} {scheme.schemeName} 最小管径: {scheme.minDiameter} · 用户:{" "} {creatorName(scheme.username)} · 日期:{" "} {formatShortDate(scheme.create_time)} {/* 操作按钮 */} setQueryField( "expandedId", expandedId === scheme.id ? null : scheme.id, ) } color="primary" className="p-1" > {/* 可折叠的详细信息 */} {/* 信息网格布局 */} {/* 传感器信息列 */} 传感器数量: {scheme.sensorNumber} 最小管径: {scheme.minDiameter} {/* 方案信息列 */} 用户: {creatorName(scheme.username)} 创建时间: {moment(scheme.create_time).format( "YYYY-MM-DD HH:mm", )} {/* 传感器位置列表 */} {scheme.sensorLocation && scheme.sensorLocation.length > 0 && ( 传感器位置 ({scheme.sensorLocation.length}个): {scheme.sensorLocation.map( (sensorId, index) => ( { e.preventDefault(); handleLocateSensors([sensorId]); }} > {sensorId} ), )} )} {/* 操作按钮区域 */} {scheme.sensorLocation && scheme.sensorLocation.length > 0 && ( )} ))} )} ); }; export default SchemeQuery;