Material-UI 表面组件 #
概述 #
表面组件是用于承载内容的容器,提供视觉层次和分组效果。
text
表面组件体系
│
├── Paper 纸张容器
├── Card 卡片
├── Accordion 手风琴
├── Collapse 折叠
└── Container 容器
Paper 纸张容器 #
基础用法 #
jsx
import { Paper, Box, Typography } from '@mui/material';
<Paper elevation={3}>
<Box sx={{ p: 2 }}>
<Typography>这是一个 Paper 组件</Typography>
</Box>
</Paper>
elevation 阴影级别 #
jsx
import { Stack } from '@mui/material';
<Stack spacing={2} direction="row" sx={{ flexWrap: 'wrap' }}>
{[0, 1, 2, 3, 4, 6, 8, 12, 16, 24].map((elevation) => (
<Paper key={elevation} elevation={elevation} sx={{ p: 2, width: 100, height: 100, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<Typography>elevation={elevation}</Typography>
</Paper>
))}
</Stack>
variant 变体 #
jsx
<Stack spacing={2}>
<Paper variant="elevation" sx={{ p: 2 }}>
elevation 变体(默认)
</Paper>
<Paper variant="outlined" sx={{ p: 2 }}>
outlined 变体
</Paper>
</Stack>
square 圆角 #
jsx
<Stack direction="row" spacing={2}>
<Paper sx={{ p: 2 }}>圆角 Paper</Paper>
<Paper square sx={{ p: 2 }}>
无圆角 Paper
</Paper>
</Stack>
圆形 Paper #
jsx
<Paper
sx={{
width: 100,
height: 100,
borderRadius: '50%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
圆形
</Paper>
实际应用 #
jsx
function PaperCard() {
return (
<Paper
elevation={3}
sx={{
p: 2,
margin: 'auto',
maxWidth: 500,
flexGrow: 1,
backgroundColor: (theme) => theme.palette.mode === 'dark' ? '#1A2027' : '#fff',
}}
>
<Grid container spacing={2}>
<Grid item>
<ButtonBase sx={{ width: 128, height: 128 }}>
<Img alt="complex" src="/static/images/grid/complex.jpg" />
</ButtonBase>
</Grid>
<Grid item xs={12} sm container>
<Grid item xs container direction="column" spacing={2}>
<Grid item xs>
<Typography gutterBottom variant="subtitle1" component="div">
Standard license
</Typography>
<Typography variant="body2" gutterBottom>
Full resolution 1920x1080 • JPEG
</Typography>
<Typography variant="body2" color="text.secondary">
ID: 1030114
</Typography>
</Grid>
<Grid item>
<Typography sx={{ cursor: 'pointer' }} variant="body2">
Remove
</Typography>
</Grid>
</Grid>
<Grid item>
<Typography variant="subtitle1" component="div">
$19.00
</Typography>
</Grid>
</Grid>
</Grid>
</Paper>
);
}
Card 卡片 #
基础卡片 #
jsx
import { Card, CardContent, Typography } from '@mui/material';
<Card sx={{ minWidth: 275 }}>
<CardContent>
<Typography sx={{ fontSize: 14 }} color="text.secondary" gutterBottom>
单词卡片
</Typography>
<Typography variant="h5" component="div">
be•nev•o•lent
</Typography>
<Typography sx={{ mb: 1.5 }} color="text.secondary">
形容词
</Typography>
<Typography variant="body2">
善良的,仁慈的
<br />
{'"她是一个善良的人"'}
</Typography>
</CardContent>
</Card>
Card 组件结构 #
text
Card 组件结构
│
├── CardHeader 卡片头部
│ ├── avatar 头像
│ ├── title 标题
│ ├── subheader 副标题
│ └── action 操作按钮
│
├── CardMedia 媒体内容
│
├── CardContent 卡片内容
│
├── CardActions 卡片操作
│
└── Collapse 可折叠内容
带媒体卡片 #
jsx
import { CardMedia, CardActions, Button } from '@mui/material';
<Card sx={{ maxWidth: 345 }}>
<CardMedia
sx={{ height: 140 }}
image="/static/images/cards/contemplative-reptile.jpg"
title="green iguana"
/>
<CardContent>
<Typography gutterBottom variant="h5" component="div">
蜥蜴
</Typography>
<Typography variant="body2" color="text.secondary">
蜥蜴是一类广泛分布的爬行动物,全球有超过6000种,分布于各大洲(南极洲除外)。
</Typography>
</CardContent>
<CardActions>
<Button size="small">分享</Button>
<Button size="small">了解更多</Button>
</CardActions>
</Card>
带头部卡片 #
jsx
import { CardHeader, Avatar, IconButton } from '@mui/material';
import { MoreVert as MoreVertIcon } from '@mui/icons-material';
<Card sx={{ maxWidth: 345 }}>
<CardHeader
avatar={
<Avatar sx={{ bgcolor: 'red' }} aria-label="recipe">
R
</Avatar>
}
action={
<IconButton aria-label="settings">
<MoreVertIcon />
</IconButton>
}
title="Shrimp and Chorizo Paella"
subheader="September 14, 2016"
/>
<CardMedia
sx={{ height: 194 }}
image="/static/images/cards/paella.jpg"
title="Paella dish"
/>
<CardContent>
<Typography variant="body2" color="text.secondary">
这是一道令人印象深刻的派对菜肴。
</Typography>
</CardContent>
</Card>
交互式卡片 #
jsx
import { Collapse, IconButton, CardActionArea } from '@mui/material';
import { ExpandMore as ExpandMoreIcon, Favorite as FavoriteIcon, Share as ShareIcon } from '@mui/icons-material';
function RecipeReviewCard() {
const [expanded, setExpanded] = useState(false);
return (
<Card sx={{ maxWidth: 345 }}>
<CardHeader
avatar={<Avatar sx={{ bgcolor: 'red' }}>R</Avatar>}
action={
<IconButton aria-label="settings">
<MoreVertIcon />
</IconButton>
}
title="Shrimp and Chorizo Paella"
subheader="September 14, 2016"
/>
<CardMedia component="img" height="194" image="/static/images/cards/paella.jpg" alt="Paella dish" />
<CardContent>
<Typography variant="body2" color="text.secondary">
这是一道令人印象深刻的派对菜肴。
</Typography>
</CardContent>
<CardActions disableSpacing>
<IconButton aria-label="add to favorites">
<FavoriteIcon />
</IconButton>
<IconButton aria-label="share">
<ShareIcon />
</IconButton>
<IconButton
onClick={() => setExpanded(!expanded)}
aria-expanded={expanded}
aria-label="show more"
sx={{ transform: !expanded ? 'rotate(0deg)' : 'rotate(180deg)', marginLeft: 'auto', transition: (theme) => theme.transitions.create('transform', { duration: theme.transitions.duration.shortest }) }}
>
<ExpandMoreIcon />
</IconButton>
</CardActions>
<Collapse in={expanded} timeout="auto" unmountOnExit>
<CardContent>
<Typography paragraph>方法:</Typography>
<Typography paragraph>热油,加入洋葱和辣椒...</Typography>
</CardContent>
</Collapse>
</Card>
);
}
可点击卡片 #
jsx
<Card sx={{ maxWidth: 345 }}>
<CardActionArea>
<CardMedia component="img" height="140" image="/static/images/cards/contemplative-reptile.jpg" alt="green iguana" />
<CardContent>
<Typography gutterBottom variant="h5" component="div">
蜥蜴
</Typography>
<Typography variant="body2" color="text.secondary">
蜥蜴是一类广泛分布的爬行动物。
</Typography>
</CardContent>
</CardActionArea>
</Card>
卡片网格 #
jsx
function CardsGrid() {
const cards = [1, 2, 3, 4, 5, 6];
return (
<Container sx={{ py: 8 }} maxWidth="md">
<Grid container spacing={4}>
{cards.map((card) => (
<Grid item key={card} xs={12} sm={6} md={4}>
<Card sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
<CardMedia component="img" sx={{ pt: '56.25%' }} image="https://source.unsplash.com/random" alt="random" />
<CardContent sx={{ flexGrow: 1 }}>
<Typography gutterBottom variant="h5" component="h2">
Heading
</Typography>
<Typography>
This is a media card. You can use this section to describe the content.
</Typography>
</CardContent>
<CardActions>
<Button size="small">View</Button>
<Button size="small">Edit</Button>
</CardActions>
</Card>
</Grid>
))}
</Grid>
</Container>
);
}
卡片变体 #
jsx
<Stack spacing={2}>
<Card variant="elevation" sx={{ p: 2 }}>
elevation 变体
</Card>
<Card variant="outlined" sx={{ p: 2 }}>
outlined 变体
</Card>
</Stack>
Accordion 手风琴 #
基础用法 #
jsx
import { Accordion, AccordionSummary, AccordionDetails, Typography } from '@mui/material';
import { ExpandMore as ExpandMoreIcon } from '@mui/icons-material';
<Stack spacing={1}>
<Accordion>
<AccordionSummary expandIcon={<ExpandMoreIcon />} aria-controls="panel1a-content" id="panel1a-header">
<Typography>手风琴 1</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortis eget.
</Typography>
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary expandIcon={<ExpandMoreIcon />} aria-controls="panel2a-content" id="panel2a-header">
<Typography>手风琴 2</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortis eget.
</Typography>
</AccordionDetails>
</Accordion>
</Stack>
默认展开 #
jsx
<Accordion defaultExpanded>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography>默认展开</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>这个手风琴默认是展开状态</Typography>
</AccordionDetails>
</Accordion>
禁用手风琴 #
jsx
<Accordion disabled>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography>禁用手风琴</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>这个手风琴被禁用了</Typography>
</AccordionDetails>
</Accordion>
受控手风琴 #
jsx
function ControlledAccordions() {
const [expanded, setExpanded] = useState(false);
const handleChange = (panel) => (event, isExpanded) => {
setExpanded(isExpanded ? panel : false);
};
return (
<div>
<Accordion expanded={expanded === 'panel1'} onChange={handleChange('panel1')}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography>手风琴 1</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>内容 1</Typography>
</AccordionDetails>
</Accordion>
<Accordion expanded={expanded === 'panel2'} onChange={handleChange('panel2')}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography>手风琴 2</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>内容 2</Typography>
</AccordionDetails>
</Accordion>
</div>
);
}
带图标 #
jsx
import { Settings as SettingsIcon, People as PeopleIcon, BarChart as BarChartIcon } from '@mui/icons-material';
<Accordion>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<SettingsIcon sx={{ mr: 1 }} />
<Typography>设置</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>设置内容</Typography>
</AccordionDetails>
</Accordion>
带操作按钮 #
jsx
<Accordion>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography sx={{ width: '33%', flexShrink: 0 }}>General settings</Typography>
<Typography sx={{ color: 'text.secondary' }}>I am an accordion</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>
Nulla facilisi. Phasellus sollicitudin nulla et quam mattis feugiat.
</Typography>
</AccordionDetails>
<AccordionActions>
<Button>取消</Button>
<Button>保存</Button>
</AccordionActions>
</Accordion>
Collapse 折叠 #
基础用法 #
jsx
import { Collapse, Button, Paper } from '@mui/material';
function SimpleCollapse() {
const [checked, setChecked] = useState(false);
return (
<Box>
<Button onClick={() => setChecked(!checked)}>
{checked ? '隐藏' : '显示'}
</Button>
<Collapse in={checked}>
<Paper sx={{ p: 2, mt: 1 }}>
<Typography>折叠内容</Typography>
</Paper>
</Collapse>
</Box>
);
}
带过渡效果 #
jsx
<Collapse in={checked} timeout={500}>
<Paper sx={{ p: 2 }}>
<Typography>带过渡效果的折叠内容</Typography>
</Paper>
</Collapse>
折叠高度 #
jsx
<Collapse in={checked} collapsedSize={40}>
<Paper sx={{ p: 2 }}>
<Typography>
这是一段很长的内容,当折叠时会显示部分内容。
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</Typography>
</Paper>
</Collapse>
嵌套折叠 #
jsx
function NestedCollapse() {
const [outerOpen, setOuterOpen] = useState(false);
const [innerOpen, setInnerOpen] = useState(false);
return (
<Box>
<Button onClick={() => setOuterOpen(!outerOpen)}>
外层折叠
</Button>
<Collapse in={outerOpen}>
<Paper sx={{ p: 2, m: 1 }}>
<Typography>外层内容</Typography>
<Button onClick={() => setInnerOpen(!innerOpen)}>
内层折叠
</Button>
<Collapse in={innerOpen}>
<Paper sx={{ p: 2, m: 1 }}>
<Typography>内层内容</Typography>
</Paper>
</Collapse>
</Paper>
</Collapse>
</Box>
);
}
实战示例:FAQ 页面 #
jsx
function FAQPage() {
const [expanded, setExpanded] = useState(false);
const faqs = [
{
question: '如何开始使用?',
answer: '首先安装依赖,然后按照文档进行配置...',
},
{
question: '支持哪些浏览器?',
answer: '支持所有现代浏览器,包括 Chrome、Firefox、Safari、Edge...',
},
{
question: '如何自定义主题?',
answer: '使用 createTheme 创建自定义主题,然后通过 ThemeProvider 应用...',
},
];
const handleChange = (panel) => (event, isExpanded) => {
setExpanded(isExpanded ? panel : false);
};
return (
<Container maxWidth="md" sx={{ py: 4 }}>
<Typography variant="h4" gutterBottom>
常见问题
</Typography>
<Stack spacing={2}>
{faqs.map((faq, index) => (
<Accordion
key={index}
expanded={expanded === `panel${index}`}
onChange={handleChange(`panel${index}`)}
>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography variant="h6">{faq.question}</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>{faq.answer}</Typography>
</AccordionDetails>
</Accordion>
))}
</Stack>
</Container>
);
}
实战示例:产品展示卡片 #
jsx
function ProductCard({ product }) {
const [expanded, setExpanded] = useState(false);
return (
<Card sx={{ maxWidth: 345 }}>
<CardMedia
component="img"
height="200"
image={product.image}
alt={product.name}
/>
<CardContent>
<Typography gutterBottom variant="h5" component="div">
{product.name}
</Typography>
<Typography variant="h6" color="primary">
¥{product.price}
</Typography>
<Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}>
{product.description}
</Typography>
</CardContent>
<CardActions>
<Button size="small" color="primary">
加入购物车
</Button>
<Button size="small" onClick={() => setExpanded(!expanded)}>
{expanded ? '收起详情' : '查看详情'}
</Button>
</CardActions>
<Collapse in={expanded} timeout="auto" unmountOnExit>
<CardContent>
<Typography paragraph>产品详情:</Typography>
<Typography paragraph>{product.details}</Typography>
</CardContent>
</Collapse>
</Card>
);
}
下一步 #
继续学习 高级主题定制,深入了解 MUI 的主题系统!
最后更新:2026-03-28