| | |
| | | import React, { useEffect } from 'react'; |
| | | import React, { useEffect, useState } from 'react'; |
| | | import { useForm, Controller } from 'react-hook-form'; |
| | | import { |
| | | Box, |
| | |
| | | Divider, |
| | | } from '@mui/material'; |
| | | import MuiInput from '@mui/material/Input'; |
| | | import { styled } from '@mui/material/styles'; |
| | | 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, onSubmit } = 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, |
| | | 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, |
| | | // 设置其他字段的初始值 |
| | | 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.scale.x = data.scaleX; |
| | | sprite.scale.y = data.scaleY; |
| | | sprite.rotation = (data.rotation * Math.PI) / 180; |
| | | // 更新其他属性... |
| | | } |
| | | }; |
| | | |
| | | // 处理表单提交 |
| | | const onFormSubmit = (data) => { |
| | | updateSprite(data); |
| | | if (onSubmit) { |
| | |
| | | } |
| | | }; |
| | | |
| | | // 旋转滑块的刻度 |
| | | const rotationMarks = [ |
| | | { value: 0, label: '0°' }, |
| | | { value: 90, label: '90°' }, |
| | | { value: 180, label: '180°' }, |
| | | { value: 270, label: '270°' }, |
| | | { value: 360, label: '360°' }, |
| | | ]; |
| | | |
| | | 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> |
| | | <> |
| | | <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> |
| | | {/* 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)) { |
| | | {/* 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 }); |
| | | } |
| | | }} |
| | | 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 > |
| | | <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> |
| | | </Box> |
| | | </Grid> |
| | | </Grid> |
| | | |
| | | <Grid item xs={12}> |
| | | <Divider /> |
| | | </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 |
| | | {/* 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.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"> |
| | | {translate('page.map.settings.map.copy.execute')} |
| | | </Button> |
| | | <Button variant="text" color="primary"> |
| | | {translate('page.map.settings.map.copy.reverse')} |
| | | </Button> |
| | | </Stack> |
| | | </Grid> |
| | | 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> |
| | | <Grid item xs={12}> |
| | | <Divider /> |
| | | </Grid> |
| | | |
| | | {/* more */} |
| | | <Grid item xs={12}> |
| | | <Typography variant="inherit"> |
| | | {translate('page.map.settings.map.more.title')} |
| | | </Typography> |
| | | {/* 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> |
| | | <Grid item xs={12}> |
| | | <Stack direction="row" spacing={2}> |
| | | <Button variant="outlined" color="error"> |
| | | {translate('ra.action.delete')} |
| | | </Button> |
| | | </Stack> |
| | | </Grid> |
| | | </Grid> |
| | | </Box> |
| | | </Box > |
| | | |
| | | <CopyDrawer |
| | | open={copyVisible} |
| | | onCancel={() => { |
| | | setCopyVisible(false); |
| | | }} |
| | | width={width} |
| | | sprite={sprite} |
| | | value={{ |
| | | copyDirect: watch('copyDirection'), |
| | | copyCount: watch('copyCount'), |
| | | }} |
| | | handleOnCopy={() => { |
| | | |
| | | }} |
| | | setLastCopiedSprites={setLastCopiedSprites} |
| | | setSpriteSettings={setSpriteSettings} |
| | | /> |
| | | </> |
| | | ); |
| | | }; |
| | | |