增加渲染节点功能,优化工具操作和样式
This commit is contained in:
@@ -81,6 +81,7 @@ const formatToolTitle = (item: ChatProgress) => {
|
|||||||
if (text.includes("locate_features")) return "地图定位";
|
if (text.includes("locate_features")) return "地图定位";
|
||||||
if (text.includes("view_history")) return "打开历史曲线";
|
if (text.includes("view_history")) return "打开历史曲线";
|
||||||
if (text.includes("view_scada")) return "打开 SCADA 面板";
|
if (text.includes("view_scada")) return "打开 SCADA 面板";
|
||||||
|
if (text.includes("render_junctions")) return "渲染节点";
|
||||||
return item.title;
|
return item.title;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -131,6 +131,12 @@ const TOOL_META: Record<string, ToolMeta> = {
|
|||||||
actionLabel: "显示",
|
actionLabel: "显示",
|
||||||
color: "#73c0de",
|
color: "#73c0de",
|
||||||
},
|
},
|
||||||
|
render_junctions: {
|
||||||
|
label: "渲染节点",
|
||||||
|
icon: <LocationOnRounded sx={{ fontSize: 18 }} />,
|
||||||
|
actionLabel: "应用渲染",
|
||||||
|
color: "#3b82f6",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ---------- helpers ---------- */
|
/* ---------- helpers ---------- */
|
||||||
@@ -261,6 +267,14 @@ function getToolDescription(toolCall: ToolCall): string {
|
|||||||
case "show_chart": {
|
case "show_chart": {
|
||||||
return (params.title as string | undefined) ?? "数据图表";
|
return (params.title as string | undefined) ?? "数据图表";
|
||||||
}
|
}
|
||||||
|
case "render_junctions": {
|
||||||
|
const nodeAreaMap =
|
||||||
|
params.node_area_map && typeof params.node_area_map === "object"
|
||||||
|
? (params.node_area_map as Record<string, unknown>)
|
||||||
|
: {};
|
||||||
|
const areaIds = Array.isArray(params.area_ids) ? params.area_ids : [];
|
||||||
|
return `${Object.keys(nodeAreaMap).length} 个节点 · ${areaIds.length || new Set(Object.values(nodeAreaMap).map(String)).size} 个分区`;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@@ -383,6 +397,31 @@ function buildAction(toolCall: ToolCall): ChatToolAction | null {
|
|||||||
xAxisName: params.x_axis_name as string | undefined,
|
xAxisName: params.x_axis_name as string | undefined,
|
||||||
yAxisName: params.y_axis_name as string | undefined,
|
yAxisName: params.y_axis_name as string | undefined,
|
||||||
};
|
};
|
||||||
|
case "render_junctions": {
|
||||||
|
const nodeAreaMap =
|
||||||
|
params.node_area_map && typeof params.node_area_map === "object"
|
||||||
|
? Object.fromEntries(
|
||||||
|
Object.entries(params.node_area_map as Record<string, unknown>)
|
||||||
|
.map(([key, value]) => [String(key), String(value ?? "")])
|
||||||
|
.filter(([, value]) => value.trim().length > 0),
|
||||||
|
)
|
||||||
|
: {};
|
||||||
|
return {
|
||||||
|
type: "render_junctions",
|
||||||
|
nodeAreaMap,
|
||||||
|
areaIds: Array.isArray(params.area_ids)
|
||||||
|
? params.area_ids.map((item) => String(item).trim()).filter(Boolean)
|
||||||
|
: [],
|
||||||
|
areaColors:
|
||||||
|
params.area_colors && typeof params.area_colors === "object"
|
||||||
|
? Object.fromEntries(
|
||||||
|
Object.entries(params.area_colors as Record<string, unknown>)
|
||||||
|
.map(([key, value]) => [String(key), String(value ?? "")])
|
||||||
|
.filter(([, value]) => value.trim().length > 0),
|
||||||
|
)
|
||||||
|
: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -136,6 +136,26 @@ const resolveTimeRange = (params: Record<string, unknown>) => ({
|
|||||||
(params.end as string | undefined),
|
(params.end as string | undefined),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const resolveStringRecord = (value: unknown): Record<string, string> => {
|
||||||
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.fromEntries(
|
||||||
|
Object.entries(value as Record<string, unknown>)
|
||||||
|
.map(([key, recordValue]) => [String(key), String(recordValue ?? "")])
|
||||||
|
.filter(([, recordValue]) => recordValue.trim().length > 0),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolveStringArray = (value: unknown): string[] => {
|
||||||
|
if (!Array.isArray(value)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.map((item) => String(item).trim()).filter(Boolean);
|
||||||
|
};
|
||||||
|
|
||||||
const compactNames = (names: string[]) => {
|
const compactNames = (names: string[]) => {
|
||||||
if (!names.length) return "";
|
if (!names.length) return "";
|
||||||
return names.length > 3
|
return names.length > 3
|
||||||
@@ -230,6 +250,24 @@ const buildToolAction = (
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tool === "render_junctions") {
|
||||||
|
const nodeAreaMap = resolveStringRecord(params.node_area_map);
|
||||||
|
const areaIds = resolveStringArray(params.area_ids);
|
||||||
|
const areaColors = resolveStringRecord(params.area_colors);
|
||||||
|
|
||||||
|
return {
|
||||||
|
action: {
|
||||||
|
type: "render_junctions",
|
||||||
|
nodeAreaMap,
|
||||||
|
areaIds,
|
||||||
|
areaColors,
|
||||||
|
},
|
||||||
|
kind: "map",
|
||||||
|
title: "渲染节点分区",
|
||||||
|
description: `${Object.keys(nodeAreaMap).length} 个节点`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
action: null,
|
action: null,
|
||||||
kind: "tool",
|
kind: "tool",
|
||||||
|
|||||||
@@ -17,18 +17,14 @@ import {
|
|||||||
ChevronRight,
|
ChevronRight,
|
||||||
FormatListBulleted,
|
FormatListBulleted,
|
||||||
} from "@mui/icons-material";
|
} from "@mui/icons-material";
|
||||||
import WebGLVectorTileLayer from "ol/layer/WebGLVectorTile";
|
|
||||||
import VectorTileSource from "ol/source/VectorTile";
|
|
||||||
import { VectorTile } from "ol";
|
|
||||||
import { FlatStyleLike } from "ol/style/flat";
|
|
||||||
import { useMap } from "@components/olmap/core/MapComponent";
|
import { useMap } from "@components/olmap/core/MapComponent";
|
||||||
import StyleLegend from "@components/olmap/core/Controls/StyleLegend";
|
import StyleLegend from "@components/olmap/core/Controls/StyleLegend";
|
||||||
import AnalysisParameters from "./AnalysisParameters";
|
import AnalysisParameters from "./AnalysisParameters";
|
||||||
import SchemeQuery from "./SchemeQuery";
|
import SchemeQuery from "./SchemeQuery";
|
||||||
import RecognitionResults from "./RecognitionResults";
|
import RecognitionResults from "./RecognitionResults";
|
||||||
|
import { applyJunctionAreaRender } from "./applyJunctionAreaRender";
|
||||||
import { getAreaColor } from "./utils";
|
import { getAreaColor } from "./utils";
|
||||||
import { LeakageResultDetail, LeakageSchemeRecord } from "./types";
|
import { LeakageResultDetail, LeakageSchemeRecord } from "./types";
|
||||||
import { config } from "@/config/config";
|
|
||||||
|
|
||||||
const TabPanel = ({
|
const TabPanel = ({
|
||||||
value,
|
value,
|
||||||
@@ -82,101 +78,26 @@ const DMALeakDetectionPanel: React.FC = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!map) return;
|
if (!map) return;
|
||||||
const junctionLayer = map
|
|
||||||
.getAllLayers()
|
|
||||||
.find(
|
|
||||||
(layer) =>
|
|
||||||
layer instanceof WebGLVectorTileLayer && layer.get("value") === "junctions",
|
|
||||||
) as WebGLVectorTileLayer | undefined;
|
|
||||||
if (!junctionLayer) return;
|
|
||||||
const source = junctionLayer.getSource() as VectorTileSource;
|
|
||||||
if (!source) return;
|
|
||||||
|
|
||||||
if (!loadedResult || !loadedResult.node_area_map) {
|
|
||||||
junctionLayer.setStyle(config.MAP_DEFAULT_STYLE as FlatStyleLike);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fallbackAreaIds = Array.from(
|
const fallbackAreaIds = Array.from(
|
||||||
new Set(Object.values(loadedResult.node_area_map || {}).map(String)),
|
new Set(Object.values(loadedResult?.node_area_map ?? {}).map(String)),
|
||||||
);
|
);
|
||||||
const areaIds = (loadedResult.areas || []).length
|
const areaIds = (loadedResult?.areas ?? []).length
|
||||||
? loadedResult.areas.map((area) => String(area.area_id))
|
? (loadedResult?.areas ?? []).map((area) => String(area.area_id))
|
||||||
: fallbackAreaIds;
|
: fallbackAreaIds;
|
||||||
if (areaIds.length === 0) {
|
const areaColors = Object.fromEntries(
|
||||||
junctionLayer.setStyle(config.MAP_DEFAULT_STYLE as FlatStyleLike);
|
areaIds.map((areaId) => [areaId, getAreaColor(areaId)]),
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const areaIdToIndex = new Map<string, number>();
|
|
||||||
areaIds.forEach((areaId, index) => {
|
|
||||||
areaIdToIndex.set(areaId, index + 1);
|
|
||||||
});
|
|
||||||
|
|
||||||
const nodeAreaIndexMap = new Map<string, number>();
|
|
||||||
Object.entries(loadedResult.node_area_map || {}).forEach(([nodeId, areaId]) => {
|
|
||||||
const idx = areaIdToIndex.get(String(areaId));
|
|
||||||
if (idx !== undefined) {
|
|
||||||
nodeAreaIndexMap.set(String(nodeId), idx);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const applyFeatureAreaIndex = (renderFeature: any) => {
|
|
||||||
const featureId = String(renderFeature.get("id") ?? "");
|
|
||||||
const areaIndex = nodeAreaIndexMap.get(featureId);
|
|
||||||
if (areaIndex !== undefined) {
|
|
||||||
renderFeature.properties_[DMA_AREA_INDEX_PROPERTY] = areaIndex;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const sourceTiles = (source as any).sourceTiles_;
|
|
||||||
if (sourceTiles) {
|
|
||||||
Object.values(sourceTiles).forEach((vectorTile: any) => {
|
|
||||||
const renderFeatures = vectorTile.getFeatures();
|
|
||||||
if (!renderFeatures || renderFeatures.length === 0) return;
|
|
||||||
renderFeatures.forEach((renderFeature: any) => {
|
|
||||||
applyFeatureAreaIndex(renderFeature);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const listener = (event: any) => {
|
|
||||||
try {
|
|
||||||
if (event.tile instanceof VectorTile) {
|
|
||||||
const renderFeatures = event.tile.getFeatures();
|
|
||||||
if (!renderFeatures || renderFeatures.length === 0) return;
|
|
||||||
renderFeatures.forEach((renderFeature: any) => {
|
|
||||||
applyFeatureAreaIndex(renderFeature);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error applying DMA area mapping:", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
source.on("tileloadend", listener);
|
|
||||||
|
|
||||||
const fillCases: any[] = [];
|
|
||||||
areaIds.forEach((areaId, index) => {
|
|
||||||
fillCases.push(
|
|
||||||
["==", ["get", DMA_AREA_INDEX_PROPERTY], index + 1],
|
|
||||||
getAreaColor(areaId),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
const defaultFillColor = String(config.MAP_DEFAULT_STYLE["circle-fill-color"]);
|
|
||||||
const defaultStrokeColor = String(
|
|
||||||
config.MAP_DEFAULT_STYLE["circle-stroke-color"],
|
|
||||||
);
|
);
|
||||||
const dmaStyle: FlatStyleLike = {
|
|
||||||
...config.MAP_DEFAULT_STYLE,
|
|
||||||
"circle-fill-color": ["case", ...fillCases, defaultFillColor],
|
|
||||||
"circle-stroke-color": ["case", ...fillCases, defaultStrokeColor],
|
|
||||||
};
|
|
||||||
junctionLayer.setStyle(dmaStyle);
|
|
||||||
|
|
||||||
return () => {
|
return applyJunctionAreaRender(
|
||||||
source.un("tileloadend", listener);
|
map,
|
||||||
junctionLayer.setStyle(config.MAP_DEFAULT_STYLE as FlatStyleLike);
|
{
|
||||||
};
|
nodeAreaMap: loadedResult?.node_area_map ?? {},
|
||||||
|
areaIds,
|
||||||
|
areaColors,
|
||||||
|
},
|
||||||
|
{ propertyKey: DMA_AREA_INDEX_PROPERTY },
|
||||||
|
);
|
||||||
}, [map, loadedResult]);
|
}, [map, loadedResult]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -0,0 +1,147 @@
|
|||||||
|
import { Map as OlMap, VectorTile } from "ol";
|
||||||
|
import WebGLVectorTileLayer from "ol/layer/WebGLVectorTile";
|
||||||
|
import VectorTileSource from "ol/source/VectorTile";
|
||||||
|
import { FlatStyleLike } from "ol/style/flat";
|
||||||
|
|
||||||
|
import { config } from "@/config/config";
|
||||||
|
import { getAreaColor } from "./utils";
|
||||||
|
|
||||||
|
const JUNCTION_LAYER_VALUE = "junctions";
|
||||||
|
const RENDER_OWNER_KEY = "junction-area-render-owner";
|
||||||
|
|
||||||
|
export type JunctionAreaRenderPayload = {
|
||||||
|
nodeAreaMap: Record<string, string>;
|
||||||
|
areaIds?: string[];
|
||||||
|
areaColors?: Record<string, string>;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ApplyJunctionAreaRenderOptions = {
|
||||||
|
propertyKey?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DEFAULT_PROPERTY_KEY = "junction_area_render_index";
|
||||||
|
|
||||||
|
const getJunctionLayer = (map: OlMap) =>
|
||||||
|
map
|
||||||
|
.getAllLayers()
|
||||||
|
.find(
|
||||||
|
(layer) =>
|
||||||
|
layer instanceof WebGLVectorTileLayer &&
|
||||||
|
layer.get("value") === JUNCTION_LAYER_VALUE,
|
||||||
|
) as WebGLVectorTileLayer | undefined;
|
||||||
|
|
||||||
|
export const applyJunctionAreaRender = (
|
||||||
|
map: OlMap,
|
||||||
|
payload: JunctionAreaRenderPayload,
|
||||||
|
options: ApplyJunctionAreaRenderOptions = {},
|
||||||
|
) => {
|
||||||
|
const propertyKey = options.propertyKey ?? DEFAULT_PROPERTY_KEY;
|
||||||
|
const junctionLayer = getJunctionLayer(map);
|
||||||
|
if (!junctionLayer) {
|
||||||
|
return () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const source = junctionLayer.getSource() as VectorTileSource | null;
|
||||||
|
if (!source) {
|
||||||
|
return () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const ownerId = `${propertyKey}-${Date.now().toString(36)}-${Math.random()
|
||||||
|
.toString(36)
|
||||||
|
.slice(2, 8)}`;
|
||||||
|
|
||||||
|
const normalizedNodeAreaMap = Object.fromEntries(
|
||||||
|
Object.entries(payload.nodeAreaMap ?? {}).map(([nodeId, areaId]) => [
|
||||||
|
String(nodeId),
|
||||||
|
String(areaId),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
const areaIds = (
|
||||||
|
payload.areaIds?.length
|
||||||
|
? payload.areaIds
|
||||||
|
: Array.from(new Set(Object.values(normalizedNodeAreaMap)))
|
||||||
|
)
|
||||||
|
.map(String)
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
if (Object.keys(normalizedNodeAreaMap).length === 0 || areaIds.length === 0) {
|
||||||
|
junctionLayer.setStyle(config.MAP_DEFAULT_STYLE as FlatStyleLike);
|
||||||
|
return () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const areaIdToIndex = new Map<string, number>();
|
||||||
|
areaIds.forEach((areaId, index) => {
|
||||||
|
areaIdToIndex.set(areaId, index + 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
const nodeAreaIndexMap = new Map<string, number>();
|
||||||
|
Object.entries(normalizedNodeAreaMap).forEach(([nodeId, areaId]) => {
|
||||||
|
const areaIndex = areaIdToIndex.get(areaId);
|
||||||
|
if (areaIndex !== undefined) {
|
||||||
|
nodeAreaIndexMap.set(nodeId, areaIndex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const applyFeatureAreaIndex = (renderFeature: any) => {
|
||||||
|
const featureId = String(renderFeature.get("id") ?? "");
|
||||||
|
const areaIndex = nodeAreaIndexMap.get(featureId);
|
||||||
|
if (areaIndex !== undefined) {
|
||||||
|
renderFeature.properties_[propertyKey] = areaIndex;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const sourceTiles = (source as any).sourceTiles_;
|
||||||
|
if (sourceTiles) {
|
||||||
|
Object.values(sourceTiles).forEach((vectorTile: any) => {
|
||||||
|
const renderFeatures = vectorTile.getFeatures();
|
||||||
|
if (!renderFeatures || renderFeatures.length === 0) return;
|
||||||
|
renderFeatures.forEach((renderFeature: any) => {
|
||||||
|
applyFeatureAreaIndex(renderFeature);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const listener = (event: any) => {
|
||||||
|
try {
|
||||||
|
if (!(event.tile instanceof VectorTile)) return;
|
||||||
|
const renderFeatures = event.tile.getFeatures();
|
||||||
|
if (!renderFeatures || renderFeatures.length === 0) return;
|
||||||
|
renderFeatures.forEach((renderFeature: any) => {
|
||||||
|
applyFeatureAreaIndex(renderFeature);
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error applying junction area render:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
source.on("tileloadend", listener);
|
||||||
|
|
||||||
|
const fillCases: any[] = [];
|
||||||
|
areaIds.forEach((areaId, index) => {
|
||||||
|
fillCases.push(
|
||||||
|
["==", ["get", propertyKey], index + 1],
|
||||||
|
payload.areaColors?.[areaId] ?? getAreaColor(areaId),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const defaultFillColor = String(config.MAP_DEFAULT_STYLE["circle-fill-color"]);
|
||||||
|
const defaultStrokeColor = String(
|
||||||
|
config.MAP_DEFAULT_STYLE["circle-stroke-color"],
|
||||||
|
);
|
||||||
|
|
||||||
|
junctionLayer.set(RENDER_OWNER_KEY, ownerId);
|
||||||
|
junctionLayer.setStyle({
|
||||||
|
...config.MAP_DEFAULT_STYLE,
|
||||||
|
"circle-fill-color": ["case", ...fillCases, defaultFillColor],
|
||||||
|
"circle-stroke-color": ["case", ...fillCases, defaultStrokeColor],
|
||||||
|
} as FlatStyleLike);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
source.un("tileloadend", listener);
|
||||||
|
if (junctionLayer.get(RENDER_OWNER_KEY) === ownerId) {
|
||||||
|
junctionLayer.unset(RENDER_OWNER_KEY, true);
|
||||||
|
junctionLayer.setStyle(config.MAP_DEFAULT_STYLE as FlatStyleLike);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect, useCallback } from "react";
|
import React, { useState, useEffect, useCallback, useRef } from "react";
|
||||||
import { useData, useMap } from "../MapComponent";
|
import { useData, useMap } from "../MapComponent";
|
||||||
import ToolbarButton from "@/components/olmap/common/ToolbarButton";
|
import ToolbarButton from "@/components/olmap/common/ToolbarButton";
|
||||||
import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
|
import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
|
||||||
@@ -24,6 +24,7 @@ import StyleLegend from "./StyleLegend"; // 引入图例组件
|
|||||||
import { handleMapClickSelectFeatures as mapClickSelectFeatures, queryFeaturesByIds } from "@/utils/mapQueryService";
|
import { handleMapClickSelectFeatures as mapClickSelectFeatures, queryFeaturesByIds } from "@/utils/mapQueryService";
|
||||||
import { useNotification } from "@refinedev/core";
|
import { useNotification } from "@refinedev/core";
|
||||||
import { useChatToolActionHandler } from "@/hooks/useChatToolActionHandler";
|
import { useChatToolActionHandler } from "@/hooks/useChatToolActionHandler";
|
||||||
|
import { applyJunctionAreaRender } from "@components/olmap/DMALeakDetection/applyJunctionAreaRender";
|
||||||
|
|
||||||
import { config } from "@/config/config";
|
import { config } from "@/config/config";
|
||||||
import { apiFetch } from "@/lib/apiFetch";
|
import { apiFetch } from "@/lib/apiFetch";
|
||||||
@@ -81,8 +82,16 @@ const Toolbar: React.FC<ToolbarProps> = ({
|
|||||||
startTime?: string;
|
startTime?: string;
|
||||||
endTime?: string;
|
endTime?: string;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
const chatJunctionRenderCleanupRef = useRef<(() => void) | null>(null);
|
||||||
|
|
||||||
// Wire up chat tool actions (locate, view_history, view_scada)
|
const disposeChatJunctionRender = useCallback(() => {
|
||||||
|
chatJunctionRenderCleanupRef.current?.();
|
||||||
|
chatJunctionRenderCleanupRef.current = null;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => () => disposeChatJunctionRender(), [disposeChatJunctionRender]);
|
||||||
|
|
||||||
|
// Wire up chat tool actions (locate, view_history, view_scada, render_junctions)
|
||||||
useChatToolActionHandler(
|
useChatToolActionHandler(
|
||||||
useCallback(
|
useCallback(
|
||||||
(action) => {
|
(action) => {
|
||||||
@@ -161,9 +170,29 @@ const Toolbar: React.FC<ToolbarProps> = ({
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "render_junctions": {
|
||||||
|
disposeChatJunctionRender();
|
||||||
|
|
||||||
|
if (Object.keys(action.nodeAreaMap).length === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (map) {
|
||||||
|
chatJunctionRenderCleanupRef.current = applyJunctionAreaRender(
|
||||||
|
map,
|
||||||
|
{
|
||||||
|
nodeAreaMap: action.nodeAreaMap,
|
||||||
|
areaIds: action.areaIds,
|
||||||
|
areaColors: action.areaColors,
|
||||||
|
},
|
||||||
|
{ propertyKey: "chat_junction_render_index" },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[map],
|
[disposeChatJunctionRender, map],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,12 @@ export type ChatToolAction =
|
|||||||
series?: Array<{ name: string; data: number[]; type?: "line" | "bar" }>;
|
series?: Array<{ name: string; data: number[]; type?: "line" | "bar" }>;
|
||||||
xAxisName?: string;
|
xAxisName?: string;
|
||||||
yAxisName?: string;
|
yAxisName?: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "render_junctions";
|
||||||
|
nodeAreaMap: Record<string, string>;
|
||||||
|
areaIds?: string[];
|
||||||
|
areaColors?: Record<string, string>;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface ChatToolState {
|
interface ChatToolState {
|
||||||
|
|||||||
Reference in New Issue
Block a user