import React, { useEffect, useCallback, useState, useRef } from "react"; import VectorLayer from "ol/layer/Vector"; import VectorSource from "ol/source/Vector"; import Style from "ol/style/Style"; import Fill from "ol/style/Fill"; import { Stroke } from "ol/style"; import GeoJson from "ol/format/GeoJSON"; import config from "@config/config"; import { useMap } from "@app/OlMap/MapComponent"; import { useProject } from "@/contexts/ProjectContext"; interface PropertyItem { key: string; value: string | number | boolean; label?: string; } interface ZonePropsPanelProps { title?: string; isVisible?: boolean; onClose?: () => void; } const ZonePropsPanel: React.FC = ({ title = "分区属性信息", isVisible = true, onClose, }) => { const map = useMap(); const project = useProject(); const workspace = project?.workspace; const [props, setProps] = React.useState< PropertyItem[] | Record >({}); const [highlightedFeature, setHighlightedFeature] = useState(null); const highlightLayerRef = useRef | null>(null); const handleMapClickSelectFeatures = useCallback( (pixel: number[]) => { if (!map || !highlightLayerRef.current) return; let clickedFeature: any = null; map.forEachFeatureAtPixel(pixel, (feature) => { if (!clickedFeature) { clickedFeature = feature; } }); if (clickedFeature) { const layer = clickedFeature?.getId()?.toString().split(".")[0]; if (layer !== "network_zone") { return; } setHighlightedFeature(clickedFeature); setProps(clickedFeature.getProperties()); // 更新高亮图层 const source = highlightLayerRef.current.getSource(); source?.clear(); source?.addFeature(clickedFeature); } else { setHighlightedFeature(null); setProps({}); // 清空高亮图层 const source = highlightLayerRef.current.getSource(); source?.clear(); } }, [map] ); // 将 properties 转换为统一格式 const formatProperties = ( props: PropertyItem[] | Record ): PropertyItem[] => { if (Array.isArray(props)) { return props.filter((item) => !shouldHideProperty(item.key)); } return Object.entries(props) .filter(([key]) => !shouldHideProperty(key)) .map(([key, value]) => ({ key, value, label: getChineseLabel(key), })); }; // 判断是否应该隐藏某个属性 const shouldHideProperty = (key: string): boolean => { const hiddenKeys = [ "id", "geometry", "Note1", "Note3", "Note4", "Note5", "Note6", "Note7", "Note8", "Note9", "Note10", ]; return hiddenKeys.includes(key); }; useEffect(() => { if (!map) { return; } const workspaceValue = workspace || config.MAP_WORKSPACE; const networkZoneLayer = new VectorLayer({ source: new VectorSource({ url: `${config.MAP_URL}/${workspaceValue}/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=${workspaceValue}:network_zone&outputFormat=application/json`, format: new GeoJson(), }), style: new Style({ fill: new Fill({ color: "rgba(255, 255, 255, 0)", }), stroke: new Stroke({ color: "#e01414ff", width: 5, }), }), properties: { name: "管网分区", value: "network_zone", }, }); map.addLayer(networkZoneLayer); // 创建高亮图层 const highlightLayer = new VectorLayer({ source: new VectorSource(), style: new Style({ fill: new Fill({ color: "rgba(255, 255, 0, 0.3)", }), stroke: new Stroke({ color: "#ff0000", width: 3, }), }), properties: { name: "高亮分区", value: "highlight_zone", }, }); map.addLayer(highlightLayer); highlightLayerRef.current = highlightLayer; const clickListener = (evt: any) => { handleMapClickSelectFeatures(evt.pixel); }; map.on("click", clickListener); return () => { map.removeLayer(networkZoneLayer); map.removeLayer(highlightLayer); map.un("click", clickListener); }; }, [map, handleMapClickSelectFeatures, workspace]); // 获取中文标签 const getChineseLabel = (key: string): string => { const labelMap: Record = { Id: "ID", Area: "面积", Complete: "完成度", Consumptio: "消耗", Descriptio: "描述", FlowError: "流量误差", Level: "级别", ModelFlow: "模型流量", NRW: "无收益水量", NRWPercent: "无收益水量百分比", Name: "名称", Note1: "备注1", Note2: "备注2", Note3: "备注3", Note4: "备注4", Note5: "备注5", Note6: "备注6", Note7: "备注7", Note8: "备注8", Note9: "备注9", Note10: "备注10", ParentZone: "父区域", PipeLength: "管道长度", Population: "人口", ScadaFlow: "SCADA流量", Tag: "标签", TotalFlowE: "总流量误差", TotalModel: "总模型", TotalScada: "总SCADA", WaterConsu: "水消耗", WaterSuppl: "水供应", }; return labelMap[key] || key; }; // 优先使用从store中获取的props,如果没有则使用传入的properties const dataToShow = props; const formattedProperties = formatProperties(dataToShow); // 定义属性的显示顺序 const propertyOrder = [ "Id", "Name", "PipeLength", "ModelFlow", "Population", "Level", "Note2", "Area", "Descriptio", "ParentZone", "Tag", "Complete", "Consumptio", "FlowError", "NRW", "NRWPercent", "ScadaFlow", "TotalFlowE", "TotalModel", "TotalScada", "WaterConsu", "WaterSuppl", ]; // 根据自定义顺序对属性进行排序 const sortedProperties = [...formattedProperties].sort((a, b) => { const aIndex = propertyOrder.indexOf(a.key); const bIndex = propertyOrder.indexOf(b.key); // 如果属性不在排序列表中,则将其放在末尾 if (aIndex === -1) return 1; if (bIndex === -1) return -1; return aIndex - bIndex; }); // 格式化值显示 const formatValue = (value: any, key: string): string => { if (value === null || value === undefined) { return "-"; } if (typeof value === "boolean") { return value ? "是" : "否"; } if (typeof value === "string" && value.trim() === "") { return "-"; } // 对于特定的数值字段,添加单位 if (typeof value === "number") { switch (key) { case "Area": return `${value.toLocaleString()} m²`; case "PipeLength": return `${value.toLocaleString()} m`; case "Population": return `${value.toLocaleString()} 人`; case "ModelFlow": return `${value.toLocaleString()} L/天`; case "ScadaFlow": case "TotalModel": case "TotalScada": case "WaterConsu": case "WaterSuppl": return `${value.toLocaleString()} L/s`; case "NRWPercent": return value !== null ? `${value}%` : "-"; default: return value.toLocaleString(); } } return String(value); }; if (!isVisible) { return null; } const isImportantKeys = ["Name", "Id", "ModelFlow", "Area", "PipeLength"]; return (
{/* 头部 */}

{title}

{onClose && ( )}
{/* 内容区域 */}
{sortedProperties.length === 0 ? (

暂无属性信息

点击地图分区查看详情

) : (
{sortedProperties.map((item, index) => { const isImportant = isImportantKeys.includes(item.key); return (
{item.label || item.key} {formatValue(item.value, item.key)}
); })}
)}
{/* 底部统计区域 */}
共 {sortedProperties.length} 个属性 {highlightedFeature && ( 已选中 )}
); }; export default ZonePropsPanel;