添加工具调用解析和聊天工具操作处理
This commit is contained in:
@@ -59,6 +59,10 @@ export interface SCADADataPanelProps {
|
||||
defaultTab?: "chart" | "table";
|
||||
/** Y 轴数值的小数位数 */
|
||||
fractionDigits?: number;
|
||||
/** 外部传入开始时间(ISO8601 字符串),用于初始化并触发查询 */
|
||||
start_time?: string;
|
||||
/** 外部传入结束时间(ISO8601 字符串),用于初始化并触发查询 */
|
||||
end_time?: string;
|
||||
}
|
||||
|
||||
type PanelTab = "chart" | "table";
|
||||
@@ -396,6 +400,8 @@ const SCADADataPanel: React.FC<SCADADataPanelProps> = ({
|
||||
scheme_name,
|
||||
defaultTab = "chart",
|
||||
fractionDigits = 2,
|
||||
start_time,
|
||||
end_time,
|
||||
}) => {
|
||||
// 从 featureInfos 中提取设备 ID 列表
|
||||
const deviceIds = useMemo(
|
||||
@@ -403,8 +409,24 @@ const SCADADataPanel: React.FC<SCADADataPanelProps> = ({
|
||||
[featureInfos]
|
||||
);
|
||||
|
||||
const [from, setFrom] = useState<Dayjs>(() => dayjs().subtract(1, "day"));
|
||||
const [to, setTo] = useState<Dayjs>(() => dayjs());
|
||||
const [from, setFrom] = useState<Dayjs>(() => {
|
||||
if (start_time) {
|
||||
const parsedStart = dayjs(start_time);
|
||||
if (parsedStart.isValid()) {
|
||||
return parsedStart;
|
||||
}
|
||||
}
|
||||
return dayjs().subtract(1, "day");
|
||||
});
|
||||
const [to, setTo] = useState<Dayjs>(() => {
|
||||
if (end_time) {
|
||||
const parsedEnd = dayjs(end_time);
|
||||
if (parsedEnd.isValid()) {
|
||||
return parsedEnd;
|
||||
}
|
||||
}
|
||||
return dayjs();
|
||||
});
|
||||
const [activeTab, setActiveTab] = useState<PanelTab>(defaultTab);
|
||||
const [timeSeries, setTimeSeries] = useState<TimeSeriesPoint[]>([]);
|
||||
const [loadingState, setLoadingState] = useState<LoadingState>("idle");
|
||||
@@ -418,6 +440,22 @@ const SCADADataPanel: React.FC<SCADADataPanelProps> = ({
|
||||
setActiveTab(defaultTab);
|
||||
}, [defaultTab]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!start_time && !end_time) return;
|
||||
if (start_time) {
|
||||
const parsedStart = dayjs(start_time);
|
||||
if (parsedStart.isValid()) {
|
||||
setFrom((prev) => (parsedStart.isSame(prev) ? prev : parsedStart));
|
||||
}
|
||||
}
|
||||
if (end_time) {
|
||||
const parsedEnd = dayjs(end_time);
|
||||
if (parsedEnd.isValid()) {
|
||||
setTo((prev) => (parsedEnd.isSame(prev) ? prev : parsedEnd));
|
||||
}
|
||||
}
|
||||
}, [start_time, end_time]);
|
||||
|
||||
const normalizedRange = useMemo(() => ensureValidRange(from, to), [from, to]);
|
||||
|
||||
const hasDevices = deviceIds.length > 0;
|
||||
|
||||
@@ -8,16 +8,20 @@ import QueryStatsOutlinedIcon from "@mui/icons-material/QueryStatsOutlined";
|
||||
import PropertyPanel from "./PropertyPanel"; // 引入属性面板组件
|
||||
import DrawPanel from "./DrawPanel"; // 引入绘图面板组件
|
||||
import HistoryDataPanel from "./HistoryDataPanel"; // 引入绘图面板组件
|
||||
import SCADADataPanel from "@components/olmap/SCADA/SCADADataPanel";
|
||||
|
||||
import VectorSource from "ol/source/Vector";
|
||||
import VectorLayer from "ol/layer/Vector";
|
||||
import { Style, Stroke, Fill, Circle } from "ol/style";
|
||||
import Feature from "ol/Feature";
|
||||
import { GeoJSON } from "ol/format";
|
||||
import { bbox, featureCollection } from "@turf/turf";
|
||||
import StyleEditorPanel from "./StyleEditorPanel";
|
||||
import { LayerStyleState } from "./StyleEditorPanel";
|
||||
import StyleLegend from "./StyleLegend"; // 引入图例组件
|
||||
import { handleMapClickSelectFeatures as mapClickSelectFeatures } from "@/utils/mapQueryService";
|
||||
import { handleMapClickSelectFeatures as mapClickSelectFeatures, queryFeaturesByIds } from "@/utils/mapQueryService";
|
||||
import { useNotification } from "@refinedev/core";
|
||||
import { useChatToolActionHandler } from "@/hooks/useChatToolActionHandler";
|
||||
|
||||
import { config } from "@/config/config";
|
||||
import { apiFetch } from "@/lib/apiFetch";
|
||||
@@ -51,6 +55,89 @@ const Toolbar: React.FC<ToolbarProps> = ({
|
||||
const selectedDate = data?.selectedDate;
|
||||
const schemeName = data?.schemeName;
|
||||
|
||||
// Chat tool action → direct featureInfos override (bypasses OL Feature lookup)
|
||||
const [chatPanelFeatureInfos, setChatPanelFeatureInfos] = useState<
|
||||
[string, string][] | null
|
||||
>(null);
|
||||
const [chatPanelType, setChatPanelType] = useState<
|
||||
"realtime" | "scheme" | "none"
|
||||
>("none");
|
||||
const [chatPanelTimeRange, setChatPanelTimeRange] = useState<{
|
||||
startTime?: string;
|
||||
endTime?: string;
|
||||
} | null>(null);
|
||||
|
||||
// Wire up chat tool actions (locate, view_history, view_scada)
|
||||
useChatToolActionHandler(
|
||||
useCallback(
|
||||
(action) => {
|
||||
const geojsonFormat = new GeoJSON();
|
||||
const zoomToFeatures = (features: Feature[]) => {
|
||||
if (features.length === 0) return;
|
||||
const geojsonFeatures = features.map((f) =>
|
||||
geojsonFormat.writeFeatureObject(f),
|
||||
);
|
||||
const extent = bbox(featureCollection(geojsonFeatures as any));
|
||||
if (extent) {
|
||||
map?.getView().fit(extent, { maxZoom: 18, duration: 1000 });
|
||||
}
|
||||
};
|
||||
|
||||
switch (action.type) {
|
||||
case "locate_nodes": {
|
||||
queryFeaturesByIds(action.ids, "geo_junctions_mat").then(
|
||||
(features) => {
|
||||
if (features.length > 0) {
|
||||
setHighlightFeatures(features);
|
||||
zoomToFeatures(features);
|
||||
}
|
||||
},
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "locate_pipes": {
|
||||
queryFeaturesByIds(action.ids, "geo_pipes_mat").then(
|
||||
(features) => {
|
||||
if (features.length > 0) {
|
||||
setHighlightFeatures(features);
|
||||
zoomToFeatures(features);
|
||||
}
|
||||
},
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "view_history": {
|
||||
setChatPanelFeatureInfos(action.featureInfos);
|
||||
setChatPanelType(action.dataType);
|
||||
setChatPanelTimeRange({
|
||||
startTime: action.startTime,
|
||||
endTime: action.endTime,
|
||||
});
|
||||
setShowHistoryPanel(true);
|
||||
break;
|
||||
}
|
||||
case "view_scada": {
|
||||
setChatPanelFeatureInfos(action.featureInfos);
|
||||
setChatPanelType("none");
|
||||
setChatPanelTimeRange({
|
||||
startTime: action.startTime,
|
||||
endTime: action.endTime,
|
||||
});
|
||||
setShowHistoryPanel(true);
|
||||
setActiveTools((prev) => {
|
||||
if (prev.includes("history")) {
|
||||
return prev;
|
||||
}
|
||||
return [...prev, "history"];
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
[map],
|
||||
),
|
||||
);
|
||||
|
||||
// 样式状态管理 - 在 Toolbar 中管理,带有默认样式
|
||||
const [layerStyleStates, setLayerStyleStates] = useState<LayerStyleState[]>([
|
||||
{
|
||||
@@ -328,6 +415,8 @@ const Toolbar: React.FC<ToolbarProps> = ({
|
||||
case "history":
|
||||
setShowHistoryPanel(false);
|
||||
setHighlightFeatures([]);
|
||||
setChatPanelFeatureInfos(null);
|
||||
setChatPanelTimeRange(null);
|
||||
break;
|
||||
}
|
||||
};
|
||||
@@ -354,6 +443,8 @@ const Toolbar: React.FC<ToolbarProps> = ({
|
||||
setHighlightFeatures([]);
|
||||
setShowDrawPanel(false);
|
||||
setShowHistoryPanel(false);
|
||||
setChatPanelFeatureInfos(null);
|
||||
setChatPanelTimeRange(null);
|
||||
// 样式编辑器保持其当前状态,不自动关闭
|
||||
};
|
||||
const [computedProperties, setComputedProperties] = useState<
|
||||
@@ -770,9 +861,16 @@ const Toolbar: React.FC<ToolbarProps> = ({
|
||||
/>
|
||||
</div>
|
||||
{showHistoryPanel &&
|
||||
(HistoryPanel ? (
|
||||
(chatPanelType === "none" && chatPanelFeatureInfos ? (
|
||||
<SCADADataPanel
|
||||
deviceIds={chatPanelFeatureInfos.map(([id]) => id)}
|
||||
visible={showHistoryPanel}
|
||||
start_time={chatPanelTimeRange?.startTime}
|
||||
end_time={chatPanelTimeRange?.endTime}
|
||||
/>
|
||||
) : HistoryPanel ? (
|
||||
<HistoryPanel
|
||||
featureInfos={(() => {
|
||||
featureInfos={chatPanelFeatureInfos ?? (() => {
|
||||
if (highlightFeatures.length === 0 || !showHistoryPanel)
|
||||
return [];
|
||||
|
||||
@@ -810,11 +908,13 @@ const Toolbar: React.FC<ToolbarProps> = ({
|
||||
})()}
|
||||
scheme_type="burst_Analysis"
|
||||
scheme_name={schemeName}
|
||||
type={queryType as "realtime" | "scheme" | "none"}
|
||||
type={chatPanelFeatureInfos ? chatPanelType : (queryType as "realtime" | "scheme" | "none")}
|
||||
start_time={chatPanelTimeRange?.startTime}
|
||||
end_time={chatPanelTimeRange?.endTime}
|
||||
/>
|
||||
) : (
|
||||
<HistoryDataPanel
|
||||
featureInfos={(() => {
|
||||
featureInfos={chatPanelFeatureInfos ?? (() => {
|
||||
if (highlightFeatures.length === 0 || !showHistoryPanel)
|
||||
return [];
|
||||
|
||||
@@ -852,7 +952,9 @@ const Toolbar: React.FC<ToolbarProps> = ({
|
||||
})()}
|
||||
scheme_type="burst_Analysis"
|
||||
scheme_name={schemeName}
|
||||
type={queryType as "realtime" | "scheme" | "none"}
|
||||
type={chatPanelFeatureInfos ? chatPanelType : (queryType as "realtime" | "scheme" | "none")}
|
||||
start_time={chatPanelTimeRange?.startTime}
|
||||
end_time={chatPanelTimeRange?.endTime}
|
||||
/>
|
||||
))}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user