/** * Stepper 步骤条组件 — 用于多步骤工作流引导 * 支持横向/纵向布局、可点击步骤导航、完成/当前/待办状态 */ import clsx from 'clsx' import { Check } from 'lucide-react' export interface Step { key: string title: string description?: string status: 'complete' | 'current' | 'pending' | 'error' } interface StepperProps { steps: Step[] orientation?: 'horizontal' | 'vertical' onStepClick?: (key: string) => void className?: string } /** * Stepper 步骤条 — 展示工作流进度和步骤导航 */ export function Stepper({ steps, orientation = 'horizontal', onStepClick, className }: StepperProps) { if (orientation === 'vertical') { return (
{steps.map((step, i) => { const isLast = i === steps.length - 1 const isClickable = onStepClick && (step.status === 'complete' || step.status === 'current') return (
{/* 左侧指示器 + 连接线 */}
{!isLast && (
)}
{/* 右侧内容 */}
onStepClick?.(step.key) : undefined} className={clsx( 'text-sm font-medium', step.status === 'current' && 'text-ink-900', step.status === 'complete' && 'text-ink-700', step.status === 'pending' && 'text-ink-400', step.status === 'error' && 'text-danger', isClickable && 'cursor-pointer hover:text-ink-900', )} > {step.title}
{step.description && (
{step.description}
)}
) })}
) } // 横向布局 return (
{steps.map((step, i) => { const isLast = i === steps.length - 1 const isClickable = onStepClick && (step.status === 'complete' || step.status === 'current') return (
{/* 圆点 + 标题 */}
onStepClick?.(step.key) : undefined} className={clsx( 'text-sm font-medium whitespace-nowrap', step.status === 'current' && 'text-ink-900', step.status === 'complete' && 'text-ink-700', step.status === 'pending' && 'text-ink-400', step.status === 'error' && 'text-danger', isClickable && 'cursor-pointer hover:text-ink-900', )} > {step.title}
{step.description && (
{step.description}
)}
{/* 连接线 */} {!isLast && (
)}
) })}
) }