文章目录
- 前言
- 一、更新记录
- 二、设计侧规范
- 三、技术框架选型
- 四、页面与代码规范 (Core Specs)
-
- [4.1 页面基础布局](#4.1 页面基础布局)
- [4.2 表格页面规范 (ProTable)](#4.2 表格页面规范 (ProTable))
- [4.3 详情页交互 (Drawer)](#4.3 详情页交互 (Drawer))
- [4.4 新建与编辑 (ModalForm)](#4.4 新建与编辑 (ModalForm))
- [4.5 Tabs 子页面布局](#4.5 Tabs 子页面布局)
- 五、标准交互行为
- 六、拿来即用的代码模板
-
- [模板 1:标准表格 (含详情 Drawer)](#模板 1:标准表格 (含详情 Drawer))
前言
本文档总结了基于 Ant Design Pro 和 Ant Design 5.x 的 Web 后台开发规范。通过统一的技术栈、页面布局、交互标准和代码模板,旨在提升团队开发效率,保证系统的一致性和可维护性。
一、更新记录
| 日期 | 版本 | 更新内容 | 备注 |
|---|---|---|---|
| 2026-01-12 | V1.2 | 合并技术实施细节,统一规范与代码标准 | 基于 V1.0 优化 |
| 2025-12-30 | V1.0 | 初始版本:技术栈、基础布局、表格交互 | 基础规范 |
二、设计侧规范
在开发之初,明确以下设计原则至关重要:
- 数据权限 :明确定义四种基础权限:
view: 可查看create: 可新建edit: 可编辑delete: 可删除
- 数据脱敏:敏感数据(如手机号、身份证)在前端展示时必须明确是否脱敏。
- 命名规范 :字段 Label 名称应精简,尽量保持在 4 个字以内,避免表格列宽过大。
三、技术框架选型
本项目统一采用以下技术栈,请参考官方文档进行开发:
- 核心框架 :Ant Design Pro - 开箱即用的中后台前端解决方案
- 组件库 :Ant Design 5.x - 企业级 UI 设计语言和 React 组件库
四、页面与代码规范 (Core Specs)
4.1 页面基础布局
为了保持整个系统的视觉一致性,所有页面需遵循以下布局规则:
- 组件包裹 :所有页面根节点必须是
PageContainer。 - 面包屑处理 :为了界面简洁,移除默认面包屑,仅保留标题。
- Code :
breadcrumbRender={false}或在 header 属性中设置breadcrumb: {}。
- Code :
- 统一间距 (Padding) :
-
普通 CRUD 页面 :PageContent、Card、Form 的 Padding 统一为 16px。
-
Implementation :
javascriptcardProps={{ bodyStyle: { padding: 16 } }} -
Tabs 嵌套页面 :为了视觉无缝衔接,Tabs 内部的表格 Padding 设为 0 。
javascriptcardProps={{ bodyStyle: { padding: 0 } }}
-
4.2 表格页面规范 (ProTable)
ProTable 是后台最常用的组件,必须严格遵循以下配置:
- 组件封装 :使用封装后的
ProTableWithColumnSetting(或自带设置功能的 ProTable),且不显示 右上角默认的 Density/FullScreen options。- Code :
<ProTableWithColumnSetting options={false} ... />
- Code :
- 标准列配置 :
- 序号列:第一列固定为序号,宽 60px,居中。
- 主键列 :选取关键业务字段(如"名称")作为主键,蓝色高亮,点击进入详情 Drawer。
- 排序与筛选 :重要列前置;第 2-8 列默认开启查询;数字/时间列需开启
sorter: true。
- 查询区域 :
- 固定高度,避免抖动。
- Label 宽度统一为 88px,左对齐。
- Code :
search={``{ labelWidth: 88, labelAlign: 'left', span: 8 }}
- FormItem 校验陷阱 :
- ⚠️ 禁止 在
columns的通用formItemProps中直接加required: true。这会导致查询表单也变成必填,导致无法查询! - 正确做法:仅在新建/编辑的 ModalForm 中单独定义 rules,或通过 valueType 区分。
- ⚠️ 禁止 在
- 批量操作 :
- 选中行后,使用
FooterToolbar悬浮显示操作按钮。
- 选中行后,使用
4.3 详情页交互 (Drawer)
详情页统一采用右侧抽屉 (Drawer) 模式,而非跳转新页面或弹窗。
- 触发方式:点击表格中的"蓝色链接"字段。
- 容器规格 :建议宽度 560px 或 600px。
- 内容布局 :使用
<Descriptions />组件,bordered={false},均分两列 (column={2})。 - 操作区:Drawer 的 footer 或 extra 区域放置"编辑"、"删除"按钮。
4.4 新建与编辑 (ModalForm)
- 表单项 > 6 :
- 两列布局 (
grid={true},colProps={``{ span: 12 }}) - 宽度 800px ,
layout="vertical"(垂直布局适应长 Label)。
- 两列布局 (
- 表单项 ≤ 6 :
- 单列布局
- 宽度 400px。
- 防重复提交:提交函数必须返回 Promise,以此触发 ModalForm 的 loading 状态。
4.5 Tabs 子页面布局
- 当页面包含多个子 Tab 时(如:基本信息 | 关联记录 | 操作日志),结构应为:
Card > Tabs > 独立组件。 - 注意:顶部的"新建/导入"按钮仍保留在 PageContainer 的 extra 中,Tab 切换不应影响页面级操作按钮的位置。
五、标准交互行为
-
删除确认 :所有删除操作必须 有二次确认。
javascript// 推荐使用 Popconfirm 或 Modal.confirm -
结果反馈 :
- 成功:
message.success('操作成功') - 失败:
message.error('操作失败:xx'),必须告知用户具体原因。
- 成功:
-
Loading 状态:任何异步操作(查询列表、提交表单)都必须有 Loading 反馈,禁止界面"假死"。
六、拿来即用的代码模板
为了提升开发效率,你可以直接复制以下模板进行修改。
模板 1:标准表格 (含详情 Drawer)
适用于 80% 的后台管理 CRUD 场景。
typescript
import React, { useState, useRef } from 'react';
import { PageContainer } from '@ant-design/pro-components';
import { Drawer, Descriptions, Button, Space, message, Modal } from 'antd';
import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons';
import type { ActionType, ProColumns } from '@ant-design/pro-components';
import ProTableWithColumnSetting from '@/components/ProTable';
export default () => {
const [drawerVisible, setDrawerVisible] = useState(false);
const [currentRow, setCurrentRow] = useState<any>(null);
const actionRef = useRef<ActionType>();
// 核心列定义
const columns: ProColumns<any>[] = [
{
title: '序号',
dataIndex: 'index',
width: 60,
fixed: 'left',
align: 'center',
search: false,
render: (_, __, index) => index + 1,
},
{
title: '名称(主键)',
dataIndex: 'name',
render: (dom, record) => (
<a
style={{ cursor: 'pointer', color: '#1890ff' }}
onClick={() => {
setCurrentRow(record);
setDrawerVisible(true);
}}
>
{dom}
</a>
),
},
// ... 其他业务列
];
return (
<PageContainer
header={{ title: '标题', breadcrumb: {} }}
extra={[
<Button key="create" type="primary" icon={<PlusOutlined />}>
新建
</Button>,
]}
>
<ProTableWithColumnSetting
columns={columns}
actionRef={actionRef}
options={false}
search={{ labelWidth: 88, labelAlign: 'left', span: 8 }}
cardProps={{ bodyStyle: { padding: 16 } }}
request={async (params) => {
// TODO: 调用 Service
return { data: [], success: true };
}}
/>
{/* 详情抽屉 */}
<Drawer
title="详情"
width={600}
open={drawerVisible}
onClose={() => setDrawerVisible(false)}
extra={
<Space>
<Button icon={<EditOutlined />}>编辑</Button>
<Button danger icon={<DeleteOutlined />}>删除</Button>
</Space>
}
>
<Descriptions column={2}>
{/* TODO: 详情字段 */}
<Descriptions.Item label="名称">{currentRow?.name}</Descriptions.Item>
</Descriptions>
</Drawer>
</PageContainer>
);
};