"use client"; import { Check, Copy, Database, MapPin, Radio, X } from "lucide-react"; import Image from "next/image"; import type { ComponentType } from "react"; import { useEffect, useRef, useState } from "react"; import { MAP_MAJOR_PANEL_RADIUS_CLASS_NAME } from "@/features/map/core/components/map-control-styles"; import { showMapNotice } from "@/features/map/core/components/notice"; import { cn } from "@/lib/utils"; import { SCADA_ICON_PATHS } from "../map/scada"; import type { WaterNetworkSourceId } from "../map/sources"; import type { DetailFeature } from "../types"; import { formatFeaturePropertyValue, getFeaturePanelModel, type FeaturePanelBadgeTone } from "../utils/feature-properties"; import { ConduitFeatureIcon, JunctionFeatureIcon, OrificeFeatureIcon, OutfallFeatureIcon, PumpFeatureIcon, type DrainageFeatureIconProps } from "./drainage-feature-icons"; type FeaturePopoverProps = { feature: DetailFeature | null; onClose: () => void; }; const FEATURE_LAYER_META: Record< WaterNetworkSourceId, { icon: ComponentType; label: string; headerClassName: string; labelClassName: string; iconClassName: string; } > = { conduits: { icon: ConduitFeatureIcon, label: "管渠", headerClassName: "glass-normal", labelClassName: "text-teal-800", iconClassName: "bg-teal-700 text-white ring-teal-900/10" }, junctions: { icon: JunctionFeatureIcon, label: "检查井", headerClassName: "glass-info", labelClassName: "text-blue-800", iconClassName: "bg-sky-700 text-white ring-sky-900/10" }, orifices: { icon: OrificeFeatureIcon, label: "孔口", headerClassName: "bg-[var(--glass-readable)]", labelClassName: "text-slate-700", iconClassName: "bg-slate-600 text-white ring-slate-900/10" }, outfalls: { icon: OutfallFeatureIcon, label: "排放口", headerClassName: "glass-info", labelClassName: "text-blue-800", iconClassName: "bg-slate-700 text-white ring-slate-900/10" }, pumps: { icon: PumpFeatureIcon, label: "泵", headerClassName: "glass-normal", labelClassName: "text-teal-800", iconClassName: "bg-cyan-800 text-white ring-cyan-950/10" }, scada: { icon: JunctionFeatureIcon, label: "SCADA 测点", headerClassName: "glass-info", labelClassName: "text-blue-800", iconClassName: "bg-blue-700 text-white ring-blue-900/10" } }; const BADGE_CLASS_NAMES: Record = { active: "bg-emerald-50 text-emerald-700 ring-emerald-600/15", inactive: "bg-slate-100 text-slate-600 ring-slate-500/15", warning: "bg-orange-50 text-orange-700 ring-orange-600/15", critical: "bg-red-50 text-red-700 ring-red-600/15", info: "bg-blue-50 text-blue-700 ring-blue-600/15" }; const BADGE_DOT_CLASS_NAMES: Record = { active: "bg-emerald-500", inactive: "bg-slate-400", warning: "bg-orange-500", critical: "bg-red-500", info: "bg-blue-500" }; export function FeaturePopover({ feature, onClose }: FeaturePopoverProps) { const [copiedId, setCopiedId] = useState(null); const copyResetTimeoutRef = useRef | null>(null); useEffect(() => () => { if (copyResetTimeoutRef.current) { clearTimeout(copyResetTimeoutRef.current); } }, []); if (!feature) { return null; } const panelModel = getFeaturePanelModel(feature.layer, feature.properties, feature.id); const layerMeta = FEATURE_LAYER_META[feature.layer]; const LayerIcon = layerMeta.icon; const copied = copiedId === panelModel.id; const canCopy = panelModel.id !== "暂无"; const handleCopyId = async () => { try { if (!navigator.clipboard) { throw new Error("Clipboard API unavailable"); } await navigator.clipboard.writeText(panelModel.id); setCopiedId(panelModel.id); if (copyResetTimeoutRef.current) { clearTimeout(copyResetTimeoutRef.current); } copyResetTimeoutRef.current = setTimeout(() => setCopiedId(null), 1500); } catch { showMapNotice({ tone: "error", title: "无法复制编号", message: "请手动选择编号后复制。" }); } }; if (feature.layer === "scada") { return ( void handleCopyId()} onClose={onClose} /> ); } return ( ); } type ScadaFeaturePopoverProps = { feature: DetailFeature; copied: boolean; canCopy: boolean; onCopy: () => void; onClose: () => void; }; function ScadaFeaturePopover({ feature, copied, canCopy, onCopy, onClose }: ScadaFeaturePopoverProps) { const properties = feature.properties; const presentation = getScadaPresentation(properties.device_type_id); const active = properties.active === true || String(properties.active).toLowerCase() === "true"; const pointId = formatFeaturePropertyValue("point_external_id", properties.point_external_id ?? feature.id); const attributes = [ ["设备编号", formatFeaturePropertyValue("device_external_id", properties.device_external_id)], ["关联检查井", formatFeaturePropertyValue("junction_id", properties.junction_id)], ["同步时间", formatFeaturePropertyValue("synced_at", properties.synced_at)] ]; return ( ); } function getScadaPresentation(rawTypeId: unknown) { const typeId = Number(rawTypeId); if (typeId === 72) return { iconPath: SCADA_ICON_PATHS.waterQuality, headerClassName: "glass-normal", labelClassName: "text-teal-800" }; if (typeId === 58) return { iconPath: SCADA_ICON_PATHS.radarLevel, headerClassName: "glass-info", labelClassName: "text-blue-800" }; if (typeId === 45) return { iconPath: SCADA_ICON_PATHS.ultrasonicFlow, headerClassName: "glass-warning", labelClassName: "text-orange-800" }; return { iconPath: SCADA_ICON_PATHS.unknown, headerClassName: "bg-[var(--glass-readable)]", labelClassName: "text-slate-700" }; }