Ant Design 栅格卡片布局实战:用 Row/Col 在概览页中编排 Card
系统概览页(Overview / Dashboard)最常见的形态,就是"标题 + 一组并排的信息卡片"。Ant Design 的官方示例明确给出了这一范式:Card 常与栅格(Row/Col)列布局配合使用(详见 components/card/demo/in-column.md)。本文以该示例为核心,结合仓库中 Card、Row、Col 的源码与测试快照,讲清"卡片放进栅格"的标准写法、间距(gutter)的底层实现、无边框变体(variant)的作用,以及如何改造成响应式多列布局。读完你将能直接在概览页中落地一套可对齐、可换行、可适配屏幕的卡片栅格。
一、示例速览:原文档说明了什么
官方为 Card 提供了名为 栅格卡片(Card in column) 的演示,文档描述非常聚焦:
- 中文说明:在系统概览页面常常和栅格进行配合。
- 英文说明:Cards usually cooperate with grid column layout in overview page.
该演示以 background="grey" 方式注册在组件文档中(见 components/card/index.en-US.md 与 components/card/index.zh-CN.md),对应实现文件为 components/card/demo/in-column.tsx。它的业务价值很清晰:概览页需要把多条并列信息用同一套视觉框架展示,栅格负责"分列 + 间距 + 响应式",Card 负责"每条信息的内容容器"。
二、完整示例代码
演示代码本身非常精简,全文如下(源自 components/card/demo/in-column.tsx):
import React from 'react';
import { Card, Col, Row } from 'antd';
const App: React.FC = () => (
<Row gutter={16}>
<Col span={8}>
<Card title="Card title" variant="borderless">
Card content
</Card>
</Col>
<Col span={8}>
<Card title="Card title" variant="borderless">
Card content
</Card>
</Col>
<Col span={8}>
<Card title="Card title" variant="borderless">
Card content
</Card>
</Col>
</Row>
);
export default App;
这段代码一共只用了三个 API,却覆盖了栅格卡片布局的全部要点:
| API | 用法 | 作用 |
|---|---|---|
Row |
gutter={16} |
声明列与列之间的水平间距(单位 px),并向下传递给所有 Col |
Col |
span={8} |
三列各占 8/24,合计 24,恰好一行排满,宽度一致 |
Card |
title + variant="borderless" |
提供统一的内容容器;borderless 关闭描边,风格更轻盈 |
三张卡片的 title 与 body 结构完全一致,配上等宽的三列,视觉上就形成了规整的"概览卡片组"。这正是原文档所说的"在系统概览页面常常和栅格进行配合"的最小可运行实现。
三、原理拆解:gutter 间距是怎么"恰好"落在列中间的
把卡片放进栅格后,最值得理解的细节是 Row gutter={16} 带来的间距机制。从 components/grid/row.tsx 的源码可以看到它做了两件事:
- 在
Row容器上设置负向水平外边距(rowStyle.marginInline = gutter / -2),并支持通过rowGap表达纵向间距gutterV; - 通过
RowContext把计算后的 gutter 传给子级Col。
而在 components/grid/col.tsx 中,Col 会从 RowContext 取出 gutter,然后给自己设置等量的一半内边距(mergedStyle.paddingInline = gutter / 2)。
把两者拼起来看就明白了:gutter={16} 时,Row 左右各让出 -8px 的外边距,同时每个 Col 左右各吃到 8px 的内边距,于是列与列之间净间距恒为 16px,而整行两端的 8px 又被 Row 的负外边距吸收,不会在页面边缘留下多余空白。这段渲染结果被固化在测试快照中,见 components/card/tests/snapshots/demo.test.ts.snap:
<div class="ant-row" style="margin-inline: -8px">
<div class="ant-col ant-col-8" style="padding-inline: 8px">
<div class="ant-card">
<div class="ant-card-head">
<div class="ant-card-head-wrapper">
<div class="ant-card-head-title">Card title</div>
</div>
</div>
<div class="ant-card-body">Card content</div>
</div>
</div>
<!-- 另外两列结构相同 -->
</div>
这份快照还顺带证明了 Card 渲染出的语义结构:.ant-card 下先渲染 .ant-card-head(内含 .ant-card-head-title),再渲染 .ant-card-body。也就是说,title="Card title" 会被渲染进卡片头部,而不是 body。
四、variant="borderless":无边框卡片背后的参数演进
示例特意给每张卡都传了 variant="borderless"。翻阅 components/card/index.en-US.md 的 API 表可以确认其取值:
variant:'outlined' | 'borderless',默认'outlined',自 5.24.0 引入;- 旧属性
bordered已被标记为废弃(deprecated),官方建议改用variant替代;同样被替代的还有headStyle→styles.header、bodyStyle→styles.body。
在 components/card/Card.tsx 中,bordered 会通过 useVariant('card', customVariant, bordered)(该 hook 位于 components/form/hooks/useVariants.ts)统一换算成最终的 variant,随后:
classString = clsx(prefixCls, ..., {
[`${prefixCls}-bordered`]: variant !== 'borderless',
...
});
可见"是否有边框"最终体现为是否追加 .ant-card-bordered 类。因此在概览页中,当你希望整组卡片去边框、只靠内容与间距组织视觉层级时,统一使用 variant="borderless" 即可与栅格背景(示例中的灰色 background="grey" 演示底)自然融为一体。
五、实操改造:从三列示例到响应式概览页
理解示例后,可以按需扩展,核心仍不离开 Row/Col 的栅格语义:
-
调整列数:24 栅格体系下,两列用
span={12},四列用span={6},三列示例则维持span={8}。只要各列span之和不超过 24,即可保证一行排布不换行。 -
响应式断点:
Col支持以断点命名的属性(xs、sm、md、lg、xl、xxl等,定义见 components/grid/col.tsx 的ColSize与响应式属性声明)。例如宽屏三列、窄屏单列可写为:<Row gutter={[16, 16]}> <Col xs={24} md={12} xl={8}> <Card title="指标一">Card content</Card> </Col> {/* 其余卡片同理 */} </Row>注意
gutter也支持数组[horizontal, vertical],写成[16, 16]即可让行与行之间也有纵向间距,这是多行卡片网格的常用写法。 -
列内内容的组织:把每张卡放进独立
Col后,Card 作为块级元素会占满所在列的宽度,列内标题与 body 天然对齐。需要更丰富内容时,仍可在 Card 内部使用actions、cover、tabList、loading等能力(这些 prop 的声明见 components/card/Card.tsx 中的CardProps),外围栅格结构无需改变。
六、证据与延伸阅读
本文涉及的关键事实均可在仓库中复现核对:
- 演示文档与源码:components/card/demo/in-column.md、components/card/demo/in-column.tsx;
- 演示在组件文档中的注册位置:components/card/index.en-US.md、components/card/index.zh-CN.md;
- Card 的渲染与参数实现:components/card/Card.tsx,API 表见 components/card/index.en-US.md;
- 栅格实现:components/grid/row.tsx(gutter 负边距与上下文传递)、components/grid/col.tsx(等分 span 与 gutter 内边距);
- 渲染结果快照:components/card/tests/snapshots/demo.test.ts.snap。
若需要"卡片内部再分块"的相邻方案,可对比参考同目录下的 grid-card.md 与 grid-card.tsx,它演示的是在一张 Card 内部通过 Card.Grid 分割区域的用法——前者是"多卡片进栅格",后者是"单卡片内做栅格",两者结合即可覆盖概览页绝大多数信息呈现需求。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0625
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00