mui开发总结 9 months ago
博客大纲
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>
modal
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>
);
}
dropdown menu
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
- 上一篇: RTK query