3270 字
约 10 分钟
0
Ant Design 6 入门
2026-09-07

Ant Design 6 入门

面向 VOZEB PRO 项目,讲解 Ant Design 6 是什么、在本仓库怎么接入、常用写法和什么时候该用它


0. 什么时候使用 Ant Design?

适合使用 Ant Design 的场景:

  • 管理后台 SaaS 控件 — 表格、表单、分页、抽屉、日期选择、数字输入、开关、标签。本项目后台几乎全靠这一套
  • 标准交互 overlayModalDrawerDropdownPopoverPopconfirmTooltip。键盘、焦点、遮罩、滚动锁定都已处理好
  • 需要统一反馈 — 成功/失败提示、确认框。必须走 App.useApp(),不要自己 alert 或再引一套 Toast
  • 需要主题一致 — 浅色/深色、主色、圆角、表格选中色已在 getAntThemeConfig() 配好,用 antd 组件会自动跟上
  • 密集表单/列表工作流 — 后台约定是「列表 + 创建按钮 + Modal/Drawer 表单」,antd 的 Table + Form + Modal 就是为这个设计的
  • 中文 locale — 分页、空态、日期选择已经是中文,不用自己翻译

不建议使用(或需要权衡)的场景:

  • ⚠️ 页面骨架和间距 — 用 Tailwind:flexgridgappx-*max-w-*。不要用 antd 的 Row/Col/Layout 再套一层栅格
  • ⚠️ 高度定制的创作界面 — Canvas 节点、工作台结果墙、瀑布流卡片,交互和视觉都是项目自研,只在局部借用 Button/Modal/Switch
  • ⚠️ antd 已经有等价物时再引 shadcn — 同一功能不要两套组件各写一遍。本项目 shadcn 只是补充(例如已有的 @/components/ui/select.tsx
  • ⚠️ 在 Server Component 里直接 import antd 组件 — antd 组件依赖客户端状态和 Context,必须写在 "use client" 文件里
  • ⚠️ 用 Tailwind 大面积覆盖 antd 内部 class — 能改主题 token 就改 token;!py-2 这种选择器只作局部微调
  • ⚠️ 自己写 dark ? ... 去改 antd 组件颜色 — 后台主题统一走 app-theme.ts / AppProviders,页面里不要再分叉

在 VOZEB PRO 中的定位: Ant Design 是主体 UI 库。Tailwind 管布局和微调,shadcn/Radix 只补 antd 不方便的点。新需求先问「antd 有没有现成组件」,有就用,没有再考虑手写或 shadcn。


1. 什么是 Ant Design 6

Ant Design(简称 antd)是一套带完整样式和交互的企业级 React 组件库。和 shadcn 不同,它是真正的 npm 依赖:安装后直接 import { Button } from "antd",不把源码复制进仓库。

本项目锁定:

版本(扫描时) 作用
antd ^6.5.3 组件本体
@ant-design/nextjs-registry ^1.3.0 Next.js App Router 下的样式注入,避免 SSR 闪烁
antd/locale/zh_CN 随 antd 中文文案
dayjs ^1.11.20 DatePicker 的日期库,locale 用 zh-cn

v6 相对旧版,对你写业务代码影响最大的几点:

  • 主题继续走 ConfigProvidertoken / components,本项目还开了 cssVar
  • 全局提示、对话框必须挂在 <App> 下,用 App.useApp()message / modal / notification
  • 和 Next.js App Router 搭配时,根布局要用 AntdRegistry 包一层

它解决的是「表格怎么筛、表单怎么校验、抽屉怎么在手机上别撑破屏幕」这类产品控件问题,不是「这一行怎么 flex 居中」。


2. 和 Tailwind、shadcn 怎么分工

页面结构 / 间距 / 响应式     →  Tailwind
按钮、表单、表格、弹层、提示 →  Ant Design
antd 没有、又要可改源码     →  shadcn(少用)
图标                         →  lucide-react(不要用 @ant-design/icons 另起一套)

对照:

维度 Ant Design Tailwind shadcn/ui
提供什么 现成控件 + 交互 原子 class 复制进仓库的控件源码
主题 ConfigProvider token @theme / dark: CSS 变量 + Tailwind
本项目用量 约 157 个文件直接 import 几乎所有页面 components/ui 里少量
改外观 app-theme.ts 改 class 改组件源码
典型场景 后台表格、支付表单、确认框 工作区壳、卡片网格 个别定制 Select

一个真实组合(「我的提示词」页):外层 div 用 Tailwind 排版,里面的 Table / Modal / Form / Popconfirm / Button 用 antd。


3. 本项目是怎么接入的

链路从上到下只有三层,新页面不要再包一套 ConfigProvider

3.1 根布局:注入样式

web/src/app/layout.tsx

import { AntdRegistry } from "@ant-design/nextjs-registry";
import { AppProviders } from "@/components/layout/app-providers";
import "antd/dist/reset.css";

<AntdRegistry>
    <AppProviders>{children}</AppProviders>
</AntdRegistry>
  • antd/dist/reset.css:清掉浏览器默认,和 antd 控件对齐
  • AntdRegistry:把 antd 的 CSS-in-JS 抽到 SSR HTML 里,避免首屏样式跳动

3.2 AppProviders:主题、中文、全局 App

web/src/components/layout/app-providers.tsx

<ConfigProvider locale={zhCN} theme={getAntThemeConfig(dark)}>
    <App message={{ top: 84, duration: 2.4, maxCount: 3 }}>
        <QueryClientProvider client={queryClient}>
            <ClientRootInit>{children}</ClientRootInit>
        </QueryClientProvider>
    </App>
</ConfigProvider>

同时做了三件事:

  1. locale={zhCN} + dayjs.locale("zh-cn") — 控件中文
  2. theme={getAntThemeConfig(dark)} — 跟 Zustand 的主题 store 同步浅色/深色
  3. <App> — 给全站 message / modal 提供上下文;top: 84 是为了避开顶部导航

3.3 主题只改一处

web/src/lib/app-theme.tsgetAntThemeConfig(dark) 集中写了:

  • 算法:defaultAlgorithm / darkAlgorithm
  • cssVar.keyvozeb-pro-light / vozeb-pro-dark
  • 全局 token:主色、背景、边框、文字、圆角 8
  • 组件级:ButtonMenuSelectCascaderTreeSelectTable

主色是中性黑/白,不是 antd 默认蓝。以后要改品牌色,改这个文件,不要在页面里写死 #1677ff


4. 最小用法

antd 组件必须放在客户端组件里。

"use client";

import { App, Button } from "antd";

export function SaveButton() {
    const { message } = App.useApp();

    return (
        <Button
            type="primary"
            onClick={() => message.success("已保存")}
        >
            保存
        </Button>
    );
}

要点:

  • 文件第一行 "use client"
  • 提示用 App.useApp().message,不要 import { message } from "antd" 再静态调用(在 App Router 里会丢上下文、主题也对不上)
  • 主操作按钮用 type="primary",删除用 danger
  • 图标用 lucide,通过 icon={<Plus className="size-4" />} 传入

5. 本项目里最常见的五种写法

5.1 反馈:复制、成功、失败

全局 hook 已经包好了,重复动作不要再手写一遍。

import { useCopyText } from "@/hooks/use-copy-text";

const copyText = useCopyText();
copyText(record.prompt, "提示词已复制");

useCopyText 内部就是 App.useApp().message。下载、确认框同类副作用,优先抽到 web/src/hooks/

业务失败则:

const { message } = App.useApp();
try {
    await createMyPrompt(value);
    message.success("提示词已保存");
} catch (error) {
    message.error(error instanceof Error ? error.message : "新增提示词失败");
}

5.2 列表页:Table + Modal + Form

后台和「我的提示词」都是这个骨架。

"use client";

import { App, Button, Form, Input, Modal, Popconfirm, Table } from "antd";
import type { TableColumnsType } from "antd";

type PromptFormValue = { title: string; prompt: string };

export function ExampleList() {
    const { message } = App.useApp();
    const [form] = Form.useForm<PromptFormValue>();
    const [open, setOpen] = useState(false);

    const columns: TableColumnsType<Item> = [
        { title: "标题", dataIndex: "title" },
        {
            title: "操作",
            render: (_, record) => (
                <Popconfirm title="删除?" okText="删除" cancelText="取消" onConfirm={() => onDelete(record.id)}>
                    <Button size="small" danger>
                        删除
                    </Button>
                </Popconfirm>
            ),
        },
    ];

    return (
        <>
            <Button type="primary" onClick={() => setOpen(true)}>
                添加
            </Button>
            <Table rowKey="id" columns={columns} dataSource={items} pagination={false} />
            <Modal title="添加提示词" open={open} onCancel={() => setOpen(false)} footer={null} destroyOnHidden>
                <Form form={form} layout="vertical" onFinish={onCreate}>
                    <Form.Item name="title" label="标题" rules={[{ required: true, message: "请填写标题" }]}>
                        <Input />
                    </Form.Item>
                    <Form.Item name="prompt" label="内容" rules={[{ required: true }]}>
                        <Input.TextArea rows={6} />
                    </Form.Item>
                    <Button type="primary" htmlType="submit">
                        保存
                    </Button>
                </Form>
            </Modal>
        </>
    );
}

约定:

  • 大表单不要常驻铺在页面主体,放进 Modal / Drawer
  • destroyOnHidden(或项目里已有的 destroyOnHidden)避免关掉后残留校验状态
  • 表格列类型用 TableColumnsType<T>,不要写成 any
  • 短字段用网格铺满一行,密钥/长文本单独通栏(见 AGENTS.md 后台表单规则)

5.3 抽屉:宽度必须响应式

不要写 size="large",手机上会撑出横向滚动。

<Drawer
    title={channel.name || "渠道详情"}
    width="min(720px, 100vw)"
    open={open}
    destroyOnHidden
    onClose={onClose}
>
    ...
</Drawer>

AdminChannelDetailDrawerMobileNavDrawer 都是这个模式。改完要在 390px / 430px 看抽屉根节点和底部按钮有没有被裁切。

5.4 受控开关、选择,只借控件不借布局

工作台设置面板经常只要 antd 的交互,外壳仍是 Tailwind:

import { Switch } from "antd";

<Switch checked={enabled} onChange={setEnabled} />

image-settings-panel.tsx 甚至会再包一层局部 ConfigProvider,只为了让某个 Switch 跟画布主题走。这是例外,默认仍用根上的主题。

5.5 确认删除:Popconfirm 或 App.modal

单行危险操作:

<Popconfirm title="删除提示词?" okText="删除" cancelText="取消" onConfirm={() => deletePrompt(id)}>
    <Button size="small" danger />
</Popconfirm>

需要动态文案、异步逻辑时,用 App.useApp().modal.confirm(...)(促销活动等后台页是这样)。


6. 本项目高频组件速查

按「先想场景再找组件」,不要先翻官方 80 个组件。

你想做的事 用这个 项目里能对照的地方
主按钮 / 次按钮 / 危险按钮 Button 几乎所有页
单行、密码、多行输入 Input / Input.Password / Input.TextArea auth-form.tsx
数字、金额 InputNumber 促销、计费后台
下拉选择 Select 画廊筛选、后台筛选项
开关 Switch 图片/视频设置、促销启用
分段切换 Segmented 后台列表视图切换
日期范围 DatePicker / DatePicker.RangePicker 促销活动
标签 Tag 能力、状态、协议
表格 Table 我的提示词、对账、作品治理
分页(表格外) Pagination 素材库、作品、促销
表单校验 Form + Form.useForm 我的提示词、促销、优惠券
弹窗表单 Modal 后台创建/编辑
侧栏详情 Drawer 渠道详情、移动导航
轻确认 Popconfirm 行内删除
轻提示 App.useApp().message 登录、保存、复制
空数据 Empty 提示词选择弹窗
加载 Spin 列表请求中
下拉菜单 Dropdown 顶栏用户操作
气泡 Popover / Tooltip 画布工具、简短说明
时间线 Timeline 版本发布说明

用不到就别引。Canvas 主画布、瀑布流、对话气泡都不是 antd 的活。


7. 决策清单:这一处该不该上 antd

写 UI 前按顺序问:

  1. 这是后台列表/表单/筛选/弹层吗? → 用 antd,并沿用「列表 + Modal/Drawer」。
  2. 这是全站都要一致的轻反馈吗?(复制成功、保存失败)App.useApp() 或已有 hook。
  3. 这只是排版(宽、高、间距、两栏、粘性顶栏)吗? → Tailwind,不要 Row/Col
  4. 这是创作画布、结果墙、对话流、自定义节点吗? → 自研结构 + 必要时借一两个 antd 控件。
  5. antd 没有、交互还很特殊、又希望拥有源码? → 才考虑 shadcn。
  6. 能不能改 app-theme.ts 解决外观? → 能就不要在 JSX 里堆 className="[&_.ant-xxx]"

反例:

  • 用 antd Card 再包一层只为了阴影 — 用 Tailwind rounded-lg border bg-card 即可,项目里大量卡片是这样
  • 用 antd Typography.Title 当页面主标题 — 直接 h1 + Tailwind,和用户工作区其它页一致
  • 为了一个定制下拉再 npx shadcn add select,而页面隔壁已经在用 antdSelect

8. 和 Next.js App Router 相处的注意点

  1. 客户端边界
    含 antd 的文件必须 "use client"。服务端 page.tsx 可以渲染一个客户端子组件,不要在 Server Component 里直接写 <Table />
  2. 不要静态 message.success
    v5 以后官方也不推荐静态方法。本项目已经用 <App> 包住全树,统一 App.useApp()
  3. 水合
    根布局有 suppressHydrationWarning,主题 class 在 useLayoutEffect 里写到 <html>。不要在首屏用依赖 window 的 antd 默认值去渲和服务器不一致的 DOM。
  4. 包体积
    按需 import { Button, Table } from "antd" 即可,不要 import antd from "antd"。项目里已经是具名导入。
  5. Drawer / Modal 宽度
    移动端必须 min(..., 100vw),并确认内部滚动容器,不要让 antd 默认固定宽度破坏 html, body { overflow: hidden } 的工作区壳。
  6. 覆盖样式的优先级
    主题 token > 组件 props(sizevariant)> 极少量 classNameAGENTS.md 要求按钮在浅色/深色/hover/disabled 下都可读;主按钮深底必须白字,不要用 Tailwind 把 type="primary" 改回黑字。

9. 官方文档怎么查

写 antd 代码时,以当前大版本文档为准,并结合本仓库已有写法:

  • 组件与 API:https://ant.design/components/overview-cn
  • 主题 Token:https://ant.design/docs/react/customize-theme-cn
  • App 包裹与静态方法:https://ant.design/components/app-cn
  • 给 LLM 用的全量参考(AGENTS.md 指定):https://ant.design/llms-full.txt

查 API 时看 antd 6,不要抄 v4 的 Form.create() 或 v3 的 antd.xxx


10. 相关文档

  • Tailwind CSS 4 入门 — 布局、间距、暗色 class
  • shadcn/ui 入门 — 仅在 antd 不够用时
  • web/src/lib/app-theme.ts — 全站 antd 主题
  • web/src/components/layout/app-providers.tsx — ConfigProvider / App 挂载
  • AGENTS.md「前端规范」— 后台信息密度、Drawer 宽度、按钮对比度
Ant Design 6 入门
http://www.clxhxhhr.top/posts/471/
作者
clxstart
发布于
2026-09-07
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。