为 SCADA 设备列表实现虚拟列表,优化渲染性能
This commit is contained in:
36
package-lock.json
generated
36
package-lock.json
generated
@@ -37,7 +37,7 @@
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-window": "^2.2.2",
|
||||
"react-window": "^1.8.10",
|
||||
"tailwindcss": "^4.1.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -46,6 +46,7 @@
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19.1.0",
|
||||
"@types/react-dom": "^19.1.0",
|
||||
"@types/react-window": "^1.8.8",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "^15.0.3",
|
||||
@@ -7992,6 +7993,16 @@
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-window": {
|
||||
"version": "1.8.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.8.tgz",
|
||||
"integrity": "sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/unist": {
|
||||
"version": "2.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
|
||||
@@ -14590,6 +14601,12 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/memoize-one": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
|
||||
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/merge-descriptors": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
|
||||
@@ -16462,13 +16479,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-window": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/react-window/-/react-window-2.2.2.tgz",
|
||||
"integrity": "sha512-kvHKwFImKBWNbx2S87NZOhQhAVkBthjmnOfHlhQI45p3A+D+V53E+CqQMsyHrxNe3ke+YtWXuKDa1eoHAaIWJg==",
|
||||
"version": "1.8.10",
|
||||
"resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.10.tgz",
|
||||
"integrity": "sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0",
|
||||
"memoize-one": ">=3.1.1 <6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">8.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0"
|
||||
"react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/readable-stream": {
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-window": "^2.2.2",
|
||||
"react-window": "^1.8.10",
|
||||
"tailwindcss": "^4.1.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -51,6 +51,7 @@
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19.1.0",
|
||||
"@types/react-dom": "^19.1.0",
|
||||
"@types/react-window": "^1.8.8",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "^15.0.3",
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user