import { Camera, Circle, Copy, Database, Dot, Eraser, Keyboard, Layers3, MapPinned, Pentagon, Redo2, RefreshCw, Route, Ruler, Share2, Square, Trash2, Undo2, Waypoints, type LucideIcon } from "lucide-react"; import { useEffect, useMemo, useState } from "react"; import { MapAnnotationPanel, MapActionRow, MapControlPanel, MapDrawToolbar, MapLayerPanel, MapLegendList, MapMeasurePanel, MapPanelSection, type BaseLayerOption, type MapAnnotationItem, type MapAnnotationShareAction, type MapLayerControlItem, type MapLegendItem } from "@/features/map/core"; import { type WorkbenchDrawingAnnotation, type WorkbenchDrawMode } from "../hooks/use-workbench-drawing"; import { type WorkbenchMeasurementResult, type WorkbenchMeasureMode, type WorkbenchMeasureUnit } from "../hooks/use-workbench-measurement"; import { waterNetworkToolbarItems } from "../map/toolbar-config"; import type { WorkbenchGeocoding } from "../hooks/use-workbench-geocoding"; import { GeocodingPanel } from "./geocoding-panel"; import { FeatureLocationPanel, type FeatureLocationPanelProps } from "./feature-location-panel"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/shared/ui/tabs"; export type ToolbarToolId = "geocoding" | "layers" | "measure" | "analysis" | "annotation" | "more"; export type ExportViewPreset = "current" | "4k"; type MeasureModeOption = { id: WorkbenchMeasureMode; label: string; icon: LucideIcon; }; const MEASURE_MODES: MeasureModeOption[] = [ { id: "distance", label: "距离", icon: Route }, { id: "area", label: "面积", icon: Pentagon }, { id: "segment", label: "管段", icon: Waypoints } ]; const MEASURE_UNITS: Array<{ id: WorkbenchMeasureUnit; label: string }> = [ { id: "m", label: "m" }, { id: "km", label: "km" } ]; const ANNOTATION_SHARE_ACTIONS: MapAnnotationShareAction[] = [ { id: "export", icon: Share2, label: "导出标注", description: "GeoJSON / 截图报告" } ]; const TOOL_PANEL_EXIT_MS = 160; const DRAW_TOOL_OPTIONS: Array<{ id: WorkbenchDrawMode | "undo" | "redo" | "delete" | "clear"; label: string; icon: LucideIcon; tone?: "danger"; }> = [ { id: "point", label: "点标注", icon: Dot }, { id: "line", label: "线标注", icon: Route }, { id: "polygon", label: "范围标注", icon: Pentagon }, { id: "circle", label: "圆形标注", icon: Circle }, { id: "undo", label: "撤销", icon: Undo2 }, { id: "redo", label: "重做", icon: Redo2 }, { id: "delete", label: "删除选中", icon: Trash2, tone: "danger" }, { id: "clear", label: "清空全部", icon: Trash2, tone: "danger" } ]; export type ToolbarPanelProps = { geocoding: WorkbenchGeocoding; featureLocation: FeatureLocationPanelProps; activeToolId: ToolbarToolId | null; activeMeasureModeId: WorkbenchMeasureMode; activeMeasureUnitId: WorkbenchMeasureUnit; measuring: boolean; measureResult: WorkbenchMeasurementResult; measureEnabled: boolean; hasMeasurePoints: boolean; activeDrawToolId: WorkbenchDrawMode | null; drawingEnabled: boolean; drawingAnnotations: WorkbenchDrawingAnnotation[]; canUndoDrawing: boolean; canRedoDrawing: boolean; canDeleteSelectedDrawing: boolean; hasDrawingFeatures: boolean; layerItems: MapLayerControlItem[]; legendItems: MapLegendItem[]; baseLayerOptions: BaseLayerOption[]; activeBaseLayerId: string; onSelectMeasureMode: (id: WorkbenchMeasureMode) => void; onSelectMeasureUnit: (id: WorkbenchMeasureUnit) => void; onStartMeasure: () => void; onStopMeasure: () => void; onClearMeasure: () => void; onCopyMeasure: () => void; onSelectDrawTool: (id: WorkbenchDrawMode | null) => void; onUndoDrawing: () => void; onRedoDrawing: () => void; onDeleteSelectedDrawing: () => void; onClearDrawing: () => void; onExportAnnotations: () => void; onSelectBaseLayer: (id: string) => void; onToggleLayer: (id: string, visible: boolean) => void; onExportView: (preset: ExportViewPreset) => void; onRefreshTiles: () => void; onShowDataStatus: () => void; onShowShortcuts: () => void; onClearMapOverlays: () => void; panelWidthClassName?: string; }; export function ToolbarPanel({ geocoding, featureLocation, activeToolId, activeMeasureModeId, activeMeasureUnitId, measuring, measureResult, measureEnabled, hasMeasurePoints, activeDrawToolId, drawingEnabled, drawingAnnotations, canUndoDrawing, canRedoDrawing, canDeleteSelectedDrawing, hasDrawingFeatures, layerItems, legendItems, baseLayerOptions, activeBaseLayerId, onSelectMeasureMode, onSelectMeasureUnit, onStartMeasure, onStopMeasure, onClearMeasure, onCopyMeasure, onSelectDrawTool, onUndoDrawing, onRedoDrawing, onDeleteSelectedDrawing, onClearDrawing, onExportAnnotations, onSelectBaseLayer, onToggleLayer, onExportView, onRefreshTiles, onShowDataStatus, onShowShortcuts, onClearMapOverlays, panelWidthClassName }: ToolbarPanelProps) { const annotationItems = useMemo( () => drawingAnnotations.map((annotation) => ({ id: annotation.id, icon: getDrawingAnnotationIcon(annotation.kind), label: annotation.label, description: annotation.description, status: annotation.hidden ? "隐藏" : "显示", selected: annotation.selected, muted: annotation.hidden, onClick: annotation.onToggleVisibility })), [drawingAnnotations] ); const annotationShareActions = useMemo( () => ANNOTATION_SHARE_ACTIONS.map((action) => ({ ...action, onClick: action.id === "export" ? onExportAnnotations : action.onClick })), [onExportAnnotations] ); const [renderedToolId, setRenderedToolId] = useState(activeToolId); const [locationMode, setLocationMode] = useState("address"); const panelVisible = Boolean(activeToolId); useEffect(() => { if (activeToolId) { setRenderedToolId(activeToolId); return; } if (!renderedToolId) { return; } const exitTimer = window.setTimeout(() => { setRenderedToolId(null); }, TOOL_PANEL_EXIT_MS); return () => window.clearTimeout(exitTimer); }, [activeToolId, renderedToolId]); if (!renderedToolId) { return null; } const activeTool = waterNetworkToolbarItems.find((item) => item.id === renderedToolId); const Icon = activeTool?.icon ?? Layers3; if (renderedToolId === "geocoding") { return ( { if (value === "features" && geocoding.picking) geocoding.togglePicking(); setLocationMode(value); }} className="flex min-h-0 flex-1 flex-col gap-3"> 地址地名 管网要素 ); } if (renderedToolId === "layers") { return ( item.visible).length}/${layerItems.length} 个业务图层可见`} icon={Icon} widthClassName={panelWidthClassName ?? "flex w-[324px] max-h-[calc(100dvh-13.25rem)] flex-col"} contentClassName={panelWidthClassName ? undefined : "min-h-0 overflow-y-auto overscroll-contain pr-0.5"} visible={panelVisible} > ); } if (renderedToolId === "measure") { const measureStatus = getMeasureStatus({ enabled: measureEnabled, measuring, hasPoints: hasMeasurePoints, mode: activeMeasureModeId }); return ( onSelectMeasureMode(id as WorkbenchMeasureMode)} onSelectUnit={(id) => onSelectMeasureUnit(id as WorkbenchMeasureUnit)} /> ); } if (renderedToolId === "analysis") { return ( ); } if (renderedToolId === "annotation") { return ( ({ ...item, active: activeDrawToolId === item.id, disabled: !drawingEnabled || (item.id === "undo" && !canUndoDrawing) || (item.id === "redo" && !canRedoDrawing) || (item.id === "delete" && !canDeleteSelectedDrawing) || (item.id === "clear" && !canUndoDrawing && !hasDrawingFeatures), onClick: () => { if (item.id === "undo") { onUndoDrawing(); return; } if (item.id === "redo") { onRedoDrawing(); return; } if (item.id === "delete") { onDeleteSelectedDrawing(); return; } if (item.id === "clear") { onClearDrawing(); return; } onSelectDrawTool(activeDrawToolId === item.id ? null : item.id); } }))} /> ); } return ( ); } function getMeasureStatus({ enabled, measuring, hasPoints, mode }: { enabled: boolean; measuring: boolean; hasPoints: boolean; mode: WorkbenchMeasureMode; }) { if (!enabled) { return { label: "地图未就绪", description: "等待地图加载后再开始测量", tone: "disabled" as const }; } if (measuring) { return { label: "测量中", description: mode === "segment" ? "点击地图中的管线追加资产" : mode === "area" ? "连续点击范围顶点,停止后保留读数" : "连续点击地图取点,停止后保留读数", tone: "active" as const }; } if (hasPoints) { return { label: "读数已保留", description: "可继续测量、复制结果或清除", tone: "ready" as const }; } return { label: "等待开始", description: mode === "segment" ? "开始后点击管线选择资产" : mode === "area" ? "开始后连续点击范围顶点" : "开始后连续点击地图取点", tone: "idle" as const }; } function LegendPanel({ legendItems }: { legendItems: MapLegendItem[] }) { return (

图例随图层显隐同步

管线颜色和线宽表示口径,设施颜色表示类型,风险色仅用于分析覆盖。

); } type MorePanelProps = { onClearMapOverlays: () => void; onExportView: (preset: ExportViewPreset) => void; onRefreshTiles: () => void; onShowDataStatus: () => void; onShowShortcuts: () => void; }; function MorePanel({ onClearMapOverlays, onExportView, onRefreshTiles, onShowDataStatus, onShowShortcuts }: MorePanelProps) { return (
onExportView("current")} /> onExportView("4k")} />
); } function getDrawingAnnotationIcon(kind: WorkbenchDrawMode) { if (kind === "line") { return Route; } if (kind === "polygon") { return Pentagon; } if (kind === "circle") { return Circle; } return MapPinned; }