#
vincentlu
2025-12-15 8429a44614733562117601fd4d9084d426003557
#
4个文件已添加
3个文件已修改
1092 ■■■■■ 已修改文件
zy-acs-flow/src/i18n/en.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/i18n/zh.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/map/MapPage.jsx 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/map/areaSettings/ConfigSettings.jsx 178 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/map/areaSettings/CopyDrawer.jsx 412 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/map/areaSettings/MapSettings.jsx 376 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/map/areaSettings/index.jsx 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/i18n/en.js
@@ -637,6 +637,7 @@
                direction: 'DIRECTION',
                agv: 'AGV',
                point: 'POINT',
                area: 'AREA',
            },
            action: {
                startup: 'Startup',
zy-acs-flow/src/i18n/zh.js
@@ -637,6 +637,7 @@
                direction: '方向',
                agv: '车辆',
                point: '定位点',
                area: '区域',
            },
            action: {
                startup: '启动RCS',
zy-acs-flow/src/map/MapPage.jsx
@@ -16,6 +16,7 @@
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";
@@ -49,6 +50,7 @@
    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);
@@ -133,6 +135,7 @@
        setDeviceVisible(false);
        setSettingsVisible(false);
        setBatchSelectionVisible(false);
        setAreaSettingsVisible(false);
        setAreaDrawing(false);
        Tool.cancelAreaDrawing();
@@ -221,12 +224,13 @@
            }
            if (mode === MAP_MODE.AREA_MODE) {
                Tool.showSelectedEffect(curSprite);
                setSettingsVisible(true);
                setAreaSettingsVisible(true);
            }
        } else {
            Tool.removeSelectedEffect();
            setInsightVisible(false);
            setSettingsVisible(false);
            setAreaSettingsVisible(false);
        }
    }, [curSprite]);
@@ -532,6 +536,16 @@
                width={570}
            />
            <AreaSettings
                open={areaSettingsVisible}
                onCancel={() => {
                    setCurSprite(null);
                }}
                sprite={curSprite}
                setSpriteSettings={setCurSprite}
                width={570}
            />
        </Box>
    );
}
zy-acs-flow/src/map/areaSettings/ConfigSettings.jsx
New file
@@ -0,0 +1,178 @@
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;
zy-acs-flow/src/map/areaSettings/CopyDrawer.jsx
New file
@@ -0,0 +1,412 @@
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;
zy-acs-flow/src/map/areaSettings/MapSettings.jsx
New file
@@ -0,0 +1,376 @@
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;
zy-acs-flow/src/map/areaSettings/index.jsx
New file
@@ -0,0 +1,108 @@
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;