12 KiB
12 KiB
DetailDrawer 组件
组件说明
详情抽屉组件,用于从页面右侧滑出显示详细信息。支持自定义标题、操作按钮、标签页等功能,内容区域可滚动,顶部标题栏固定。
组件位置
src/components/DetailDrawer/DetailDrawer.jsx
src/components/DetailDrawer/DetailDrawer.css
参数说明
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| visible | boolean | 是 | - | 是否显示抽屉 |
| onClose | function | 是 | - | 关闭抽屉回调 |
| title | TitleConfig | 否 | - | 标题配置对象 |
| headerActions | Array | 否 | [] | 顶部操作按钮数组 |
| width | number | 否 | 1080 | 抽屉宽度(像素) |
| children | ReactNode | 否 | - | 主要内容区域 |
| tabs | Array | 否 | - | 标签页配置数组 |
1. 小型抽屉 (Small) - 480px
适用场景:
- 简单的信息展示
- 少量字段的表单(1-3个字段)
- 快速操作面板
- 通知详情
示例:
<Drawer width={480} ... />
2. 中型抽屉 (Medium) - 720px
适用场景:
- 详细信息展示(如主机详情)
- 中等复杂度的表单(4-10个字段)
- 数据编辑面板
- 配置设置
示例:
<Drawer width={720} ... />
当前主机列表页面使用此宽度模式
3. 大型抽屉 (Large) - 1080px
适用场景:
- 复杂的多步骤表单
- 需要并排展示多列信息
- 包含图表或复杂可视化内容
- 嵌套子表格或列表
示例:
<Drawer width={1080} ... />
TitleConfig 配置项
| 属性名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| text | string | 是 | 标题文本 |
| badge | ReactNode | 否 | 状态徽标(如 Tag、Badge 组件) |
| icon | ReactNode | 否 | 标题图标 |
ActionConfig 配置项
| 属性名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| key | string | 是 | 按钮唯一标识 |
| label | string | 是 | 按钮文本 |
| icon | ReactNode | 否 | 按钮图标 |
| type | string | 否 | 按钮类型(primary/default/dashed/text/link) |
| danger | boolean | 否 | 是否为危险按钮 |
| disabled | boolean | 否 | 是否禁用 |
| onClick | function | 否 | 点击回调函数 |
TabConfig 配置项
| 属性名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| key | string | 是 | 标签页唯一标识 |
| label | ReactNode | 是 | 标签页标题(支持图标+文字) |
| content | ReactNode | 是 | 标签页内容 |
使用示例
基础用法
import { useState } from 'react'
import DetailDrawer from '../components/DetailDrawer/DetailDrawer'
function MyPage() {
const [showDrawer, setShowDrawer] = useState(false)
const [selectedItem, setSelectedItem] = useState(null)
return (
<>
<Button onClick={() => setShowDrawer(true)}>查看详情</Button>
<DetailDrawer
visible={showDrawer}
onClose={() => setShowDrawer(false)}
title={{
text: selectedItem?.name || '详情',
}}
>
<div style={{ padding: 24 }}>
<p>详情内容</p>
</div>
</DetailDrawer>
</>
)
}
带状态徽标
import { Tag, Badge } from 'antd'
<DetailDrawer
visible={showDetailDrawer}
onClose={() => setShowDetailDrawer(false)}
title={{
text: selectedUser?.userName || '',
badge: (
<Tag color={selectedUser?.status === 'enabled' ? 'green' : 'default'}>
{selectedUser?.status === 'enabled' ? '启用' : '停用'}
</Tag>
),
}}
>
{/* 内容 */}
</DetailDrawer>
带操作按钮
import { EditOutlined, DeleteOutlined } from '@ant-design/icons'
<DetailDrawer
visible={showDetailDrawer}
onClose={() => setShowDetailDrawer(false)}
title={{
text: selectedUser?.userName || '',
}}
headerActions={[
{
key: 'edit',
label: '编辑',
icon: <EditOutlined />,
onClick: () => {
setEditMode('edit')
setShowEditDrawer(true)
setShowDetailDrawer(false)
},
},
{
key: 'delete',
label: '删除',
icon: <DeleteOutlined />,
danger: true,
onClick: () => {
setShowDetailDrawer(false)
handleDelete(selectedUser)
},
},
]}
>
{/* 内容 */}
</DetailDrawer>
使用 InfoPanel 显示信息
import DetailDrawer from '../components/DetailDrawer/DetailDrawer'
import InfoPanel from '../components/InfoPanel/InfoPanel'
const userFields = [
{ key: 'userName', label: '用户名', span: 6 },
{ key: 'group', label: '用户分组', span: 6 },
{ key: 'name', label: '姓名', span: 6 },
{ key: 'userType', label: '用户类型', span: 6 },
{
key: 'status',
label: '状态',
span: 6,
render: (value) => (
<Tag color={value === 'enabled' ? 'green' : 'default'}>
{value === 'enabled' ? '启用' : '停用'}
</Tag>
),
},
]
<DetailDrawer
visible={showDetailDrawer}
onClose={() => setShowDetailDrawer(false)}
title={{
text: selectedUser?.userName || '',
}}
>
<InfoPanel
data={selectedUser}
fields={userFields}
actions={[
{ key: 'reset', label: '重置密码', onClick: () => console.log('重置密码') },
{ key: 'disable', label: '停用', onClick: () => console.log('停用') },
]}
/>
</DetailDrawer>
带标签页
import { DatabaseOutlined, UserOutlined } from '@ant-design/icons'
<DetailDrawer
visible={showDetailDrawer}
onClose={() => setShowDetailDrawer(false)}
title={{
text: selectedHost?.name || '',
badge: (
<Badge
status={selectedHost?.status === 'online' ? 'success' : 'default'}
text={selectedHost?.status === 'online' ? '在线' : '离线'}
/>
),
}}
headerActions={[
{
key: 'edit',
label: '编辑',
icon: <EditOutlined />,
onClick: handleEdit,
},
]}
width={1080}
tabs={[
{
key: 'images',
label: (
<span>
<DatabaseOutlined style={{ marginRight: 8 }} />
终端镜像
</span>
),
content: (
<div className="card-list">
{/* 镜像列表内容 */}
</div>
),
},
{
key: 'users',
label: (
<span>
<UserOutlined style={{ marginRight: 8 }} />
终端用户
</span>
),
content: (
<div className="card-list">
{/* 用户列表内容 */}
</div>
),
},
]}
>
<InfoPanel data={selectedHost} fields={hostFields} />
</DetailDrawer>
完整示例
import { useState } from 'react'
import DetailDrawer from '../components/DetailDrawer/DetailDrawer'
import InfoPanel from '../components/InfoPanel/InfoPanel'
import { Tag, Badge, Card } from 'antd'
import { EditOutlined, DeleteOutlined, DatabaseOutlined, UserOutlined } from '@ant-design/icons'
function UserListPage() {
const [showDetailDrawer, setShowDetailDrawer] = useState(false)
const [selectedUser, setSelectedUser] = useState(null)
const userFields = [
{ key: 'userName', label: '用户名', span: 6 },
{ key: 'group', label: '用户分组', span: 6 },
{ key: 'name', label: '姓名', span: 6 },
{ key: 'grantedImages', label: '授权镜像', span: 6 },
{ key: 'userType', label: '用户类型', span: 6 },
{ key: 'grantedTerminals', label: '授权终端', span: 6 },
{
key: 'status',
label: '启停用',
span: 6,
render: (value) => (
<Tag color={value === 'enabled' ? 'green' : 'default'}>
{value === 'enabled' ? '启用' : '停用'}
</Tag>
),
},
]
const detailTabs = [
{
key: 'terminals',
label: (
<span>
<DesktopOutlined style={{ marginRight: 8 }} />
授权终端
</span>
),
content: (
<div className="card-list">
{selectedUser?.terminals?.map((terminal) => (
<Card key={terminal.id}>
<h4>{terminal.name}</h4>
<p>IP: {terminal.ip}</p>
</Card>
))}
</div>
),
},
{
key: 'images',
label: (
<span>
<DatabaseOutlined style={{ marginRight: 8 }} />
授权镜像
</span>
),
content: (
<div className="card-list">
{selectedUser?.images?.map((image) => (
<Card key={image.id}>
<h4>{image.name}</h4>
<p>系统: {image.os}</p>
</Card>
))}
</div>
),
},
]
return (
<>
{/* 列表页面 */}
<ListTable
columns={columns}
dataSource={users}
onRowClick={(record) => {
setSelectedUser(record)
setShowDetailDrawer(true)
}}
/>
{/* 详情抽屉 */}
<DetailDrawer
visible={showDetailDrawer}
onClose={() => setShowDetailDrawer(false)}
title={{
text: selectedUser?.userName || '',
badge: (
<Tag color={selectedUser?.status === 'enabled' ? 'green' : 'default'}>
{selectedUser?.status === 'enabled' ? '启用' : '停用'}
</Tag>
),
}}
headerActions={[
{
key: 'edit',
label: '编辑',
icon: <EditOutlined />,
onClick: () => {
setShowDetailDrawer(false)
handleEdit(selectedUser)
},
},
{
key: 'delete',
label: '删除',
icon: <DeleteOutlined />,
danger: true,
onClick: () => {
setShowDetailDrawer(false)
handleDelete(selectedUser)
},
},
]}
width={1080}
tabs={detailTabs}
>
<InfoPanel
data={selectedUser}
fields={userFields}
actions={[
{ key: 'move', label: '转移分组', type: 'primary', onClick: () => console.log('转移分组') },
{ key: 'reset', label: '重置密码', onClick: () => console.log('重置密码') },
{ key: 'disable', label: '停用', onClick: () => console.log('停用') },
]}
/>
</DetailDrawer>
</>
)
}
布局结构
抽屉采用固定头部、可滚动内容的布局:
┌─────────────────────────────────────┐
│ 标题栏(固定,不滚动) │
│ [关闭] [标题] [徽标] [操作按钮] │
├─────────────────────────────────────┤
│ │
│ 内容区域(可滚动) │
│ - children 主要内容 │
│ - tabs 标签页(可选) │
│ │
└─────────────────────────────────────┘
样式定制
组件提供以下 CSS 类名供自定义样式:
.detail-drawer-content- 抽屉内容容器.detail-drawer-header- 顶部标题栏.detail-drawer-header-left- 标题栏左侧区域.detail-drawer-header-right- 标题栏右侧区域.detail-drawer-close-button- 关闭按钮.detail-drawer-header-info- 标题信息容器.detail-drawer-title-icon- 标题图标.detail-drawer-title- 标题文本.detail-drawer-badge- 徽标容器.detail-drawer-scrollable-content- 可滚动内容区域.detail-drawer-tabs- 标签页容器.detail-drawer-tab-content- 标签页内容
使用场景
- 查看详细信息 - 点击列表行显示详细信息
- 多标签页详情 - 在详情中展示不同类型的关联数据
- 带快捷操作的详情 - 在详情顶部提供编辑、删除等操作
- 复杂数据展示 - 配合 InfoPanel、Card 等组件展示复杂信息
注意事项
- 抽屉宽度默认 1080px,可根据内容调整,建议取值范围:720-1200px
- 标题栏固定在顶部,不随内容滚动,确保操作按钮始终可见
children内容区域会自动应用内边距,tabs内容需要自行控制样式- 操作按钮数量不宜过多,建议不超过 3 个
- 使用
tabs时,第一个标签页默认激活 - 关闭抽屉时建议清空选中状态,避免下次打开时显示旧数据
- 配合 InfoPanel 使用时,InfoPanel 会自动处理内边距