nex_design/docs/components/ButtonExtension.md

17 KiB
Raw Blame History

按钮扩展组件设计文档

版本: v1.2.0 更新时间: 2025-11-17 作者: Nex Design Team 状态: 已完成


📖 目录

  1. 概述
  2. 设计方案
  3. 组件API
  4. 使用指南
  5. 最佳实践
  6. 更新日志

概述

设计目标

为终端列表页面的按钮提供清晰的操作介绍和帮助信息,解决以下问题:

  • 用户不了解按钮功能
  • 复杂操作缺少引导
  • 需要快速的上下文帮助

设计原则

  1. 简洁至上 - 去除不必要的动画,使用扁平化设计
  2. 功能独立 - 帮助功能不影响主操作流程
  3. 易于理解 - 直观的图标和交互模式
  4. 现代美学 - 符合Material Design和Fluent Design趋势

设计方案

我们提供了5种不同的设计方案可根据实际场景选择使用

方案1增强型工具提示 (Enhanced Tooltip)

特点:

  • 渐变色彩背景,视觉效果出众
  • 支持标题、描述、快捷键、注意事项
  • 带有脉冲动画的提示图标
  • 响应式设计,移动端友好

适用场景:

  • 简单操作,需要快速了解功能
  • 不希望占用额外页面空间
  • 信息量较少的提示

效果预览:

[按钮] (i) ← 脉冲动画图标
  ↓ 悬停
┌────────────────────────┐
│ 新增主机                │
│ 向系统中添加新的主机... │
│ 快捷键: Ctrl+N         │
│ 注意: 请确保IP不冲突   │
└────────────────────────┘

方案2智能帮助面板 (Smart Help Panel) 推荐

特点:

  • 侧边抽屉式设计,信息完整
  • 实时显示当前悬停按钮的详细信息
  • 包含使用场景、操作步骤、注意事项等
  • 支持查看所有可用操作的快速索引
  • 点击"?"图标直接打开帮助面板

适用场景:

  • 复杂业务操作,需要详细说明
  • 新用户培训和引导
  • 功能较多,需要分步骤引导

交互流程:

1. 悬停按钮 → 出现"?"图标
2. 点击"?" → 打开右侧帮助面板
3. 显示详细内容:
   - 功能说明
   - 使用场景
   - 操作步骤
   - 注意事项
   - 快捷键
   - 权限要求

特殊优化:

  • 面板打开时,鼠标离开按钮不会清空内容
  • 点击"所有可用操作"卡片可切换内容
  • 自动展开"当前操作"区域

方案3悬浮展开卡片 (Hover Expand Card)

特点:

  • 精美的悬浮卡片设计
  • 滑入动画,视觉流畅
  • 分层信息展示
  • 使用React Portal渲染永不被遮挡

适用场景:

  • 需要展示较多信息
  • 不想打断操作流程
  • 希望信息就近显示

技术实现:

// 使用Portal避免z-index遮挡问题
import { createPortal } from 'react-dom'

{createPortal(renderCard(), document.body)}

方案4智能引导 (Smart Guide)

特点:

  • 简洁扁平设计,独立帮助图标
  • 点击查看弹窗式详细引导
  • 包含步骤式操作说明

适用场景:

  • 需要详细引导,但不希望干扰主流程
  • 功能上线初期的用户引导
  • 复杂操作的分步说明

设计对比:

旧版(已废弃):脉冲动画徽章,过于复杂
新版:简洁帮助图标,点击查看详情

┌────────────┐  ┌─┐
│ 新增主机   │  │?│  ← 简洁优雅
└────────────┘  └─┘

方案5底部固定提示栏 (Bottom Hint Bar)

特点:

  • 固定底部位置,不遮挡操作区域
  • 实时更新,流畅切换
  • 三种主题可选(渐变、浅色、深色)

适用场景:

  • 需要始终可见的提示信息
  • 不希望被tooltip遮挡操作区域
  • 简单的实时信息展示

组件API

ButtonWithTip

增强型工具提示按钮组件。

Props:

interface ButtonWithTipProps {
  label: string                 // 按钮文本
  icon?: ReactNode             // 按钮图标
  type?: 'primary' | 'default' // 按钮类型
  danger?: boolean             // 是否为危险按钮
  disabled?: boolean           // 是否禁用
  onClick?: () => void         // 点击回调
  size?: 'small' | 'middle' | 'large'
  showTipIcon?: boolean        // 是否显示提示图标
  tip?: {
    title?: string             // 提示标题
    description?: string       // 详细描述
    shortcut?: string          // 快捷键
    notes?: string[]           // 注意事项
    placement?: 'top' | 'bottom' | 'left' | 'right'
  }
}

使用示例:

import ButtonWithTip from '@/components/ButtonWithTip/ButtonWithTip'

<ButtonWithTip
  label="新增主机"
  icon={<PlusOutlined />}
  type="primary"
  tip={{
    title: '新增主机',
    description: '向系统中添加新的主机终端设备',
    shortcut: 'Ctrl+N',
    notes: ['请确保IP地址不与现有主机冲突', 'MAC地址必须唯一']
  }}
  onClick={handleAdd}
/>

ActionHelpPanel

智能帮助面板组件方案2

Props:

interface ActionHelpPanelProps {
  visible: boolean              // 是否显示面板
  onClose: () => void          // 关闭回调
  currentAction?: {            // 当前操作信息
    title: string
    icon: ReactNode
    description: string
    scenarios?: string[]       // 使用场景
    steps?: string[]           // 操作步骤
    warnings?: string[]        // 注意事项
    shortcut?: string          // 快捷键
    permission?: string        // 权限要求
    badge?: {
      text: string
      color: string
    }
  }
  allActions?: Array<Action>   // 所有可用操作
  placement?: 'left' | 'right' // 面板位置
  onActionSelect?: (action: Action) => void  // 选择操作回调
}

使用示例:

import ActionHelpPanel from '@/components/ActionHelpPanel/ActionHelpPanel'

const [showPanel, setShowPanel] = useState(false)
const [currentAction, setCurrentAction] = useState(null)

// 在按钮外层添加hover和点击事件
<div
  onMouseEnter={() => setCurrentAction(actionsConfig.add)}
  onMouseLeave={() => !showPanel && setCurrentAction(null)}
>
  <Button>新增主机</Button>
  <button onClick={() => setShowPanel(true)}>?</button>
</div>

<ActionHelpPanel
  visible={showPanel}
  onClose={() => setShowPanel(false)}
  currentAction={currentAction}
  allActions={Object.values(actionsConfig)}
  onActionSelect={(action) => setCurrentAction(action)}
/>

ButtonWithHoverCard

悬浮展开卡片按钮组件方案3

Props:

interface ButtonWithHoverCardProps {
  label: string
  icon?: ReactNode
  type?: 'primary' | 'default'
  danger?: boolean
  disabled?: boolean
  onClick?: () => void
  size?: 'small' | 'middle' | 'large'
  cardInfo?: {
    title: string
    icon: ReactNode
    description: string
    scenarios?: string[]
    quickTips?: string[]
    warnings?: string[]
    shortcut?: string
    badge?: { text: string; color: string }
  }
}

技术要点:

  • 使用 createPortal 渲染卡片到 document.body
  • 使用 useRef 获取按钮位置
  • 避免z-index遮挡问题

ButtonWithGuide

智能引导按钮组件方案4

Props:

interface ButtonWithGuideProps {
  label: string
  icon?: ReactNode
  type?: 'primary' | 'default'
  danger?: boolean
  disabled?: boolean
  onClick?: () => void
  size?: 'small' | 'middle' | 'large'
  guide?: {
    title: string
    icon: ReactNode
    description: string
    steps?: string[]
    scenarios?: string[]
    warnings?: string[]
    shortcut?: string
    permission?: string
    badge?: { text: string; color: string }
  }
}

特点:

  • 简洁的帮助图标(不影响按钮文字)
  • 点击打开弹窗式详细引导
  • 包含步骤式说明使用Ant Design Steps组件

BottomHintBar

底部固定提示栏组件方案5

Props:

interface BottomHintBarProps {
  visible: boolean
  hintInfo?: {
    title: string
    icon: ReactNode
    description: string
    quickTip?: string
    warning?: string
    shortcut?: string
    badge?: { text: string; color: string }
  }
  onClose?: () => void
  theme?: 'light' | 'dark' | 'gradient'
}

使用示例:

import BottomHintBar from '@/components/BottomHintBar/BottomHintBar'

const [showHint, setShowHint] = useState(false)
const [hintInfo, setHintInfo] = useState(null)

<div
  onMouseEnter={() => {
    setHintInfo(actionConfig)
    setShowHint(true)
  }}
  onMouseLeave={() => setShowHint(false)}
>
  <Button>操作按钮</Button>
</div>

<BottomHintBar
  visible={showHint}
  hintInfo={hintInfo}
  theme="gradient"
/>

使用指南

快速开始

  1. 选择合适的方案

根据使用场景选择组件:

场景 推荐方案
简单列表页面,快速提示 方案1 增强型工具提示
复杂后台系统,详细引导 方案2 智能帮助面板
数据密集页面,信息丰富 方案3 悬浮展开卡片
新功能上线,用户引导 方案4 智能引导
操作频繁,始终可见 方案5 底部固定提示栏
  1. 导入组件
// 方案1
import ButtonWithTip from '@/components/ButtonWithTip/ButtonWithTip'

// 方案2
import ActionHelpPanel from '@/components/ActionHelpPanel/ActionHelpPanel'

// 方案3
import ButtonWithHoverCard from '@/components/ButtonWithHoverCard/ButtonWithHoverCard'

// 方案4
import ButtonWithGuide from '@/components/ButtonWithGuide/ButtonWithGuide'

// 方案5
import BottomHintBar from '@/components/BottomHintBar/BottomHintBar'
  1. 配置操作信息

建议将操作配置统一管理:

const actionsConfig = {
  add: {
    title: '新增主机',
    icon: <PlusOutlined />,
    description: '向系统中添加新的主机终端设备',
    scenarios: [
      '当有新设备需要接入系统管理时',
      '需要扩展终端设备数量时'
    ],
    steps: [
      '点击"新增主机"按钮',
      '填写主机基本信息',
      '选择主机所属分组',
      '保存并等待主机上线'
    ],
    warnings: [
      '请确保IP地址不与现有主机冲突',
      'MAC地址必须唯一且格式正确'
    ],
    shortcut: 'Ctrl+N',
    permission: '管理员权限',
    badge: { text: '常用', color: 'blue' }
  },
  // ... 其他操作
}

方案2完整实现示例

import { useState } from 'react'
import { Button } from 'antd'
import { PlusOutlined } from '@ant-design/icons'
import ActionHelpPanel from '@/components/ActionHelpPanel/ActionHelpPanel'

function MyPage() {
  const [showHelpPanel, setShowHelpPanel] = useState(false)
  const [currentAction, setCurrentAction] = useState(null)

  // 处理悬停
  const handleHover = (actionKey) => {
    if (!showHelpPanel) {
      setCurrentAction(actionsConfig[actionKey])
    }
  }

  // 处理离开
  const handleLeave = () => {
    if (!showHelpPanel) {
      setCurrentAction(null)
    }
  }

  // 处理点击
  const handleHelpClick = (e, actionKey) => {
    e.stopPropagation()
    setCurrentAction(actionsConfig[actionKey])
    setShowHelpPanel(true)
  }

  return (
    <div>
      {/* 按钮区域 */}
      <div
        className="button-wrapper"
        onMouseEnter={() => handleHover('add')}
        onMouseLeave={handleLeave}
      >
        <Button type="primary" icon={<PlusOutlined />}>
          新增主机
        </Button>
        <button
          className="help-icon"
          onClick={(e) => handleHelpClick(e, 'add')}
        >
          ?
        </button>
      </div>

      {/* 帮助面板 */}
      <ActionHelpPanel
        visible={showHelpPanel}
        onClose={() => setShowHelpPanel(false)}
        currentAction={currentAction}
        allActions={Object.values(actionsConfig)}
        onActionSelect={(action) => setCurrentAction(action)}
      />
    </div>
  )
}

最佳实践

1. 内容编写规范

功能描述:

  • 用简洁的语言描述功能1-2句话
  • 突出重点和目的
  • 避免技术术语

使用场景:

  • 列举2-4个实际使用场景
  • 使用"当...时"的句式
  • 帮助用户理解何时使用

操作步骤:

  • 按实际操作顺序编写
  • 每步一句话,清晰明确
  • 使用动词开头

注意事项:

  • 危险操作必须有明确警告
  • 使用醒目的颜色标记
  • 提供实际的使用建议

2. 性能优化

// ✅ 使用useMemo缓存配置
const actionsConfig = useMemo(() => ({
  add: { ... },
  delete: { ... }
}), [])

// ✅ 防止不必要的重渲染
const handleHover = useCallback((key) => {
  if (!showPanel) {
    setCurrentAction(actionsConfig[key])
  }
}, [showPanel, actionsConfig])

// ✅ 面板打开时避免hover更新
if (!showPanel) {
  setCurrentAction(null)
}

3. 可访问性

// 添加title属性
<button className="help-icon" title="查看帮助">
  ?
</button>

// 添加aria标签
<Button aria-label="新增主机">
  新增主机
</Button>

// 快捷键支持
useEffect(() => {
  const handleKeyPress = (e) => {
    if (e.ctrlKey && e.key === 'n') {
      handleAdd()
    }
  }
  window.addEventListener('keydown', handleKeyPress)
  return () => window.removeEventListener('keydown', handleKeyPress)
}, [])

4. 响应式适配

/* 移动端隐藏某些提示 */
@media (max-width: 768px) {
  .help-icon {
    display: none;
  }

  .hover-info-card {
    width: 100%;
    left: 0 !important;
    transform: translateY(-50%);
  }
}

更新日志

v1.2.0 (2025-11-17)

新增:

  • 新增 ButtonWithGuide 组件,替代复杂的徽章设计
  • 方案2添加 onActionSelect 回调,支持点击卡片切换内容

修复:

  • 🐛 修复方案2点击"?"后内容消失的问题
  • 🐛 修复方案2"所有可用操作"卡片无法点击的问题
  • 🐛 修复方案3悬浮卡片被Card遮挡使用Portal
  • 🐛 修复菜单图标,从 GlobalOutlined 改为 BlockOutlined

优化:

  • 💄 简化方案4设计去掉复杂的脉冲动画
  • 💄 方案2面板打开时不响应鼠标hover事件
  • 💄 提升整体视觉一致性

v1.1.0 (2025-11-17)

新增:

  • 新增 ActionHelpPanel 智能帮助面板组件
  • 新增 BottomHintBar 底部提示栏组件
  • 方案2添加点击"?"图标直接打开面板功能

修复:

  • 🐛 修复 ButtonWithTip 的 Tooltip overlayClassName 警告
  • 🐛 修复 AppSider 的 findDOMNode 警告(使用 items 配置)
  • 🐛 修复方案5底部提示栏鼠标移动时闪烁的问题

优化:

  • 💄 为方案2按钮添加悬停时的"?"图标提示
  • 💄 提升方案3悬浮卡片的 z-index 和阴影效果

v1.0.0 (2025-11-17)

初始版本:

  • 🎉 发布5种按钮扩展设计方案
  • 📦 提供完整的组件API和使用文档
  • 📝 提供详细的设计指南和最佳实践

技术架构

依赖项

{
  "react": "^18.2.0",
  "react-dom": "^18.2.0",
  "antd": "^5.x",
  "@ant-design/icons": "^5.x"
}

目录结构

src/components/
├── ButtonWithTip/
│   ├── ButtonWithTip.jsx
│   └── ButtonWithTip.css
├── ActionHelpPanel/
│   ├── ActionHelpPanel.jsx
│   └── ActionHelpPanel.css
├── ButtonWithHoverCard/
│   ├── ButtonWithHoverCard.jsx
│   └── ButtonWithHoverCard.css
├── ButtonWithGuide/
│   ├── ButtonWithGuide.jsx
│   └── ButtonWithGuide.css
└── BottomHintBar/
    ├── BottomHintBar.jsx
    └── BottomHintBar.css

src/pages/
└── AllButtonDesigns.jsx        # 演示页面

docs/
└── components/
    └── ButtonExtensions.md      # 本文档

常见问题

Q1: 如何选择合适的方案?

A: 根据以下因素选择:

  • 信息量:少 → 方案1多 → 方案2/3
  • 复杂度:简单 → 方案1/5复杂 → 方案2/4
  • 使用频率:频繁 → 方案5偶尔 → 方案1/3
  • 用户类型:新手 → 方案2/4熟练 → 方案1/5

Q2: 可以混合使用多种方案吗?

A: 可以。建议:

  • 同一页面使用统一的方案
  • 不同页面可以使用不同方案
  • 核心操作使用方案2次要操作使用方案1

Q3: 如何自定义样式?

A: 所有组件都支持自定义样式:

// 通过className
<ButtonWithTip className="my-custom-button" />

// 通过style
<ButtonWithTip style={{ marginRight: 16 }} />

// 修改CSS变量
:root {
  --tip-primary-gradient: linear-gradient(...);
}

Q4: 移动端如何适配?

A: 所有组件都内置了响应式支持:

  • 方案1移动端隐藏提示图标
  • 方案2面板宽度自适应
  • 方案3卡片居中显示
  • 方案5提示栏自适应布局

最后更新: 2025-11-17 文档版本: v1.2.0