| | |
| | | // update a list of records based on an array of ids and a common patch |
| | | updateMany: async (resource, params) => { |
| | | console.log("updateMany", resource, params); |
| | | |
| | | return Promise.resolve(); |
| | | const res = await request.post( |
| | | resource + '/update/many' |
| | | , params.ids.map(id => ({ id, ...params.data })) |
| | | ); |
| | | const { code, msg, data } = res.data; |
| | | if (code === 200) { |
| | | return Promise.resolve({ |
| | | data: data, |
| | | }); |
| | | } |
| | | return Promise.reject(new Error(msg)); |
| | | }, |
| | | |
| | | // delete a record by id |
| | |
| | | close: '关闭', |
| | | open_menu: 'Open menu', |
| | | close_menu: 'Close menu', |
| | | update: 'Update', |
| | | update: '修改', |
| | | move_up: 'Move up', |
| | | move_down: 'Move down', |
| | | open: 'Open', |
| | |
| | | const newRotation = this.mapContainer.rotation + value; |
| | | |
| | | const rotationDegrees = (newRotation * 180 / Math.PI) % 360; |
| | | this.rotationText.text = `{ rotation: ${rotationDegrees.toFixed(1)}° }`; |
| | | this.rotationText.text = `{ ROTATION: ${rotationDegrees.toFixed(1)}° }`; |
| | | |
| | | new TWEEDLE.Tween(this.mapContainer) |
| | | .to({ rotation: newRotation }, 200) |
| | |
| | | } |
| | | |
| | | showCoordinates = () => { |
| | | this.coordinatesText = new PIXI.Text('{ x: 0, y: 0 }', { |
| | | this.coordinatesText = new PIXI.Text('{ X: 0, Y: 0 }', { |
| | | fill: this.themeMode === 'dark' ? 0xdddddd : 0x333333, |
| | | fontSize: 13, |
| | | fontFamily: 'Microsoft YaHei', |
| | |
| | | this.coordinatesText.position.set(10, 10); |
| | | this.app.stage.addChild(this.coordinatesText); |
| | | |
| | | this.rotationText = new PIXI.Text('Rotation: 0°', { |
| | | this.rotationText = new PIXI.Text('ROTATION: 0°', { |
| | | fill: this.themeMode === 'dark' ? 0xdddddd : 0x333333, |
| | | fontSize: 13, |
| | | fontFamily: 'Microsoft YaHei', |
| | |
| | | |
| | | this.app.stage.on('pointermove', (event) => { |
| | | const worldPos = event.data.getLocalPosition(this.mapContainer); |
| | | this.coordinatesText.text = `{ x: ${worldPos.x.toFixed(2)}, y: ${worldPos.y.toFixed(2)} }`; |
| | | this.coordinatesText.text = `{ X: ${worldPos.x.toFixed(2)}, Y: ${worldPos.y.toFixed(2)} }`; |
| | | }); |
| | | } |
| | | |
| | |
| | | // src/components/BulkUpdateButton.js |
| | | import React, { useState } from 'react'; |
| | | import { useUpdateMany, useRefresh, useNotify, useUnselectAll, Button as RaButton } from 'react-admin'; |
| | | import { Dialog, DialogActions, DialogContent, DialogTitle, TextField, Select, MenuItem } from '@mui/material'; |
| | | import { |
| | | useUpdateMany, |
| | | useRefresh, |
| | | useNotify, |
| | | useListContext, |
| | | useUnselectAll, |
| | | Button as RaButton, |
| | | Form, |
| | | ReferenceInput, |
| | | AutocompleteInput, |
| | | NumberInput, |
| | | TextInput, |
| | | useTranslate, |
| | | } from 'react-admin'; |
| | | import { Dialog, DialogActions, DialogContent, DialogTitle, Grid, Stack, Divider } from '@mui/material'; |
| | | import UpdateIcon from '@mui/icons-material/Update'; |
| | | import MemoInput from "../components/MemoInput"; |
| | | import ContentSave from '@mui/icons-material/Save'; |
| | | import CloseIcon from '@mui/icons-material/Close'; |
| | | |
| | | const BulkUpdateButton = ({ selectedIds, resource, label }) => { |
| | | console.log('BulkUpdateButton rendered with selectedIds:', selectedIds); |
| | | |
| | | const BulkUpdateButton = ({ resource, label = 'ra.action.update', ...rest }) => { |
| | | const [open, setOpen] = useState(false); |
| | | const [formData, setFormData] = useState({}); |
| | | |
| | | // Hooks 调用顺序调整 |
| | | const refresh = useRefresh(); |
| | | const notify = useNotify(); |
| | | const translate = useTranslate(); |
| | | const unselectAll = useUnselectAll(resource); |
| | | const { selectedIds, onUnselectItems } = useListContext(); |
| | | |
| | | const [updateMany, { loading }] = useUpdateMany( |
| | | resource, |
| | |
| | | onSuccess: () => { |
| | | setOpen(false); |
| | | refresh(); |
| | | notify('批量更新成功', { type: 'success' }); |
| | | notify('common.response.success', { type: 'info' }); |
| | | unselectAll(); |
| | | }, |
| | | onError: (error) => { |
| | | notify(`批量更新失败: ${error.message}`, { type: 'warning' }); |
| | | notify(error.msg || 'common.response.fail', { type: 'error' }); |
| | | } |
| | | } |
| | | ); |
| | |
| | | }; |
| | | |
| | | const handleSubmit = () => { |
| | | console.log(formData); |
| | | updateMany(); |
| | | }; |
| | | |
| | | return ( |
| | | <> |
| | | <RaButton onClick={handleOpen} startIcon={<UpdateIcon />} label={label || '批量修改'}> |
| | | {/* 如果使用 React-Admin 的 Button,可以省略子节点 */} |
| | | </RaButton> |
| | | <RaButton onClick={handleOpen} startIcon={<UpdateIcon />} label={label} /> |
| | | <Dialog open={open} onClose={handleClose}> |
| | | <DialogTitle>批量修改</DialogTitle> |
| | | <DialogTitle>{translate('ra.action.update')}</DialogTitle> |
| | | <DialogContent> |
| | | {/* 使用 Select 组件限制状态值 */} |
| | | <Select |
| | | margin="dense" |
| | | name="status" |
| | | label="状态" |
| | | fullWidth |
| | | variant="standard" |
| | | onChange={handleChange} |
| | | defaultValue="" |
| | | > |
| | | <MenuItem value=""> |
| | | <em>无</em> |
| | | </MenuItem> |
| | | <MenuItem value="1">启用</MenuItem> |
| | | <MenuItem value="0">禁用</MenuItem> |
| | | </Select> |
| | | <TextField |
| | | margin="dense" |
| | | name="memo" |
| | | label="备注" |
| | | type="text" |
| | | fullWidth |
| | | variant="standard" |
| | | onChange={handleChange} |
| | | /> |
| | | <Form> |
| | | <Grid container rowSpacing={2} columnSpacing={2}> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <ReferenceInput |
| | | source="code" |
| | | reference="code" |
| | | > |
| | | <AutocompleteInput |
| | | label="table.field.loc.code" |
| | | optionText="data" |
| | | filterToQuery={(val) => ({ data: val })} |
| | | /> |
| | | </ReferenceInput> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <ReferenceInput |
| | | source="locSts" |
| | | reference="locSts" |
| | | > |
| | | <AutocompleteInput |
| | | label="table.field.loc.locSts" |
| | | optionText="name" |
| | | filterToQuery={(val) => ({ name: val })} |
| | | /> |
| | | </ReferenceInput> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <NumberInput |
| | | label="table.field.loc.offset" |
| | | source="offset" |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={12} display="flex" gap={1}> |
| | | <Stack direction="column" spacing={1} width={'100%'}> |
| | | <MemoInput /> |
| | | </Stack> |
| | | </Grid> |
| | | </Grid> |
| | | </Form> |
| | | </DialogContent> |
| | | <DialogActions> |
| | | <RaButton onClick={handleClose} color="primary"> |
| | | 取消 |
| | | <Divider sx={{ mb: 1 }} /> |
| | | <DialogActions sx={{ mb: 1 }}> |
| | | <RaButton |
| | | onClick={handleClose} |
| | | variant="contained" |
| | | color="inherit" |
| | | size='large' |
| | | label='ra.action.cancel' |
| | | sx={{ mr: 1 }} |
| | | > |
| | | <CloseIcon /> |
| | | </RaButton> |
| | | <RaButton onClick={handleSubmit} color="primary" disabled={loading}> |
| | | 确认 |
| | | <RaButton |
| | | onClick={handleSubmit} |
| | | variant="contained" |
| | | color="primary" |
| | | size='large' |
| | | label='ra.action.save' |
| | | disabled={loading} |
| | | sx={{ mr: 1 }} |
| | | > |
| | | <ContentSave /> |
| | | </RaButton> |
| | | </DialogActions> |
| | | </Dialog> |
| | |
| | | ] |
| | | |
| | | const LocBulkActionButtons = (props) => { |
| | | const translate = useTranslate(); |
| | | |
| | | return ( |
| | | <> |
| | | <BulkUpdateButton |
| | | {...props} |
| | | resource="loc" |
| | | label={translate('ra.action.update')} |
| | | /> |
| | | <BulkDeleteButton mutationMode={OPERATE_MODE} /> |
| | | </> |