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>
);
}
|