第七章 第五节:Ant Design 布局与容器组件篇 —— 深挖信息骨架设计理念

7.13. 布局与容器组件:构建信息骨架

摘要:在本章中,我们将学习如何使用 Ant Design 提供的容器与布局组件,将第一阶段学习的各种基础(原子化)信息有效地 组织和包裹 起来。这些组件是构建页面结构、划分信息层级的“骨架”,是从零散元素到完整页面的关键一步。

7.13.1. Card: 信息聚合的基础容器

Card (卡片) 是 Web UI 中最通用、最基础的内容容器。它的核心价值在于,将一组相关的信息(无论是文字、图片、列表还是图表)收纳在一个独立的、带有视觉边界的矩形区域内,从而为用户提供清晰、规整、易于聚焦的信息区块。

核心应用场景:

  • 仪表盘(Dashboard):每一个 KPI 指标、每一个图表,都可以用一个 Card 来承载。
  • 内容展示:文章摘要、商品信息、用户资料,都可以用 Card 来呈现。
  • 信息分组:在复杂的表单或设置页面中,使用 Card 将相关的配置项分组。

第一步:基础卡片结构

一个标准的 Card 由头部(title, extra)、内容(children)和底部(actions)三部分构成。此外,它还内置了加载状态,可以在数据获取期间显示优雅的骨架屏。

img

核心属性:

  • title: 卡片头部的标题
  • extra: 显示在卡片头部右上角的额外元素,通常是一个链接或操作按钮。
  • actions: 出现在卡片底部的操作按钮组,接收一个 React 节点数组。
  • loading: 布尔值,当为 true 时,卡片内容区会显示为加载中的骨架屏。
  • hoverable: 鼠标悬浮时显示阴影,提供可交互的视觉反馈。

文件路径: src/components/demos/BasicCardDemo.tsx (新建文件)

image-20250930085904173

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import React from 'react';
import { Card, Space } from 'antd';
import { EditOutlined, EllipsisOutlined, SettingOutlined } from '@ant-design/icons';

const BasicCardDemo: React.FC = () => (
<div className="p-8 bg-gray-100 rounded-lg shadow-inner">
<h3 className="text-xl font-bold mb-4 text-center">基础卡片结构</h3>
<Space>
{/* 1. 一个标准的卡片 */}
<Card
title="标准卡片"
extra={<a href="#">更多</a>}
style={{ width: 300 }}
hoverable
actions={[
<SettingOutlined key="setting" />,
<EditOutlined key="edit" />,
<EllipsisOutlined key="ellipsis" />,
]}
>
<p>这是卡片的内容区。</p>
<p>可以放入任意文本和组件。</p>
</Card>

{/* 2. 一个加载中的卡片 */}
<Card
title="加载中..."
extra={<a href="#">More</a>}
style={{ width: 300 }}
loading={true}
>
<p>这里的内容会被骨架屏替代。</p>
</Card>
</Space>
</div>
);

export default BasicCardDemo;

App.tsx 中使用此 Demo:

文件路径: src/App.tsx (修改文件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React from 'react';
import { ConfigProvider } from 'antd';
import zhCN from 'antd/locale/zh_CN';
import 'dayjs/locale/zh-cn';
import BasicCardDemo from './components/demos/BasicCardDemo';

const App: React.FC = () => {
return (
<ConfigProvider locale={zhCN}>
<div className="bg-gray-100 min-h-screen p-8 flex items-center justify-center">
<BasicCardDemo />
</div>
</ConfigProvider>
);
};

export default App;

第二步:丰富内容 - 封面与 Card.Meta

对于需要展示图片和结构化描述的场景(如文章、商品),Card 提供了 cover 属性和 Card.Meta 子组件来快速构建图文并茂的卡片。

核心属性/组件:

  • cover: 在卡片顶部(标题之下,内容之上)渲染一个封面图片或视频
  • Card.Meta: 一个用于生成标准元数据布局的辅助组件,它通常包含 avatar (头像)、title (标题) 和 description (描述) 三部分,是 cover 的绝佳搭档。

文件路径: src/components/demos/MetaCardDemo.tsx (新建文件)

image-20250930091143999

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import React from 'react';
import { Avatar, Card } from 'antd';
import { EditOutlined, EllipsisOutlined, SettingOutlined } from '@ant-design/icons';

const { Meta } = Card;

const MetaCardDemo: React.FC = () => (
<div className="p-8 bg-gray-100 rounded-lg shadow-inner">
<h3 className="text-xl font-bold mb-4 text-center">图文卡片 (Meta)</h3>
<Card
style={{ width: 300 }}
cover={
<img
alt="example"
src="https://gw.alipayobjects.com/zos/rmsportal/JiqGstEfoWAOHiTxclqi.png"
/>
}
actions={[
<SettingOutlined key="setting" />,
<EditOutlined key="edit" />,
<EllipsisOutlined key="ellipsis" />,
]}
>
<Meta
avatar={<Avatar src="https://api.dicebear.com/7.x/miniavs/svg?seed=1" />}
title="欧洲风情"
description="这是对卡片内容的详细描述,展示在标题下方。"
/>
</Card>
</div>
);

export default MetaCardDemo;

App.tsx 中切换到新 Demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { ConfigProvider } from 'antd';
import zhCN from 'antd/locale/zh_CN';
import MetaCardDemo from './components/demos/MetaCardDemo';


const App: React.FC = () => {
return (
<ConfigProvider locale={zhCN}>
<div className="bg-gray-100 min-h-screen p-8 flex items-center justify-center">
<MetaCardDemo />
</div>
</ConfigProvider>
);
};

export default App;

第三步:内部布局 - 网格与内联卡片

Card 不仅能作为外部容器,还提供了 Card.Gridtype="inner" 两种方式来组织其 内部 的内容。

1. Card.Grid (网格型内嵌卡片)
Card.Grid 允许您在卡片内部创建一块块 uniform 的网格。每个网格都像一个可点击的、没有边距的迷你卡片,非常适合展示一组同类的入口或项目。

2. type="inner" (内部卡片)
当您需要在一个 Card 内部,再嵌套一个带有标题和边框的、结构完整的子卡片时,可以为内部的 Card 添加 type="inner" 属性。它会以一种更紧凑、视觉上更协调的样式进行渲染。

文件路径: src/components/demos/LayoutCardDemo.tsx (新建文件)

image-20250930090932716

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import React from "react";
import { Card } from "antd";

const LayoutCardDemo: React.FC = () => {
const gridData = [
{ title: "总访问量", value: "1234" },
{ title: "新用户", value: "567" },
{ title: "订单数", value: "890" },
{ title: "销售额", value: "12.3K" },
{ title: "转化率", value: "3.2%" },
{ title: "活跃用户", value: "456" },
{ title: "增长率", value: "8.5%" },
{ title: "活跃用户", value: "456" },
];

return (
<div className="p-8 bg-gray-100 rounded-lg shadow-inner">
<h3 className="text-xl font-bold mb-4 text-center">内部布局</h3>
{/* 1. 网格型卡片 */}
<Card title="项目仪表盘" className="mb-4">
{gridData.map((item, index) => (
<Card.Grid key={index} style={{ width: "25%" }}>
<div className="text-center">
<p>{item.title}</p>
<p>{item.value}</p>
</div>
</Card.Grid>
))}
</Card>

{/* 2. 内部卡片 */}
<Card title="外部卡片">
<p>这里是外部卡片的内容。</p>
<Card type="inner" title="内部卡片标题" extra={<a href="#">更多</a>}>
这里是内部卡片的内容。
</Card>
<Card type="inner" title="另一个内部卡片" style={{ marginTop: 16 }}>
这里是另一个内部卡片的内容。
</Card>
</Card>
</div>
);
};

export default LayoutCardDemo;

App.tsx 中切换到新 Demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// import MetaCardDemo from './components/demos/MetaCardDemo';
import LayoutCardDemo from './components/demos/LayoutCardDemo';
// ... 其他 import ...

const App: React.FC = () => {
return (
<ConfigProvider locale={zhCN}>
<div className="bg-gray-100 min-h-screen p-8 flex items-center justify-center">
{/* <MetaCardDemo /> */}
<LayoutCardDemo />
</div>
</ConfigProvider>
);
};

通过这三个层层递进的示例,我们掌握了 Card 组件从外到内的全部核心用法。它既能作为承载万物的通用容器,又能通过 Meta, Grid 等子组件实现精细化的内部布局,是构建结构化页面的绝对主力。

7.13.2. Segmented: 视图切换的开关

Segmented (分段控制器) 是一个轻量级、现代化的选择控件。它的核心功能是在一组互斥的选项中,提供清晰的单选能力。相比于传统的 TabsRadio 按钮组,Segmented 在视觉上更紧凑、整体感更强,非常适合用作工具栏中的视图切换器或状态筛选器。

核心应用场景:

  • 视图切换:在“地图模式”与“列表模式”之间切换。
  • 数据周期选择:在“日”、“周”、“月”、“年”等时间维度间切换图表数据。
  • 状态筛选:在“全部”、“处理中”、“已完成”等状态间筛选任务列表。

第一步:基础用法与视图切换

Segmented 最核心的用途就是通过其值的变化,来驱动页面其他部分内容的条件渲染。这是一个典型的 受控组件 用法。

核心属性:

  • options: 设置分段器的所有选项。最简单的用法是传入一个字符串数组。
  • value: 当前选中的值。该属性需要绑定到一个 React State。
  • onChange: 选项变化时的回调函数。当用户点击不同选项时触发,我们需要在此函数中更新 value 绑定的 State。

文件路径: src/components/demos/BasicSegmentedDemo.tsx (新建文件)

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import React, { useState } from 'react';
import { Segmented, Card } from 'antd';

// 模拟不同视图的组件
const ListView = () => <Card style={{ backgroundColor: '#e6f4ff' }}>这是列表视图内容</Card>;
const CardView = () => <Card style={{ backgroundColor: '#f6ffed' }}>这是卡片视图内容</Card>;
const KanbanView = () => <Card style={{ backgroundColor: '#fffbe6' }}>这是看板视图内容</Card>;

const BasicSegmentedDemo: React.FC = () => {
// 1. 使用 useState 管理当前选中的值,默认为 'List'
const [currentView, setCurrentView] = useState<string | number>('List');

// 2. 一个渲染函数,根据当前选中的值返回对应的组件
const renderView = () => {
switch (currentView) {
case 'Card':
return <CardView />;
case 'Kanban':
return <KanbanView />;
case 'List':
default:
return <ListView />;
}
};

return (
<div className="p-8 bg-white rounded-lg shadow-lg w-[500px]">
<h3 className="text-xl font-bold mb-4 text-center">基础用法与视图切换</h3>
<Segmented
options={['List', 'Card', 'Kanban']} // 3. 传入简单的字符串数组
value={currentView} // 4. 绑定 state
onChange={setCurrentView} // 5. 传入 state 的更新函数
block // 使分段控制器宽度撑满父容器
/>
<div className="mt-4 p-4 border rounded">
{renderView()}
</div>
</div>
);
};

export default BasicSegmentedDemo;

App.tsx 中使用此 Demo:

文件路径: src/App.tsx (修改文件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React from 'react';
import { ConfigProvider } from 'antd';
import zhCN from 'antd/locale/zh_CN';
import 'dayjs/locale/zh-cn';
import BasicSegmentedDemo from './components/demos/BasicSegmentedDemo';

const App: React.FC = () => {
return (
<ConfigProvider locale={zhCN}>
<div className="bg-gray-100 min-h-screen p-8 flex items-center justify-center">
<BasicSegmentedDemo />
</div>
</ConfigProvider>
);
};

export default App;

这个示例完整地展示了 Segmented 的核心工作流:用户点击选项 -> onChange 触发 -> 更新 state -> state 变化导致页面重新渲染,从而显示出与新 state 匹配的视图内容。


第二步:自定义渲染 - 图标与复杂选项

当纯文本无法满足需求时,我们可以通过传入一个 对象数组options 属性,来实现更丰富的自定义渲染,例如为选项添加图标,或者禁用某个特定选项。

options 的对象结构:
{ label, value, icon, disabled, className }

  • label: 显示的文本或 React 节点
  • value: 该选项的唯一值
  • icon: 在 label 前显示的图标。
  • disabled: 禁用该选项。

文件路径: src/components/demos/CustomSegmentedDemo.tsx (新建文件)

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import React from 'react';
import { Segmented } from 'antd';
import { AppstoreOutlined, BarsOutlined, TableOutlined, UserOutlined } from '@ant-design/icons';

const CustomSegmentedDemo: React.FC = () => (
<div className="p-8 bg-white rounded-lg shadow-lg">
<h3 className="text-xl font-bold mb-4 text-center">自定义渲染</h3>
<Segmented
options={[
{
label: '列表',
value: 'List',
icon: <BarsOutlined />,
},
{
label: '卡片',
value: 'Card',
icon: <AppstoreOutlined />,
},
{
label: '表格',
value: 'Table',
icon: <TableOutlined />,
disabled: true, // 禁用这个选项
},
{
// 只有图标的选项
value: 'User',
icon: <UserOutlined />,
},
]}
/>
</div>
);

export default CustomSegmentedDemo;

App.tsx 中切换到新 Demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// import BasicSegmentedDemo from './components/demos/BasicSegmentedDemo';
import CustomSegmentedDemo from './components/demos/CustomSegmentedDemo';
// ... 其他 import ...

const App: React.FC = () => {
return (
<ConfigProvider locale={zhCN}>
<div className="bg-gray-100 min-h-screen p-8 flex items-center justify-center">
{/* <BasicSegmentedDemo /> */}
<CustomSegmentedDemo />
</div>
</ConfigProvider>
);
};

通过使用对象数组作为 options,我们获得了对每个分段项的完全控制权,可以轻松地构建出既美观又功能丰富的选择器,极大地提升了用户交互的清晰度和效率。


7.13.3. Collapse: 折叠收纳的内容面板

Collapse (折叠面板) 是一种高效的内容组织容器,它允许您将大量信息按逻辑分组,并默认将它们收起,只展示标题。用户可以按需点击标题,展开或收起对应的内容区域。这种交互模式对于保持页面整洁、减少信息过载、让用户聚焦于当前任务至关重要。

核心应用场景:

  • FAQ 页面:将每个“问题”作为面板标题,将“答案”作为可折叠的内容。
  • 配置中心:将复杂的设置项按功能分组,收纳在不同的面板中。
  • 版本日志:展示多个版本的更新记录,用户可以自由展开感兴趣的版本详情。

重要升级提示 (v5.6.0+): 在 antd v5.6.0 之后,官方 强烈推荐 使用 items 属性来数据驱动地配置面板内容。旧有的通过 JSX 嵌套 <Collapse.Panel> 的写法已被废弃。本教程将完全遵循 2025 年的最佳实践,只使用现代的 items 写法。

第一步:基础用法与数据驱动 (items)

创建 Collapse 组件的最佳方式是为其提供一个 items 数组。数组中的每一个对象都代表一个可折叠的面板,这种数据驱动的方式让代码更清晰、更易于维护。

核心属性:

  • items: 一个对象数组,用于定义所有面板。每个对象的核心结构为:
    • key: 每个面板的唯一标识符。
    • label: 显示在面板头部的标题内容。
    • children: 折叠区域内的主体内容。
  • defaultActiveKey: 一个包含 key 的数组,用于指定初始状态下哪些面板是展开的。
  • onChange: 当展开的面板发生变化时触发的回调,参数为当前所有展开面板的 key 组成的数组。

文件路径: src/components/demos/BasicCollapseDemo.tsx (新建文件)

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import React from 'react';
import { Collapse } from 'antd';
import type { CollapseProps } from 'antd';

const text = `
Ant Design, a design language for background applications, is refined by Ant UED Team.
It is a set of enterprise-class UI design language and React UI library with a set of easy-to-use demos.
`;

// 1. 使用 items 数组来定义面板数据
const items: CollapseProps['items'] = [
{
key: '1',
label: '这是面板标题 1',
children: <p>{text}</p>,
},
{
key: '2',
label: '这是面板标题 2',
children: <p>{text}</p>,
},
{
key: '3',
label: '这是面板标题 3',
children: <p>{text}</p>,
},
];

const BasicCollapseDemo: React.FC = () => {
const onChange = (key: string | string[]) => {
console.log('当前展开的面板 key:', key);
};

return (
<div className="p-8 bg-white rounded-lg shadow-lg w-[600px]">
<h3 className="text-xl font-bold mb-4 text-center">基础折叠面板</h3>
<Collapse
items={items}
defaultActiveKey={['1']}
onChange={onChange}
/>
</div>
);
};

export default BasicCollapseDemo;

App.tsx 中使用此 Demo:

文件路径: src/App.tsx (修改文件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React from 'react';
import { ConfigProvider } from 'antd';
import zhCN from 'antd/locale/zh_CN';
import 'dayjs/locale/zh-cn';
import BasicCollapseDemo from './components/demos/BasicCollapseDemo';

const App: React.FC = () => {
return (
<ConfigProvider locale={zhCN}>
<div className="bg-gray-100 min-h-screen p-8 flex items-center justify-center">
<BasicCollapseDemo />
</div>
</ConfigProvider>
);
};

export default App;

第二步:手风琴模式与自定义

除了允许多个面板同时展开,Collapse 还支持一种特殊的“手风琴”模式,即 任何时候只允许一个面板展开。同时,我们还可以通过其他属性对样式和内容进行自定义。

核心属性:

  • accordion: {布尔值,设置为 true 即可开启手风琴模式。
  • bordered: 布尔值,设置是否显示边框,false 可创建更简洁的外观。
  • extra: 在 items 的对象中定义,用于在面板头的右上角添加额外节点(如图标或操作按钮)。
  • expandIconPosition: 设置展开/收起图标的位置,可选 startend

文件路径: src/components/demos/AccordionCollapseDemo.tsx (新建文件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import React from 'react';
import { Collapse, Space } from 'antd';
import type { CollapseProps } from 'antd';
import { SettingOutlined } from '@ant-design/icons';

const text = `
A dog is a type of domesticated animal.
Known for its loyalty and faithfulness,
it can be found as a welcome guest in many households across the world.
`;

const getItems = (extraNode: React.ReactNode): CollapseProps['items'] => [
{
key: '1',
label: '面板 1',
children: <p>{text}</p>,
extra: extraNode,
},
{
key: '2',
label: '面板 2',
children: <p>{text}</p>,
extra: extraNode,
},
{
key: '3',
label: '面板 3 (不可展开)',
children: <p>{text}</p>,
collapsible: 'disabled', // 禁用此面板的展开功能
},
];

const AccordionCollapseDemo: React.FC = () => (
<div className="p-8 bg-white rounded-lg shadow-lg w-[600px]">
<h3 className="text-xl font-bold mb-4 text-center">手风琴模式与自定义</h3>
<Space direction="vertical" style={{width: '100%'}}>
<p>手风琴模式 (Accordion)</p>
<Collapse
accordion
items={getItems(<SettingOutlined />)}
/>

<p className='mt-4'>无边框样式 (bordered=false)</p>
<Collapse
bordered={false}
defaultActiveKey={['1']}
items={getItems(<SettingOutlined />)}
/>

<p className='mt-4'>图标位置 (expandIconPosition='end')</p>
<Collapse
expandIconPosition="end"
defaultActiveKey={['1']}
items={getItems(<SettingOutlined />)}
/>
</Space>
</div>
);

export default AccordionCollapseDemo;

App.tsx 中切换到新 Demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// import BasicCollapseDemo from './components/demos/BasicCollapseDemo';
import AccordionCollapseDemo from './components/demos/AccordionCollapseDemo';
// ... 其他 import ...

const App: React.FC = () => {
return (
<ConfigProvider locale={zhCN}>
<div className="bg-gray-100 min-h-screen p-8 flex items-center justify-center">
{/* <BasicCollapseDemo /> */}
<AccordionCollapseDemo />
</div>
</ConfigProvider>
);
};

通过 accordion 属性,我们可以轻松切换折叠面板的核心交互模式。结合 borderedextra 等自定义属性,Collapse 能够灵活地适应各种信息收纳场景,是优化长页面内容展示、提升信息架构清晰度的得力工具。


7.13.4. Descriptions: 结构化的详情展示

Descriptions (描述列表) 是一个专为 展示键值对(Key-Value)信息 而设计的组件。当您需要在一个详情页或弹窗中,清晰、整齐地陈列多个只读字段时,Descriptions 能够自动处理对齐、列分布和响应式布局,省去了手动编写表格或栅格布局的繁琐工作。

核心应用场景:

  • 用户详情页:展示用户的姓名、电话、邮箱、地址等。
  • 订单详情:展示订单号、下单时间、支付状态、收货地址等。
  • 系统信息:展示应用的名称、版本号、服务器 IP、运行状态等。

重要升级提示 (v5.8.0+): 在 antd v5.8.0 之后,官方 强烈推荐 使用 items 属性来数据驱动地配置描述列表。旧有的通过 JSX 嵌套 <Descriptions.Item> 的写法已被废弃。本教程将完全遵循 2025 年的最佳实践,只使用现代的 items 写法。

第一步:基础用法与 span 布局

创建 Descriptions 的最佳方式是为其提供一个 items 数组。其布局基于一个响应式的网格系统,我们可以通过 columnspan 属性来精确控制每一项的排列。

核心属性:

  • title: 描述列表的整体标题
  • items: 一个对象数组,用于定义所有描述项。每个对象的核心结构为:
    • key: 唯一标识符。
    • label: 描述项的标签(Key)。
    • children: 描述项的内容(Value)。
    • span: 该项占据的 列数
  • column: 描述列表的总列数,默认为 3。所有 itemsspan 值将在这个总列数内进行分配。

文件路径: src/components/demos/BasicDescriptionsDemo.tsx (新建文件)

image-20250930093146004

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import React from "react";
import { Descriptions } from "antd";
import type { DescriptionsProps } from "antd";

const BasicDescriptionsDemo: React.FC = () => {
// 使用 items 数组定义所有描述项
const items: DescriptionsProps["items"] = [
{ key: "1", label: "产品名称", children: "Ant Design Pro" },
{ key: "2", label: "计费模式", children: "预付费" },
{ key: "3", label: "创建时间", children: "2023-01-10" },
{ key: "4", label: "关联订单", children: "123456789" },
{ key: "5", label: "生效时间", children: "2023-01-10" },
{
key: "6",
label: "配置信息",
span: 2,
children: (
<>
CPU: 4 核<br />
内存: 16 GB
<br />
带宽: 5 Mbps
</>
),
},
{ key: "7", label: "备注", children: "无" },
];

return (
<div className="p-8 bg-white rounded-lg shadow-lg w-[800px]">
<h3 className="text-xl font-bold mb-4 text-center">基础用法</h3>
<Descriptions items={items} column={3} title="产品配置详情" />
</div>
);
};
export default BasicDescriptionsDemo;

App.tsx 中使用此 Demo:

文件路径: src/App.tsx (修改文件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React from 'react';
import { ConfigProvider } from 'antd';
import zhCN from 'antd/locale/zh_CN';
import 'dayjs/locale/zh-cn';
import BasicDescriptionsDemo from './components/demos/BasicDescriptionsDemo';

const App: React.FC = () => {
return (
<ConfigProvider locale={zhCN}>
<div className="bg-gray-100 min-h-screen p-8 flex items-center justify-center">
<BasicDescriptionsDemo />
</div>
</ConfigProvider>
);
};

export default App;

第二步:布局与样式 - 边框、垂直与尺寸

除了默认的无边框、水平布局,Descriptions 还提供了多种样式变体,以适应不同的展示需求,例如更正式的、类似表格的带边框样式。

核心属性:

  • bordered: 布尔值,设置为 true 可为描述列表添加边框和背景色,使其外观类似表格。
  • layout: 布局方式,可选 horizontal (水平,默认) 或 vertical (垂直,标签在内容上方)。
  • size: 设置列表的尺寸,可选 default, middle, small此属性仅在 bordered={true} 时生效,用于创建更紧凑的列表。
  • extra: 在 title 同一行的右上角添加额外操作区,例如一个“编辑”按钮。

文件路径: src/components/demos/StyledDescriptionsDemo.tsx (新建文件)

image-20250930093440206

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import React from 'react';
import { Descriptions, Badge, Button } from 'antd';
import type { DescriptionsProps } from 'antd';

const items: DescriptionsProps['items'] = [
{ key: '1', label: '产品', children: '云服务器 ECS' },
{ key: '2', label: '计费模式', children: '按量付费' },
{ key: '3', label: '自动续费', children: '是' },
{ key: '4', label: '订单号', children: '1234567890' },
{ key: '5', label: '创建时间', children: '2023-01-10 18:00:00' },
{ key: '6', label: '状态', span: 3, children: <Badge status="processing" text="运行中" /> },
{ key: '7', label: '折扣', children: '¥ 20.00' },
{ key: '8', label: '总计', children: '¥ 60.00' },
{ key: '9', label: '实付', children: '¥ 40.00' },
];


const StyledDescriptionsDemo: React.FC = () => (
<div className="p-8 bg-white rounded-lg shadow-lg w-[800px]">
<h3 className="text-xl font-bold mb-4 text-center">布局与样式</h3>

{/* 1. 带边框的样式 */}
<Descriptions
title="订单详情 (带边框)"
bordered
extra={<Button type="primary">编辑</Button>}
items={items}
className="mb-8"
/>

{/* 2. 垂直布局 + 小尺寸 */}
<Descriptions
title="订单详情 (垂直 + 小尺寸)"
bordered
layout="vertical"
size="small"
items={items}
/>
</div>
);

export default StyledDescriptionsDemo;

App.tsx 中切换到新 Demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// import BasicDescriptionsDemo from './components/demos/BasicDescriptionsDemo';
import StyledDescriptionsDemo from './components/demos/StyledDescriptionsDemo';
// ... 其他 import ...

const App: React.FC = () => {
return (
<ConfigProvider locale={zhCN}>
<div className="bg-gray-100 min-h-screen p-8 flex items-center justify-center">
{/* <BasicDescriptionsDemo /> */}
<StyledDescriptionsDemo />
</div>
</ConfigProvider>
);
};

通过 bordered, layout, size 等属性的组合,Descriptions 能够灵活地在“简洁信息列表”和“正式详情表格”两种风格之间切换。其数据驱动的 items API 和强大的 span 布局能力,使其成为详情页开发中不可或缺的效率工具。


Carousel (走马灯),在网页设计中通常也被称为“Slider”或“轮播图”,是一种在有限的界面空间内,循环展示多个内容面板(通常是图片或卡片)的容器组件。它通过自动或手动切换,有效地利用了黄金展示区域,是官网首页、产品宣传等场景的理想选择。

核心应用场景:

  • 网站首页 Banner:轮播展示最新的活动、产品或新闻。
  • 产品图集:在商品详情页,以轮播形式展示多张高清图片。
  • 功能介绍:在新用户引导流程中,轮播展示核心功能的介绍卡片。

第一步:基础用法与切换效果

创建一个 Carousel 非常简单,您只需将需要轮播的各个面板作为其 children 传入即可。同时,Carousel 也提供了多种切换效果和指示器位置的配置。

核心属性:

  • children: 需要轮播的 React 节点数组。每个直接子元素都会被视为一个独立的面板。
  • effect: 切换的动画效果。可选值为 'scrollx' (水平滚动,默认) 或 'fade' (淡入淡出)。
  • dotPosition: 面板指示点(小圆点)的位置,可选 top, bottom (默认), left, right

文件路径: src/components/demos/BasicCarouselDemo.tsx (新建文件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import React from "react";
import { Carousel, Divider } from "antd";

// 为轮播面板统一定义样式
const contentStyle: React.CSSProperties = {
margin: 0,
height: "160px",
color: "#fff",
lineHeight: "160px",
textAlign: "center",
background: "#364d79",
};

const BasicCarouselDemo: React.FC = () => (
<div className="p-8 bg-white rounded-lg shadow-lg w-[600px]">
<h3 className="text-xl font-bold mb-4 text-center">基础用法与切换效果</h3>
<Divider orientation="left">默认滚动效果 (scrollx)</Divider>
<Carousel autoplay>
<div>
<h3 style={contentStyle}>面板 1</h3>
</div>
<div>
<h3 style={contentStyle}>面板 2</h3>
</div>
<div>
<h3 style={contentStyle}>面板 3</h3>
</div>
</Carousel>

<Divider orientation="left">垂直滚动效果 (vertical)</Divider>
<Carousel vertical autoplay>
<div>
<h3 style={contentStyle}>面板 1</h3>
</div>
<div>
<h3 style={contentStyle}>面板 2</h3>
</div>
</Carousel>

<Carousel effect="fade" autoplay>
<div>
<h3 style={contentStyle}>面板 1</h3>
</div>
<div>
<h3 style={{...contentStyle, background: '#5a8d9b'}}>面板 2</h3>
</div>
<div>
<h3 style={{...contentStyle, background: '#8ab4c2'}}>面板 3</h3>
</div>
</Carousel>
</div>
);


export default BasicCarouselDemo;

App.tsx 中使用此 Demo:

文件路径: src/App.tsx (修改文件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React from 'react';
import { ConfigProvider } from 'antd';
import zhCN from 'antd/locale/zh_CN';
import 'dayjs/locale/zh-cn';
import BasicCarouselDemo from './components/demos/BasicCarouselDemo';

const App: React.FC = () => {
return (
<ConfigProvider locale={zhCN}>
<div className="bg-gray-100 min-h-screen p-8 flex items-center justify-center">
<BasicCarouselDemo />
</div>
</ConfigProvider>
);
};

export default App;

第二步:自动播放与程序化控制

在大多数场景下,我们希望走马灯能够自动播放。有时,我们还需要通过外部的按钮来手动控制其切换,这就需要使用 ref 来获取 Carousel 实例并调用其方法。

核心属性/方法:

  • autoplay: 布尔值,设置为 true 即可开启自动播放。
  • ref: 用于获取 Carousel 组件的实例。
  • ref.current.next(): 切换到下一个面板。
  • ref.current.prev(): 切换到上一个面板。
  • ref.current.goTo(slideNumber): 跳转到指定索引的面板。

文件路径: src/components/demos/ControlledCarouselDemo.tsx (新建文件)

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import React, { useRef } from 'react';
import { Carousel, Button, Space, Divider } from 'antd';
import type { CarouselRef } from 'antd/es/carousel';

const contentStyle: React.CSSProperties = {
margin: 0,
height: '160px',
color: '#fff',
lineHeight: '160px',
textAlign: 'center',
background: '#364d79',
};

const ControlledCarouselDemo: React.FC = () => {
// 1. 使用 useRef 创建一个 ref 来引用 Carousel 实例
const carouselRef = useRef<CarouselRef>(null);

const handleNext = () => {
carouselRef.current?.next(); // 2. 调用 next 方法
};

const handlePrev = () => {
carouselRef.current?.prev(); // 3. 调用 prev 方法
};

const handleGoTo = () => {
carouselRef.current?.goTo(0); // 4. 调用 goTo 方法跳转到第一页
}

return (
<div className="p-8 bg-white rounded-lg shadow-lg w-[600px]">
<h3 className="text-xl font-bold mb-4 text-center">自动播放与手动控制</h3>

<Divider orientation="left">自动播放</Divider>
<Carousel autoplay>
<div><h3 style={contentStyle}>面板 1</h3></div>
<div><h3 style={contentStyle}>面板 2</h3></div>
<div><h3 style={contentStyle}>面板 3</h3></div>
</Carousel>

<Divider orientation="left">程序化控制</Divider>
<Carousel ref={carouselRef}>
<div><h3 style={contentStyle}>面板 1</h3></div>
<div><h3 style={{...contentStyle, background: '#5a8d9b'}}>面板 2</h3></div>
<div><h3 style={{...contentStyle, background: '#8ab4c2'}}>面板 3</h3></div>
</Carousel>
<Space className="mt-4">
<Button onClick={handlePrev}>上一张</Button>
<Button onClick={handleNext}>下一张</Button>
<Button onClick={handleGoTo}>回到第一张</Button>
</Space>
</div>
);
};

export default ControlledCarouselDemo;

App.tsx 中切换到新 Demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// import BasicCarouselDemo from './components/demos/BasicCarouselDemo';
import ControlledCarouselDemo from './components/demos/ControlledCarouselDemo';
// ... 其他 import ...

const App: React.FC = () => {
return (
<ConfigProvider locale={zhCN}>
<div className="bg-gray-100 min-h-screen p-8 flex items-center justify-center">
{/* <BasicCarouselDemo /> */}
<ControlledCarouselDemo />
</div>
</ConfigProvider>
);
};

通过 autoplay 属性和 ref 的程序化控制,我们可以灵活地管理 Carousel 的动态行为,使其既能自动展示内容,也能响应用户的自定义交互。

至此,我们已经完成了第二阶段所有核心容器组件的学习。我们学会了如何使用 Card 聚合信息,用 CollapseDescriptions 组织详情,以及用 Carousel 动态轮播内容。接下来,我们将进入 阶段三:高级数据组件,挑战 List, Table 等功能更强大的组件。