unis_sip/oms_web/docs/components/DetailDrawer.md

12 KiB
Raw Blame History

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 - 标签页内容

使用场景

  1. 查看详细信息 - 点击列表行显示详细信息
  2. 多标签页详情 - 在详情中展示不同类型的关联数据
  3. 带快捷操作的详情 - 在详情顶部提供编辑、删除等操作
  4. 复杂数据展示 - 配合 InfoPanel、Card 等组件展示复杂信息

注意事项

  1. 抽屉宽度默认 1080px可根据内容调整建议取值范围720-1200px
  2. 标题栏固定在顶部,不随内容滚动,确保操作按钮始终可见
  3. children 内容区域会自动应用内边距,tabs 内容需要自行控制样式
  4. 操作按钮数量不宜过多,建议不超过 3 个
  5. 使用 tabs 时,第一个标签页默认激活
  6. 关闭抽屉时建议清空选中状态,避免下次打开时显示旧数据
  7. 配合 InfoPanel 使用时InfoPanel 会自动处理内边距