为 SCADA 设备列表实现虚拟列表,优化渲染性能

This commit is contained in:
JIANG
2025-10-29 17:24:35 +08:00
parent a5954624a0
commit 09d037fd5a
3 changed files with 144 additions and 105 deletions

View File

@@ -12,7 +12,6 @@ import {
Box,
Paper,
Typography,
List,
ListItem,
ListItemButton,
ListItemText,
@@ -39,6 +38,7 @@ import {
Clear,
DeviceHub,
} from "@mui/icons-material";
import { FixedSizeList } from "react-window";
import { useMap } from "@app/OlMap/MapComponent";
import { GeoJSON } from "ol/format";
@@ -452,108 +452,122 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
</Typography>
</Box>
) : (
<List dense sx={{ p: 0 }}>
{filteredDevices.map((device, index) => (
<React.Fragment key={device.id}>
<ListItem disablePadding>
<ListItemButton
selected={activeSelection.includes(device.id)}
onClick={(event) => handleDeviceClick(device, event)}
sx={{
"&.Mui-selected": {
backgroundColor: "primary.50",
borderLeft: 3,
borderColor: "primary.main",
},
"&:hover": {
backgroundColor: "grey.50",
},
}}
>
<ListItemIcon sx={{ minWidth: 36 }}>
<Typography
variant="caption"
sx={{
color: `${getStatusColor(device.status)}.main`,
fontWeight: "bold",
fontSize: 16,
}}
>
{getStatusIcon(device.status)}
</Typography>
</ListItemIcon>
<ListItemText
primary={
<Stack
direction="row"
alignItems="center"
spacing={1}
>
<Typography
variant="body2"
sx={{ fontWeight: "medium" }}
>
{device.name}
</Typography>
<Chip
label={device.type}
size="small"
variant="outlined"
sx={{ fontSize: "0.7rem", height: 20 }}
/>
</Stack>
}
secondary={
<Stack spacing={0.5}>
<Typography
variant="caption"
color="text.secondary"
>
ID: {device.id}
</Typography>
<Typography
variant="caption"
color="text.secondary"
>
: {device.coordinates[0].toFixed(6)},{" "}
{device.coordinates[1].toFixed(6)}
</Typography>
</Stack>
}
slotProps={{
secondary: {
component: "div", // 使其支持多行
<FixedSizeList
height={400}
itemCount={filteredDevices.length}
itemSize={80}
width="100%"
>
{({
index,
style,
}: {
index: number;
style: React.CSSProperties;
}) => {
const device = filteredDevices[index];
return (
<div style={style}>
<ListItem disablePadding>
<ListItemButton
selected={activeSelection.includes(device.id)}
onClick={(event) => handleDeviceClick(device, event)}
sx={{
"&.Mui-selected": {
backgroundColor: "primary.50",
borderLeft: 3,
borderColor: "primary.main",
},
"&:hover": {
backgroundColor: "grey.50",
},
}}
/>
>
<ListItemIcon sx={{ minWidth: 36 }}>
<Typography
variant="caption"
sx={{
color: `${getStatusColor(device.status)}.main`,
fontWeight: "bold",
fontSize: 16,
}}
>
{getStatusIcon(device.status)}
</Typography>
</ListItemIcon>
<Tooltip title="缩放到设备位置">
<IconButton
size="small"
onClick={(event) => {
event.stopPropagation();
handleZoomToDevice(device);
}}
sx={{
ml: 1,
color: "primary.main",
"&:hover": {
backgroundColor: "primary.50",
<ListItemText
primary={
<Stack
direction="row"
alignItems="center"
spacing={1}
>
<Typography
variant="body2"
sx={{ fontWeight: "medium" }}
>
{device.name}
</Typography>
<Chip
label={device.type}
size="small"
variant="outlined"
sx={{ fontSize: "0.7rem", height: 20 }}
/>
</Stack>
}
secondary={
<Stack spacing={0.5}>
<Typography
variant="caption"
color="text.secondary"
>
ID: {device.id}
</Typography>
<Typography
variant="caption"
color="text.secondary"
>
: {device.coordinates[0].toFixed(6)},{" "}
{device.coordinates[1].toFixed(6)}
</Typography>
</Stack>
}
slotProps={{
secondary: {
component: "div", // 使其支持多行
},
}}
>
<MyLocation fontSize="small" />
</IconButton>
</Tooltip>
</ListItemButton>
</ListItem>
{index < filteredDevices.length - 1 && (
<Divider variant="inset" />
)}
</React.Fragment>
))}
</List>
/>
<Tooltip title="缩放到设备位置">
<IconButton
size="small"
onClick={(event) => {
event.stopPropagation();
handleZoomToDevice(device);
}}
sx={{
ml: 1,
color: "primary.main",
"&:hover": {
backgroundColor: "primary.50",
},
}}
>
<MyLocation fontSize="small" />
</IconButton>
</Tooltip>
</ListItemButton>
</ListItem>
{index < filteredDevices.length - 1 && (
<Divider variant="inset" />
)}
</div>
);
}}
</FixedSizeList>
)}
</Box>
</Collapse>