'use client';
import Link from 'next/link';
import { motion, AnimatePresence, useScroll, useTransform } from 'framer-motion';
import { useState, useCallback, useEffect } from 'react';
import {
HomeIcon,
ArrowLeftIcon,
ExclamationTriangleIcon,
XCircleIcon,
ClockIcon,
ServerIcon,
WifiIcon,
ClipboardDocumentIcon,
ArrowPathIcon,
CubeIcon,
QuestionMarkCircleIcon,
SparklesIcon,
RocketLaunchIcon
} from '@heroicons/react/24/outline';
import { messageManager } from './MessageNotification';
export interface ErrorPageProps {
code?: number;
title?: string;
message?: string;
description?: string;
type?: '404' | '500' | '403' | 'network' | 'timeout' | 'maintenance' | 'custom';
actions?: {
primary?: {
label: string;
href?: string;
onClick?: () => void;
};
secondary?: {
label: string;
href?: string;
onClick?: () => void;
};
};
showContact?: boolean;
showRetry?: boolean;
onRetry?: () => void;
showCopyError?: boolean;
errorDetails?: string;
className?: string;
}
const errorConfigs = {
'404': {
icon: ,
title: '页面未找到',
message: '这个页面似乎不存在于我们的世界中',
description: '页面可能已被移除、重命名,或者您输入的地址不正确。',
suggestions: [
'检查网址拼写是否正确',
'返回主页重新探索',
'使用搜索功能寻找内容'
]
},
'500': {
icon: ,
title: '服务器错误',
message: '我们的服务器遇到了一些技术问题',
description: '工程师们正在紧急修复中,请稍后再试。',
suggestions: [
'稍后刷新页面重试',
'清除浏览器缓存',
'检查网络连接'
]
},
'403': {
icon: ,
title: '访问被拒绝',
message: '您没有权限进入这个区域',
description: '请检查您的权限等级或联系管理员获取访问权限。',
suggestions: [
'确认您是否已登录',
'检查账户权限等级',
'联系管理员申请权限'
]
},
network: {
icon: ,
title: '网络连接问题',
message: '与我们的连接出现了问题',
description: '请检查您的网络连接,然后重新尝试。',
suggestions: [
'检查网络连接状态',
'尝试重新连接',
'检查防火墙设置'
]
},
timeout: {
icon: ,
title: '连接超时',
message: '服务器响应时间过长',
description: '服务器响应缓慢,请稍后再试。',
suggestions: [
'检查网络连接状态',
'稍后重新尝试连接',
'联系技术支持团队'
]
},
maintenance: {
icon: ,
title: '系统维护中',
message: '我们正在对系统进行升级改造',
description: '为了提供更好的体验,系统暂时关闭维护。',
suggestions: [
'关注官方公告获取开放时间',
'加入官方群组了解进度',
'稍后再试'
]
},
custom: {
icon: ,
title: '未知错误',
message: '发生了一些奇怪的事情',
description: '请稍后再试或联系我们的支持团队。',
suggestions: []
}
};
// Action Button Component
function ActionButton({ action, colorClass, primary = false }: {
action: { label: string; href?: string; onClick?: () => void };
colorClass?: string;
primary?: boolean;
}) {
const buttonContent = (
<>
{primary ? : }
{action.label}
>
);
const buttonClass = primary
? `inline-flex items-center justify-center px-6 py-4 bg-gradient-to-r ${colorClass} text-white font-semibold rounded-xl transition-all duration-200 shadow-lg hover:shadow-xl transform hover:scale-105`
: 'inline-flex items-center justify-center px-6 py-4 border-2 border-orange-500 text-orange-500 hover:bg-orange-500 hover:text-white font-semibold rounded-xl transition-all duration-200';
if ('href' in action && action.href) {
return (
{buttonContent}
);
} else if ('onClick' in action && action.onClick) {
return (
);
}
return null;
}
export function ErrorPage({
code,
title,
message,
description,
type = 'custom',
actions,
showContact = true,
showRetry = true,
onRetry,
showCopyError = true,
errorDetails,
className = ''
}: ErrorPageProps) {
const [isRetrying, setIsRetrying] = useState(false);
const [showDetails, setShowDetails] = useState(false);
const [copySuccess, setCopySuccess] = useState(false);
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
const { scrollYProgress } = useScroll();
const opacity = useTransform(scrollYProgress, [0, 0.3], [1, 0.8]);
const config = errorConfigs[type] || {};
const displayTitle = title || config.title || '出错了';
const displayMessage = message || config.message || '发生了一些错误';
const displayDescription = description || config.description || '';
// 生成详细的错误信息
const generateErrorDetails = useCallback(() => {
const details = {
timestamp: new Date().toISOString(),
errorType: type,
errorCode: code,
userAgent: typeof window !== 'undefined' ? window.navigator.userAgent : 'Unknown',
url: typeof window !== 'undefined' ? window.location.href : 'Unknown',
customDetails: errorDetails
};
return JSON.stringify(details, null, 2);
}, [type, code, errorDetails]);
const defaultActions = {
primary: {
label: '返回主城',
href: '/'
},
secondary: {
label: '返回上页',
onClick: () => {
if (typeof window !== 'undefined') {
window.history.back();
}
}
}
};
const finalActions = { ...defaultActions, ...actions };
const getThemeStyles = () => {
return {
bg: 'bg-gradient-to-br from-slate-50 via-orange-50 to-amber-50 dark:from-gray-900 dark:via-gray-800 dark:to-gray-900',
card: 'bg-white/70 dark:bg-gray-800/70 backdrop-blur-lg',
text: 'text-gray-900 dark:text-white',
subtext: 'text-gray-600 dark:text-gray-300',
accent: 'text-orange-500 dark:text-orange-400'
};
};
const getIconColor = () => {
const colors = {
'404': 'text-orange-500',
'500': 'text-red-500',
'403': 'text-yellow-500',
'network': 'text-blue-500',
'timeout': 'text-purple-500',
'maintenance': 'text-gray-500',
'custom': 'text-gray-500'
};
return colors[type] || 'text-gray-500';
};
const getCodeColor = () => {
const colors = {
'404': 'from-orange-400 via-orange-500 to-amber-500',
'500': 'from-red-400 via-red-500 to-pink-500',
'403': 'from-yellow-400 via-yellow-500 to-orange-500',
'network': 'from-blue-400 via-blue-500 to-cyan-500',
'timeout': 'from-purple-400 via-purple-500 to-pink-500',
'maintenance': 'from-gray-400 via-gray-500 to-slate-500',
'custom': 'from-gray-400 via-gray-500 to-slate-500'
};
return colors[type] || 'from-gray-400 via-gray-500 to-slate-500';
};
const getButtonColor = () => {
return 'from-orange-500 to-amber-500 hover:from-orange-600 hover:to-amber-600';
};
const handleRetry = async () => {
if (onRetry) {
setIsRetrying(true);
try {
await onRetry();
messageManager.success('重试成功!', { duration: 3000 });
} catch (error) {
messageManager.error('重试失败,请稍后重试', { duration: 5000 });
} finally {
setIsRetrying(false);
}
} else {
// 默认重试逻辑:刷新页面
if (typeof window !== 'undefined') {
window.location.reload();
}
}
};
const handleCopyError = async () => {
try {
const details = generateErrorDetails();
if (typeof navigator !== 'undefined' && navigator.clipboard) {
await navigator.clipboard.writeText(details);
setCopySuccess(true);
messageManager.success('错误信息已复制到剪贴板', { duration: 2000 });
setTimeout(() => setCopySuccess(false), 2000);
} else {
// 降级方案
const textArea = document.createElement('textarea');
textArea.value = details;
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
setCopySuccess(true);
messageManager.success('错误信息已复制到剪贴板', { duration: 2000 });
setTimeout(() => setCopySuccess(false), 2000);
}
} catch (error) {
messageManager.error('复制失败,请手动复制', { duration: 3000 });
}
};
const handleReportError = () => {
messageManager.info('感谢您的反馈,我们会尽快处理', { duration: 3000 });
// 这里可以添加实际的错误报告逻辑
};
// 键盘快捷键支持
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'r' && (event.ctrlKey || event.metaKey)) {
event.preventDefault();
handleRetry();
}
};
if (typeof window !== 'undefined') {
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}
}, [handleRetry]);
const themeStyles = getThemeStyles();
return (
{/* Animated Background - 简化背景动画 */}
{/* Main Content - 考虑navbar高度的居中布局 */}
{/* Error Code - 更突出的错误代码 */}
{code && (
{code}
)}
{/* Main Error Message - 简洁有力的错误信息 */}
{displayTitle}
{displayMessage}
{displayDescription && (
{displayDescription}
)}
{/* Icon - 更简洁的图标展示 */}
{config.icon || }
{/* Action Buttons - 更简洁的按钮布局 */}
{finalActions.primary && (
)}
{finalActions.secondary && (
)}
{showRetry && (
{isRetrying ? '重试中...' : '重新加载'}
)}
{/* Suggestions - 更简洁的建议展示 */}
{config.suggestions && config.suggestions.length > 0 && (
您可以尝试:
{config.suggestions.map((suggestion, index) => (
{suggestion}
))}
)}
{/* Error Details - 更简洁的错误详情 */}
{showCopyError && (
{showDetails && (
错误详情:
{generateErrorDetails()}
)}
)}
{/* Contact Info - 更简洁的联系信息 */}
{showContact && (
问题仍未解决?
{
e.preventDefault();
messageManager.info('联系我们页面正在开发中', { duration: 3000 });
}}
>
联系我们
)}
);
}
// 预设的错误页面组件
export function NotFoundPage() {
return ;
}
export function ServerErrorPage() {
return ;
}
export function ForbiddenPage() {
return ;
}
export function NetworkErrorPage() {
return ;
}
export function TimeoutErrorPage() {
return ;
}
export function MaintenancePage() {
return ;
}
// 现代化的错误页面
export function ModernNotFoundPage() {
return ;
}
export function ModernServerErrorPage() {
return ;
}
// 使用示例和最佳实践
/*
增强后的ErrorPage组件提供了以下改进:
1. **统一的消息提示系统**
- 使用MessageNotification组件替代alert
- 支持成功、错误、警告、信息、加载等多种消息类型
- 更好的用户体验和视觉效果
2. **多种主题风格**
- Minecraft风格:适合游戏相关网站
- Modern风格:现代化简洁设计
- Minimal风格:极简主义设计
3. **增强的功能**
- 重试功能,支持自定义重试逻辑
- 错误信息复制功能
- 错误详情显示/隐藏
- 键盘快捷键支持 (Ctrl+R/⌘+R 重试)
- 进度条显示(可选)
- 自定义操作按钮
4. **改进的用户体验**
- 针对每种错误类型提供具体建议
- 动态颜色主题匹配错误类型
- 平滑的动画过渡效果
- 响应式设计,适配移动端
- Minecraft风格的游戏化提示
5. **更好的错误处理**
- 详细的错误信息生成
- 错误报告功能
- 降级处理(如复制功能)
- 支持自定义错误详情
使用示例:
```tsx
// 基础使用 - Minecraft风格404页面
// 现代风格500错误
// 自定义错误信息
// 自定义操作按钮
reconnect()
},
secondary: {
label: '离线模式',
href: '/offline'
}
}}
/>
// 启用重试功能
{
// 自定义重试逻辑
await fetchData();
}}
/>
// 显示错误详情
```
预设组件使用:
```tsx
// 在页面中使用预设的错误组件
import { NotFoundPage, ServerErrorPage, ModernNotFoundPage } from '@/components/ErrorPage';
// Minecraft风格404页面
export default function Custom404() {
return ;
}
// 现代化404页面
export default function Modern404() {
return ;
}
// 500页面
export default function Custom500() {
return ;
}
```
*/