File size: 2,683 Bytes
a43ac26
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import React from 'react';
import { cn } from '@/lib/utils';
import { Loader2, AlertCircle, CheckCircle2, XCircle, Inbox } from 'lucide-react';

/**
 * 控制台状态面板组件
 * 统一展示 loading/empty/error/success/disabled 状态
 */

type PanelState = 'loading' | 'empty' | 'error' | 'success' | 'disabled';

interface StatePanelProps {
  /** 面板状态 */
  state: PanelState;
  /** 标题 */
  title?: string;
  /** 描述信息 */
  description?: string;
  /** 错误时的错误信息 */
  error?: string;
  /** 自定义图标 */
  icon?: React.ReactNode;
  /** 操作按钮 */
  action?: React.ReactNode;
  /** 额外的 className */
  className?: string;
}

// 状态图标映射
const stateIcons: Record<PanelState, React.ReactNode> = {
  loading: <Loader2 className="w-5 h-5 text-console-text-muted animate-spin" />,
  empty: <Inbox className="w-5 h-5 text-console-text-muted" />,
  error: <XCircle className="w-5 h-5 text-console-status-error" />,
  success: <CheckCircle2 className="w-5 h-5 text-console-status-enabled" />,
  disabled: <AlertCircle className="w-5 h-5 text-console-text-muted" />,
};

// 状态默认文案
const stateTexts: Record<PanelState, { title: string; description: string }> = {
  loading: { title: '加载中', description: '正在获取数据...' },
  empty: { title: '暂无数据', description: '当前没有可显示的内容' },
  error: { title: '加载失败', description: '数据获取出错,请稍后重试' },
  success: { title: '操作成功', description: '已完成' },
  disabled: { title: '已禁用', description: '此功能当前不可用' },
};

/**
 * 状态面板组件
 * 用于控制台中统一展示各种状态场景
 */
export function StatePanel({
  state,
  title,
  description,
  error,
  icon,
  action,
  className,
}: StatePanelProps) {
  const defaults = stateTexts[state];
  const displayTitle = title || defaults.title;
  const displayDescription = error || description || defaults.description;

  return (
    <div
      className={cn(
        'flex flex-col items-center justify-center py-8 px-4 text-center',
        className
      )}
      role="status"
      aria-live="polite"
    >
      {/* 图标 */}
      <div className="mb-3">
        {icon || stateIcons[state]}
      </div>

      {/* 标题 */}
      <h3 className="text-sm font-medium text-console-text-primary mb-1">
        {displayTitle}
      </h3>

      {/* 描述 */}
      <p className="text-xs text-console-text-secondary max-w-[280px]">
        {displayDescription}
      </p>

      {/* 操作按钮 */}
      {action && (
        <div className="mt-4">
          {action}
        </div>
      )}
    </div>
  );
}