From 8429a44614733562117601fd4d9084d426003557 Mon Sep 17 00:00:00 2001
From: vincentlu <t1341870251@gmail.com>
Date: 星期一, 15 十二月 2025 09:52:14 +0800
Subject: [PATCH] #
---
zy-acs-flow/src/i18n/en.js | 1
zy-acs-flow/src/map/areaSettings/MapSettings.jsx | 376 +++++++++++++++++++
zy-acs-flow/src/map/areaSettings/ConfigSettings.jsx | 178 +++++++++
zy-acs-flow/src/i18n/zh.js | 1
zy-acs-flow/src/map/areaSettings/CopyDrawer.jsx | 412 +++++++++++++++++++++
zy-acs-flow/src/map/areaSettings/index.jsx | 108 +++++
zy-acs-flow/src/map/MapPage.jsx | 16
7 files changed, 1,091 insertions(+), 1 deletions(-)
diff --git a/zy-acs-flow/src/i18n/en.js b/zy-acs-flow/src/i18n/en.js
index bff65a7..3aa3872 100644
--- a/zy-acs-flow/src/i18n/en.js
+++ b/zy-acs-flow/src/i18n/en.js
@@ -637,6 +637,7 @@
direction: 'DIRECTION',
agv: 'AGV',
point: 'POINT',
+ area: 'AREA',
},
action: {
startup: 'Startup',
diff --git a/zy-acs-flow/src/i18n/zh.js b/zy-acs-flow/src/i18n/zh.js
index 575200b..87f6658 100644
--- a/zy-acs-flow/src/i18n/zh.js
+++ b/zy-acs-flow/src/i18n/zh.js
@@ -637,6 +637,7 @@
direction: '鏂瑰悜',
agv: '杞﹁締',
point: '瀹氫綅鐐�',
+ area: '鍖哄煙',
},
action: {
startup: '鍚姩RCS',
diff --git a/zy-acs-flow/src/map/MapPage.jsx b/zy-acs-flow/src/map/MapPage.jsx
index 89aa0c6..1b6ef7e 100644
--- a/zy-acs-flow/src/map/MapPage.jsx
+++ b/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>
);
}
diff --git a/zy-acs-flow/src/map/areaSettings/ConfigSettings.jsx b/zy-acs-flow/src/map/areaSettings/ConfigSettings.jsx
new file mode 100644
index 0000000..2ac2917
--- /dev/null
+++ b/zy-acs-flow/src/map/areaSettings/ConfigSettings.jsx
@@ -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;
diff --git a/zy-acs-flow/src/map/areaSettings/CopyDrawer.jsx b/zy-acs-flow/src/map/areaSettings/CopyDrawer.jsx
new file mode 100644
index 0000000..19fc976
--- /dev/null
+++ b/zy-acs-flow/src/map/areaSettings/CopyDrawer.jsx
@@ -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;
\ No newline at end of file
diff --git a/zy-acs-flow/src/map/areaSettings/MapSettings.jsx b/zy-acs-flow/src/map/areaSettings/MapSettings.jsx
new file mode 100644
index 0000000..6bc16c2
--- /dev/null
+++ b/zy-acs-flow/src/map/areaSettings/MapSettings.jsx
@@ -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;
diff --git a/zy-acs-flow/src/map/areaSettings/index.jsx b/zy-acs-flow/src/map/areaSettings/index.jsx
new file mode 100644
index 0000000..39b0fd1
--- /dev/null
+++ b/zy-acs-flow/src/map/areaSettings/index.jsx
@@ -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;
\ No newline at end of file
--
Gitblit v1.9.1