From 3b4f7fd694aeebe03f0f3fa543c3f3ceb0f4f339 Mon Sep 17 00:00:00 2001 From: JIANG Date: Fri, 19 Dec 2025 16:27:59 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9Efeatures=E5=A4=9A=E9=80=89?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/OlMap/Controls/Toolbar.tsx | 212 +++++++++++++++++++---------- 1 file changed, 141 insertions(+), 71 deletions(-) diff --git a/src/app/OlMap/Controls/Toolbar.tsx b/src/app/OlMap/Controls/Toolbar.tsx index 7a84306..2ea00aa 100644 --- a/src/app/OlMap/Controls/Toolbar.tsx +++ b/src/app/OlMap/Controls/Toolbar.tsx @@ -12,12 +12,12 @@ import HistoryDataPanel from "./HistoryDataPanel"; // 引入绘图面板组件 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 { LayerStyleState } from "./StyleEditorPanel"; import StyleLegend from "./StyleLegend"; // 引入图例组件 import { handleMapClickSelectFeatures as mapClickSelectFeatures } from "@/utils/mapQueryService"; +import { useNotification } from "@refinedev/core"; import { config } from "@/config/config"; const backendUrl = config.BACKEND_URL; @@ -35,12 +35,11 @@ const Toolbar: React.FC = ({ }) => { const map = useMap(); const data = useData(); + const { open } = useNotification(); if (!data) return null; const { currentTime, selectedDate, schemeName } = data; const [activeTools, setActiveTools] = useState([]); - const [highlightFeature, setHighlightFeature] = useState( - null - ); + const [highlightFeatures, setHighlightFeatures] = useState([]); const [showPropertyPanel, setShowPropertyPanel] = useState(false); const [showDrawPanel, setShowDrawPanel] = useState(false); const [showStyleEditor, setShowStyleEditor] = useState(false); @@ -56,7 +55,7 @@ const Toolbar: React.FC = ({ layerName: "节点图层", styleConfig: { property: "pressure", - classificationMethod: "pretty_breaks", + classificationMethod: "custom_breaks", segments: 6, minSize: 4, maxSize: 12, @@ -175,18 +174,74 @@ const Toolbar: React.FC = ({ // 清除之前的高亮 source.clear(); // 添加新的高亮要素 - if (highlightFeature instanceof Feature) { - source.addFeature(highlightFeature); - } - }, [highlightFeature]); + highlightFeatures.forEach((feature) => { + if (feature instanceof Feature) { + source.addFeature(feature); + } + }); + }, [highlightFeatures, highlightLayer]); // 地图点击选择要素事件处理函数 const handleMapClickSelectFeatures = useCallback( async (event: { coordinate: number[] }) => { if (!map) return; const feature = await mapClickSelectFeatures(event, map); // 调用导入的函数 - setHighlightFeature(feature); + if (!feature || !(feature instanceof Feature)) return; + + if (activeTools.includes("history")) { + // 历史查询模式:支持同类型多选 + const featureId = feature.getProperties().id; + const layerId = feature.getId()?.toString().split(".")[0] || ""; + + // 简单的类型检查函数 + const getBaseType = (lid: string) => { + if (lid.includes("pipe")) return "pipe"; + if (lid.includes("junction")) return "junction"; + if (lid.includes("tank")) return "tank"; + if (lid.includes("reservoir")) return "reservoir"; + if (lid.includes("pump")) return "pump"; + if (lid.includes("valve")) return "valve"; + return lid; + }; + + // 检查是否与已选要素类型一致 + if (highlightFeatures.length > 0) { + const firstLayerId = + highlightFeatures[0].getId()?.toString().split(".")[0] || ""; + + if (getBaseType(layerId) !== getBaseType(firstLayerId)) { + // 如果点击的是已选中的要素(为了取消选中),则不报错 + const isAlreadySelected = highlightFeatures.some( + (f) => f.getProperties().id === featureId + ); + if (!isAlreadySelected) { + open?.({ + type: "error", + message: "请选择相同类型的要素进行多选查询。", + }); + return; + } + } + } + + setHighlightFeatures((prev) => { + const existingIndex = prev.findIndex( + (f) => f.getProperties().id === featureId + ); + + if (existingIndex !== -1) { + // 如果已存在,移除 + return prev.filter((_, i) => i !== existingIndex); + } else { + // 如果不存在,添加 + return [...prev, feature]; + } + }); + } else { + // 其他模式(如 info):单选 + setHighlightFeatures([feature]); + } }, - [map, setHighlightFeature] + [map, activeTools, highlightFeatures, open] ); // 添加矢量属性查询事件监听器 useEffect(() => { @@ -243,13 +298,14 @@ const Toolbar: React.FC = ({ switch (tool) { case "info": setShowPropertyPanel(false); - setHighlightFeature(null); + setHighlightFeatures([]); break; case "draw": setShowDrawPanel(false); break; case "history": setShowHistoryPanel(false); + setHighlightFeatures([]); break; } }; @@ -273,7 +329,7 @@ const Toolbar: React.FC = ({ // 关闭所有面板(除了样式编辑器) const closeAllPanelsExceptStyle = () => { setShowPropertyPanel(false); - setHighlightFeature(null); + setHighlightFeatures([]); setShowDrawPanel(false); setShowHistoryPanel(false); // 样式编辑器保持其当前状态,不自动关闭 @@ -283,11 +339,12 @@ const Toolbar: React.FC = ({ >({}); // 添加 useEffect 来查询计算属性 useEffect(() => { - if (!highlightFeature || !selectedDate || !showPropertyPanel) { + if (highlightFeatures.length === 0 || !selectedDate || !showPropertyPanel) { setComputedProperties({}); return; } + const highlightFeature = highlightFeatures[0]; const id = highlightFeature.getProperties().id; if (!id) { setComputedProperties({}); @@ -334,11 +391,12 @@ const Toolbar: React.FC = ({ }; // 仅当 currentTime 有效时查询 if (currentTime !== -1 && queryType) queryComputedProperties(); - }, [highlightFeature, currentTime, selectedDate]); + }, [highlightFeatures, currentTime, selectedDate]); // 从要素属性中提取属性面板需要的数据 const getFeatureProperties = useCallback(() => { - if (!highlightFeature) return {}; + if (highlightFeatures.length === 0) return {}; + const highlightFeature = highlightFeatures[0]; const layer = highlightFeature?.getId()?.toString().split(".")[0]; const properties = highlightFeature.getProperties(); // 计算属性字段,增加 key 字段 @@ -613,7 +671,7 @@ const Toolbar: React.FC = ({ return result; } return {}; - }, [highlightFeature, computedProperties]); + }, [highlightFeatures, computedProperties]); return ( <> @@ -663,34 +721,40 @@ const Toolbar: React.FC = ({ (HistoryPanel ? ( { - if (!highlightFeature || !showHistoryPanel) return []; - const properties = highlightFeature.getProperties(); - const id = properties.id; - if (!id) return []; - - // 从图层名称推断类型 - const layerId = - highlightFeature.getId()?.toString().split(".")[0] || ""; - let type = "unknown"; - - if (layerId.includes("pipe")) { - type = "pipe"; - } else if (layerId.includes("junction")) { - type = "junction"; - } else if (layerId.includes("tank")) { - type = "tank"; - } else if (layerId.includes("reservoir")) { - type = "reservoir"; - } else if (layerId.includes("pump")) { - type = "pump"; - } else if (layerId.includes("valve")) { - type = "valve"; - } - // 仅处理 type 为 pipe 或 junction 的情况 - if (type !== "pipe" && type !== "junction") { + if (highlightFeatures.length === 0 || !showHistoryPanel) return []; - } - return [[id, type]]; + + return highlightFeatures + .map((feature) => { + const properties = feature.getProperties(); + const id = properties.id; + if (!id) return null; + + // 从图层名称推断类型 + const layerId = + feature.getId()?.toString().split(".")[0] || ""; + let type = "unknown"; + + if (layerId.includes("pipe")) { + type = "pipe"; + } else if (layerId.includes("junction")) { + type = "junction"; + } else if (layerId.includes("tank")) { + type = "tank"; + } else if (layerId.includes("reservoir")) { + type = "reservoir"; + } else if (layerId.includes("pump")) { + type = "pump"; + } else if (layerId.includes("valve")) { + type = "valve"; + } + // 仅处理 type 为 pipe 或 junction 的情况 + if (type !== "pipe" && type !== "junction") { + return null; + } + return [id, type]; + }) + .filter(Boolean) as [string, string][]; })()} scheme_type="burst_Analysis" scheme_name={schemeName} @@ -699,34 +763,40 @@ const Toolbar: React.FC = ({ ) : ( { - if (!highlightFeature || !showHistoryPanel) return []; - const properties = highlightFeature.getProperties(); - const id = properties.id; - if (!id) return []; - - // 从图层名称推断类型 - const layerId = - highlightFeature.getId()?.toString().split(".")[0] || ""; - let type = "unknown"; - - if (layerId.includes("pipe")) { - type = "pipe"; - } else if (layerId.includes("junction")) { - type = "junction"; - } else if (layerId.includes("tank")) { - type = "tank"; - } else if (layerId.includes("reservoir")) { - type = "reservoir"; - } else if (layerId.includes("pump")) { - type = "pump"; - } else if (layerId.includes("valve")) { - type = "valve"; - } - // 仅处理 type 为 pipe 或 junction 的情况 - if (type !== "pipe" && type !== "junction") { + if (highlightFeatures.length === 0 || !showHistoryPanel) return []; - } - return [[id, type]]; + + return highlightFeatures + .map((feature) => { + const properties = feature.getProperties(); + const id = properties.id; + if (!id) return null; + + // 从图层名称推断类型 + const layerId = + feature.getId()?.toString().split(".")[0] || ""; + let type = "unknown"; + + if (layerId.includes("pipe")) { + type = "pipe"; + } else if (layerId.includes("junction")) { + type = "junction"; + } else if (layerId.includes("tank")) { + type = "tank"; + } else if (layerId.includes("reservoir")) { + type = "reservoir"; + } else if (layerId.includes("pump")) { + type = "pump"; + } else if (layerId.includes("valve")) { + type = "valve"; + } + // 仅处理 type 为 pipe 或 junction 的情况 + if (type !== "pipe" && type !== "junction") { + return null; + } + return [id, type]; + }) + .filter(Boolean) as [string, string][]; })()} scheme_type="burst_Analysis" scheme_name={schemeName}