| | |
| | | direction: 'DIRECTION', |
| | | agv: 'AGV', |
| | | point: 'POINT', |
| | | area: 'AREA', |
| | | }, |
| | | action: { |
| | | startup: 'Startup', |
| | |
| | | direction: '方向', |
| | | agv: '车辆', |
| | | point: '定位点', |
| | | area: '区域', |
| | | }, |
| | | action: { |
| | | startup: '启动RCS', |
| | |
| | | import Device from "./Device"; |
| | | import Settings from "./settings"; |
| | | import Batch from "./batch"; |
| | | import AreaSettings from "./areaSettings"; |
| | | import * as Http from './http'; |
| | | import WebSocketClient from './websocket' |
| | | import ConfirmButton from "../page/components/ConfirmButton"; |
| | |
| | | const [deviceVisible, setDeviceVisible] = useState(false); |
| | | const [settingsVisible, setSettingsVisible] = useState(false); |
| | | const [batchSelectionVisible, setBatchSelectionVisible] = useState(false); |
| | | const [areaSettingsVisible, setAreaSettingsVisible] = useState(false); |
| | | const [areaDrawing, setAreaDrawing] = useState(false); |
| | | |
| | | const [curSprite, setCurSprite] = useState(null); |
| | |
| | | setDeviceVisible(false); |
| | | setSettingsVisible(false); |
| | | setBatchSelectionVisible(false); |
| | | setAreaSettingsVisible(false); |
| | | setAreaDrawing(false); |
| | | Tool.cancelAreaDrawing(); |
| | | |
| | |
| | | } |
| | | if (mode === MAP_MODE.AREA_MODE) { |
| | | Tool.showSelectedEffect(curSprite); |
| | | setSettingsVisible(true); |
| | | setAreaSettingsVisible(true); |
| | | } |
| | | } else { |
| | | Tool.removeSelectedEffect(); |
| | | setInsightVisible(false); |
| | | setSettingsVisible(false); |
| | | setAreaSettingsVisible(false); |
| | | } |
| | | }, [curSprite]); |
| | | |
| | |
| | | width={570} |
| | | /> |
| | | |
| | | <AreaSettings |
| | | open={areaSettingsVisible} |
| | | onCancel={() => { |
| | | setCurSprite(null); |
| | | }} |
| | | sprite={curSprite} |
| | | setSpriteSettings={setCurSprite} |
| | | width={570} |
| | | /> |
| | | |
| | | </Box> |
| | | ); |
| | | } |
| New file |
| | |
| | | import React, { useEffect } from 'react'; |
| | | import { useForm, Controller } from 'react-hook-form'; |
| | | import { |
| | | Box, |
| | | Grid, |
| | | Typography, |
| | | TextField, |
| | | Slider, |
| | | Button, |
| | | Select, |
| | | MenuItem, |
| | | InputLabel, |
| | | FormControl, |
| | | Stack, |
| | | Divider, |
| | | } from '@mui/material'; |
| | | import { useTranslate } from 'react-admin'; |
| | | import * as Tool from '../tool'; |
| | | import ConfirmButton from '../../page/components/ConfirmButton'; |
| | | import { |
| | | DEVICE_TYPE, |
| | | } from '../constants'; |
| | | import { useNotification } from '../Notification'; |
| | | |
| | | const ConfigSettings = (props) => { |
| | | const { sprite, onSubmit } = props; |
| | | const notify = useNotification(); |
| | | const translate = useTranslate(); |
| | | |
| | | const { control, handleSubmit, reset, watch, setValue, formState: { errors } } = useForm({ |
| | | defaultValues: { ...sprite?.data }, |
| | | }); |
| | | |
| | | useEffect(() => { |
| | | if (sprite?.data) { |
| | | reset({ |
| | | ...sprite.data |
| | | }); |
| | | } |
| | | }, [sprite, reset]); |
| | | |
| | | const deviceType = sprite?.data?.type; |
| | | |
| | | const rowValue = watch('row'); |
| | | const bayValue = watch('bay'); |
| | | |
| | | useEffect(() => { |
| | | if (deviceType === DEVICE_TYPE.SHELF) { |
| | | if (rowValue != null && bayValue != null && rowValue !== '' && bayValue !== '') { |
| | | setValue('no', `${rowValue}-${bayValue}`); |
| | | } else { |
| | | setValue('no', ''); |
| | | } |
| | | } |
| | | }, [ |
| | | setValue, |
| | | deviceType, |
| | | rowValue, |
| | | bayValue, |
| | | ]); |
| | | |
| | | const onFormSubmit = (data) => { |
| | | if (sprite?.data) { |
| | | Object.keys(data).forEach((key) => { |
| | | sprite.data[key] = data[key]; |
| | | }); |
| | | } |
| | | if (onSubmit) { |
| | | onSubmit(data); |
| | | } |
| | | notify.info(translate('common.response.success')); |
| | | }; |
| | | |
| | | return ( |
| | | <> |
| | | <Box component="form" onSubmit={handleSubmit(onFormSubmit)} noValidate sx={{ mt: 0 }}> |
| | | <Grid container spacing={1.4}> |
| | | |
| | | {deviceType === DEVICE_TYPE.SHELF && ( |
| | | <> |
| | | <Grid item xs={6}> |
| | | <Controller |
| | | name="row" |
| | | control={control} |
| | | render={({ field }) => ( |
| | | <TextField |
| | | {...field} |
| | | label={translate('page.map.settings.config.shelf.row')} |
| | | type="number" |
| | | value={field.value ?? ''} |
| | | fullWidth |
| | | onChange={(e) => { |
| | | field.onChange(e.target.value === '' ? '' : Number(e.target.value)); |
| | | }} |
| | | /> |
| | | )} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6}> |
| | | <Controller |
| | | name="bay" |
| | | control={control} |
| | | render={({ field }) => ( |
| | | <TextField |
| | | {...field} |
| | | label={translate('page.map.settings.config.shelf.bay')} |
| | | type="number" |
| | | value={field.value ?? ''} |
| | | fullWidth |
| | | onChange={(e) => { |
| | | field.onChange(e.target.value === '' ? '' : Number(e.target.value)); |
| | | }} |
| | | /> |
| | | )} |
| | | /> |
| | | </Grid> |
| | | </> |
| | | )} |
| | | |
| | | {deviceType === DEVICE_TYPE.CHARGE && ( |
| | | <> |
| | | </> |
| | | )} |
| | | |
| | | {deviceType === DEVICE_TYPE.STATION && ( |
| | | <> |
| | | </> |
| | | )} |
| | | |
| | | {deviceType === DEVICE_TYPE.POINT && ( |
| | | <> |
| | | </> |
| | | )} |
| | | |
| | | <Grid item xs={12}> |
| | | <Divider /> |
| | | </Grid> |
| | | |
| | | <Grid item xs={6}> |
| | | <Controller |
| | | name="no" |
| | | control={control} |
| | | rules={{ |
| | | required: translate('ra.validation.required') // warn msg |
| | | }} |
| | | render={({ field }) => { |
| | | return ( |
| | | <TextField |
| | | {...field} |
| | | label={translate('page.map.settings.config.base.no')} |
| | | type="text" |
| | | value={field.value ?? ''} |
| | | error={!!errors.no} // show red warn |
| | | helperText={errors.no ? errors.no.message : null} // show warn msg |
| | | fullWidth |
| | | onChange={(e) => { |
| | | field.onChange(e); |
| | | }} |
| | | /> |
| | | ) |
| | | }} |
| | | /> |
| | | </Grid> |
| | | |
| | | <Grid item xs={12} mt={2}> |
| | | <Stack direction="row" spacing={2}> |
| | | <Button variant="contained" color="primary" type="submit"> |
| | | {translate('ra.action.confirm')} |
| | | </Button> |
| | | </Stack> |
| | | </Grid> |
| | | </Grid> |
| | | </Box> |
| | | </> |
| | | ); |
| | | }; |
| | | |
| | | export default ConfigSettings; |
| New file |
| | |
| | | import React, { useEffect } from 'react'; |
| | | import { useForm, useWatch, Controller } from 'react-hook-form'; |
| | | import { |
| | | Box, |
| | | Grid, |
| | | Typography, |
| | | TextField, |
| | | Card, |
| | | CardContent, |
| | | Button, |
| | | Select, |
| | | MenuItem, |
| | | useTheme, |
| | | FormControl, |
| | | Stack, |
| | | Divider, |
| | | Drawer, |
| | | IconButton, |
| | | Switch, |
| | | FormControlLabel, |
| | | FormLabel, |
| | | ToggleButtonGroup, |
| | | ToggleButton, |
| | | } from '@mui/material'; |
| | | import CloseIcon from '@mui/icons-material/Close'; |
| | | import { useTranslate } from 'react-admin'; |
| | | import * as Tool from '../tool'; |
| | | import { PAGE_DRAWER_WIDTH } from '@/config/setting'; |
| | | import { |
| | | DEVICE_TYPE, |
| | | } from '../constants'; |
| | | import { useNotification } from '../Notification'; |
| | | |
| | | const incrementOptionsMap = { |
| | | [DEVICE_TYPE.SHELF]: [ |
| | | { value: 'row', label: 'page.map.settings.config.shelf.row' }, |
| | | { value: 'bay', label: 'page.map.settings.config.shelf.bay' }, |
| | | ], |
| | | [DEVICE_TYPE.CHARGE]: [ |
| | | { value: 'no', label: 'page.map.settings.config.base.no' }, |
| | | ], |
| | | [DEVICE_TYPE.STATION]: [ |
| | | { value: 'no', label: 'page.map.settings.config.base.no' }, |
| | | ], |
| | | [DEVICE_TYPE.POINT]: [ |
| | | { value: 'no', label: 'page.map.settings.config.base.no' }, |
| | | ], |
| | | }; |
| | | |
| | | |
| | | const validateIncrement = (value, deviceType, sprite, translate) => { |
| | | if (!value) { |
| | | return true; |
| | | } |
| | | switch (deviceType) { |
| | | case DEVICE_TYPE.SHELF: |
| | | if (!sprite?.data?.row || !sprite?.data?.bay) { |
| | | return translate('page.map.settings.map.copy.valid.shelf'); |
| | | } |
| | | break; |
| | | default: |
| | | if (!sprite?.data?.no) { |
| | | return translate('page.map.settings.map.copy.valid.common'); |
| | | } |
| | | break; |
| | | } |
| | | return true; |
| | | }; |
| | | |
| | | const getDefaultFormValues = (value = {}) => ({ |
| | | copyDirect: value.copyDirect || '', |
| | | copyCount: value.copyCount || '', |
| | | gap: 0.0, |
| | | autoIncrement: false, |
| | | incrementMode: 'ascending', |
| | | incrementValue: undefined, |
| | | }); |
| | | |
| | | const CopyDrawer = (props) => { |
| | | const { |
| | | open, |
| | | onCancel, |
| | | sprite, |
| | | value, |
| | | width = PAGE_DRAWER_WIDTH, |
| | | handleOnCopy, |
| | | setLastCopiedSprites, |
| | | setSpriteSettings, |
| | | } = props; |
| | | const notify = useNotification(); |
| | | const translate = useTranslate(); |
| | | const theme = useTheme(); |
| | | const themeMode = theme.palette.mode; |
| | | |
| | | const deviceType = sprite?.data?.type; |
| | | const incrementOptions = incrementOptionsMap[deviceType] |
| | | |
| | | const { control, handleSubmit, reset, watch, setValue, formState: { errors } } = useForm({ |
| | | defaultValues: getDefaultFormValues(value), |
| | | }); |
| | | |
| | | const autoIncrement = useWatch({ control, name: 'autoIncrement' }); |
| | | const incrementValue = useWatch({ control, name: 'incrementValue' }); |
| | | |
| | | useEffect(() => { |
| | | if (sprite && value && Object.keys(value).length > 0) { |
| | | reset(getDefaultFormValues(value)); |
| | | } |
| | | }, [sprite, value, reset, incrementOptions, setValue]); |
| | | |
| | | useEffect(() => { |
| | | if (autoIncrement && incrementOptions && incrementOptions.length > 0) { |
| | | if (!incrementValue) { |
| | | setValue('incrementValue', incrementOptions[0].value); |
| | | } |
| | | } else { |
| | | setValue('incrementValue', undefined); |
| | | } |
| | | }, [autoIncrement, incrementOptions, setValue]); |
| | | |
| | | const handleClose = () => { |
| | | onCancel(); |
| | | } |
| | | |
| | | const onFormSubmit = (data) => { |
| | | if (!sprite || !data || Object.keys(data).length === 0) { |
| | | return; |
| | | } |
| | | |
| | | const { copyCount, copyDirect, gap, autoIncrement, incrementMode, incrementValue } = data; |
| | | const copiedSprites = []; |
| | | |
| | | const adjustPosition = (sprite, direction, gap, index) => { |
| | | const factor = index + 1; |
| | | switch (direction) { |
| | | case 'left': |
| | | sprite.position.x -= factor * (gap + sprite.width); |
| | | break; |
| | | case 'right': |
| | | sprite.position.x += factor * (gap + sprite.width); |
| | | break; |
| | | case 'up': |
| | | sprite.position.y -= factor * (gap + sprite.height); |
| | | break; |
| | | case 'down': |
| | | sprite.position.y += factor * (gap + sprite.height); |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | }; |
| | | |
| | | const incrementSpriteData = (copiedSprite, index) => { |
| | | const incrementAmount = incrementMode === 'descending' ? -(index + 1) : index + 1; |
| | | switch (deviceType) { |
| | | case DEVICE_TYPE.SHELF: |
| | | if (incrementValue === 'row') { |
| | | copiedSprite.data.row = sprite.data.row + incrementAmount; |
| | | } |
| | | if (incrementValue === 'bay') { |
| | | copiedSprite.data.bay = sprite.data.bay + incrementAmount; |
| | | } |
| | | if (copiedSprite.data.row && copiedSprite.data.bay) { |
| | | copiedSprite.data.no = `${copiedSprite.data.row}-${copiedSprite.data.bay}`; |
| | | } |
| | | break; |
| | | default: |
| | | if (incrementValue === 'no') { |
| | | copiedSprite.data.no = Tool.incrementSpriteNo(sprite.data.no, incrementAmount); |
| | | } |
| | | break; |
| | | } |
| | | }; |
| | | |
| | | for (let i = 0; i < copyCount; i++) { |
| | | const copiedSprite = Tool.copySprite(sprite); |
| | | |
| | | adjustPosition(copiedSprite, copyDirect, gap, i); |
| | | |
| | | // auto-increment-value |
| | | if (autoIncrement && deviceType) { |
| | | incrementSpriteData(copiedSprite, i); |
| | | } |
| | | |
| | | Tool.getMapContainer().addChild(copiedSprite); |
| | | Tool.beSettings(copiedSprite, setSpriteSettings); |
| | | copiedSprites.push(copiedSprite); |
| | | } |
| | | |
| | | setLastCopiedSprites(copiedSprites); |
| | | onCancel(); |
| | | handleOnCopy?.(data); |
| | | notify.info(translate('common.response.success')); |
| | | }; |
| | | |
| | | return ( |
| | | <> |
| | | <Drawer |
| | | variant="persistent" |
| | | open={open} |
| | | anchor="right" |
| | | onClose={handleClose} |
| | | sx={{ zIndex: 100, opacity: 1 }} |
| | | > |
| | | {(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"> |
| | | {translate('page.map.settings.map.copy.title')} |
| | | </Typography> |
| | | <IconButton onClick={handleClose} size="small"> |
| | | <CloseIcon /> |
| | | </IconButton> |
| | | </Stack> |
| | | |
| | | <Box p={3}> |
| | | <Card sx={{ |
| | | p: 2, |
| | | 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> |
| | | <Box component="form" onSubmit={handleSubmit(onFormSubmit)} noValidate sx={{ mt: 0 }}> |
| | | <Grid container spacing={1.4}> |
| | | <Grid item xs={6}> |
| | | <Controller |
| | | name="copyDirect" |
| | | control={control} |
| | | render={({ field }) => ( |
| | | <TextField |
| | | {...field} |
| | | label={translate('page.map.settings.map.copy.direction')} |
| | | InputProps={{ |
| | | readOnly: true, |
| | | }} |
| | | fullWidth |
| | | /> |
| | | )} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6}> |
| | | <Controller |
| | | name="copyCount" |
| | | control={control} |
| | | render={({ field }) => ( |
| | | <TextField |
| | | {...field} |
| | | label={translate('page.map.settings.map.copy.count')} |
| | | InputProps={{ |
| | | readOnly: true, |
| | | }} |
| | | fullWidth |
| | | /> |
| | | )} |
| | | /> |
| | | </Grid> |
| | | |
| | | <Grid item xs={12}> |
| | | <Controller |
| | | name="gap" |
| | | control={control} |
| | | render={({ field }) => ( |
| | | <TextField |
| | | {...field} |
| | | label={translate('page.map.settings.map.copy.field.gap')} |
| | | type="number" |
| | | sx={{ width: '50%' }} |
| | | fullWidth |
| | | inputProps={{ |
| | | // min: 0, |
| | | step: 1, |
| | | }} |
| | | onChange={(e) => { |
| | | field.onChange(e.target.value === '' ? '' : Number(e.target.value)); |
| | | }} |
| | | /> |
| | | )} |
| | | /> |
| | | </Grid> |
| | | |
| | | <Grid item xs={12} mt={2} mb={1}> |
| | | <Divider /> |
| | | </Grid> |
| | | |
| | | <Grid item xs={12}> |
| | | <Controller |
| | | name="autoIncrement" |
| | | control={control} |
| | | rules={{ validate: (value) => validateIncrement(value, deviceType, sprite, translate) }} |
| | | render={({ field }) => ( |
| | | <FormControlLabel |
| | | control={ |
| | | <Switch |
| | | {...field} |
| | | checked={field.value || false} |
| | | onChange={(e) => field.onChange(e.target.checked)} |
| | | /> |
| | | } |
| | | label={translate('page.map.settings.map.copy.field.autoIncrement')} |
| | | /> |
| | | )} |
| | | /> |
| | | {errors.autoIncrement && ( |
| | | <Typography color="error"> |
| | | {errors.autoIncrement.message} |
| | | </Typography> |
| | | )} |
| | | </Grid> |
| | | |
| | | {(!!incrementOptions?.length && autoIncrement) && ( |
| | | <Grid item xs={12}> |
| | | <FormControl fullWidth> |
| | | <FormLabel sx={{ mb: 1 }}> |
| | | {translate('page.map.settings.map.copy.field.incrementValue')} |
| | | </FormLabel> |
| | | <Controller |
| | | name='incrementValue' |
| | | control={control} |
| | | render={({ field }) => ( |
| | | <ToggleButtonGroup |
| | | {...field} |
| | | value={field.value} |
| | | exclusive |
| | | onChange={(event, value) => { |
| | | if (value !== null) { |
| | | field.onChange(value); |
| | | } |
| | | }} |
| | | fullWidth |
| | | > |
| | | {incrementOptions.map((option, idx) => ( |
| | | <ToggleButton key={idx} value={option.value}> |
| | | {translate(option.label)} |
| | | </ToggleButton> |
| | | ))} |
| | | </ToggleButtonGroup> |
| | | )} |
| | | /> |
| | | </FormControl> |
| | | </Grid> |
| | | )} |
| | | |
| | | {autoIncrement && ( |
| | | <Grid item xs={12}> |
| | | <FormControl fullWidth> |
| | | <FormLabel sx={{ |
| | | mb: 1 |
| | | }}> |
| | | {translate('page.map.settings.map.copy.field.incrementMode')} |
| | | </FormLabel> |
| | | <Controller |
| | | name="incrementMode" |
| | | control={control} |
| | | render={({ field }) => ( |
| | | <ToggleButtonGroup |
| | | {...field} |
| | | value={field.value} |
| | | exclusive |
| | | onChange={(event, value) => { |
| | | if (value !== null) { |
| | | field.onChange(value); |
| | | } |
| | | }} |
| | | fullWidth |
| | | > |
| | | <ToggleButton value="ascending"> |
| | | {translate('page.map.settings.map.copy.field.ascend')} |
| | | </ToggleButton> |
| | | <ToggleButton value="descending"> |
| | | {translate('page.map.settings.map.copy.field.descend')} |
| | | </ToggleButton> |
| | | </ToggleButtonGroup> |
| | | )} |
| | | /> |
| | | </FormControl> |
| | | </Grid> |
| | | )} |
| | | |
| | | <Grid item xs={12} mt={2}> |
| | | <Divider /> |
| | | </Grid> |
| | | |
| | | <Grid item xs={12} mt={2}> |
| | | <Stack direction="row" spacing={2}> |
| | | <Button variant="contained" color="primary" type="submit"> |
| | | {translate('ra.action.confirm')} |
| | | </Button> |
| | | </Stack> |
| | | </Grid> |
| | | |
| | | </Grid> |
| | | </Box> |
| | | </CardContent> |
| | | </Card> |
| | | </Box> |
| | | </Box> |
| | | )} |
| | | </Drawer> |
| | | </> |
| | | ) |
| | | } |
| | | |
| | | export default CopyDrawer; |
| New file |
| | |
| | | import React, { useEffect, useState } from 'react'; |
| | | import { useForm, Controller } from 'react-hook-form'; |
| | | import { |
| | | Box, |
| | | Grid, |
| | | Typography, |
| | | TextField, |
| | | Slider, |
| | | Button, |
| | | Select, |
| | | MenuItem, |
| | | InputLabel, |
| | | FormControl, |
| | | Stack, |
| | | Divider, |
| | | } from '@mui/material'; |
| | | import MuiInput from '@mui/material/Input'; |
| | | import { useTranslate } from 'react-admin'; |
| | | import * as Tool from '../tool'; |
| | | import ConfirmButton from '../../page/components/ConfirmButton'; |
| | | import CopyDrawer from './CopyDrawer'; |
| | | |
| | | const MapSettings = (props) => { |
| | | const { sprite, setSpriteSettings, onSubmit, width, lastCopiedSprites, setLastCopiedSprites } = props; |
| | | const translate = useTranslate(); |
| | | const [copyVisible, setCopyVisible] = useState(false); |
| | | |
| | | const { control, handleSubmit, reset, watch } = useForm({ |
| | | defaultValues: { |
| | | x: sprite?.position?.x || 0, |
| | | y: sprite?.position?.y || 0, |
| | | scaleX: sprite?.scale?.x || 1, |
| | | scaleY: sprite?.scale?.y || 1, |
| | | rotation: (sprite?.rotation * 180) / Math.PI || 0, |
| | | copyDirection: 'right', |
| | | copyCount: 1, |
| | | }, |
| | | }); |
| | | |
| | | const watchAllFields = watch(); |
| | | |
| | | useEffect(() => { |
| | | if (sprite) { |
| | | setCopyVisible(false); |
| | | reset({ |
| | | x: sprite?.position.x, |
| | | y: sprite?.position.y, |
| | | scaleX: sprite?.scale.x, |
| | | scaleY: sprite?.scale.y, |
| | | rotation: (sprite?.rotation * 180) / Math.PI, |
| | | }); |
| | | } |
| | | }, [sprite, reset]); |
| | | |
| | | const updateSprite = (data) => { |
| | | if (sprite) { |
| | | sprite.position.x = data.x; |
| | | sprite.position.y = data.y; |
| | | sprite.scale.x = data.scaleX; |
| | | sprite.scale.y = data.scaleY; |
| | | sprite.rotation = (data.rotation * Math.PI) / 180; |
| | | } |
| | | }; |
| | | |
| | | const onFormSubmit = (data) => { |
| | | updateSprite(data); |
| | | if (onSubmit) { |
| | | onSubmit(data); |
| | | } |
| | | }; |
| | | |
| | | return ( |
| | | <> |
| | | <Box component="form" onSubmit={handleSubmit(onFormSubmit)} noValidate sx={{ mt: 0 }}> |
| | | <Grid container spacing={1.4}> |
| | | {/* position */} |
| | | <Grid item xs={12}> |
| | | <Typography variant="inherit"> |
| | | {translate('page.map.settings.map.base.position')} |
| | | </Typography> |
| | | </Grid> |
| | | <Grid item xs={6} pt={0} sx={{ |
| | | paddingTop: '8px !important', |
| | | }}> |
| | | <Controller |
| | | name="x" |
| | | control={control} |
| | | render={({ field }) => ( |
| | | <TextField |
| | | {...field} |
| | | label="X" |
| | | type="number" |
| | | fullWidth |
| | | onChange={(e) => { |
| | | const value = parseFloat(e.target.value); |
| | | field.onChange(e); |
| | | if (!isNaN(value)) { |
| | | updateSprite({ ...watchAllFields, x: value }); |
| | | } |
| | | }} |
| | | /> |
| | | )} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} sx={{ |
| | | paddingTop: '8px !important', |
| | | }}> |
| | | <Controller |
| | | name="y" |
| | | control={control} |
| | | render={({ field }) => ( |
| | | <TextField |
| | | {...field} |
| | | label="Y" |
| | | type="number" |
| | | fullWidth |
| | | onChange={(e) => { |
| | | const value = parseFloat(e.target.value); |
| | | field.onChange(e); |
| | | if (!isNaN(value)) { |
| | | updateSprite({ ...watchAllFields, y: value }); |
| | | } |
| | | }} |
| | | /> |
| | | )} |
| | | /> |
| | | </Grid> |
| | | |
| | | {/* scale */} |
| | | <Grid item xs={12}> |
| | | <Typography variant="inherit"> |
| | | {translate('page.map.settings.map.base.scale')} |
| | | </Typography> |
| | | </Grid> |
| | | <Grid item xs={6} sx={{ |
| | | paddingTop: '8px !important', |
| | | }}> |
| | | <Controller |
| | | name="scaleX" |
| | | control={control} |
| | | render={({ field }) => ( |
| | | <TextField |
| | | {...field} |
| | | label="X" |
| | | type="number" |
| | | fullWidth |
| | | inputProps={{ step: 0.1, min: 0.1, max: 10 }} |
| | | onChange={(e) => { |
| | | const value = parseFloat(e.target.value); |
| | | field.onChange(e); |
| | | if (!isNaN(value)) { |
| | | updateSprite({ ...watchAllFields, scaleX: value }); |
| | | } |
| | | }} |
| | | /> |
| | | )} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} sx={{ |
| | | paddingTop: '8px !important', |
| | | }}> |
| | | <Controller |
| | | name="scaleY" |
| | | control={control} |
| | | render={({ field }) => ( |
| | | <TextField |
| | | {...field} |
| | | label="Y" |
| | | type="number" |
| | | fullWidth |
| | | inputProps={{ step: 0.1, min: 0.1, max: 10 }} |
| | | onChange={(e) => { |
| | | const value = parseFloat(e.target.value); |
| | | field.onChange(e); |
| | | if (!isNaN(value)) { |
| | | updateSprite({ ...watchAllFields, scaleY: value }); |
| | | } |
| | | }} |
| | | /> |
| | | )} |
| | | /> |
| | | </Grid> |
| | | |
| | | {/* rotation */} |
| | | <Grid item xs={12}> |
| | | <Typography variant="inherit"> |
| | | {translate('page.map.settings.map.base.rotation')} |
| | | </Typography> |
| | | </Grid> |
| | | <Grid item xs={12}> |
| | | <Box display="flex" alignItems="center"> |
| | | <Box flex={1} mr={3}> |
| | | <Controller |
| | | name="rotation" |
| | | control={control} |
| | | render={({ field }) => ( |
| | | <Slider |
| | | {...field} |
| | | // size="small" |
| | | min={0} |
| | | max={360} |
| | | step={1} |
| | | valueLabelDisplay="auto" |
| | | valueLabelFormat={(value) => `${value}°`} |
| | | onChange={(e, value) => { |
| | | field.onChange(value); |
| | | updateSprite({ ...watchAllFields, rotation: value }); |
| | | }} |
| | | /> |
| | | )} |
| | | /> |
| | | </Box> |
| | | <Box > |
| | | <Controller |
| | | name="rotation" |
| | | control={control} |
| | | render={({ field }) => ( |
| | | <MuiInput |
| | | {...field} |
| | | size="small" |
| | | value={field.value} |
| | | onChange={(e) => { |
| | | const value = e.target.value === '' ? 0 : Number(e.target.value); |
| | | if (!isNaN(value)) { |
| | | field.onChange(value); |
| | | updateSprite({ ...watchAllFields, rotation: value }); |
| | | } |
| | | }} |
| | | onBlur={() => { |
| | | if (field.value < 0) { |
| | | field.onChange(0); |
| | | } else if (field.value > 360) { |
| | | field.onChange(360); |
| | | } |
| | | }} |
| | | inputProps={{ |
| | | step: 1, |
| | | min: 0, |
| | | max: 360, |
| | | type: 'number', |
| | | 'aria-labelledby': 'input-slider', |
| | | }} |
| | | /> |
| | | )} |
| | | /> |
| | | </Box> |
| | | </Box> |
| | | </Grid> |
| | | |
| | | <Grid item xs={12}> |
| | | <Divider /> |
| | | </Grid> |
| | | |
| | | {/* copy */} |
| | | <Grid item xs={12}> |
| | | <Typography variant="inherit"> |
| | | {translate('page.map.settings.map.copy.title')} |
| | | </Typography> |
| | | </Grid> |
| | | <Grid item xs={6} sx={{ |
| | | paddingTop: '8px !important', |
| | | }}> |
| | | <Controller |
| | | name="copyDirection" |
| | | control={control} |
| | | render={({ field }) => ( |
| | | <FormControl fullWidth> |
| | | <InputLabel> |
| | | {translate('page.map.settings.map.copy.direction')} |
| | | </InputLabel> |
| | | <Select |
| | | {...field} |
| | | label={translate('page.map.settings.map.copy.direction')} |
| | | > |
| | | <MenuItem value="left"> |
| | | {translate('page.map.settings.map.copy.left')} |
| | | </MenuItem> |
| | | <MenuItem value="right"> |
| | | {translate('page.map.settings.map.copy.right')} |
| | | </MenuItem> |
| | | <MenuItem value="up"> |
| | | {translate('page.map.settings.map.copy.up')} |
| | | </MenuItem> |
| | | <MenuItem value="down"> |
| | | {translate('page.map.settings.map.copy.down')} |
| | | </MenuItem> |
| | | </Select> |
| | | </FormControl> |
| | | )} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} sx={{ |
| | | paddingTop: '8px !important', |
| | | }}> |
| | | <Controller |
| | | name="copyCount" |
| | | control={control} |
| | | render={({ field }) => ( |
| | | <TextField |
| | | {...field} |
| | | label={translate('page.map.settings.map.copy.count')} |
| | | type="number" |
| | | fullWidth |
| | | inputProps={{ min: 1 }} |
| | | /> |
| | | )} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={12}> |
| | | <Stack direction="row" spacing={2}> |
| | | <Button variant="contained" color="primary" onClick={() => { |
| | | setCopyVisible(true); |
| | | }}> |
| | | {translate('page.map.settings.map.copy.execute')} |
| | | </Button> |
| | | <Button variant="text" color="primary" onClick={() => { |
| | | if (lastCopiedSprites && lastCopiedSprites.length > 0) { |
| | | lastCopiedSprites.forEach(copiedSprite => { |
| | | Tool.getMapContainer().removeChild(copiedSprite); |
| | | }) |
| | | setLastCopiedSprites([]); |
| | | } |
| | | }}> |
| | | {translate('page.map.settings.map.copy.reverse')} |
| | | </Button> |
| | | </Stack> |
| | | </Grid> |
| | | |
| | | <Grid item xs={12}> |
| | | <Divider /> |
| | | </Grid> |
| | | |
| | | {/* more */} |
| | | <Grid item xs={12}> |
| | | <Typography variant="inherit"> |
| | | {translate('page.map.settings.map.more.title')} |
| | | </Typography> |
| | | </Grid> |
| | | <Grid item xs={12}> |
| | | <Stack direction="row" spacing={2}> |
| | | <Button variant="outlined" color="error" onClick={() => { |
| | | Tool.getMapContainer().removeChild(sprite); |
| | | setSpriteSettings(null); |
| | | Tool.removeSelectedEffect(); |
| | | reset(); |
| | | }}> |
| | | {translate('ra.action.delete')} |
| | | </Button> |
| | | {/* <ConfirmButton /> */} |
| | | </Stack> |
| | | </Grid> |
| | | </Grid> |
| | | </Box > |
| | | |
| | | <CopyDrawer |
| | | open={copyVisible} |
| | | onCancel={() => { |
| | | setCopyVisible(false); |
| | | }} |
| | | width={width} |
| | | sprite={sprite} |
| | | value={{ |
| | | copyDirect: watch('copyDirection'), |
| | | copyCount: watch('copyCount'), |
| | | }} |
| | | handleOnCopy={() => { |
| | | |
| | | }} |
| | | setLastCopiedSprites={setLastCopiedSprites} |
| | | setSpriteSettings={setSpriteSettings} |
| | | /> |
| | | </> |
| | | ); |
| | | }; |
| | | |
| | | export default MapSettings; |
| New file |
| | |
| | | import React, { useState, useRef, 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 MapSettings from './MapSettings'; |
| | | import ConfigSettings from './ConfigSettings'; |
| | | |
| | | const AreaSettings = (props) => { |
| | | const { open, onCancel, sprite, width = PAGE_DRAWER_WIDTH, title, setSpriteSettings } = props; |
| | | const theme = useTheme(); |
| | | const themeMode = theme.palette.mode; |
| | | const translate = useTranslate(); |
| | | |
| | | const [lastCopiedSprites, setLastCopiedSprites] = useState([]); |
| | | |
| | | const handleClose = () => { |
| | | onCancel(); |
| | | } |
| | | |
| | | const [activeTab, setActiveTab] = useState(0); |
| | | |
| | | const handleTabChange = (event, newValue) => { |
| | | setActiveTab(newValue); |
| | | }; |
| | | |
| | | 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.settings.map.title')} /> |
| | | <Tab label={translate('page.map.settings.config.title')} /> |
| | | </Tabs> |
| | | |
| | | <Divider /> |
| | | |
| | | <Box p={3}> |
| | | {activeTab === 0 && ( |
| | | <MapSettings |
| | | sprite={sprite} |
| | | setSpriteSettings={setSpriteSettings} |
| | | onSubmit={() => { |
| | | }} |
| | | width={width} |
| | | lastCopiedSprites={lastCopiedSprites} |
| | | setLastCopiedSprites={setLastCopiedSprites} |
| | | /> |
| | | )} |
| | | {activeTab === 1 && ( |
| | | <ConfigSettings |
| | | sprite={sprite} |
| | | onSubmit={() => { |
| | | }} |
| | | /> |
| | | )} |
| | | </Box> |
| | | </CardContent> |
| | | </Card> |
| | | </Box> |
| | | </Box> |
| | | )} |
| | | </Drawer> |
| | | </> |
| | | ) |
| | | } |
| | | |
| | | export default AreaSettings; |