修复lint errors
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
@@ -115,7 +115,7 @@ const EmptyState = () => (
|
||||
|
||||
const LocationResults: React.FC<Props> = ({ result }) => {
|
||||
const map = useMap();
|
||||
const [highlightLayer, setHighlightLayer] = useState<VectorLayer<VectorSource> | null>(null);
|
||||
const highlightLayerRef = useRef<VectorLayer<VectorSource> | null>(null);
|
||||
const [highlightFeatures, setHighlightFeatures] = useState<Feature[]>([]);
|
||||
|
||||
const candidatePipes = useMemo<BurstCandidate[]>(() => {
|
||||
@@ -128,13 +128,13 @@ const LocationResults: React.FC<Props> = ({ result }) => {
|
||||
return base;
|
||||
}, [result]);
|
||||
|
||||
const allCandidatePipeIds = useMemo<string[]>(() => {
|
||||
const allCandidatePipeIds = (() => {
|
||||
const ids = candidatePipes.map((item) => item.pipe_id);
|
||||
if (result?.located_pipe) {
|
||||
ids.unshift(result.located_pipe);
|
||||
}
|
||||
return Array.from(new Set(ids.filter(Boolean)));
|
||||
}, [candidatePipes, result?.located_pipe]);
|
||||
})();
|
||||
|
||||
useEffect(() => {
|
||||
if (!map) return;
|
||||
@@ -159,19 +159,20 @@ const LocationResults: React.FC<Props> = ({ result }) => {
|
||||
},
|
||||
});
|
||||
map.addLayer(layer);
|
||||
setHighlightLayer(layer);
|
||||
highlightLayerRef.current = layer;
|
||||
|
||||
return () => {
|
||||
highlightLayerRef.current = null;
|
||||
map.removeLayer(layer);
|
||||
};
|
||||
}, [map]);
|
||||
|
||||
useEffect(() => {
|
||||
const source = highlightLayer?.getSource();
|
||||
const source = highlightLayerRef.current?.getSource();
|
||||
if (!source) return;
|
||||
source.clear();
|
||||
highlightFeatures.forEach((feature) => source.addFeature(feature));
|
||||
}, [highlightFeatures, highlightLayer]);
|
||||
}, [highlightFeatures]);
|
||||
|
||||
const locatePipes = async (pipeIds: string[]) => {
|
||||
if (!pipeIds.length || !map) return;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
@@ -41,8 +41,7 @@ interface LocationResultsProps {
|
||||
const LocationResults: React.FC<LocationResultsProps> = ({
|
||||
results = [],
|
||||
}) => {
|
||||
const [highlightLayer, setHighlightLayer] =
|
||||
useState<VectorLayer<VectorSource> | null>(null);
|
||||
const highlightLayerRef = useRef<VectorLayer<VectorSource> | null>(null);
|
||||
const [highlightFeatures, setHighlightFeatures] = useState<Feature[]>([]);
|
||||
const map = useMap();
|
||||
|
||||
@@ -145,19 +144,17 @@ const LocationResults: React.FC<LocationResultsProps> = ({
|
||||
});
|
||||
|
||||
map.addLayer(highlightLayer);
|
||||
setHighlightLayer(highlightLayer);
|
||||
highlightLayerRef.current = highlightLayer;
|
||||
|
||||
return () => {
|
||||
highlightLayerRef.current = null;
|
||||
map.removeLayer(highlightLayer);
|
||||
};
|
||||
}, [map]);
|
||||
|
||||
// 高亮要素的函数
|
||||
useEffect(() => {
|
||||
if (!highlightLayer) {
|
||||
return;
|
||||
}
|
||||
const source = highlightLayer.getSource();
|
||||
const source = highlightLayerRef.current?.getSource();
|
||||
if (!source) {
|
||||
return;
|
||||
}
|
||||
@@ -169,7 +166,7 @@ const LocationResults: React.FC<LocationResultsProps> = ({
|
||||
source.addFeature(feature);
|
||||
}
|
||||
});
|
||||
}, [highlightFeatures, highlightLayer]);
|
||||
}, [highlightFeatures]);
|
||||
|
||||
// 取第一条记录或空对象
|
||||
const result = results.length > 0 ? results[0] : null;
|
||||
|
||||
@@ -1090,7 +1090,7 @@ const ValveIsolation: React.FC<ValveIsolationProps> = ({
|
||||
</>
|
||||
) : (
|
||||
<Alert severity="info" variant="outlined">
|
||||
请先在流程2中选择不可用阀门,然后点击"扩大搜索"按钮
|
||||
请先在流程2中选择不可用阀门,然后点击“扩大搜索”按钮
|
||||
</Alert>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState, useEffect, useRef, useCallback } from "react";
|
||||
import React, { useState, useEffect, useRef, useCallback, useMemo } from "react";
|
||||
import { useNotification } from "@refinedev/core";
|
||||
import WebGLVectorTileLayer from "ol/layer/WebGLVectorTile";
|
||||
|
||||
@@ -27,7 +27,6 @@ import dayjs from "dayjs";
|
||||
import { PlayArrow, Pause, Stop, Refresh } from "@mui/icons-material";
|
||||
import { TbArrowBackUp, TbArrowForwardUp } from "react-icons/tb";
|
||||
import { FiSkipBack, FiSkipForward } from "react-icons/fi";
|
||||
import { useData } from "@components/olmap/core/MapComponent";
|
||||
import { config, NETWORK_NAME } from "@/config/config";
|
||||
import { apiFetch } from "@/lib/apiFetch";
|
||||
import { useMap } from "@components/olmap/core/MapComponent";
|
||||
@@ -63,10 +62,6 @@ interface TimelineProps {
|
||||
const Timeline: React.FC<TimelineProps> = ({
|
||||
disableDateSelection = false,
|
||||
}) => {
|
||||
const data = useData();
|
||||
if (!data) {
|
||||
return <div>Loading...</div>; // 或其他占位符
|
||||
}
|
||||
const { open } = useNotification();
|
||||
const {
|
||||
predictionResults,
|
||||
@@ -79,7 +74,6 @@ const Timeline: React.FC<TimelineProps> = ({
|
||||
const [isPlaying, setIsPlaying] = useState<boolean>(false);
|
||||
const [playInterval, setPlayInterval] = useState<number>(5000); // 毫秒
|
||||
const [isPredicting, setIsPredicting] = useState<boolean>(false);
|
||||
const [pipeLayer, setPipeLayer] = useState<WebGLVectorTileLayer | null>(null);
|
||||
|
||||
// 使用 ref 存储当前的健康数据,供事件监听器读取,避免重复绑定
|
||||
const healthDataRef = useRef<Map<string, number>>(new Map());
|
||||
@@ -228,10 +222,21 @@ const Timeline: React.FC<TimelineProps> = ({
|
||||
clearTimeout(debounceRef.current);
|
||||
}
|
||||
};
|
||||
}, [pipeLayer]);
|
||||
}, []);
|
||||
|
||||
// 获取地图实例
|
||||
const map = useMap();
|
||||
const pipeLayer = useMemo(() => {
|
||||
if (!map) return null;
|
||||
|
||||
const layers = map.getLayers().getArray();
|
||||
return (
|
||||
layers.find(
|
||||
(layer) =>
|
||||
layer instanceof WebGLVectorTileLayer && layer.get("value") === "pipes",
|
||||
) as WebGLVectorTileLayer | undefined
|
||||
) ?? null;
|
||||
}, [map]);
|
||||
|
||||
// 根据索引从 survival_function 中获取生存概率
|
||||
const getSurvivalProbabilityAtYear = useCallback(
|
||||
@@ -362,21 +367,6 @@ const Timeline: React.FC<TimelineProps> = ({
|
||||
updatePipeHealthData,
|
||||
]);
|
||||
|
||||
// 初始化管道图层
|
||||
useEffect(() => {
|
||||
if (!map) return;
|
||||
|
||||
const layers = map.getLayers().getArray();
|
||||
const pipesLayer = layers.find(
|
||||
(layer) =>
|
||||
layer instanceof WebGLVectorTileLayer && layer.get("value") === "pipes",
|
||||
) as WebGLVectorTileLayer | undefined;
|
||||
|
||||
if (pipesLayer) {
|
||||
setPipeLayer(pipesLayer);
|
||||
}
|
||||
}, [map]);
|
||||
|
||||
// 监听依赖变化,更新样式
|
||||
useEffect(() => {
|
||||
if (predictionResults.length > 0 && pipeLayer) {
|
||||
|
||||
@@ -31,9 +31,7 @@ import { useMap } from "../MapComponent";
|
||||
const DrawPanel: React.FC = () => {
|
||||
const map = useMap();
|
||||
const [activeTool, setActiveTool] = useState<string>("pan");
|
||||
const [drawLayer, setDrawLayer] = useState<VectorLayer<VectorSource> | null>(
|
||||
null
|
||||
);
|
||||
const drawLayerRef = useRef<VectorLayer<VectorSource> | null>(null);
|
||||
const [drawnFeatures, setDrawnFeatures] = useState<Feature<Geometry>[]>([]);
|
||||
const [history, setHistory] = useState<{
|
||||
stack: Feature<Geometry>[][];
|
||||
@@ -79,13 +77,14 @@ const DrawPanel: React.FC = () => {
|
||||
});
|
||||
|
||||
map.addLayer(drawVectorLayer);
|
||||
setDrawLayer(drawVectorLayer);
|
||||
drawLayerRef.current = drawVectorLayer;
|
||||
|
||||
return () => {
|
||||
if (drawInteractionRef.current && map) {
|
||||
map.removeInteraction(drawInteractionRef.current);
|
||||
drawInteractionRef.current = null;
|
||||
}
|
||||
drawLayerRef.current = null;
|
||||
map.removeLayer(drawVectorLayer);
|
||||
};
|
||||
}, [map, drawInteractionRef]);
|
||||
@@ -110,6 +109,7 @@ const DrawPanel: React.FC = () => {
|
||||
type: GeometryType,
|
||||
geometryFunction?: GeometryFunction
|
||||
) => {
|
||||
const drawLayer = drawLayerRef.current;
|
||||
if (!drawLayer) return;
|
||||
if (!map) return;
|
||||
|
||||
@@ -285,6 +285,7 @@ const DrawPanel: React.FC = () => {
|
||||
|
||||
// 删除所有绘制的要素
|
||||
const handleDelete = () => {
|
||||
const drawLayer = drawLayerRef.current;
|
||||
if (!drawLayer) return;
|
||||
|
||||
const source = drawLayer.getSource();
|
||||
@@ -301,6 +302,7 @@ const DrawPanel: React.FC = () => {
|
||||
|
||||
// 更新绘图图层
|
||||
const updateDrawLayer = (features: Feature<Geometry>[]) => {
|
||||
const drawLayer = drawLayerRef.current;
|
||||
if (!drawLayer) return;
|
||||
|
||||
const source = drawLayer.getSource();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import React, { useState, useEffect, useMemo } from "react";
|
||||
import { useData, useMap } from "../MapComponent";
|
||||
import { Checkbox, FormControlLabel } from "@mui/material";
|
||||
import WebGLVectorTileLayer from "ol/layer/WebGLVectorTile";
|
||||
@@ -18,15 +18,12 @@ interface LayerItem {
|
||||
const LayerControl: React.FC = () => {
|
||||
const map = useMap();
|
||||
const data = useData();
|
||||
if (!data) return;
|
||||
const {
|
||||
deckLayer,
|
||||
isContourLayerAvailable,
|
||||
isWaterflowLayerAvailable,
|
||||
setShowWaterflowLayer,
|
||||
setShowContourLayer,
|
||||
} = data;
|
||||
const [layerItems, setLayerItems] = useState<LayerItem[]>([]);
|
||||
const [refreshKey, setRefreshKey] = useState(0);
|
||||
const deckLayer = data?.deckLayer;
|
||||
const isContourLayerAvailable = data?.isContourLayerAvailable;
|
||||
const isWaterflowLayerAvailable = data?.isWaterflowLayerAvailable;
|
||||
const setShowWaterflowLayer = data?.setShowWaterflowLayer;
|
||||
const setShowContourLayer = data?.setShowContourLayer;
|
||||
|
||||
const layerOrder = [
|
||||
"junctions",
|
||||
@@ -40,16 +37,12 @@ const LayerControl: React.FC = () => {
|
||||
"junctionContourLayer",
|
||||
];
|
||||
|
||||
// 更新图层列表
|
||||
const updateLayers = useCallback(() => {
|
||||
if (!map || !data) return;
|
||||
const layerItems = useMemo(() => {
|
||||
if (!map || !data) return [];
|
||||
|
||||
const items: LayerItem[] = [];
|
||||
|
||||
// 1. 获取 OpenLayers 图层
|
||||
const mapLayers = map.getLayers().getArray();
|
||||
mapLayers.forEach((layer) => {
|
||||
// 筛选特定类型的 OpenLayers 图层
|
||||
map.getLayers().getArray().forEach((layer) => {
|
||||
if (
|
||||
layer instanceof WebGLVectorTileLayer ||
|
||||
layer instanceof VectorTileLayer ||
|
||||
@@ -57,7 +50,6 @@ const LayerControl: React.FC = () => {
|
||||
) {
|
||||
const value = layer.get("value");
|
||||
const name = layer.get("name");
|
||||
// 只有设置了 value (作为 ID) 的图层才会被纳入控制
|
||||
if (value) {
|
||||
items.push({
|
||||
id: value,
|
||||
@@ -70,66 +62,56 @@ const LayerControl: React.FC = () => {
|
||||
}
|
||||
});
|
||||
|
||||
// 2. 获取 DeckLayer 中的子图层
|
||||
if (deckLayer && deckLayer instanceof DeckLayer) {
|
||||
const deckLayers = deckLayer.getDeckLayers();
|
||||
deckLayers.forEach((layer: any) => {
|
||||
if (layer && layer.id) {
|
||||
// 仅处理 junctionContourLayer 和 waterflowLayer
|
||||
if (
|
||||
layer.id !== "junctionContourLayer" &&
|
||||
layer.id !== "waterflowLayer"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// 检查可用性
|
||||
if (
|
||||
(layer.id === "junctionContourLayer" && !isContourLayerAvailable) ||
|
||||
(layer.id === "waterflowLayer" && !isWaterflowLayerAvailable)
|
||||
) {
|
||||
return; // 跳过不可用图层
|
||||
}
|
||||
const visible =
|
||||
deckLayer.getDeckLayerVisible(layer.id) ??
|
||||
layer.props?.visible ??
|
||||
true;
|
||||
items.push({
|
||||
id: layer.props.id,
|
||||
name: layer.props.name, // 使用 name 属性作为显示名称
|
||||
visible: visible,
|
||||
type: "deck",
|
||||
layerRef: layer,
|
||||
});
|
||||
deckLayer.getDeckLayers().forEach((layer: any) => {
|
||||
if (!layer?.id) return;
|
||||
if (layer.id !== "junctionContourLayer" && layer.id !== "waterflowLayer") {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
(layer.id === "junctionContourLayer" && !isContourLayerAvailable) ||
|
||||
(layer.id === "waterflowLayer" && !isWaterflowLayerAvailable)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
items.push({
|
||||
id: layer.props.id,
|
||||
name: layer.props.name,
|
||||
visible:
|
||||
deckLayer.getDeckLayerVisible(layer.id) ?? layer.props?.visible ?? true,
|
||||
type: "deck",
|
||||
layerRef: layer,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 过滤并排序
|
||||
const sortedItems = items
|
||||
return items
|
||||
.filter((item) => layerOrder.includes(item.id))
|
||||
.sort((a, b) => {
|
||||
const indexA = layerOrder.indexOf(a.id);
|
||||
const indexB = layerOrder.indexOf(b.id);
|
||||
return indexA - indexB;
|
||||
});
|
||||
|
||||
setLayerItems(sortedItems);
|
||||
}, [map, deckLayer, isWaterflowLayerAvailable, isContourLayerAvailable]);
|
||||
.sort((a, b) => layerOrder.indexOf(a.id) - layerOrder.indexOf(b.id));
|
||||
}, [
|
||||
map,
|
||||
data,
|
||||
deckLayer,
|
||||
isContourLayerAvailable,
|
||||
isWaterflowLayerAvailable,
|
||||
refreshKey,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
updateLayers();
|
||||
if (!map) return;
|
||||
|
||||
if (map) {
|
||||
const layerCollection = map.getLayers();
|
||||
layerCollection.on("change:length", updateLayers);
|
||||
}
|
||||
const layerCollection = map.getLayers();
|
||||
const handleLayerChange = () => {
|
||||
setRefreshKey((prev) => prev + 1);
|
||||
};
|
||||
|
||||
layerCollection.on("change:length", handleLayerChange);
|
||||
|
||||
return () => {
|
||||
if (map) {
|
||||
map.getLayers().un("change:length", updateLayers);
|
||||
}
|
||||
map.getLayers().un("change:length", handleLayerChange);
|
||||
};
|
||||
}, [map, updateLayers]);
|
||||
}, [map]);
|
||||
|
||||
const handleVisibilityChange = (item: LayerItem, checked: boolean) => {
|
||||
if (item.type === "ol") {
|
||||
@@ -142,10 +124,7 @@ const LayerControl: React.FC = () => {
|
||||
setShowWaterflowLayer && setShowWaterflowLayer(checked);
|
||||
}
|
||||
}
|
||||
|
||||
setLayerItems((prev) =>
|
||||
prev.map((i) => (i.id === item.id ? { ...i, visible: checked } : i)),
|
||||
);
|
||||
setRefreshKey((prev) => prev + 1);
|
||||
};
|
||||
|
||||
if (!data) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect, useCallback, useRef } from "react";
|
||||
import React, { useState, useEffect, useCallback, useRef, useMemo } from "react";
|
||||
|
||||
// 导入Material-UI图标和组件
|
||||
import ColorLensIcon from "@mui/icons-material/ColorLens";
|
||||
@@ -180,26 +180,21 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
|
||||
}) => {
|
||||
const map = useMap();
|
||||
const data = useData();
|
||||
if (!data) {
|
||||
return <div>Loading...</div>; // 或其他占位符
|
||||
}
|
||||
const {
|
||||
currentJunctionCalData,
|
||||
currentPipeCalData,
|
||||
junctionText,
|
||||
pipeText,
|
||||
setShowJunctionTextLayer,
|
||||
setShowPipeTextLayer,
|
||||
setShowJunctionId,
|
||||
setShowPipeId,
|
||||
setContourLayerAvailable,
|
||||
setWaterflowLayerAvailable,
|
||||
setJunctionText,
|
||||
setPipeText,
|
||||
setContours,
|
||||
diameterRange,
|
||||
elevationRange,
|
||||
} = data;
|
||||
const currentJunctionCalData = data?.currentJunctionCalData;
|
||||
const currentPipeCalData = data?.currentPipeCalData;
|
||||
const junctionText = data?.junctionText ?? "";
|
||||
const pipeText = data?.pipeText ?? "";
|
||||
const setShowJunctionTextLayer = data?.setShowJunctionTextLayer;
|
||||
const setShowPipeTextLayer = data?.setShowPipeTextLayer;
|
||||
const setShowJunctionId = data?.setShowJunctionId;
|
||||
const setShowPipeId = data?.setShowPipeId;
|
||||
const setContourLayerAvailable = data?.setContourLayerAvailable;
|
||||
const setWaterflowLayerAvailable = data?.setWaterflowLayerAvailable;
|
||||
const setJunctionText = data?.setJunctionText;
|
||||
const setPipeText = data?.setPipeText;
|
||||
const setContours = data?.setContours;
|
||||
const diameterRange = data?.diameterRange;
|
||||
const elevationRange = data?.elevationRange;
|
||||
|
||||
const unitHeadlossRange = [0, 5];
|
||||
|
||||
@@ -213,9 +208,6 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
|
||||
const [renderLayers, setRenderLayers] = useState<WebGLVectorTileLayer[]>([]);
|
||||
const [selectedRenderLayer, setSelectedRenderLayer] =
|
||||
useState<WebGLVectorTileLayer>();
|
||||
const [availableProperties, setAvailableProperties] = useState<
|
||||
{ name: string; value: string }[]
|
||||
>([]);
|
||||
const [styleConfig, setStyleConfig] = useState<StyleConfig>({
|
||||
property: "",
|
||||
classificationMethod: "pretty_breaks",
|
||||
@@ -237,6 +229,74 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
|
||||
customColors: [],
|
||||
});
|
||||
|
||||
const getDefaultCustomColors = (
|
||||
segments: number,
|
||||
existingColors: string[] = []
|
||||
) => {
|
||||
const nextColors = [...existingColors];
|
||||
const baseColors = RAINBOW_PALETTES[0].colors;
|
||||
|
||||
while (nextColors.length < segments) {
|
||||
nextColors.push(baseColors[nextColors.length % baseColors.length]);
|
||||
}
|
||||
|
||||
return nextColors.slice(0, segments);
|
||||
};
|
||||
|
||||
const getDefaultCustomBreaks = (
|
||||
segments: number,
|
||||
property: string,
|
||||
layer: WebGLVectorTileLayer | undefined = selectedRenderLayer
|
||||
) => {
|
||||
if (!layer || !property) {
|
||||
return Array.from({ length: segments }, () => 0);
|
||||
}
|
||||
|
||||
const selectedLayerId = layer.get("value");
|
||||
let dataArr: number[] = [];
|
||||
|
||||
const isElevation =
|
||||
selectedLayerId === "junctions" && property === "elevation";
|
||||
const isDiameter = selectedLayerId === "pipes" && property === "diameter";
|
||||
|
||||
if (isElevation && elevationRange) {
|
||||
dataArr = [elevationRange[0], elevationRange[1]];
|
||||
} else if (isDiameter && diameterRange) {
|
||||
dataArr = [diameterRange[0], diameterRange[1]];
|
||||
} else if (selectedLayerId === "junctions" && currentJunctionCalData) {
|
||||
dataArr = currentJunctionCalData.map((d: any) => d.value);
|
||||
} else if (selectedLayerId === "pipes" && currentPipeCalData) {
|
||||
dataArr = currentPipeCalData.map((d: any) => d.value);
|
||||
}
|
||||
|
||||
if (dataArr.length === 0) {
|
||||
return Array.from({ length: segments }, () => 0);
|
||||
}
|
||||
|
||||
const defaultBreaks = calculateClassification(
|
||||
dataArr,
|
||||
segments,
|
||||
"pretty_breaks"
|
||||
).slice(0, segments);
|
||||
|
||||
while (defaultBreaks.length < segments) {
|
||||
defaultBreaks.push(defaultBreaks[defaultBreaks.length - 1] ?? 0);
|
||||
}
|
||||
|
||||
return defaultBreaks;
|
||||
};
|
||||
|
||||
const availableProperties = useMemo<{ name: string; value: string }[]>(() => {
|
||||
if (!selectedRenderLayer) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return (selectedRenderLayer.get("properties") || []) as {
|
||||
name: string;
|
||||
value: string;
|
||||
}[];
|
||||
}, [selectedRenderLayer]);
|
||||
|
||||
// 根据分段数生成相应数量的渐进颜色
|
||||
const generateGradientColors = useCallback(
|
||||
(segments: number): string[] => {
|
||||
@@ -278,63 +338,56 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
|
||||
[styleConfig.rainbowPaletteIndex]
|
||||
);
|
||||
// 保存当前图层的样式状态
|
||||
const saveLayerStyle = useCallback(
|
||||
(
|
||||
layerId?: string,
|
||||
newLegendConfig?: LegendStyleConfig,
|
||||
overrideStyleConfig?: StyleConfig
|
||||
) => {
|
||||
const currentStyleConfig = overrideStyleConfig || styleConfig;
|
||||
const saveLayerStyle = (
|
||||
layerId?: string,
|
||||
newLegendConfig?: LegendStyleConfig,
|
||||
overrideStyleConfig?: StyleConfig
|
||||
) => {
|
||||
const currentStyleConfig = overrideStyleConfig || styleConfig;
|
||||
|
||||
if (!currentStyleConfig.property) {
|
||||
console.warn("无法保存样式:缺少必要的图层或样式配置");
|
||||
return;
|
||||
if (!currentStyleConfig.property) {
|
||||
console.warn("无法保存样式:缺少必要的图层或样式配置");
|
||||
return;
|
||||
}
|
||||
if (!layerId) return;
|
||||
|
||||
const layerName =
|
||||
newLegendConfig?.layerName ||
|
||||
selectedRenderLayer?.get("name") ||
|
||||
`图层${layerId}`;
|
||||
const property = availableProperties.find(
|
||||
(p) => p.value === currentStyleConfig.property
|
||||
);
|
||||
const legendConfig: LegendStyleConfig = newLegendConfig || {
|
||||
layerId,
|
||||
layerName,
|
||||
property: property?.name || currentStyleConfig.property,
|
||||
colors: [],
|
||||
type: selectedRenderLayer?.get("type") || "point",
|
||||
dimensions: [],
|
||||
breaks: [],
|
||||
};
|
||||
|
||||
const newStyleState: LayerStyleState = {
|
||||
layerId,
|
||||
layerName,
|
||||
styleConfig: { ...currentStyleConfig },
|
||||
legendConfig: { ...legendConfig },
|
||||
isActive: true,
|
||||
};
|
||||
|
||||
setLayerStyleStates((prev) => {
|
||||
const existingIndex = prev.findIndex((state) => state.layerId === layerId);
|
||||
|
||||
if (existingIndex !== -1) {
|
||||
const updated = [...prev];
|
||||
updated[existingIndex] = newStyleState;
|
||||
return updated;
|
||||
}
|
||||
if (!layerId) return; // 如果没有传入 layerId,则不保存
|
||||
// 如果没有传入图例配置,则创建一个默认的空配置
|
||||
const layerName =
|
||||
newLegendConfig?.layerName ||
|
||||
selectedRenderLayer?.get("name") ||
|
||||
`图层${layerId}`;
|
||||
const property = availableProperties.find(
|
||||
(p) => p.value === currentStyleConfig.property
|
||||
);
|
||||
let legendConfig: LegendStyleConfig = newLegendConfig || {
|
||||
layerId,
|
||||
layerName,
|
||||
property: property?.name || currentStyleConfig.property,
|
||||
colors: [],
|
||||
type: selectedRenderLayer?.get("type") || "point",
|
||||
dimensions: [],
|
||||
breaks: [],
|
||||
};
|
||||
|
||||
const newStyleState: LayerStyleState = {
|
||||
layerId,
|
||||
layerName,
|
||||
styleConfig: { ...currentStyleConfig },
|
||||
legendConfig: { ...legendConfig },
|
||||
isActive: true,
|
||||
};
|
||||
setLayerStyleStates((prev) => {
|
||||
// 检查是否已存在该图层的样式状态
|
||||
const existingIndex = prev.findIndex(
|
||||
(state) => state.layerId === layerId
|
||||
);
|
||||
|
||||
if (existingIndex !== -1) {
|
||||
// 更新已存在的状态
|
||||
const updated = [...prev];
|
||||
updated[existingIndex] = newStyleState;
|
||||
return updated;
|
||||
} else {
|
||||
// 添加新的状态
|
||||
return [...prev, newStyleState];
|
||||
}
|
||||
});
|
||||
},
|
||||
[selectedRenderLayer, styleConfig, availableProperties]
|
||||
);
|
||||
return [...prev, newStyleState];
|
||||
});
|
||||
};
|
||||
// 设置分类样式参数,触发样式应用
|
||||
const setStyleState = () => {
|
||||
if (!selectedRenderLayer) return;
|
||||
@@ -787,7 +840,7 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
|
||||
};
|
||||
|
||||
// 重置样式
|
||||
const resetStyle = useCallback(() => {
|
||||
const resetStyle = () => {
|
||||
if (!selectedRenderLayer) return;
|
||||
// 重置 WebGL 图层样式
|
||||
const defaultFlatStyle: FlatStyleLike = config.MAP_DEFAULT_STYLE;
|
||||
@@ -815,7 +868,7 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
|
||||
setWaterflowLayerAvailable && setWaterflowLayerAvailable(false);
|
||||
}
|
||||
}
|
||||
}, [selectedRenderLayer]);
|
||||
};
|
||||
// 更新当前 VectorTileSource 中的所有缓冲要素属性
|
||||
const updateVectorTileSource = (property: string, data: any[]) => {
|
||||
if (!map) return;
|
||||
@@ -857,7 +910,7 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
|
||||
});
|
||||
};
|
||||
// 新增事件,监听 VectorTileSource 的 tileloadend 事件,为新增瓦片数据动态更新要素属性
|
||||
const [tileLoadListeners, setTileLoadListeners] = useState<
|
||||
const tileLoadListenersRef = useRef<
|
||||
Map<VectorTileSource, (event: any) => void>
|
||||
>(new Map());
|
||||
|
||||
@@ -879,8 +932,6 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
|
||||
dataMap.set(d.ID, d.value || 0);
|
||||
});
|
||||
// 新增监听器并保存
|
||||
const newListeners = new Map<VectorTileSource, (event: any) => void>();
|
||||
|
||||
const listener = (event: any) => {
|
||||
try {
|
||||
if (event.tile instanceof VectorTile) {
|
||||
@@ -906,8 +957,7 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
|
||||
};
|
||||
|
||||
vectorTileSource.on("tileloadend", listener);
|
||||
newListeners.set(vectorTileSource, listener);
|
||||
setTileLoadListeners(newListeners);
|
||||
tileLoadListenersRef.current.set(vectorTileSource, listener);
|
||||
};
|
||||
// 新增函数:取消对应 layerId 已添加的 on 事件
|
||||
const removeVectorTileSourceLoadedEvent = (layerId: string) => {
|
||||
@@ -918,14 +968,10 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
|
||||
.map((layer) => layer.getSource() as VectorTileSource)
|
||||
.filter((source) => source)[0];
|
||||
if (!vectorTileSource) return;
|
||||
const listener = tileLoadListeners.get(vectorTileSource);
|
||||
const listener = tileLoadListenersRef.current.get(vectorTileSource);
|
||||
if (listener) {
|
||||
vectorTileSource.un("tileloadend", listener);
|
||||
setTileLoadListeners((prev) => {
|
||||
const newMap = new Map(prev);
|
||||
newMap.delete(vectorTileSource);
|
||||
return newMap;
|
||||
});
|
||||
tileLoadListenersRef.current.delete(vectorTileSource);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1044,117 +1090,9 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
|
||||
|
||||
updateVisibleLayers();
|
||||
}, [map]);
|
||||
// 获取选中图层的属性,并检查是否有已缓存的样式状态
|
||||
useEffect(() => {
|
||||
// 如果没有矢量图层或没有选中图层,清空属性列表
|
||||
if (!renderLayers || renderLayers.length === 0) {
|
||||
setAvailableProperties([]);
|
||||
return;
|
||||
}
|
||||
// 如果没有选中图层,清空属性列表
|
||||
if (!selectedRenderLayer) {
|
||||
setAvailableProperties([]);
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取第一个要素的数值型属性
|
||||
const properties = selectedRenderLayer.get("properties") || {};
|
||||
setAvailableProperties(properties);
|
||||
|
||||
// 设置选中的渲染图层
|
||||
const renderLayer = renderLayers.filter((layer) => {
|
||||
return layer.get("value") === selectedRenderLayer?.get("value");
|
||||
})[0];
|
||||
setSelectedRenderLayer(renderLayer);
|
||||
|
||||
// 检查是否有已缓存的样式状态,如果有则自动恢复
|
||||
const layerId = selectedRenderLayer.get("value");
|
||||
const cachedStyleState = layerStyleStates.find(
|
||||
(state) => state.layerId === layerId
|
||||
);
|
||||
if (cachedStyleState) {
|
||||
setStyleConfig(cachedStyleState.styleConfig);
|
||||
}
|
||||
}, [renderLayers, selectedRenderLayer, map, renderLayers, layerStyleStates]);
|
||||
|
||||
// 监听颜色类型变化,当切换到单一色时自动勾选宽度调整选项
|
||||
useEffect(() => {
|
||||
if (styleConfig.colorType === "single") {
|
||||
setStyleConfig((prev) => ({
|
||||
...prev,
|
||||
adjustWidthByProperty: true,
|
||||
}));
|
||||
}
|
||||
}, [styleConfig.colorType]);
|
||||
|
||||
// 初始化或调整自定义断点数组长度,默认使用 pretty_breaks 生成若存在数据
|
||||
useEffect(() => {
|
||||
if (styleConfig.classificationMethod !== "custom_breaks") return;
|
||||
|
||||
const numBreaks = styleConfig.segments;
|
||||
setStyleConfig((prev) => {
|
||||
const prevBreaks = prev.customBreaks || [];
|
||||
if (prevBreaks.length === numBreaks) return prev;
|
||||
|
||||
const selectedLayerId = selectedRenderLayer?.get("value");
|
||||
let dataArr: number[] = [];
|
||||
|
||||
const isElevation =
|
||||
selectedLayerId === "junctions" && styleConfig.property === "elevation";
|
||||
const isDiameter =
|
||||
selectedLayerId === "pipes" && styleConfig.property === "diameter";
|
||||
|
||||
if (isElevation && elevationRange) {
|
||||
dataArr = [elevationRange[0], elevationRange[1]];
|
||||
} else if (isDiameter && diameterRange) {
|
||||
dataArr = [diameterRange[0], diameterRange[1]];
|
||||
} else if (selectedLayerId === "junctions" && currentJunctionCalData) {
|
||||
dataArr = currentJunctionCalData.map((d: any) => d.value);
|
||||
} else if (selectedLayerId === "pipes" && currentPipeCalData) {
|
||||
dataArr = currentPipeCalData.map((d: any) => d.value);
|
||||
}
|
||||
|
||||
let defaultBreaks: number[] = Array.from({ length: numBreaks }, () => 0);
|
||||
if (dataArr && dataArr.length > 0) {
|
||||
defaultBreaks = calculateClassification(
|
||||
dataArr,
|
||||
styleConfig.segments,
|
||||
"pretty_breaks"
|
||||
);
|
||||
defaultBreaks = defaultBreaks.slice(0, numBreaks);
|
||||
if (defaultBreaks.length < numBreaks)
|
||||
while (defaultBreaks.length < numBreaks)
|
||||
defaultBreaks.push(defaultBreaks[defaultBreaks.length - 1] ?? 0);
|
||||
}
|
||||
|
||||
return { ...prev, customBreaks: defaultBreaks };
|
||||
});
|
||||
}, [
|
||||
styleConfig.classificationMethod,
|
||||
styleConfig.segments,
|
||||
styleConfig.property,
|
||||
selectedRenderLayer,
|
||||
currentJunctionCalData,
|
||||
currentPipeCalData,
|
||||
elevationRange,
|
||||
diameterRange,
|
||||
]);
|
||||
|
||||
// 初始化或调整自定义颜色数组长度
|
||||
useEffect(() => {
|
||||
const numColors = styleConfig.segments;
|
||||
setStyleConfig((prev) => {
|
||||
const prevColors = prev.customColors || [];
|
||||
if (prevColors.length === numColors) return prev;
|
||||
|
||||
const newColors = [...prevColors];
|
||||
const baseColors = RAINBOW_PALETTES[0].colors;
|
||||
while (newColors.length < numColors) {
|
||||
newColors.push(baseColors[newColors.length % baseColors.length]);
|
||||
}
|
||||
return { ...prev, customColors: newColors.slice(0, numColors) };
|
||||
});
|
||||
}, [styleConfig.segments]);
|
||||
if (!data) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
const getColorSetting = () => {
|
||||
if (styleConfig.colorType === "single") {
|
||||
@@ -1624,9 +1562,21 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
|
||||
const cachedStyleState = layerStyleStates.find(
|
||||
(state) => state.layerId === layerId
|
||||
);
|
||||
// 只有在没有缓存时才清空属性
|
||||
if (!cachedStyleState) {
|
||||
setStyleConfig((prev) => ({ ...prev, property: "" }));
|
||||
if (cachedStyleState) {
|
||||
setStyleConfig(cachedStyleState.styleConfig);
|
||||
} else {
|
||||
setStyleConfig((prev) => ({
|
||||
...prev,
|
||||
property: "",
|
||||
customBreaks:
|
||||
prev.classificationMethod === "custom_breaks"
|
||||
? getDefaultCustomBreaks(prev.segments, "", newLayer)
|
||||
: prev.customBreaks,
|
||||
customColors: getDefaultCustomColors(
|
||||
prev.segments,
|
||||
prev.customColors
|
||||
),
|
||||
}));
|
||||
}
|
||||
}
|
||||
}}
|
||||
@@ -1647,7 +1597,15 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
|
||||
<Select
|
||||
value={styleConfig.property}
|
||||
onChange={(e) => {
|
||||
setStyleConfig((prev) => ({ ...prev, property: e.target.value }));
|
||||
const nextProperty = e.target.value;
|
||||
setStyleConfig((prev) => ({
|
||||
...prev,
|
||||
property: nextProperty,
|
||||
customBreaks:
|
||||
prev.classificationMethod === "custom_breaks"
|
||||
? getDefaultCustomBreaks(prev.segments, nextProperty)
|
||||
: prev.customBreaks,
|
||||
}));
|
||||
}}
|
||||
disabled={!selectedRenderLayer}
|
||||
>
|
||||
@@ -1664,9 +1622,14 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
|
||||
<Select
|
||||
value={styleConfig.classificationMethod}
|
||||
onChange={(e) => {
|
||||
const nextMethod = e.target.value;
|
||||
setStyleConfig((prev) => ({
|
||||
...prev,
|
||||
classificationMethod: e.target.value,
|
||||
classificationMethod: nextMethod,
|
||||
customBreaks:
|
||||
nextMethod === "custom_breaks"
|
||||
? getDefaultCustomBreaks(prev.segments, prev.property)
|
||||
: prev.customBreaks,
|
||||
}));
|
||||
}}
|
||||
>
|
||||
@@ -1695,7 +1658,14 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
|
||||
return {
|
||||
...prev,
|
||||
segments: newSegments,
|
||||
customColors: newCustomColors,
|
||||
customBreaks:
|
||||
prev.classificationMethod === "custom_breaks"
|
||||
? getDefaultCustomBreaks(newSegments, prev.property)
|
||||
: prev.customBreaks,
|
||||
customColors: getDefaultCustomColors(
|
||||
newSegments,
|
||||
newCustomColors
|
||||
),
|
||||
};
|
||||
})
|
||||
}
|
||||
@@ -1782,6 +1752,10 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
|
||||
return {
|
||||
...prev,
|
||||
colorType: newColorType,
|
||||
adjustWidthByProperty:
|
||||
newColorType === "single"
|
||||
? true
|
||||
: prev.adjustWidthByProperty,
|
||||
customColors: newCustomColors,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -47,29 +47,21 @@ const Timeline: React.FC<TimelineProps> = ({
|
||||
schemeType = "burst_Analysis",
|
||||
}) => {
|
||||
const data = useData();
|
||||
if (!data) {
|
||||
return <div>Loading...</div>; // 或其他占位符
|
||||
}
|
||||
const {
|
||||
currentTime,
|
||||
setCurrentTime,
|
||||
selectedDate,
|
||||
setSelectedDate,
|
||||
setCurrentJunctionCalData,
|
||||
setCurrentPipeCalData,
|
||||
junctionText,
|
||||
pipeText,
|
||||
} = data;
|
||||
if (
|
||||
setCurrentTime === undefined ||
|
||||
currentTime === undefined ||
|
||||
selectedDate === undefined ||
|
||||
setSelectedDate === undefined
|
||||
) {
|
||||
return <div>Loading...</div>; // 或其他占位符
|
||||
}
|
||||
const hasTimelineState =
|
||||
data &&
|
||||
data.setCurrentTime !== undefined &&
|
||||
data.currentTime !== undefined &&
|
||||
data.selectedDate !== undefined &&
|
||||
data.setSelectedDate !== undefined;
|
||||
const currentTime = data?.currentTime ?? -1;
|
||||
const setCurrentTime = data?.setCurrentTime ?? ((_: any) => undefined);
|
||||
const selectedDate = data?.selectedDate ?? new Date();
|
||||
const setSelectedDate = data?.setSelectedDate ?? ((_: any) => undefined);
|
||||
const setCurrentJunctionCalData = data?.setCurrentJunctionCalData;
|
||||
const setCurrentPipeCalData = data?.setCurrentPipeCalData;
|
||||
const junctionText = data?.junctionText ?? "";
|
||||
const pipeText = data?.pipeText ?? "";
|
||||
const { open } = useNotification();
|
||||
|
||||
const [isPlaying, setIsPlaying] = useState<boolean>(false);
|
||||
const [playInterval, setPlayInterval] = useState<number>(15000); // 毫秒
|
||||
const [calculatedInterval, setCalculatedInterval] = useState<number>(15); // 分钟
|
||||
@@ -549,6 +541,10 @@ const Timeline: React.FC<TimelineProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
if (!hasTimelineState) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Draggable nodeRef={draggableRef} handle=".drag-handle">
|
||||
<div
|
||||
|
||||
@@ -39,8 +39,6 @@ const Toolbar: React.FC<ToolbarProps> = ({
|
||||
const map = useMap();
|
||||
const data = useData();
|
||||
const { open } = useNotification();
|
||||
if (!data) return null;
|
||||
const { currentTime, selectedDate, schemeName } = data;
|
||||
const [activeTools, setActiveTools] = useState<string[]>([]);
|
||||
const [highlightFeatures, setHighlightFeatures] = useState<Feature[]>([]);
|
||||
const [showPropertyPanel, setShowPropertyPanel] = useState<boolean>(false);
|
||||
@@ -49,6 +47,9 @@ const Toolbar: React.FC<ToolbarProps> = ({
|
||||
const [showHistoryPanel, setShowHistoryPanel] = useState<boolean>(false);
|
||||
const [highlightLayer, setHighlightLayer] =
|
||||
useState<VectorLayer<VectorSource> | null>(null);
|
||||
const currentTime = data?.currentTime;
|
||||
const selectedDate = data?.selectedDate;
|
||||
const schemeName = data?.schemeName;
|
||||
|
||||
// 样式状态管理 - 在 Toolbar 中管理,带有默认样式
|
||||
const [layerStyleStates, setLayerStyleStates] = useState<LayerStyleState[]>([
|
||||
@@ -721,6 +722,10 @@ const Toolbar: React.FC<ToolbarProps> = ({
|
||||
return {};
|
||||
}, [highlightFeatures, computedProperties]);
|
||||
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="absolute top-4 left-4 bg-white p-1 rounded-xl shadow-lg flex opacity-85 hover:opacity-100 transition-opacity">
|
||||
|
||||
@@ -78,15 +78,33 @@ const MapContext = createContext<OlMap | undefined>(undefined);
|
||||
const DataContext = createContext<DataContextType | undefined>(undefined);
|
||||
|
||||
// 添加防抖函数
|
||||
function debounce<F extends (...args: any[]) => any>(func: F, waitFor: number) {
|
||||
type DebouncedFunction<F extends (...args: any[]) => any> = ((
|
||||
...args: Parameters<F>
|
||||
) => void) & {
|
||||
cancel: () => void;
|
||||
};
|
||||
|
||||
function debounce<F extends (...args: any[]) => any>(
|
||||
func: F,
|
||||
waitFor: number
|
||||
): DebouncedFunction<F> {
|
||||
let timeout: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
return (...args: Parameters<F>): void => {
|
||||
const debounced = (...args: Parameters<F>): void => {
|
||||
if (timeout !== null) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
timeout = setTimeout(() => func(...args), waitFor);
|
||||
};
|
||||
|
||||
debounced.cancel = () => {
|
||||
if (timeout !== null) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
}
|
||||
};
|
||||
|
||||
return debounced;
|
||||
}
|
||||
|
||||
export const useMap = () => {
|
||||
@@ -187,20 +205,6 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
||||
[number, number] | undefined
|
||||
>();
|
||||
|
||||
// 防抖更新函数
|
||||
const debouncedUpdateData = useRef(
|
||||
debounce(() => {
|
||||
if (tileJunctionDataBuffer.current.length > 0) {
|
||||
setJunctionData(tileJunctionDataBuffer.current);
|
||||
tileJunctionDataBuffer.current = [];
|
||||
}
|
||||
if (tilePipeDataBuffer.current.length > 0) {
|
||||
setPipeData(tilePipeDataBuffer.current);
|
||||
tilePipeDataBuffer.current = [];
|
||||
}
|
||||
}, 100),
|
||||
);
|
||||
|
||||
const setJunctionData = (newData: any[]) => {
|
||||
const uniqueNewData = newData.filter((item) => {
|
||||
if (!item || !item.id) return false;
|
||||
@@ -232,6 +236,7 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const setPipeData = (newData: any[]) => {
|
||||
const uniqueNewData = newData.filter((item) => {
|
||||
if (!item || !item.id) return false;
|
||||
@@ -263,6 +268,28 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const debouncedUpdateDataRef = useRef<DebouncedFunction<() => void> | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
debouncedUpdateDataRef.current = debounce(() => {
|
||||
if (tileJunctionDataBuffer.current.length > 0) {
|
||||
setJunctionData(tileJunctionDataBuffer.current);
|
||||
tileJunctionDataBuffer.current = [];
|
||||
}
|
||||
if (tilePipeDataBuffer.current.length > 0) {
|
||||
setPipeData(tilePipeDataBuffer.current);
|
||||
tilePipeDataBuffer.current = [];
|
||||
}
|
||||
}, 100);
|
||||
|
||||
return () => {
|
||||
debouncedUpdateDataRef.current?.cancel();
|
||||
debouncedUpdateDataRef.current = null;
|
||||
};
|
||||
}, []);
|
||||
// 配置地图数据源、图层和样式
|
||||
const defaultFlatStyle: FlatStyleLike = config.MAP_DEFAULT_STYLE;
|
||||
// 定义 SCADA 图层的样式函数,根据 type 字段选择不同图标
|
||||
@@ -520,7 +547,7 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
||||
uniqueData.forEach((item) =>
|
||||
tileJunctionDataBuffer.current.push(item),
|
||||
);
|
||||
debouncedUpdateData.current();
|
||||
debouncedUpdateDataRef.current?.();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -600,7 +627,7 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
||||
const uniqueData = Array.from(data.values());
|
||||
if (uniqueData.length > 0) {
|
||||
uniqueData.forEach((item) => tilePipeDataBuffer.current.push(item));
|
||||
debouncedUpdateData.current();
|
||||
debouncedUpdateDataRef.current?.();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@@ -29,26 +29,20 @@ type ColorModeContextProviderProps = {
|
||||
export const ColorModeContextProvider: React.FC<
|
||||
PropsWithChildren<ColorModeContextProviderProps>
|
||||
> = ({ children, defaultMode }) => {
|
||||
const [isMounted, setIsMounted] = useState(false);
|
||||
const [mode, setMode] = useState(defaultMode || "light");
|
||||
|
||||
useEffect(() => {
|
||||
setIsMounted(true);
|
||||
}, []);
|
||||
|
||||
const systemTheme = useMediaQuery(`(prefers-color-scheme: dark)`);
|
||||
|
||||
useEffect(() => {
|
||||
if (isMounted) {
|
||||
const theme = Cookies.get("theme") || (systemTheme ? "dark" : "light");
|
||||
setMode(theme);
|
||||
const [storedMode, setStoredMode] = useState<string | null>(() => {
|
||||
if (typeof window === "undefined") {
|
||||
return defaultMode ?? null;
|
||||
}
|
||||
}, [isMounted, systemTheme]);
|
||||
|
||||
return Cookies.get("theme") || defaultMode || null;
|
||||
});
|
||||
const mode = storedMode || (systemTheme ? "dark" : "light");
|
||||
|
||||
const toggleTheme = () => {
|
||||
const nextTheme = mode === "light" ? "dark" : "light";
|
||||
|
||||
setMode(nextTheme);
|
||||
setStoredMode(nextTheme);
|
||||
Cookies.set("theme", nextTheme);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user