forked from CarrotSkin/carrotskin
feat: 完成navbar隐藏优化和侧边栏冻结功能
- 优化navbar滚动隐藏逻辑,更敏感响应 - 添加返回顶部按钮,固定在右下角 - 实现profile页面侧边栏真正冻结效果 - 修复首页滑动指示器位置 - 优化整体布局确保首屏内容完整显示
This commit is contained in:
279
src/components/ErrorPage.tsx
Normal file
279
src/components/ErrorPage.tsx
Normal file
@@ -0,0 +1,279 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { motion } from 'framer-motion';
|
||||
import {
|
||||
HomeIcon,
|
||||
ArrowLeftIcon,
|
||||
ExclamationTriangleIcon,
|
||||
XCircleIcon,
|
||||
ClockIcon,
|
||||
ServerIcon,
|
||||
WifiIcon
|
||||
} from '@heroicons/react/24/outline';
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const errorConfigs = {
|
||||
'404': {
|
||||
icon: <XCircleIcon className="w-16 h-16" />,
|
||||
title: '页面不见了',
|
||||
message: '抱歉,我们找不到您要访问的页面。',
|
||||
description: '它可能已被移动、删除,或者您输入的链接不正确。'
|
||||
},
|
||||
'500': {
|
||||
icon: <ServerIcon className="w-16 h-16" />,
|
||||
title: '服务器错误',
|
||||
message: '抱歉,服务器遇到了一些问题。',
|
||||
description: '我们的团队正在努力解决这个问题,请稍后再试。'
|
||||
},
|
||||
'403': {
|
||||
icon: <ExclamationTriangleIcon className="w-16 h-16" />,
|
||||
title: '访问被拒绝',
|
||||
message: '抱歉,您没有权限访问此页面。',
|
||||
description: '请检查您的账户权限或联系管理员。'
|
||||
},
|
||||
'network': {
|
||||
icon: <WifiIcon className="w-16 h-16" />,
|
||||
title: '网络连接错误',
|
||||
message: '无法连接到服务器。',
|
||||
description: '请检查您的网络连接,然后重试。'
|
||||
},
|
||||
'timeout': {
|
||||
icon: <ClockIcon className="w-16 h-16" />,
|
||||
title: '请求超时',
|
||||
message: '请求处理时间过长。',
|
||||
description: '请刷新页面或稍后再试。'
|
||||
},
|
||||
'maintenance': {
|
||||
icon: <ServerIcon className="w-16 h-16" />,
|
||||
title: '系统维护中',
|
||||
message: '我们正在进行系统维护。',
|
||||
description: '请稍后再试,我们会尽快恢复服务。'
|
||||
}
|
||||
};
|
||||
|
||||
export function ErrorPage({
|
||||
code,
|
||||
title,
|
||||
message,
|
||||
description,
|
||||
type = 'custom',
|
||||
actions,
|
||||
showContact = true
|
||||
}: ErrorPageProps) {
|
||||
const config = errorConfigs[type] || {};
|
||||
const displayTitle = title || config.title || '出错了';
|
||||
const displayMessage = message || config.message || '发生了一些错误';
|
||||
const displayDescription = description || config.description || '';
|
||||
|
||||
const defaultActions = {
|
||||
primary: {
|
||||
label: '返回主页',
|
||||
href: '/'
|
||||
},
|
||||
secondary: {
|
||||
label: '返回上页',
|
||||
onClick: () => window.history.back()
|
||||
}
|
||||
};
|
||||
|
||||
const finalActions = { ...defaultActions, ...actions };
|
||||
|
||||
const getIconColor = () => {
|
||||
switch (type) {
|
||||
case '404': return 'text-orange-500';
|
||||
case '500': return 'text-red-500';
|
||||
case '403': return 'text-yellow-500';
|
||||
case 'network': return 'text-blue-500';
|
||||
case 'timeout': return 'text-purple-500';
|
||||
case 'maintenance': return 'text-gray-500';
|
||||
default: return 'text-orange-500';
|
||||
}
|
||||
};
|
||||
|
||||
const getCodeColor = () => {
|
||||
switch (type) {
|
||||
case '404': return 'from-orange-400 via-orange-500 to-amber-500';
|
||||
case '500': return 'from-red-400 via-red-500 to-pink-500';
|
||||
case '403': return 'from-yellow-400 via-yellow-500 to-orange-500';
|
||||
case 'network': return 'from-blue-400 via-blue-500 to-cyan-500';
|
||||
case 'timeout': return 'from-purple-400 via-purple-500 to-pink-500';
|
||||
case 'maintenance': return 'from-gray-400 via-gray-500 to-slate-500';
|
||||
default: return 'from-orange-400 via-orange-500 to-amber-500';
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-orange-50 via-white to-amber-50 dark:from-gray-900 dark:via-gray-800 dark:to-gray-900">
|
||||
<motion.div
|
||||
className="text-center px-4 max-w-2xl mx-auto"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8, ease: 'easeOut' }}
|
||||
>
|
||||
{/* 错误代码 */}
|
||||
{code && (
|
||||
<motion.div
|
||||
className="mb-8"
|
||||
initial={{ scale: 0.5, opacity: 0 }}
|
||||
animate={{ scale: 1, opacity: 1 }}
|
||||
transition={{ delay: 0.2, duration: 0.6, type: 'spring', stiffness: 200 }}
|
||||
>
|
||||
<h1 className={`text-9xl font-black bg-gradient-to-r ${getCodeColor()} bg-clip-text text-transparent mb-4`}>
|
||||
{code}
|
||||
</h1>
|
||||
<div className={`w-24 h-1 bg-gradient-to-r ${getCodeColor()} mx-auto rounded-full`} />
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{/* 图标 */}
|
||||
<motion.div
|
||||
className="mb-8 flex justify-center"
|
||||
initial={{ opacity: 0, scale: 0.8 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ delay: 0.4, duration: 0.6 }}
|
||||
>
|
||||
<div className={`${getIconColor()}`}>
|
||||
{config.icon || <ExclamationTriangleIcon className="w-16 h-16" />}
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* 错误信息 */}
|
||||
<motion.div
|
||||
className="mb-8"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.6, duration: 0.6 }}
|
||||
>
|
||||
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-4">
|
||||
{displayTitle}
|
||||
</h2>
|
||||
<p className="text-xl text-gray-700 dark:text-gray-300 mb-2">
|
||||
{displayMessage}
|
||||
</p>
|
||||
{displayDescription && (
|
||||
<p className="text-lg text-gray-600 dark:text-gray-400 leading-relaxed">
|
||||
{displayDescription}
|
||||
</p>
|
||||
)}
|
||||
</motion.div>
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<motion.div
|
||||
className="flex flex-col sm:flex-row gap-4 justify-center items-center"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.8, duration: 0.6 }}
|
||||
>
|
||||
{finalActions.primary && (
|
||||
finalActions.primary.href ? (
|
||||
<Link
|
||||
href={finalActions.primary.href}
|
||||
className="inline-flex items-center px-6 py-3 bg-gradient-to-r from-orange-500 to-orange-600 hover:from-orange-600 hover:to-orange-700 text-white font-semibold rounded-xl transition-all duration-200 shadow-lg hover:shadow-xl transform hover:scale-105"
|
||||
>
|
||||
<HomeIcon className="w-5 h-5 mr-2" />
|
||||
{finalActions.primary.label}
|
||||
</Link>
|
||||
) : (
|
||||
<button
|
||||
onClick={finalActions.primary.onClick}
|
||||
className="inline-flex items-center px-6 py-3 bg-gradient-to-r from-orange-500 to-orange-600 hover:from-orange-600 hover:to-orange-700 text-white font-semibold rounded-xl transition-all duration-200 shadow-lg hover:shadow-xl transform hover:scale-105"
|
||||
>
|
||||
<HomeIcon className="w-5 h-5 mr-2" />
|
||||
{finalActions.primary.label}
|
||||
</button>
|
||||
)
|
||||
)}
|
||||
|
||||
{finalActions.secondary && (
|
||||
finalActions.secondary.href ? (
|
||||
<Link
|
||||
href={finalActions.secondary.href}
|
||||
className="inline-flex items-center px-6 py-3 border-2 border-orange-500 text-orange-500 hover:bg-orange-500 hover:text-white font-semibold rounded-xl transition-all duration-200"
|
||||
>
|
||||
<ArrowLeftIcon className="w-5 h-5 mr-2" />
|
||||
{finalActions.secondary.label}
|
||||
</Link>
|
||||
) : (
|
||||
<button
|
||||
onClick={finalActions.secondary.onClick}
|
||||
className="inline-flex items-center px-6 py-3 border-2 border-orange-500 text-orange-500 hover:bg-orange-500 hover:text-white font-semibold rounded-xl transition-all duration-200"
|
||||
>
|
||||
<ArrowLeftIcon className="w-5 h-5 mr-2" />
|
||||
{finalActions.secondary.label}
|
||||
</button>
|
||||
)
|
||||
)}
|
||||
</motion.div>
|
||||
|
||||
{/* 联系信息 */}
|
||||
{showContact && (
|
||||
<motion.div
|
||||
className="mt-8 text-sm text-gray-500 dark:text-gray-400"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 1, duration: 0.6 }}
|
||||
>
|
||||
<p>如果问题持续存在,请
|
||||
<Link href="/contact" className="text-orange-500 hover:text-orange-600 underline mx-1">
|
||||
联系我们
|
||||
</Link>
|
||||
的支持团队
|
||||
</p>
|
||||
</motion.div>
|
||||
)}
|
||||
</motion.div>
|
||||
|
||||
{/* 背景装饰 */}
|
||||
<div className="fixed inset-0 -z-10 overflow-hidden">
|
||||
<div className="absolute top-1/4 left-1/4 w-64 h-64 bg-orange-200/20 dark:bg-orange-900/20 rounded-full blur-3xl" />
|
||||
<div className="absolute bottom-1/4 right-1/4 w-96 h-96 bg-amber-200/20 dark:bg-amber-900/20 rounded-full blur-3xl" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 预设的错误页面组件
|
||||
export function NotFoundPage() {
|
||||
return <ErrorPage type="404" code={404} />;
|
||||
}
|
||||
|
||||
export function ServerErrorPage() {
|
||||
return <ErrorPage type="500" code={500} />;
|
||||
}
|
||||
|
||||
export function ForbiddenPage() {
|
||||
return <ErrorPage type="403" code={403} />;
|
||||
}
|
||||
|
||||
export function NetworkErrorPage() {
|
||||
return <ErrorPage type="network" />;
|
||||
}
|
||||
|
||||
export function TimeoutErrorPage() {
|
||||
return <ErrorPage type="timeout" />;
|
||||
}
|
||||
|
||||
export function MaintenancePage() {
|
||||
return <ErrorPage type="maintenance" />;
|
||||
}
|
||||
Reference in New Issue
Block a user