feat(map): manage agent overlays in layer panel

This commit is contained in:
2026-11-09 17:59:36 +08:00
parent 692bbf609c
commit 038d3458bc
10 changed files with 174 additions and 321 deletions
+44 -20
View File
@@ -99,6 +99,7 @@ import {
import { getResponsivePadding, getResponsiveWorkbenchPadding } from "./map/camera";
import { exportMapViewImage } from "./map/export-view";
import { setMapLayersVisibility } from "./map/layer-visibility";
import { MAP_STYLE_TOKENS } from "./map/map-colors";
import {
BASE_LAYER_OPTIONS,
INITIAL_LAYER_VISIBILITY,
@@ -298,9 +299,6 @@ function MapWorkbenchContent({
const artifactActive = artifactWorkspace.activeArtifact !== null;
const activeSpatialOverlay = artifactWorkspace.activeArtifact?.spatialOverlay ?? null;
const activeArtifactHasSpatialView = Boolean(activeSpatialOverlay?.features.length);
const allowDemoSimulation =
(!getWorkbenchProject(getAccessToken) || getWorkbenchProject(getAccessToken)?.gs_workspace === "tjwater_next")
&& (artifactWorkspace.activeArtifact === null || artifactWorkspace.activeArtifact.origin === "demo");
const latestAnalysisAvailable = Boolean(
latestAnalysisArtifact?.blocks.some(
(block) =>
@@ -355,11 +353,6 @@ function MapWorkbenchContent({
useEffect(() => {
setImpactVisible(activeArtifactHasSpatialView);
setLayerVisibility((current) =>
current.simulation === activeArtifactHasSpatialView
? current
: { ...current, simulation: activeArtifactHasSpatialView }
);
}, [activeArtifactHasSpatialView, artifactWorkspace.activeArtifact?.id]);
function openAgentWorkspaceForViewport() {
@@ -458,7 +451,6 @@ function MapWorkbenchContent({
onSelectFeature: handleSelectFeature,
selectedFeature: detailFeature,
simulationOverlay: activeSpatialOverlay,
allowDemoSimulation,
networkView: projectPresentation.networkView,
geoserverWorkspace: getWorkbenchProject(getAccessToken)?.gs_workspace,
getAccessToken
@@ -690,23 +682,55 @@ function MapWorkbenchContent({
[activeToolId, mapReady, toggleToolbarTool]
);
const layerControlItems = useMemo(
() => createLayerControlItems(layerVisibility, availableSourceIds, sourceFeatureCounts),
[availableSourceIds, layerVisibility, sourceFeatureCounts]
const overlayLayers = [
{ scope: "investigation", description: "Agent 调查", surface: presentation },
{ scope: "chat", description: "Agent 对话", surface: chatPresentation },
{ scope: "preview", description: "本地预览", surface: previewPresentation }
].flatMap(({ scope, description, surface }) => surface.overlay ? [{
id: `presentation:${scope}:${surface.overlay.overlay_id}`,
title: surface.overlay.title,
description: `${description} · ${surface.overlay.mode === "heatmap" ? "热力分布" : "空间范围"}`,
featureCount: surface.overlay.data.features.length,
color: MAP_STYLE_TOKENS.state.risk as string,
visible: surface.overlay.visible,
disabled: surface.history.selectedId !== null,
onToggle: (visible: boolean) => {
if (surface.overlay?.visible !== visible) surface.toggleOverlay();
}
}] : []);
if (activeArtifactHasSpatialView && artifactWorkspace.activeArtifact) {
overlayLayers.push({
id: `artifact:${artifactWorkspace.activeArtifact.id}`,
title: artifactWorkspace.activeArtifact.title,
description: "分析成果 · 空间范围",
featureCount: activeSpatialOverlay!.features.length,
color: MAP_STYLE_TOKENS.state.agent,
visible: impactVisible,
disabled: false,
onToggle: setImpactVisible
});
}
const layerControlItems = createLayerControlItems(
layerVisibility, availableSourceIds, sourceFeatureCounts, overlayLayers
);
const visibleLegendItems = useMemo(
() => createVisibleMapLegendItems(layerControlItems),
[layerControlItems]
);
function handleToggleLayer(layerControlId: string, visible: boolean) {
if (layerControlId === "simulation") {
setImpactVisible(visible);
return;
}
const overlay = overlayLayers.find((item) => item.id === layerControlId);
if (overlay) {
if (!overlay.disabled) overlay.onToggle(visible);
return;
}
presentationLayerChangesRef.current.delete(layerControlId);
const map = mapRef.current;
setLayerVisibility((current) => ({ ...current, [layerControlId]: visible }));
if (layerControlId === "simulation") {
setImpactVisible(visible);
}
if (!map || !mapReady) {
return;
}
@@ -1079,7 +1103,7 @@ function MapWorkbenchContent({
measurement.clear();
drawing.clear();
geocoding.clear();
handleToggleLayer("simulation", false);
setImpactVisible(false);
showMapNotice({ tone: "success", message: "已清空所有高亮和覆盖层。" });
}
@@ -1471,10 +1495,10 @@ function MapWorkbenchContent({
</div>
{!devPanelOpen ? (
<>
<div className="pointer-events-auto absolute right-3 top-10 hidden lg:block">
<div className="pointer-events-auto absolute right-3 top-10 z-50 hidden lg:block">
<MapToolbar items={toolbarItems} />
</div>
<div className="pointer-events-auto absolute right-16 top-10 hidden lg:block">
<div className="pointer-events-auto absolute right-16 top-10 z-50 hidden lg:block">
<ToolbarPanel {...toolbarPanelProps} />
</div>
</>
@@ -1570,7 +1594,7 @@ function MapWorkbenchContent({
{mobileSheet === null && !readingWorkspace?.active && artifactWorkspace.surfaceMode !== "focus" ? (
<div
className="absolute left-3 right-3 z-30 transition-[bottom] duration-200 ease-out motion-reduce:transition-none lg:hidden"
className="absolute left-3 right-3 z-50 transition-[bottom] duration-200 ease-out motion-reduce:transition-none lg:hidden"
style={{
bottom: mobileOverlayLayout.mapControlBottom,
"--mobile-tool-available-height": `calc(100dvh - ${mobileOverlayLayout.mapControlBottom}px - 4.5rem)`