Scott

mui开发总结 9 months ago

mui
20677个字符
共有187人围观

mui开发总结

隐藏节点用theme.breakpoints

import { useTheme } from '@mui/material';
const theme = useTheme()

<StyledTableCell align="center" 
 	sx={{
	    [theme.breakpoints.down('sm')]: {
	        display: 'none'
	    }
	}} 
>
# ID
</StyledTableCell>

增删改查等提交行为用LoadingButton

为提高用户体验并且避免重复操作行为LoadingButton是比较合适的

多一个loading的效果就意味着多一个state

  • isXXXCreateLoading
  • isXXXDeleteLoading
  • isXXXUpdateLoading
  • isXXXSearchLoading

builder case demo如下:

  • pending
  • fulfilled
  • rejected
 extraReducers: (builder) => {
        builder.addCase(getAllRolePermissionsByRoleId.pending, (state) => {
            state.isPermissionsLoading = true
        }).addCase(getAllRolePermissionsByRoleId.fulfilled, (state, action) => {
            state.isPermissionsLoading = false
            //参数可以在action中获得
            const resp = action.payload
            if (resp.code != 200) {
                return
            }
            //success

            state.permissions = resp.data

        }).addCase(getAllRolePermissionsByRoleId.rejected, (state) => {
            state.isPermissionsLoading = false
        })
    }

demo:

types

  • api所用的type建议用xxxForm xxxEnt命名
  • slice的initialState建议用xxxState命名

屏幕自适应用Grid

<Grid container spacing={2}>
  <Grid item xs={4} sm={3} md={2} >
    <Item>xs=8</Item>
  </Grid>
  <Grid item xs={4}>
    <Item>xs=4</Item>
  </Grid>
  <Grid item xs={4}>
    <Item>xs=4</Item>
  </Grid>
  <Grid item xs={8}>
    <Item>xs=8</Item>
  </Grid>
</Grid>
<Grid2 container rowSpacing={2} columnSpacing={2}>
    <Grid2 size={{ xs: 12, sm: 6, md: 3 }}>
        <TextField id="filled-basic" label="ID" variant="outlined" size="small"
            value={brand.id} disabled
            fullWidth
        />
    </Grid2>

    <Grid2 size={{ xs: 12, sm: 6, md: 3 }}>
    </Grid2>          
</Grid2>

IconButton

<IconButton onClick={() => navigate(`/admin/product/detail/${item.id}`)}>
    <InfoIcon color='primary' />
</IconButton>

LoadingButton

<Grid2 size={{ xs: 12, sm: 12, md: 12 }}>
    <LoadingButton variant="contained"
        loading={isSkUUpdating}
        color="primary"
        size='small'
        fullWidth
        onClick={updateMySku}>
        更新
    </LoadingButton>
</Grid2>

Select

const tax_options = [
    {
        value: "true",
        label: '是',
    },
    {
        value: "false",
        label: '否',
    },
];

const [isTaxIncluded, setIsTaxIncluded] = React.useState<boolean>(false);

const isTaxIncludedChange = (e: React.ChangeEvent<HTMLInputElement>) => {
        setIsTaxIncluded(e.target.value === "true" ? true : false)
}

<Grid2 size={{ xs: 12, sm: 6, md: 4 }}>
    <TextField
        id="outlined-select-currency"
        label="是否含税"
        select
        value={isTaxIncluded ? "true" : "false"}
        size="small"
        onChange={isTaxIncludedChange}
        fullWidth
    // helperText="Please select your currency"
    >
        {tax_options.map((option) => (
            <MenuItem key={option.value} value={option.value}>
                {option.label}
            </MenuItem>
        ))}
    </TextField>

</Grid2>

true false间的切换用Switch

表单

  • 单行表单
  • 多行表单
  • 实时验证
  • 不用验证

单行text:

const [sku, setSku] = React.useState<string>('');

const skuChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setSku(e.target.value)
}


<Grid2 size={{ xs: 12, sm: 6, md: 4 }}>
    <TextField id="outlined-basic" label="SKU编号" variant="outlined"
        size="small"
        value={sku}
        fullWidth
        onChange={skuChange}
    />
</Grid2>

多行text:

<Grid2 size={{ xs: 12, sm: 12, md: 12 }}>
    <section>
        <TextField
            id="outlined-select-for2"
            label="Canada Process"
            size="small"
            value={procssCan}
            onChange={procssCanChanged}
            multiline
            fullWidth
            minRows={5}
        />
    </section>
</Grid2>

number:

const [weight, setWeight] = React.useState<number>(0);

const weightChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setWeight(Number(e.target.value))
}

<Grid2 size={{ xs: 12, sm: 6, md: 4 }}>
    <TextField id="outlined-basic" label="重量(单位:kg, 每盒10支, 数量固定)" variant="outlined"
        size="small"
        value={weight}
        type='number'
        fullWidth
        onChange={weightChange}
    />
</Grid2>
import * as React from 'react';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Typography from '@mui/material/Typography';
import Modal from '@mui/material/Modal';
import { blue } from '@mui/material/colors';
import ClearIcon from '@mui/icons-material/Clear';
import { Stack } from '@mui/system';

const style = {
    position: 'fixed',
    top: "40px",
    left: "40px",
    bottom: "40px",
    right: "40px",
    bgcolor: 'background.paper',
    borderRadius: '8px',
    boxShadow: 24,
    p: 4,
};

export default function MyModal() {
    const [open, setOpen] = React.useState(false);
    const handleOpen = () => setOpen(true);
    const handleClose = () => setOpen(false);

    const confirm = () => {
        setOpen(false);
    }

    const cancel = () => {
        setOpen(false);
    }

    return (
        <div>
            <Button onClick={handleOpen}>Open modal</Button>
            <Modal
                open={open}
                onClose={handleClose}
                aria-labelledby="modal-modal-title"
                aria-describedby="modal-modal-description"
                sx={{ backgroundColor: blue[200], zIndex: 2001, }}
            >
                <Box sx={style}>
                    <Box sx={{ height: '80px', position: 'fixed', top: '70px', left: "70px", right: '70px' }}>
                        <Stack direction={'row'} justifyContent={'space-between'} sx={{ width: '100%' }}>
                            <Typography id="modal-modal-title" variant="h6" component="h2">
                                这是个标题
                            </Typography>

                            <ClearIcon onClick={handleClose}
                                sx={{
                                    '&:hover': {
                                        cursor: 'pointer'
                                    }
                                }}
                            />

                        </Stack>
                    </Box>

                    <Box sx={{
                        position: 'fixed',
                        top: "110px",
                        left: '70px',
                        right: '70px',
                        bottom: '110px',
                        overflowY: "auto",
                    }}>
                        <Typography id="modal-modal-description" sx={{ mt: 2 }}>
                            We’ve released final files to vendor and the attached PDF is for your record:
                        </Typography>

                    </Box>

                    <Box sx={{ height: '80px', position: 'fixed', bottom: '40px', right: '80px' }}>
                        <Stack direction={'row'} gap={2} sx={{ my: 3 }}>
                            <Button variant='outlined' size='small'
                                onClick={cancel}
                            >取消</Button>
                            <Button variant='contained' size='small'
                                onClick={confirm}
                            >确认</Button>
                        </Stack>
                    </Box>

                </Box>
            </Modal>
        </div>
    );
}

basic menu:

import * as React from 'react';
import Button from '@mui/material/Button';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';

export default function Test1() {
    const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
    const open = Boolean(anchorEl);
    const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
        setAnchorEl(event.currentTarget);
    };
    const handleClose = () => {
        setAnchorEl(null);
    };

    return (
        <div>
            <Button
                id="basic-button"
                aria-controls={open ? 'basic-menu' : undefined}
                aria-haspopup="true"
                aria-expanded={open ? 'true' : undefined}
                onClick={handleClick}
            >
                DropDownMenu
            </Button>
            <Menu
                id="basic-menu"
                anchorEl={anchorEl}
                open={open}
                onClose={handleClose}
                MenuListProps={{
                    'aria-labelledby': 'basic-button',
                }}
            >
                <MenuItem onClick={handleClose}>Profile</MenuItem>
                <MenuItem onClick={handleClose}>My account</MenuItem>
                <MenuItem onClick={handleClose}>Logout</MenuItem>
            </Menu>
        </div>
    );
}

icon menu:

import * as React from 'react';
import Divider from '@mui/material/Divider';
import Paper from '@mui/material/Paper';
import MenuList from '@mui/material/MenuList';
import MenuItem from '@mui/material/MenuItem';
import ListItemText from '@mui/material/ListItemText';
import ListItemIcon from '@mui/material/ListItemIcon';
import Typography from '@mui/material/Typography';
import ContentCut from '@mui/icons-material/ContentCut';
import ContentCopy from '@mui/icons-material/ContentCopy';
import ContentPaste from '@mui/icons-material/ContentPaste';
import Cloud from '@mui/icons-material/Cloud';
import Button from '@mui/material/Button';
import Menu from '@mui/material/Menu';
import { Box } from '@mui/material';

export default function Test1() {
    const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
    const open = Boolean(anchorEl);
    const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
        setAnchorEl(event.currentTarget);
    };
    const handleClose = () => {
        setAnchorEl(null);
    };

    return (
        <div>
            <Button
                id="basic-button"
                aria-controls={open ? 'basic-menu' : undefined}
                aria-haspopup="true"
                aria-expanded={open ? 'true' : undefined}
                onClick={handleClick}
            >
                DropDownMenu
            </Button>
            <Menu
                id="basic-menu"
                anchorEl={anchorEl}
                open={open}
                onClose={handleClose}
                MenuListProps={{
                    'aria-labelledby': 'basic-button',
                }}
            >
                <Box sx={{ width: 320, maxWidth: '100%' }}>
                    <MenuList>
                        <MenuItem onClick={handleClose}>
                            <ListItemIcon>
                                <ContentCut fontSize="small" />
                            </ListItemIcon>
                            <ListItemText>Cut</ListItemText>
                            <Typography variant="body2" sx={{ color: 'text.secondary' }}>
                                ⌘X
                            </Typography>
                        </MenuItem>
                        <MenuItem onClick={handleClose}>
                            <ListItemIcon>
                                <ContentCopy fontSize="small" />
                            </ListItemIcon>
                            <ListItemText>Copy</ListItemText>
                            <Typography variant="body2" sx={{ color: 'text.secondary' }}>
                                ⌘C
                            </Typography>
                        </MenuItem>
                        <MenuItem onClick={handleClose}>
                            <ListItemIcon>
                                <ContentPaste fontSize="small" />
                            </ListItemIcon>
                            <ListItemText>Paste</ListItemText>
                            <Typography variant="body2" sx={{ color: 'text.secondary' }}>
                                ⌘V
                            </Typography>
                        </MenuItem>
                        <Divider />
                        <MenuItem onClick={handleClose}>
                            <ListItemIcon>
                                <Cloud fontSize="small" />
                            </ListItemIcon>
                            <ListItemText>Web Clipboard</ListItemText>
                        </MenuItem>
                    </MenuList>
                </Box>
            </Menu>

        </div>
    );
}

dense menu:

对于具有长列表和长文本的菜单,可以使用 dense 属性来减少padding和文字字体大小。

import * as React from 'react';
import Divider from '@mui/material/Divider';
import MenuList from '@mui/material/MenuList';
import MenuItem from '@mui/material/MenuItem';
import ListItemText from '@mui/material/ListItemText';
import ListItemIcon from '@mui/material/ListItemIcon';
import Button from '@mui/material/Button';
import Menu from '@mui/material/Menu';
import { Box } from '@mui/material';
import Check from '@mui/icons-material/Check';

export default function Test1() {
    const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
    const open = Boolean(anchorEl);
    const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
        setAnchorEl(event.currentTarget);
    };
    const handleClose = () => {
        setAnchorEl(null);
    };

    return (
        <div>
            <Button
                id="basic-button"
                aria-controls={open ? 'basic-menu' : undefined}
                aria-haspopup="true"
                aria-expanded={open ? 'true' : undefined}
                onClick={handleClick}
            >
                DropDownMenu
            </Button>
            <Menu
                id="basic-menu"
                anchorEl={anchorEl}
                open={open}
                onClose={handleClose}
                MenuListProps={{
                    'aria-labelledby': 'basic-button',
                }}
            >
                <Box sx={{ width: 320, maxWidth: '100%' }}>
                    <MenuList dense>
                        <MenuItem onClick={handleClose}>
                            <ListItemText inset>Single</ListItemText>
                        </MenuItem>
                        <MenuItem onClick={handleClose}>
                            <ListItemText inset>1.15</ListItemText>
                        </MenuItem>
                        <MenuItem onClick={handleClose}>
                            <ListItemText inset>Double</ListItemText>
                        </MenuItem>
                        <MenuItem onClick={handleClose}>
                            <ListItemIcon>
                                <Check />
                            </ListItemIcon>
                            Custom: 1.2
                        </MenuItem>
                        <Divider />
                        <MenuItem onClick={handleClose}>
                            <ListItemText>Add space before paragraph</ListItemText>
                        </MenuItem>
                        <MenuItem onClick={handleClose}>
                            <ListItemText>Add space after paragraph</ListItemText>
                        </MenuItem>
                        <Divider />
                        <MenuItem onClick={handleClose}>
                            <ListItemText>Custom spacing...</ListItemText>
                        </MenuItem>
                    </MenuList>
                </Box>
            </Menu>

        </div>
    );
}

positioned menu:

import * as React from 'react';
import Button from '@mui/material/Button';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';

export default function Test1() {
    const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
    const open = Boolean(anchorEl);
    const handleClick = (event: React.MouseEvent<HTMLElement>) => {
        setAnchorEl(event.currentTarget);
    };
    const handleClose = () => {
        setAnchorEl(null);
    };

    return (
        <div>
            <Button
                id="demo-positioned-button"
                aria-controls={open ? 'demo-positioned-menu' : undefined}
                aria-haspopup="true"
                aria-expanded={open ? 'true' : undefined}
                onClick={handleClick}
            >
                Dashboard
            </Button>
            <Menu
                id="demo-positioned-menu"
                aria-labelledby="demo-positioned-button"
                anchorEl={anchorEl}
                open={open}
                onClose={handleClose}
                anchorOrigin={{
                    vertical: 'top',
                    horizontal: 'right',
                }}
                transformOrigin={{
                    vertical: 'top',
                    horizontal: 'left',
                }}
            >
                <MenuItem onClick={handleClose}>Profile</MenuItem>
                <MenuItem onClick={handleClose}>My account</MenuItem>
                <MenuItem onClick={handleClose}>Logout</MenuItem>
            </Menu>
        </div>
    );
}

Limitations:

有时候<Typography/>文字太长,可以加noWrap属性

import * as React from 'react';
import Button from '@mui/material/Button';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import MenuList from '@mui/material/MenuList';
import ListItemIcon from '@mui/material/ListItemIcon';
import Typography from '@mui/material/Typography';
import DraftsIcon from '@mui/icons-material/Drafts';
import SendIcon from '@mui/icons-material/Send';
import PriorityHighIcon from '@mui/icons-material/PriorityHigh';
import { Box } from '@mui/material';

export default function Test1() {
    const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
    const open = Boolean(anchorEl);
    const handleClick = (event: React.MouseEvent<HTMLElement>) => {
        setAnchorEl(event.currentTarget);
    };
    const handleClose = () => {
        setAnchorEl(null);
    };

    return (
        <div>
            <Button
                id="demo-positioned-button"
                aria-controls={open ? 'demo-positioned-menu' : undefined}
                aria-haspopup="true"
                aria-expanded={open ? 'true' : undefined}
                onClick={handleClick}
            >
                Dashboard
            </Button>
            <Menu
                id="demo-positioned-menu"
                aria-labelledby="demo-positioned-button"
                anchorEl={anchorEl}
                open={open}
                onClose={handleClose}
                anchorOrigin={{
                    vertical: 'top',
                    horizontal: 'left',
                }}
                transformOrigin={{
                    vertical: 'top',
                    horizontal: 'left',
                }}
            >
                <Box sx={{ width: '250px' }}>
                    <MenuList>
                        <MenuItem>
                            <ListItemIcon>
                                <SendIcon fontSize="small" />
                            </ListItemIcon>
                            <Typography variant="inherit">A short message</Typography>
                        </MenuItem>
                        <MenuItem>
                            <ListItemIcon>
                                <PriorityHighIcon fontSize="small" />
                            </ListItemIcon>
                            <Typography variant="inherit">A very long text that overflows</Typography>
                        </MenuItem>
                        <MenuItem>
                            <ListItemIcon>
                                <DraftsIcon fontSize="small" />
                            </ListItemIcon>
                            <Typography variant="inherit" noWrap>
                                A very long text that overflows
                            </Typography>
                        </MenuItem>
                    </MenuList>
                </Box>
            </Menu>
        </div>
    );
}

动画transitions

https://mui.com/material-ui/transitions/