import React, { useState, useEffect } from 'react';
|
import { useTranslate } from "react-admin";
|
import {
|
Drawer,
|
Box,
|
Typography,
|
Tabs,
|
Tab,
|
IconButton,
|
Stack,
|
useTheme,
|
Card,
|
CardContent,
|
Divider,
|
} from '@mui/material';
|
import CloseIcon from '@mui/icons-material/Close';
|
import { PAGE_DRAWER_WIDTH } from '@/config/setting';
|
import AreaBasicTab from './AreaBasicTab';
|
import AreaAdvancedTab from './AreaAdvancedTab';
|
import { getAreaInfo, fetchAgvListAll, updateAreaData, removeArea } from '../http';
|
|
const getAgvOptionId = (option) => {
|
if (option == null) {
|
return '';
|
}
|
if (typeof option === 'string' || typeof option === 'number') {
|
return String(option);
|
}
|
return option?.id ?? '';
|
};
|
|
const areAgvSelectionsEqual = (aIds = [], bIds = []) => {
|
if (aIds.length !== bIds.length) {
|
return false;
|
}
|
const setA = new Set(aIds);
|
return bIds.every(id => setA.has(id));
|
};
|
|
const mapSelectionToOptions = (selection = [], options = []) => {
|
const optionMap = new Map(options.map(option => [getAgvOptionId(option), option]));
|
return selection
|
.map(item => optionMap.get(item) || null)
|
.filter(Boolean);
|
|
};
|
|
const AreaSettings = (props) => {
|
const { open, onCancel, sprite, width = PAGE_DRAWER_WIDTH } = props;
|
const theme = useTheme();
|
const themeMode = theme.palette.mode;
|
const translate = useTranslate();
|
|
const handleClose = () => {
|
onCancel();
|
}
|
|
const [activeTab, setActiveTab] = useState(0);
|
const [name, setName] = useState('');
|
const [agvList, setAgvList] = useState([]);
|
const [codeList, setCodeList] = useState([]);
|
const [code, setCode] = useState('');
|
const [maxCount, setMaxCount] = useState('');
|
const [speedLimit, setSpeedLimit] = useState('');
|
const [memo, setMemo] = useState('');
|
const [priority, setPriority] = useState('');
|
const [agvOptions, setAgvOptions] = useState([]);
|
const [initialBasic, setInitialBasic] = useState({ name: '', agvIds: [] });
|
const [curAreaInfo, setCurAreaInfo] = useState(null);
|
|
const fetchAreaInfo = (areaId) => {
|
getAreaInfo({ params: { areaId: areaId } }, (data) => {
|
setCurAreaInfo(data);
|
})
|
}
|
|
useEffect(() => {
|
if (sprite?.data?.id) {
|
fetchAreaInfo(sprite.data.id);
|
} else {
|
setCurAreaInfo(null);
|
setName('');
|
setCode('');
|
setMaxCount('');
|
setSpeedLimit('');
|
setMemo('');
|
setPriority('');
|
setAgvList([]);
|
setCodeList([]);
|
setInitialBasic({ name: '', agvIds: [] });
|
}
|
}, [sprite]);
|
|
useEffect(() => {
|
if (!open) {
|
return;
|
}
|
fetchAgvListAll().then((options) => {
|
setAgvOptions(options || []);
|
});
|
}, [open]);
|
|
useEffect(() => {
|
if (curAreaInfo) {
|
setName(curAreaInfo.name || '');
|
setCode(curAreaInfo.code || '');
|
setMaxCount(curAreaInfo.maxCount ?? '');
|
setSpeedLimit(curAreaInfo.speedLimit ?? '');
|
setMemo(curAreaInfo.memo || '');
|
setPriority(curAreaInfo.priority ?? '');
|
|
const selected = curAreaInfo.agvList || [];
|
const normalizedSelection = mapSelectionToOptions(selected, agvOptions);
|
setAgvList(normalizedSelection);
|
setInitialBasic({
|
name: curAreaInfo.name || '',
|
agvIds: normalizedSelection.map(getAgvOptionId)
|
});
|
|
const codes = curAreaInfo.codeList || [];
|
setCodeList(Array.isArray(codes) ? codes : []);
|
}
|
}, [curAreaInfo, agvOptions]);
|
|
const handleTabChange = (event, newValue) => {
|
setActiveTab(newValue);
|
};
|
|
const handleSaveBasic = async () => {
|
console.log(agvList);
|
const id = sprite?.data?.id;
|
if (!id) {
|
return;
|
}
|
const payload = {
|
id,
|
name,
|
agvIds: agvList.map(getAgvOptionId),
|
};
|
const data = await updateAreaData(payload);
|
if (data) {
|
setCurAreaInfo(data);
|
}
|
};
|
|
const handleDeleteArea = async () => {
|
const id = sprite?.data?.id;
|
if (!id) {
|
return;
|
}
|
const success = await removeArea(id);
|
if (success) {
|
onCancel?.();
|
}
|
};
|
|
const handleSaveAdvanced = () => {
|
// placeholder for save logic
|
};
|
|
const basicDirty = name !== initialBasic.name
|
|| !areAgvSelectionsEqual(
|
agvList.map(getAgvOptionId),
|
initialBasic.agvIds
|
);
|
return (
|
<>
|
<Drawer
|
variant="persistent"
|
open={open}
|
anchor="right"
|
onClose={handleClose}
|
sx={{ zIndex: 100, opacity: .9 }}
|
>
|
{open && (
|
<Box pt={12} width={{ xs: '100vW', sm: width }} height={'calc(100vh - 200px);'} mt={{ xs: 2, sm: 1 }} sx={{
|
}}>
|
<Stack direction="row" p={2}>
|
<Typography variant="h6" flex="1">
|
{sprite
|
? translate(`page.map.devices.${sprite?.data?.type?.toLowerCase()}`) + ' - ' + sprite?.data?.name
|
: translate('page.map.settings.title')}
|
</Typography>
|
<IconButton onClick={handleClose} size="small">
|
<CloseIcon />
|
</IconButton>
|
</Stack>
|
|
<Box p={3}>
|
<Card sx={{
|
transition: '0.3s',
|
boxShadow: themeMode === 'light'
|
? '0px 2px 8px rgba(0, 0, 0, 0.1)'
|
: '0px 2px 2px rgba(255, 255, 255, 0.1)',
|
'&:hover': {
|
boxShadow: themeMode === 'light'
|
? '0px 4px 16px rgba(0, 0, 0, 0.2)'
|
: '0px 4px 8px rgba(255, 255, 255, 0.2)',
|
},
|
borderRadius: '8px',
|
}}>
|
<CardContent>
|
<Tabs
|
value={activeTab}
|
onChange={handleTabChange}
|
indicatorColor="primary"
|
textColor="primary"
|
variant="fullWidth"
|
sx={{ mb: 0 }}
|
>
|
<Tab label={translate('page.map.area.basic', { _: '基础' })} />
|
<Tab label={translate('page.map.area.advanced', { _: '高级' })} />
|
</Tabs>
|
|
<Divider />
|
|
<Box p={3}>
|
{activeTab === 0 && (
|
<AreaBasicTab
|
name={name}
|
setName={setName}
|
agvOptions={agvOptions}
|
agvList={agvList}
|
setAgvList={setAgvList}
|
codeList={codeList}
|
onSave={handleSaveBasic}
|
disableSave={!basicDirty}
|
onDelete={handleDeleteArea}
|
canDelete={Boolean(sprite?.data?.id)}
|
/>
|
)}
|
{activeTab === 1 && (
|
<AreaAdvancedTab
|
areaCode={code}
|
setAreaCode={setCode}
|
maxQty={maxCount}
|
setMaxQty={setMaxCount}
|
speedLimit={speedLimit}
|
setSpeedLimit={setSpeedLimit}
|
shapeData={memo}
|
setShapeData={setMemo}
|
priority={priority}
|
setPriority={setPriority}
|
onSave={handleSaveAdvanced}
|
/>
|
)}
|
</Box>
|
</CardContent>
|
</Card>
|
</Box>
|
</Box>
|
)}
|
</Drawer>
|
</>
|
)
|
}
|
|
export default AreaSettings;
|