| |
| |
| |
| |
|
|
| import React from 'react'; |
| import { Loader2 } from 'lucide-react'; |
|
|
| interface ProgressiveSkeletonProps { |
| type: 'card' | 'summary' | 'chart' | 'kline'; |
| title?: string; |
| lines?: number; |
| height?: number; |
| } |
|
|
| const ProgressiveSkeleton: React.FC<ProgressiveSkeletonProps> = ({ |
| type, |
| title, |
| lines = 4, |
| height = 200, |
| }) => { |
| |
| if (type === 'card') { |
| return ( |
| <div className="bg-white p-6 rounded-xl shadow-sm border border-gray-100 h-full animate-pulse"> |
| {/* 标题行 */} |
| <div className="flex items-center justify-between mb-4"> |
| <div className="flex items-center gap-2"> |
| <div className="w-5 h-5 bg-gray-200 rounded" /> |
| <div className="h-5 w-24 bg-gray-200 rounded" /> |
| </div> |
| <div className="w-4 h-4 bg-gray-200 rounded" /> |
| </div> |
| |
| {/* 内容行 */} |
| <div className="space-y-3 flex-grow"> |
| {Array.from({ length: lines }).map((_, i) => ( |
| <div |
| key={i} |
| className="h-3 bg-gray-200 rounded" |
| style={{ width: `${85 - i * 10}%` }} |
| /> |
| ))} |
| </div> |
| |
| {/* 评分条 */} |
| <div className="pt-4 mt-4 border-t border-gray-50"> |
| <div className="h-2 bg-gray-100 rounded mb-2 w-16" /> |
| <div className="flex items-center gap-3"> |
| <div className="flex-1 h-2 bg-gray-100 rounded-full overflow-hidden"> |
| <div className="h-full bg-gray-200 w-1/2 animate-pulse" /> |
| </div> |
| <div className="h-4 w-12 bg-gray-200 rounded" /> |
| </div> |
| </div> |
| |
| {/* 加载指示器 */} |
| <div className="absolute inset-0 flex items-center justify-center bg-white/50 rounded-xl"> |
| <div className="flex items-center gap-2 text-gray-400"> |
| <Loader2 className="w-5 h-5 animate-spin" /> |
| <span className="text-sm">{title ? `分析${title}中...` : '分析中...'}</span> |
| </div> |
| </div> |
| </div> |
| ); |
| } |
|
|
| |
| if (type === 'summary') { |
| return ( |
| <div className="bg-gradient-to-br from-indigo-50 to-white p-6 rounded-xl border border-indigo-100 shadow-sm animate-pulse"> |
| <div className="flex flex-col md:flex-row md:items-center justify-between gap-4 mb-4"> |
| <div className="flex items-center gap-2"> |
| <div className="w-5 h-5 bg-indigo-200 rounded" /> |
| <div className="h-6 w-24 bg-indigo-200 rounded" /> |
| </div> |
| <div className="w-full md:w-1/3"> |
| <div className="flex items-center gap-3"> |
| <div className="flex-1 h-2 bg-indigo-100 rounded-full" /> |
| <div className="h-4 w-12 bg-indigo-200 rounded" /> |
| </div> |
| </div> |
| </div> |
| |
| {/* 内容行 */} |
| <div className="space-y-3"> |
| <div className="h-4 bg-indigo-100 rounded w-full" /> |
| <div className="h-4 bg-indigo-100 rounded w-11/12" /> |
| <div className="h-4 bg-indigo-100 rounded w-10/12" /> |
| <div className="h-4 bg-indigo-100 rounded w-9/12" /> |
| </div> |
| |
| {/* 中心加载指示 */} |
| <div className="flex items-center justify-center mt-4 text-indigo-400"> |
| <Loader2 className="w-5 h-5 animate-spin mr-2" /> |
| <span className="text-sm">正在深度分析命理...</span> |
| </div> |
| </div> |
| ); |
| } |
|
|
| |
| if (type === 'kline' || type === 'chart') { |
| return ( |
| <div className="bg-white p-6 rounded-xl shadow-sm border border-gray-100 animate-pulse"> |
| <div className="flex items-center justify-between mb-4"> |
| <div className="flex items-center gap-2"> |
| <div className="w-5 h-5 bg-gray-200 rounded" /> |
| <div className="h-5 w-32 bg-gray-200 rounded" /> |
| </div> |
| </div> |
| |
| {/* K线图占位 */} |
| <div |
| className="bg-gray-100 rounded-lg flex items-center justify-center" |
| style={{ height: `${height}px` }} |
| > |
| <div className="flex flex-col items-center text-gray-400"> |
| <Loader2 className="w-8 h-8 animate-spin mb-2" /> |
| <span className="text-sm">正在生成100年K线数据...</span> |
| <span className="text-xs mt-1 text-gray-300">这可能需要10-15秒</span> |
| </div> |
| </div> |
| |
| {/* 底部时间轴占位 */} |
| <div className="mt-4 flex justify-between"> |
| {Array.from({ length: 10 }).map((_, i) => ( |
| <div key={i} className="h-3 w-8 bg-gray-200 rounded" /> |
| ))} |
| </div> |
| </div> |
| ); |
| } |
|
|
| |
| return ( |
| <div className="bg-white p-6 rounded-xl shadow-sm border border-gray-100 animate-pulse"> |
| <div className="h-4 bg-gray-200 rounded w-3/4 mb-4" /> |
| <div className="space-y-2"> |
| {Array.from({ length: lines }).map((_, i) => ( |
| <div key={i} className="h-3 bg-gray-200 rounded" style={{ width: `${90 - i * 5}%` }} /> |
| ))} |
| </div> |
| </div> |
| ); |
| }; |
|
|
| |
| export const SkeletonGrid: React.FC<{ count?: number }> = ({ count = 9 }) => { |
| return ( |
| <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> |
| {Array.from({ length: count }).map((_, i) => ( |
| <ProgressiveSkeleton key={i} type="card" lines={4} /> |
| ))} |
| </div> |
| ); |
| }; |
|
|
| |
| export const FullPageSkeleton: React.FC = () => { |
| return ( |
| <div className="w-full space-y-8"> |
| {/* Agent状态栏 */} |
| <div className="bg-gray-50 p-4 rounded-xl"> |
| <div className="flex flex-wrap gap-2 justify-center"> |
| {['核心命理', 'K线数据', '事业财富', '婚姻健康', '币圈分析'].map((name, i) => ( |
| <div key={i} className="flex items-center gap-2 px-3 py-1.5 rounded-full bg-blue-50 animate-pulse"> |
| <Loader2 className="w-4 h-4 text-blue-500 animate-spin" /> |
| <span className="text-xs font-medium text-blue-500">{name}</span> |
| </div> |
| ))} |
| </div> |
| <div className="mt-3 text-center text-xs text-gray-500 animate-pulse"> |
| 正在启动5个专业Agent并行分析... |
| </div> |
| </div> |
| |
| {/* 八字展示 */} |
| <div className="flex justify-center gap-2 md:gap-8 bg-gray-900 p-6 rounded-xl animate-pulse"> |
| {['年柱', '月柱', '日柱', '时柱'].map((label, i) => ( |
| <div key={i} className="text-center min-w-[60px]"> |
| <div className="text-xs text-gray-500 mb-1">{label}</div> |
| <div className="h-8 w-12 bg-gray-700 rounded mx-auto" /> |
| </div> |
| ))} |
| </div> |
| |
| {/* 总评 */} |
| <ProgressiveSkeleton type="summary" /> |
| |
| {/* 卡片网格 */} |
| <SkeletonGrid count={9} /> |
| </div> |
| ); |
| }; |
|
|
| export default ProgressiveSkeleton; |
|
|