"use client"; import React, { useState, useEffect, useMemo } from "react"; import { Box, Paper, TextField, Typography, List, ListItem, ListItemButton, ListItemText, ListItemIcon, Chip, IconButton, Collapse, InputAdornment, FormControl, InputLabel, Select, MenuItem, Tooltip, Stack, Divider, InputBase, } from "@mui/material"; import { Search, MyLocation, ExpandMore, ExpandLess, FilterList, Clear, Visibility, VisibilityOff, DeviceHub, } from "@mui/icons-material"; interface SCADADevice { id: string; name: string; type: string; coordinates: [number, number]; status: "online" | "offline" | "warning" | "error"; properties?: Record; } interface SCADADeviceListProps { devices?: SCADADevice[]; onDeviceClick?: (device: SCADADevice) => void; onZoomToDevice?: (coordinates: [number, number]) => void; multiSelect?: boolean; selectedDeviceIds?: string[]; onSelectionChange?: (ids: string[]) => void; } const SCADADeviceList: React.FC = ({ devices = [], onDeviceClick, onZoomToDevice, multiSelect = true, selectedDeviceIds, onSelectionChange, }) => { const [searchQuery, setSearchQuery] = useState(""); const [selectedType, setSelectedType] = useState("all"); const [selectedStatus, setSelectedStatus] = useState("all"); const [isExpanded, setIsExpanded] = useState(true); const [internalSelection, setInternalSelection] = useState([]); const [pendingSelection, setPendingSelection] = useState( null ); const activeSelection = selectedDeviceIds ?? internalSelection; useEffect(() => { if (selectedDeviceIds) { setInternalSelection(selectedDeviceIds); } }, [selectedDeviceIds]); // 添加 useEffect 来延迟调用 onSelectionChange,避免在渲染时触发父组件的 setState useEffect(() => { if (pendingSelection !== null) { onSelectionChange?.(pendingSelection); setPendingSelection(null); } }, [pendingSelection, onSelectionChange]); // 获取设备类型列表 const deviceTypes = useMemo(() => { const types = Array.from(new Set(devices.map((device) => device.type))); return types.sort(); }, [devices]); // 获取设备状态列表 const deviceStatuses = useMemo(() => { const statuses = Array.from( new Set(devices.map((device) => device.status)) ); return statuses.sort(); }, [devices]); // 过滤设备列表 const filteredDevices = useMemo(() => { return devices.filter((device) => { const matchesSearch = searchQuery === "" || device.name.toLowerCase().includes(searchQuery.toLowerCase()) || device.id.toLowerCase().includes(searchQuery.toLowerCase()) || device.type.toLowerCase().includes(searchQuery.toLowerCase()); const matchesType = selectedType === "all" || device.type === selectedType; const matchesStatus = selectedStatus === "all" || device.status === selectedStatus; return matchesSearch && matchesType && matchesStatus; }); }, [devices, searchQuery, selectedType, selectedStatus]); // 状态颜色映射 const getStatusColor = (status: string) => { switch (status) { case "online": return "success"; case "offline": return "default"; case "warning": return "warning"; case "error": return "error"; default: return "default"; } }; // 状态图标映射 const getStatusIcon = (status: string) => { switch (status) { case "online": return "●"; case "offline": return "○"; case "warning": return "▲"; case "error": return "✕"; default: return "●"; } }; // 处理设备点击 const handleDeviceClick = (device: SCADADevice, event?: React.MouseEvent) => { onDeviceClick?.(device); setInternalSelection((prev) => { const exists = prev.includes(device.id); const nextSelection = multiSelect ? exists ? prev.filter((id) => id !== device.id) : [...prev, device.id] : exists ? [] : [device.id]; setPendingSelection(nextSelection); // 设置待处理的 selection,延迟调用 return nextSelection; }); }; // 处理缩放到设备 const handleZoomToDevice = (device: SCADADevice, event: React.MouseEvent) => { event.stopPropagation(); onZoomToDevice?.(device.coordinates); }; // 清除搜索 const handleClearSearch = () => { setSearchQuery(""); }; // 重置所有筛选条件 const handleResetFilters = () => { setSearchQuery(""); setSelectedType("all"); setSelectedStatus("all"); }; return ( {/* 头部控制栏 */} SCADA 设备列表 setIsExpanded(!isExpanded)} sx={{ color: "white" }} > {isExpanded ? : } {/* 搜索和筛选栏 */} {/* 搜索框 */} setSearchQuery(e.target.value)} inputProps={{ "aria-label": "search devices" }} /> {searchQuery && ( <> )} {/* 筛选器 */} 设备类型 状态 {/* 筛选结果统计 */} 共找到 {filteredDevices.length} 个设备 {devices.length !== filteredDevices.length && ` (共 ${devices.length} 个设备)`} {/* 设备列表 */} {filteredDevices.length === 0 ? ( {searchQuery || selectedType !== "all" || selectedStatus !== "all" ? "未找到匹配的设备" : "暂无 SCADA 设备"} ) : ( {filteredDevices.map((device, index) => ( handleDeviceClick(device, event)} sx={{ "&.Mui-selected": { backgroundColor: "primary.50", borderLeft: 3, borderColor: "primary.main", }, "&:hover": { backgroundColor: "grey.50", }, }} > {getStatusIcon(device.status)} {device.name} } secondary={ ID: {device.id} 坐标: {device.coordinates[0].toFixed(6)},{" "} {device.coordinates[1].toFixed(6)} } slotProps={{ secondary: { component: "div", // 使其支持多行 }, }} /> handleZoomToDevice(device, e)} sx={{ ml: 1, color: "primary.main", "&:hover": { backgroundColor: "primary.50", }, }} > {index < filteredDevices.length - 1 && ( )} ))} )} ); }; export default SCADADeviceList;