| | |
| | | Box, |
| | | Autocomplete, |
| | | Checkbox, |
| | | Chip, |
| | | Paper, |
| | | } from '@mui/material'; |
| | | import CheckBoxOutlineBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank'; |
| | | import CheckBoxIcon from '@mui/icons-material/CheckBox'; |
| | | import { useTranslate } from 'react-admin'; |
| | | |
| | | const AreaBasicTab = ({ |
| | | areaName, |
| | | setAreaName, |
| | | name, |
| | | setName, |
| | | agvOptions, |
| | | selectedAgvs, |
| | | setSelectedAgvs, |
| | | barcodeList, |
| | | agvList, |
| | | setAgvList, |
| | | codeList, |
| | | onSave, |
| | | disableSave, |
| | | onDelete, |
| | | canDelete = false, |
| | | }) => { |
| | | const translate = useTranslate(); |
| | | const icon = <CheckBoxOutlineBlankIcon fontSize="small" />; |
| | | const checkedIcon = <CheckBoxIcon fontSize="small" />; |
| | | |
| | | const getOptionLabel = (option) => { |
| | | if (typeof option === 'string') { |
| | | return option; |
| | | const normalizeAgvId = (agv) => { |
| | | if (agv == null) { |
| | | return ''; |
| | | } |
| | | return option?.label ?? option?.name ?? option?.agvNo ?? option?.value ?? option?.id ?? ''; |
| | | if (typeof agv === 'object') { |
| | | return String(agv.uuid ?? ''); |
| | | } |
| | | return String(agv); |
| | | }; |
| | | |
| | | const getOptionId = (option) => { |
| | | if (typeof option === 'string') { |
| | | return option; |
| | | return normalizeAgvId(option) |
| | | }; |
| | | |
| | | const getOptionLabel = (option) => { |
| | | return option?.uuid ?? ''; |
| | | }; |
| | | |
| | | const handleSaveClick = () => { |
| | | if (disableSave) { |
| | | return; |
| | | } |
| | | return option?.value ?? option?.id ?? option?.agvNo ?? option?.code ?? option?.name ?? ''; |
| | | const confirmMsg = translate('page.map.area.confirm.save'); |
| | | if (window.confirm(confirmMsg)) { |
| | | onSave?.(); |
| | | } |
| | | }; |
| | | |
| | | const handleDeleteClick = () => { |
| | | if (!canDelete) { |
| | | return; |
| | | } |
| | | const confirmMsg = translate('page.map.area.confirm.delete'); |
| | | if (window.confirm(confirmMsg)) { |
| | | onDelete?.(); |
| | | } |
| | | }; |
| | | |
| | | return ( |
| | | <Stack spacing={2}> |
| | | <Stack spacing={3} sx={{ mt: 1 }}> |
| | | <Stack direction="row" spacing={1} alignItems="center"> |
| | | <TextField |
| | | label={translate('page.map.area.name', { _: '名称' })} |
| | | label={translate('page.map.area.form.name')} |
| | | size="small" |
| | | fullWidth |
| | | value={areaName} |
| | | onChange={(e) => setAreaName(e.target.value)} |
| | | variant="outlined" |
| | | value={name} |
| | | onChange={(e) => setName(e.target.value)} |
| | | /> |
| | | <Button variant="contained" onClick={onSave}> |
| | | {translate('common.action.save', { _: '保存' })} |
| | | </Button> |
| | | </Stack> |
| | | |
| | | <Box> |
| | | <Box > |
| | | <Typography variant="subtitle2" gutterBottom> |
| | | {translate('page.map.area.agv', { _: '添加AGV小车' })} |
| | | {translate('page.map.area.form.agv')} |
| | | </Typography> |
| | | <Autocomplete |
| | | multiple |
| | | disableCloseOnSelect |
| | | options={agvOptions || []} |
| | | value={selectedAgvs || []} |
| | | value={agvList || []} |
| | | getOptionLabel={getOptionLabel} |
| | | isOptionEqualToValue={(option, value) => getOptionId(option) === getOptionId(value)} |
| | | onChange={(event, newValue) => { |
| | | setSelectedAgvs(newValue); |
| | | isOptionEqualToValue={(option, value) => { |
| | | return getOptionId(option) === getOptionId(value); |
| | | }} |
| | | renderOption={(props, option, { selected }) => ( |
| | | <li {...props}> |
| | | <Checkbox |
| | | icon={icon} |
| | | checkedIcon={checkedIcon} |
| | | style={{ marginRight: 8 }} |
| | | checked={selected} |
| | | onChange={(event, newValue) => { |
| | | setAgvList(newValue); |
| | | }} |
| | | renderTags={(value, getTagProps) => |
| | | value.map((option, index) => ( |
| | | <Chip |
| | | {...getTagProps({ index })} |
| | | key={getOptionId(option) || index} |
| | | label={normalizeAgvId(option)} |
| | | size="small" |
| | | sx={{ mr: 0.5, fontWeight: 'bold' }} |
| | | /> |
| | | {getOptionLabel(option)} |
| | | </li> |
| | | )} |
| | | )) |
| | | } |
| | | ListboxProps={{ |
| | | sx: { |
| | | maxHeight: 240, |
| | | '& .MuiAutocomplete-option': { py: 0.5, fontSize: 13 }, |
| | | }, |
| | | }} |
| | | renderOption={(props, option, { selected }) => { |
| | | const { key, ...optionProps } = props; |
| | | return ( |
| | | <li |
| | | key={key} |
| | | {...optionProps} |
| | | style={{ |
| | | fontSize: 13, |
| | | display: 'flex', |
| | | alignItems: 'center', |
| | | height: 30, |
| | | ...(optionProps.style || {}), |
| | | }} |
| | | > |
| | | <Checkbox |
| | | icon={icon} |
| | | checkedIcon={checkedIcon} |
| | | sx={{ mr: 1, '& .MuiSvgIcon-root': { fontSize: 18 } }} |
| | | checked={selected} |
| | | /> |
| | | <Typography variant="body2">{getOptionLabel(option)}</Typography> |
| | | </li> |
| | | ); |
| | | }} |
| | | renderInput={(params) => ( |
| | | <TextField |
| | | {...params} |
| | | placeholder={translate('page.map.area.agv.placeholder', { _: '选择AGV' })} |
| | | size="small" |
| | | variant="outlined" |
| | | placeholder={translate('page.map.area.form.agvPlaceholder')} |
| | | /> |
| | | )} |
| | | ListboxProps={{ sx: { maxHeight: 280 } }} |
| | | /> |
| | | </Box> |
| | | |
| | | <Box> |
| | | <Typography variant="subtitle2" gutterBottom> |
| | | {translate('page.map.area.barcodes', { _: '区域内条码集合' })} |
| | | {translate('page.map.area.form.codes', { count: codeList.length })} |
| | | </Typography> |
| | | <TextField |
| | | placeholder={translate('page.map.area.barcodes.placeholder', { _: '每行一个条码' })} |
| | | fullWidth |
| | | multiline |
| | | minRows={6} |
| | | maxRows={10} |
| | | value={barcodeList} |
| | | InputProps={{ readOnly: true }} |
| | | /> |
| | | <Paper |
| | | variant="outlined" |
| | | sx={{ |
| | | minHeight: 220, |
| | | maxHeight: 320, |
| | | overflowY: 'auto', |
| | | p: 1, |
| | | display: 'flex', |
| | | flexWrap: 'wrap', |
| | | gap: 0.75, |
| | | }} |
| | | > |
| | | {(codeList || []).length > 0 ? ( |
| | | codeList.map((code) => ( |
| | | <Chip |
| | | key={code} |
| | | label={code} |
| | | size="small" |
| | | sx={{ fontFamily: 'monospace', fontSize: 12 }} |
| | | /> |
| | | )) |
| | | ) : ( |
| | | <Typography variant="body2" color="text.secondary"> |
| | | {translate('page.map.area.form.codesEmpty')} |
| | | </Typography> |
| | | )} |
| | | </Paper> |
| | | </Box> |
| | | |
| | | <Box display="flex" justifyContent="space-between" alignItems="center"> |
| | | <Button variant="contained" onClick={handleSaveClick} disabled={disableSave}> |
| | | {translate('ra.action.save')} |
| | | </Button> |
| | | <Button variant="text" color="error" onClick={handleDeleteClick} disabled={!canDelete}> |
| | | {translate('ra.action.delete')} |
| | | </Button> |
| | | </Box> |
| | | </Stack> |
| | | ); |