import React, { useEffect, useState } from "react";
|
import { useTranslate } from "react-admin";
|
import {
|
Drawer,
|
Box,
|
Typography,
|
IconButton,
|
Stack,
|
useTheme,
|
List,
|
ListItemButton,
|
ListItemText,
|
CircularProgress,
|
} from '@mui/material';
|
import CloseIcon from '@mui/icons-material/Close';
|
import { PAGE_DRAWER_WIDTH } from '@/config/setting';
|
import { fetchAreaList } from './http';
|
import * as Tool from './tool';
|
|
const AreaList = ({
|
open,
|
onClose,
|
zoneId,
|
width = PAGE_DRAWER_WIDTH,
|
setCurSprite,
|
}) => {
|
const translate = useTranslate();
|
const theme = useTheme();
|
const themeMode = theme.palette.mode;
|
const [areas, setAreas] = useState([]);
|
const [loading, setLoading] = useState(false);
|
|
useEffect(() => {
|
if (!open) {
|
setAreas([]);
|
return;
|
}
|
setLoading(true);
|
fetchAreaList(zoneId)
|
.then((list) => {
|
setAreas(Array.isArray(list) ? list : []);
|
})
|
.finally(() => setLoading(false));
|
}, [open, zoneId]);
|
|
const handleItemClick = (area) => {
|
if (!area?.id) {
|
return;
|
}
|
const sprite = Tool.findAreaSpriteById(area.id);
|
if (sprite) {
|
Tool.focusAreaSprite(sprite, 570);
|
onClose();
|
setCurSprite(sprite);
|
}
|
};
|
|
return (
|
<Drawer
|
variant="persistent"
|
open={open}
|
anchor="right"
|
onClose={onClose}
|
sx={{ zIndex: 100, opacity: 0.95 }}
|
>
|
{open && (
|
<Box pt={12} width={{ xs: '100vw', sm: width }} height={'calc(100vh - 200px)'} mt={{ xs: 2, sm: 1 }}>
|
<Stack direction="row" alignItems="center" px={3} py={2}>
|
<Typography variant="h6" flex={1}>
|
{translate('page.map.action.areaList')}
|
</Typography>
|
<IconButton onClick={onClose} size="small">
|
<CloseIcon />
|
</IconButton>
|
</Stack>
|
|
<Box px={3} pb={3}>
|
{loading ? (
|
<Box display="flex" justifyContent="center" mt={6}>
|
<CircularProgress />
|
</Box>
|
) : (
|
<List dense sx={{ maxHeight: '70vh', overflowY: 'auto' }}>
|
{areas.length === 0 && (
|
<Typography variant="body2" color="text.secondary" textAlign="center" py={4}>
|
{translate('page.map.area.form.codesEmpty')}
|
</Typography>
|
)}
|
{areas.map((area) => {
|
const agvCount = Array.isArray(area.agvList) ? area.agvList.length : 0;
|
const width = Math.abs((area?.end?.x ?? 0) - (area?.start?.x ?? 0));
|
const height = Math.abs((area?.end?.y ?? 0) - (area?.start?.y ?? 0));
|
const areaSize = (width * height) / 1_000_000;
|
const formattedSize = areaSize > 0 ? areaSize.toFixed(2) : '0.00';
|
return (
|
<ListItemButton
|
key={area.id}
|
onClick={() => handleItemClick(area)}
|
sx={{
|
borderRadius: 2,
|
mb: 1,
|
px: 2,
|
py: 1.5,
|
background:
|
themeMode === 'light'
|
? 'linear-gradient(145deg, #f8f9fa, #ffffff)'
|
: 'linear-gradient(145deg, #2d3436, #353b48)',
|
border: `1px solid ${theme.palette.divider}`,
|
display: 'flex',
|
gap: 1.5,
|
alignItems: 'center',
|
justifyContent: 'space-between',
|
transition: 'transform 0.2s ease, box-shadow 0.2s ease',
|
boxShadow:
|
themeMode === 'light'
|
? '0 6px 12px rgba(0,0,0,0.06)'
|
: '0 6px 12px rgba(0,0,0,0.25)',
|
'&:hover': {
|
transform: 'translateY(-2px)',
|
boxShadow:
|
themeMode === 'light'
|
? '0 12px 24px rgba(0,0,0,0.08)'
|
: '0 12px 24px rgba(0,0,0,0.35)',
|
},
|
}}
|
>
|
<Box
|
sx={{
|
width: 12,
|
height: 12,
|
borderRadius: '50%',
|
bgcolor: area.color ? Number(area.color) : theme.palette.info.light,
|
border: `1px solid ${theme.palette.common.white}`,
|
boxShadow: '0 0 4px rgba(0,0,0,0.2)',
|
}}
|
/>
|
<ListItemText
|
primary={area.name || translate('page.map.area.form.name')}
|
primaryTypographyProps={{
|
fontWeight: 600,
|
fontSize: 16,
|
}}
|
/>
|
<Box textAlign="right">
|
<Typography variant="body2" fontWeight={500}>
|
{translate('page.map.area.form.agvCount', { count: agvCount })}
|
</Typography>
|
<Typography variant="caption" color="text.secondary">
|
{translate('page.map.area.form.areaSize', { size: formattedSize })}
|
</Typography>
|
</Box>
|
</ListItemButton>
|
);
|
})}
|
</List>
|
)}
|
</Box>
|
</Box>
|
)}
|
</Drawer>
|
);
|
};
|
|
export default AreaList;
|