import React, { useState, useEffect, useCallback } from "react"; import { useData, useMap } from "../MapComponent"; import ToolbarButton from "@/components/olmap/common/ToolbarButton"; import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; import EditOutlinedIcon from "@mui/icons-material/EditOutlined"; import PaletteOutlinedIcon from "@mui/icons-material/PaletteOutlined"; import PropertyPanel from "./PropertyPanel"; // 引入属性面板组件 import DrawPanel from "./DrawPanel"; // 引入绘图面板组件 import VectorSource from "ol/source/Vector"; import VectorLayer from "ol/layer/Vector"; import { Style, Stroke, Fill, Circle } from "ol/style"; import { FeatureLike } from "ol/Feature"; import Feature from "ol/Feature"; import StyleEditorPanel from "./StyleEditorPanel"; import StyleLegend from "./StyleLegend"; // 引入图例组件 import { handleMapClickSelectFeatures as mapClickSelectFeatures } from "@/utils/mapQueryService"; import { config } from "@/config/config"; const backendUrl = config.backendUrl; // 图层样式状态接口 interface StyleConfig { property: string; classificationMethod: string; segments: number; minSize: number; maxSize: number; minStrokeWidth: number; maxStrokeWidth: number; fixedStrokeWidth: number; colorType: string; startColor: string; endColor: string; showLabels: boolean; opacity: number; adjustWidthByProperty: boolean; } interface LegendStyleConfig { layerId: string; layerName: string; property: string; colors: string[]; type: string; dimensions: number[]; breaks: number[]; } interface LayerStyleState { layerId: string; layerName: string; styleConfig: StyleConfig; legendConfig: LegendStyleConfig; isActive: boolean; } // 添加接口定义隐藏按钮的props interface ToolbarProps { hiddenButtons?: string[]; // 可选的隐藏按钮列表,例如 ['info', 'draw', 'style'] queryType?: string; // 可选的查询类型参数 } const Toolbar: React.FC = ({ hiddenButtons, queryType }) => { const map = useMap(); const data = useData(); if (!data) return null; const { currentTime, selectedDate, schemeName } = data; const [activeTools, setActiveTools] = useState([]); const [highlightFeature, setHighlightFeature] = useState( null ); const [showPropertyPanel, setShowPropertyPanel] = useState(false); const [showDrawPanel, setShowDrawPanel] = useState(false); const [showStyleEditor, setShowStyleEditor] = useState(false); const [highlightLayer, setHighlightLayer] = useState | null>(null); // 样式状态管理 - 在 Toolbar 中管理,带有默认样式 const [layerStyleStates, setLayerStyleStates] = useState([ { isActive: false, // 默认不激活,不显示图例 layerId: "junctions", layerName: "节点图层", styleConfig: { property: "pressure", classificationMethod: "pretty_breaks", segments: 6, minSize: 4, maxSize: 12, minStrokeWidth: 2, maxStrokeWidth: 8, fixedStrokeWidth: 3, colorType: "gradient", startColor: "rgba(51, 153, 204, 0.9)", endColor: "rgba(204, 51, 51, 0.9)", showLabels: false, opacity: 0.9, adjustWidthByProperty: true, }, legendConfig: { layerId: "junctions", layerName: "节点图层", property: "压力", // 暂时为空,等计算后更新 colors: [], type: "point", dimensions: [], breaks: [], }, }, { isActive: false, // 默认不激活,不显示图例 layerId: "pipes", layerName: "管道图层", styleConfig: { property: "flow", classificationMethod: "pretty_breaks", segments: 6, minSize: 4, maxSize: 12, minStrokeWidth: 2, maxStrokeWidth: 8, fixedStrokeWidth: 3, colorType: "gradient", startColor: "rgba(51, 153, 204, 0.9)", endColor: "rgba(204, 51, 51, 0.9)", showLabels: false, opacity: 0.9, adjustWidthByProperty: true, }, legendConfig: { layerId: "pipes", layerName: "管道图层", property: "流量", // 暂时为空,等计算后更新 colors: [], type: "linestring", dimensions: [], breaks: [], }, }, ]); // 计算激活的图例配置 const activeLegendConfigs = layerStyleStates .filter((state) => state.isActive && state.legendConfig.property) .map((state) => ({ ...state.legendConfig, layerName: state.layerName, layerId: state.layerId, })); // 创建高亮图层 useEffect(() => { if (!map) return; const highLightSource = new VectorSource(); const highLightLayer = new VectorLayer({ source: highLightSource, style: new Style({ stroke: new Stroke({ color: `rgba(255, 0, 0, 1)`, width: 5, }), fill: new Fill({ color: `rgba(255, 0, 0, 0.2)`, }), image: new Circle({ radius: 7, stroke: new Stroke({ color: `rgba(255, 0, 0, 1)`, width: 3, }), fill: new Fill({ color: `rgba(255, 0, 0, 0.2)`, }), }), }), }); map.addLayer(highLightLayer); setHighlightLayer(highLightLayer); return () => { map.removeLayer(highLightLayer); }; }, [map]); // 高亮要素的函数 useEffect(() => { if (!highlightLayer) { return; } const source = highlightLayer.getSource(); if (!source) { return; } // 清除之前的高亮 source.clear(); // 添加新的高亮要素 if (highlightFeature instanceof Feature) { source.addFeature(highlightFeature); } }, [highlightFeature]); // 地图点击选择要素事件处理函数 const handleMapClickSelectFeatures = useCallback( async (event: { coordinate: number[] }) => { if (!map) return; const feature = await mapClickSelectFeatures(event, map); // 调用导入的函数 setHighlightFeature(feature); }, [map, setHighlightFeature] ); // 添加矢量属性查询事件监听器 useEffect(() => { if (!activeTools.includes("info") || !map) return; map.on("click", handleMapClickSelectFeatures); return () => { map.un("click", handleMapClickSelectFeatures); }; }, [activeTools, map, handleMapClickSelectFeatures]); // 处理工具栏按钮点击事件 const handleToolClick = (tool: string) => { // 样式工具的特殊处理 - 只有再次点击时才会取消激活和关闭 if (tool === "style") { if (activeTools.includes("style")) { // 如果样式工具已激活,点击时关闭 setShowStyleEditor(false); setActiveTools((prev) => prev.filter((t) => t !== "style")); } else { // 激活样式工具,打开样式面板 setActiveTools((prev) => [...prev, "style"]); setShowStyleEditor(true); } return; } // 其他工具的处理逻辑 if (activeTools.includes(tool)) { // 如果当前工具已激活,再次点击时取消激活并关闭面板 deactivateTool(tool); setActiveTools((prev) => prev.filter((t) => t !== tool)); } else { // 如果当前工具未激活,先关闭所有其他工具,然后激活当前工具 // 关闭所有面板(但保持样式编辑器状态) closeAllPanelsExceptStyle(); // 取消激活所有非样式工具 setActiveTools((prev) => { const styleActive = prev.includes("style"); return styleActive ? ["style", tool] : [tool]; }); // 激活当前工具并打开对应面板 activateTool(tool); } }; // 取消激活指定工具并关闭对应面板 const deactivateTool = (tool: string) => { switch (tool) { case "info": setShowPropertyPanel(false); setHighlightFeature(null); break; case "draw": setShowDrawPanel(false); break; } }; // 激活指定工具并打开对应面板 const activateTool = (tool: string) => { switch (tool) { case "info": setShowPropertyPanel(true); break; case "draw": setShowDrawPanel(true); break; } }; // 关闭所有面板(除了样式编辑器) const closeAllPanelsExceptStyle = () => { setShowPropertyPanel(false); setHighlightFeature(null); setShowDrawPanel(false); // 样式编辑器保持其当前状态,不自动关闭 }; const [computedProperties, setComputedProperties] = useState< Record >({}); // 添加 useEffect 来查询计算属性 useEffect(() => { if (!highlightFeature || !selectedDate) { setComputedProperties({}); return; } const id = highlightFeature.getProperties().id; if (!id) { setComputedProperties({}); return; } const queryComputedProperties = async () => { try { const properties = highlightFeature?.getProperties?.() || {}; const type = properties.geometry?.getType?.() === "LineString" ? "link" : "node"; // selectedDate 格式化为 YYYY-MM-DD let dateObj: Date; if (selectedDate instanceof Date) { dateObj = new Date(selectedDate); } else { dateObj = new Date(selectedDate); } const minutes = Number(currentTime) || 0; dateObj.setHours(Math.floor(minutes / 60), minutes % 60, 0, 0); // 转为 UTC ISO 字符串 const querytime = dateObj.toISOString(); // 例如 "2025-09-16T16:30:00.000Z" let response; if (queryType === "scheme") { response = await fetch( `${backendUrl}/queryschemesimulationrecordsbyidtime/?scheme_name=${schemeName}&id=${id}&querytime=${querytime}&type=${type}` ); } else { response = await fetch( `${backendUrl}/querysimulationrecordsbyidtime/?id=${id}&querytime=${querytime}&type=${type}` ); } if (!response.ok) { throw new Error("API request failed"); } const data = await response.json(); setComputedProperties(data.results[0] || {}); } catch (error) { console.error("Error querying computed properties:", error); setComputedProperties({}); } }; // 仅当 currentTime 有效时查询 if (currentTime !== -1 && queryType) queryComputedProperties(); }, [highlightFeature, currentTime, selectedDate]); // 从要素属性中提取属性面板需要的数据 const getFeatureProperties = useCallback(() => { if (!highlightFeature) return {}; const properties = highlightFeature.getProperties(); // 计算属性字段,增加 key 字段 const pipeComputedFields = [ { key: "flow", label: "流量", unit: "m³/s" }, { key: "friction", label: "摩阻", unit: "" }, { key: "headloss", label: "水头损失", unit: "m" }, { key: "quality", label: "水质", unit: "mg/L" }, { key: "reaction", label: "反应", unit: "1/s" }, { key: "setting", label: "设置", unit: "" }, { key: "status", label: "状态", unit: "" }, { key: "velocity", label: "流速", unit: "m/s" }, ]; const nodeComputedFields = [ { key: "actualdemand", label: "实际需水量", unit: "m³/s" }, { key: "head", label: "水头", unit: "m" }, { key: "pressure", label: "压力", unit: "kPa" }, { key: "quality", label: "水质", unit: "mg/L" }, ]; if (properties.geometry.getType() === "LineString") { let result = { id: properties.id, type: "管道", properties: [ { label: "起始节点ID", value: properties.node1 }, { label: "终点节点ID", value: properties.node2 }, { label: "长度", value: properties.length?.toFixed?.(1), unit: "m" }, { label: "管径", value: properties.diameter?.toFixed?.(1), unit: "mm", }, { label: "粗糙度", value: properties.roughness }, { label: "局部损失", value: properties.minor_loss }, { label: "初始状态", value: "开" }, ], }; // 追加计算属性 if (computedProperties) { pipeComputedFields.forEach(({ key, label, unit }) => { if (computedProperties[key] !== undefined) { result.properties.push({ label, value: computedProperties[key].toFixed?.(2) || computedProperties[key], unit, }); } }); } return result; } if (properties.geometry.getType() === "Point") { let result = { id: properties.id, type: "节点", properties: [ { label: "海拔", value: properties.elevation?.toFixed?.(1), unit: "m", }, { label: "需求量", value: properties.demand?.toFixed?.(1), unit: "m³/s", }, ], }; // 追加计算属性 if (computedProperties) { nodeComputedFields.forEach(({ key, label, unit }) => { if (computedProperties[key] !== undefined) { result.properties.push({ label, value: computedProperties[key].toFixed?.(2) || computedProperties[key], unit, }); } }); } return result; } return {}; }, [highlightFeature, computedProperties]); return ( <>
{!hiddenButtons?.includes("info") && ( } name="查看属性" isActive={activeTools.includes("info")} onClick={() => handleToolClick("info")} /> )} {!hiddenButtons?.includes("draw") && ( } name="矢量编辑" isActive={activeTools.includes("draw")} onClick={() => handleToolClick("draw")} /> )} {!hiddenButtons?.includes("style") && ( } name="图层样式" isActive={activeTools.includes("style")} onClick={() => handleToolClick("style")} /> )}
{showPropertyPanel && } {showDrawPanel && map && } {showStyleEditor && ( )} {/* 图例显示 */} {activeLegendConfigs.length > 0 && (
{activeLegendConfigs.map((config, index) => ( ))}
)} ); }; export default Toolbar;