新增点击地图选中设备,查看历史数据的功能
This commit is contained in:
@@ -37,11 +37,14 @@ import {
|
|||||||
FilterList,
|
FilterList,
|
||||||
Clear,
|
Clear,
|
||||||
DeviceHub,
|
DeviceHub,
|
||||||
|
TouchApp,
|
||||||
} from "@mui/icons-material";
|
} from "@mui/icons-material";
|
||||||
import { FixedSizeList } from "react-window";
|
import { FixedSizeList } from "react-window";
|
||||||
|
import { useNotification } from "@refinedev/core";
|
||||||
|
|
||||||
import { useMap } from "@app/OlMap/MapComponent";
|
import { useMap } from "@app/OlMap/MapComponent";
|
||||||
import { GeoJSON } from "ol/format";
|
import { GeoJSON } from "ol/format";
|
||||||
|
import { handleMapClickSelectFeatures as mapClickSelectFeatures } from "@/utils/mapQueryService";
|
||||||
import VectorLayer from "ol/layer/Vector";
|
import VectorLayer from "ol/layer/Vector";
|
||||||
import VectorSource from "ol/source/Vector";
|
import VectorSource from "ol/source/Vector";
|
||||||
import { Stroke, Style, Circle, Fill } from "ol/style";
|
import { Stroke, Style, Circle, Fill } from "ol/style";
|
||||||
@@ -95,6 +98,7 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
|
|||||||
const [selectedStatus, setSelectedStatus] = useState<string>("all");
|
const [selectedStatus, setSelectedStatus] = useState<string>("all");
|
||||||
const [selectedReliability, setSelectedReliability] = useState<string>("all");
|
const [selectedReliability, setSelectedReliability] = useState<string>("all");
|
||||||
const [isExpanded, setIsExpanded] = useState<boolean>(true);
|
const [isExpanded, setIsExpanded] = useState<boolean>(true);
|
||||||
|
const [isSelecting, setIsSelecting] = useState<boolean>(false);
|
||||||
const [internalSelection, setInternalSelection] = useState<string[]>([]);
|
const [internalSelection, setInternalSelection] = useState<string[]>([]);
|
||||||
const [pendingSelection, setPendingSelection] = useState<string[] | null>(
|
const [pendingSelection, setPendingSelection] = useState<string[] | null>(
|
||||||
null
|
null
|
||||||
@@ -106,7 +110,6 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
|
|||||||
const [highlightLayer, setHighlightLayer] =
|
const [highlightLayer, setHighlightLayer] =
|
||||||
useState<VectorLayer<VectorSource> | null>(null);
|
useState<VectorLayer<VectorSource> | null>(null);
|
||||||
const [highlightFeatures, setHighlightFeatures] = useState<Feature[]>([]);
|
const [highlightFeatures, setHighlightFeatures] = useState<Feature[]>([]);
|
||||||
const [blinkingFeature, setBlinkingFeature] = useState<Feature | null>(null);
|
|
||||||
const blinkListenerKeyRef = useRef<any>(null);
|
const blinkListenerKeyRef = useRef<any>(null);
|
||||||
|
|
||||||
const debounceTimerRef = useRef<NodeJS.Timeout | null>(null);
|
const debounceTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
@@ -125,7 +128,8 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
|
|||||||
|
|
||||||
const activeSelection = selectedDeviceIds ?? internalSelection;
|
const activeSelection = selectedDeviceIds ?? internalSelection;
|
||||||
|
|
||||||
const map = useMap(); // 移到此处,确保在条件检查前调用
|
const map = useMap(); // 移到此处,确保在条件检查前调用
|
||||||
|
const { open } = useNotification();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedDeviceIds) {
|
if (selectedDeviceIds) {
|
||||||
@@ -323,7 +327,6 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
|
|||||||
const blinkFeature = new Feature({
|
const blinkFeature = new Feature({
|
||||||
geometry: new Point(coordinates),
|
geometry: new Point(coordinates),
|
||||||
});
|
});
|
||||||
setBlinkingFeature(blinkFeature);
|
|
||||||
|
|
||||||
const duration = 2000; // 闪烁持续时间
|
const duration = 2000; // 闪烁持续时间
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
@@ -334,7 +337,6 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
|
|||||||
if (elapsed > duration) {
|
if (elapsed > duration) {
|
||||||
// 动画结束
|
// 动画结束
|
||||||
unByKey(listenerKey);
|
unByKey(listenerKey);
|
||||||
setBlinkingFeature(null);
|
|
||||||
blinkListenerKeyRef.current = null;
|
blinkListenerKeyRef.current = null;
|
||||||
map.render(); // 最后渲染一次以清除效果
|
map.render(); // 最后渲染一次以清除效果
|
||||||
return;
|
return;
|
||||||
@@ -435,6 +437,91 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
|
|||||||
setPendingSelection([]);
|
setPendingSelection([]);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// 地图点击选择 SCADA 设备事件处理函数
|
||||||
|
const handleMapClickSelectFeatures = useCallback(
|
||||||
|
async (event: { coordinate: number[] }) => {
|
||||||
|
if (!map) return;
|
||||||
|
const feature = await mapClickSelectFeatures(event, map);
|
||||||
|
const layer = feature?.getId()?.toString().split(".")[0];
|
||||||
|
|
||||||
|
if (!feature) return;
|
||||||
|
if (layer !== "geo_scada_mat" && layer !== "geo_scada") {
|
||||||
|
open?.({
|
||||||
|
type: "error",
|
||||||
|
message: "请选择 SCADA 设备。",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const featureId = feature.getProperties().id;
|
||||||
|
|
||||||
|
// 在设备列表中查找对应的设备
|
||||||
|
const device = effectiveDevices.find((d) => d.id === featureId);
|
||||||
|
if (!device) {
|
||||||
|
open?.({
|
||||||
|
type: "error",
|
||||||
|
message: "未找到对应的 SCADA 设备。",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新选择状态
|
||||||
|
setInternalSelection((prev) => {
|
||||||
|
const exists = prev.includes(featureId);
|
||||||
|
const nextSelection = multiSelect
|
||||||
|
? exists
|
||||||
|
? prev.filter((id) => id !== featureId)
|
||||||
|
: [...prev, featureId]
|
||||||
|
: exists
|
||||||
|
? []
|
||||||
|
: [featureId];
|
||||||
|
|
||||||
|
setPendingSelection(nextSelection);
|
||||||
|
return nextSelection;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新高亮要素
|
||||||
|
setHighlightFeatures((prev) => {
|
||||||
|
const existingIndex = prev.findIndex(
|
||||||
|
(f) => f.getProperties().id === featureId
|
||||||
|
);
|
||||||
|
if (existingIndex !== -1) {
|
||||||
|
// 如果已存在,移除
|
||||||
|
return prev.filter((_, i) => i !== existingIndex);
|
||||||
|
} else {
|
||||||
|
// 如果不存在,添加
|
||||||
|
return [...prev, feature];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[map, effectiveDevices, multiSelect, open]
|
||||||
|
);
|
||||||
|
|
||||||
|
// 开始选择 SCADA 设备
|
||||||
|
const handleStartSelection = useCallback(() => {
|
||||||
|
if (!map) return;
|
||||||
|
setIsSelecting(true);
|
||||||
|
// 注册点击事件
|
||||||
|
map.on("click", handleMapClickSelectFeatures);
|
||||||
|
}, [map, handleMapClickSelectFeatures]);
|
||||||
|
|
||||||
|
// 结束选择 SCADA 设备
|
||||||
|
const handleEndSelection = useCallback(() => {
|
||||||
|
if (!map) return;
|
||||||
|
setIsSelecting(false);
|
||||||
|
// 移除点击事件
|
||||||
|
map.un("click", handleMapClickSelectFeatures);
|
||||||
|
}, [map, handleMapClickSelectFeatures]);
|
||||||
|
|
||||||
|
// 组件卸载时清理地图事件
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (map && isSelecting) {
|
||||||
|
map.un("click", handleMapClickSelectFeatures);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [map, isSelecting, handleMapClickSelectFeatures]);
|
||||||
|
|
||||||
// 初始化管道图层和高亮图层
|
// 初始化管道图层和高亮图层
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!map) return;
|
if (!map) return;
|
||||||
@@ -635,12 +722,35 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
|
|||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
{/* 筛选结果统计 */}
|
{/* 筛选结果统计 */}
|
||||||
|
<Stack direction="row" alignItems="center" spacing={2}>
|
||||||
<Typography variant="caption" color="text.secondary">
|
<Typography variant="caption" color="text.secondary">
|
||||||
共找到 {filteredDevices.length} 个设备
|
共找到 {filteredDevices.length} 个设备
|
||||||
{devices.length !== filteredDevices.length &&
|
{devices.length !== filteredDevices.length &&
|
||||||
` (共 ${effectiveDevices.length} 个设备)`}
|
` (共 ${effectiveDevices.length} 个设备)`}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
|
{/* 地图选择按钮 */}
|
||||||
|
<Tooltip title={isSelecting ? "结束地图选择" : "从地图选择设备"}>
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
color={isSelecting ? "primary" : "default"}
|
||||||
|
onClick={
|
||||||
|
isSelecting ? handleEndSelection : handleStartSelection
|
||||||
|
}
|
||||||
|
sx={{
|
||||||
|
border: 1,
|
||||||
|
borderColor: isSelecting ? "primary.main" : "divider",
|
||||||
|
backgroundColor: isSelecting ? "primary.50" : "transparent",
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: isSelecting ? "primary.100" : "grey.100",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TouchApp fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
{/* 清除选择按钮 */}
|
{/* 清除选择按钮 */}
|
||||||
{activeSelection.length > 0 && (
|
{activeSelection.length > 0 && (
|
||||||
<Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
|
<Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
|
||||||
|
|||||||
Reference in New Issue
Block a user