Skip to content

Commit c816b29

Browse files
committed
feat: 将ErrorBoundary提取到专用组件中,并集中元、社交和主题设置的站点配置
1 parent ce2f290 commit c816b29

File tree

5 files changed

+144
-130
lines changed

5 files changed

+144
-130
lines changed

app/components/ErrorBoundary.tsx

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { isRouteErrorResponse } from 'react-router';
2+
import { ConfigProvider } from 'antd';
3+
import type { Route } from '../+types/root';
4+
import { config } from '../config';
5+
6+
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
7+
let message = '故障';
8+
let stack: string | undefined;
9+
let is404 = false;
10+
11+
if (isRouteErrorResponse(error)) {
12+
is404 = error.status === 404;
13+
message = is404 ? '404' : '错误';
14+
15+
// 过滤 Chrome DevTools 的请求,不显示错误页面
16+
if (is404 && error.data && typeof error.data === 'string' &&
17+
error.data.includes('.well-known/appspecific/com.chrome.devtools.json')) {
18+
return null; // 不显示错误页面
19+
}
20+
} else if (import.meta.env.DEV && error && error instanceof Error) {
21+
stack = error.stack;
22+
}
23+
24+
return (
25+
<ConfigProvider theme={config.theme}>
26+
<main className='min-h-screen bg-gradient-to-br from-blue-50 via-white to-indigo-50 flex items-center justify-center px-4'>
27+
<div className='max-w-2xl w-full text-center'>
28+
{/* 404 图标和标题 */}
29+
<div className='mb-8'>
30+
<div className='text-8xl md:text-9xl font-bold bg-gradient-to-r from-blue-500 via-purple-500 to-indigo-600 text-transparent bg-clip-text mb-4'>
31+
{message}
32+
</div>
33+
<p className='text-gray-600 text-lg mb-8'>{is404 ? '页面开始了他的自由旅行…' : '页面某乱了,自我反思中…'}</p>
34+
</div>
35+
36+
{/* 404 特殊提示 */}
37+
{is404 && (
38+
<div className='bg-blue-50 border border-blue-200 rounded-2xl p-6 mb-8'>
39+
<div className='flex items-center justify-center mb-4'>
40+
<div className='w-12 h-12 bg-blue-500 rounded-full flex items-center justify-center'>
41+
<svg className='w-6 h-6 text-white' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
42+
<path strokeLinecap='round' strokeLinejoin='round' strokeWidth={2} d='M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z' />
43+
</svg>
44+
</div>
45+
</div>
46+
<h3 className='text-lg font-semibold text-blue-800 mb-2'>📝 博客已迁移</h3>
47+
<p className='text-blue-700 mb-4'>我的博客内容已迁移至新地址,欢迎访问获取最新文章和技术分享</p>
48+
<a
49+
href='https://note.weizwz.com'
50+
target='_blank'
51+
rel='noopener noreferrer'
52+
className='inline-flex items-center px-6 py-3 bg-blue-500 text-white font-medium rounded-full hover:bg-blue-600 transition-colors duration-200'>
53+
<svg className='w-5 h-5 mr-2' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
54+
<path
55+
strokeLinecap='round'
56+
strokeLinejoin='round'
57+
strokeWidth={2}
58+
d='M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14'
59+
/>
60+
</svg>
61+
访问新博客 note.weizwz.com
62+
</a>
63+
</div>
64+
)}
65+
66+
{/* 操作按钮 */}
67+
<div className='flex flex-col sm:flex-row gap-4 justify-center items-center'>
68+
<a
69+
href='/'
70+
className='inline-flex items-center px-8 py-4 bg-gradient-to-r from-blue-500 to-indigo-600 text-white font-semibold rounded-full hover:from-blue-600 hover:to-indigo-700 transition-all duration-200 shadow-lg hover:shadow-xl transform hover:-translate-y-0.5'>
71+
<svg className='w-5 h-5 mr-2' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
72+
<path
73+
strokeLinecap='round'
74+
strokeLinejoin='round'
75+
strokeWidth={2}
76+
d='M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6'
77+
/>
78+
</svg>
79+
返回首页
80+
</a>
81+
</div>
82+
83+
{/* 开发环境错误堆栈 */}
84+
{stack && (
85+
<div className='mt-8 text-left'>
86+
<details className='bg-red-50 border border-red-200 rounded-lg p-4'>
87+
<summary className='cursor-pointer text-red-800 font-medium mb-2'>开发调试信息</summary>
88+
<pre className='text-sm text-red-700 overflow-x-auto'>
89+
<code>{stack}</code>
90+
</pre>
91+
</details>
92+
</div>
93+
)}
94+
95+
{/* 装饰元素 */}
96+
<div className='absolute top-10 left-10 w-20 h-20 bg-blue-200 rounded-full opacity-20 animate-pulse'></div>
97+
<div className='absolute top-20 right-20 w-16 h-16 bg-purple-200 rounded-full opacity-20 animate-pulse' style={{ animationDelay: '1s' }}></div>
98+
<div className='absolute bottom-20 left-20 w-12 h-12 bg-indigo-200 rounded-full opacity-20 animate-pulse' style={{ animationDelay: '2s' }}></div>
99+
<div className='absolute bottom-10 right-10 w-24 h-24 bg-blue-100 rounded-full opacity-20 animate-pulse' style={{ animationDelay: '0.5s' }}></div>
100+
</div>
101+
</main>
102+
</ConfigProvider>
103+
);
104+
}

app/components/PersonalHomepage.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { About } from "./About";
1414
import { ProjectShowcase } from "./ProjectShowcase";
1515
import { Footer } from "./Footer";
1616
import type { Article } from "../types/article";
17+
import { config } from "../config";
1718

1819
interface PersonalHomepageProps {
1920
articles?: Article[];
@@ -80,7 +81,7 @@ export function PersonalHomepage({ articles = [] }: PersonalHomepageProps) {
8081
<div className="flex items-center">
8182
<Avatar
8283
size={40}
83-
src="https://p.weizwz.com/home_bc2e3ce7f2e00827.webp"
84+
src={config.social.avatar}
8485
className="cursor-pointer hover:scale-120 duration-300 ease-[cubic-bezier(0.175, 0.885, 0.32, 1.275)]"
8586
onClick={() =>
8687
document
@@ -141,7 +142,7 @@ export function PersonalHomepage({ articles = [] }: PersonalHomepageProps) {
141142
<Button
142143
type="primary"
143144
shape="round"
144-
href="https://note.weizwz.com/"
145+
href={config.blog.url}
145146
target="_blank"
146147
>
147148
博客
@@ -162,7 +163,7 @@ export function PersonalHomepage({ articles = [] }: PersonalHomepageProps) {
162163
<div className="flex items-center">
163164
<Avatar
164165
size={40}
165-
src="https://p.weizwz.com/home_bc2e3ce7f2e00827.webp"
166+
src={config.social.avatar}
166167
className="mr-2"
167168
/>
168169
<span className="ml-2 text-lg font-semibold">weizwz</span>
@@ -196,7 +197,7 @@ export function PersonalHomepage({ articles = [] }: PersonalHomepageProps) {
196197
shape="round"
197198
icon={<ReadOutlined />}
198199
className="mb-3"
199-
href="https://note.weizwz.com/"
200+
href={config.blog.url}
200201
target="_blank"
201202
>
202203
博客
@@ -206,7 +207,7 @@ export function PersonalHomepage({ articles = [] }: PersonalHomepageProps) {
206207
type="default"
207208
shape="round"
208209
icon={<MailOutlined />}
209-
href="mailto:[email protected]"
210+
href={config.social.email}
210211
>
211212
联系
212213
</Button>
@@ -258,15 +259,15 @@ export function PersonalHomepage({ articles = [] }: PersonalHomepageProps) {
258259
icon={<GithubOutlined />}
259260
className="border-gray-300 px-4 py-3 h-auto text-base rounded-full flex-1 sm:flex-none"
260261
shape="round"
261-
href="https://github.com/weizwz"
262+
href={config.social.github}
262263
target="_blank"
263264
/>
264265
<Button
265266
size="large"
266267
icon={<MailOutlined />}
267268
className="border-gray-300 px-4 py-3 h-auto text-base rounded-full flex-1 sm:flex-none"
268269
shape="round"
269-
href="mailto:[email protected]"
270+
href={config.social.email}
270271
/>
271272
</div>
272273
</div>

app/config/index.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,32 @@ export const config = {
88
logsUrl: 'https://note.weizwz.com/pages/logs',
99
},
1010

11+
// 社交链接
12+
social: {
13+
github: 'https://github.com/weizwz',
14+
email: 'mailto:[email protected]',
15+
avatar: 'https://p.weizwz.com/home_bc2e3ce7f2e00827.webp',
16+
},
17+
18+
// 站点元数据
19+
site: {
20+
title: '分享编码与科技 - weizwz',
21+
description: 'weizwz 的个人主页 - 全栈开发者,专注于编码世界与科技生活',
22+
keywords: 'weizwz, 唯知为之, 唯知笔记, 全栈开发, 前端开发, React, Vue, Nodejs, 个人主页',
23+
},
24+
25+
// 主题配置
26+
theme: {
27+
token: {
28+
colorPrimary: '#2B7FFF',
29+
colorInfo: '#2B7FFF',
30+
colorSuccess: '#67c23a',
31+
colorWarning: '#e6a23c',
32+
colorError: '#f05659',
33+
wireframe: false
34+
}
35+
},
36+
1137
// API 配置
1238
api: {
1339
rss: '/api/rss'

app/root.tsx

Lines changed: 3 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { ConfigProvider } from 'antd'
33
import '@ant-design/v5-patch-for-react-19'
44

55
import type { Route } from './+types/root'
6+
import { config } from './config'
67
import './app.css'
78
import './style/icon.scss'
89

@@ -24,17 +25,7 @@ export function Layout({ children }: { children: React.ReactNode }) {
2425
<Links />
2526
</head>
2627
<body>
27-
<ConfigProvider
28-
theme={{
29-
token: {
30-
colorPrimary: '#2B7FFF',
31-
colorInfo: '#2B7FFF',
32-
// colorSuccess: '#67c23a',
33-
// colorWarning: '#e6a23c',
34-
// colorError: '#f05659',
35-
wireframe: false
36-
}
37-
}}>
28+
<ConfigProvider theme={config.theme}>
3829
{children}
3930
</ConfigProvider>
4031
<ScrollRestoration />
@@ -48,112 +39,4 @@ export default function App() {
4839
return <Outlet />
4940
}
5041

51-
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
52-
let message = '故障'
53-
let stack: string | undefined
54-
let is404 = false
55-
56-
if (isRouteErrorResponse(error)) {
57-
is404 = error.status === 404
58-
message = is404 ? '404' : '错误'
59-
60-
// 过滤 Chrome DevTools 的请求,不显示错误页面
61-
if (is404 && error.data && typeof error.data === 'string' &&
62-
error.data.includes('.well-known/appspecific/com.chrome.devtools.json')) {
63-
return null; // 不显示错误页面
64-
}
65-
} else if (import.meta.env.DEV && error && error instanceof Error) {
66-
stack = error.stack
67-
}
68-
69-
return (
70-
<ConfigProvider
71-
theme={{
72-
token: {
73-
colorPrimary: '#409eff',
74-
colorInfo: '#409eff',
75-
colorSuccess: '#67c23a',
76-
colorWarning: '#e6a23c',
77-
colorError: '#f05659',
78-
wireframe: false
79-
}
80-
}}>
81-
<main className='min-h-screen bg-gradient-to-br from-blue-50 via-white to-indigo-50 flex items-center justify-center px-4'>
82-
<div className='max-w-2xl w-full text-center'>
83-
{/* 404 图标和标题 */}
84-
<div className='mb-8'>
85-
<div className='text-8xl md:text-9xl font-bold bg-gradient-to-r from-blue-500 via-purple-500 to-indigo-600 text-transparent bg-clip-text mb-4'>
86-
{message}
87-
</div>
88-
<p className='text-gray-600 text-lg mb-8'>{is404 ? '页面开始了他的自由旅行…' : '页面某乱了,自我反思中…'}</p>
89-
</div>
90-
91-
{/* 404 特殊提示 */}
92-
{is404 && (
93-
<div className='bg-blue-50 border border-blue-200 rounded-2xl p-6 mb-8'>
94-
<div className='flex items-center justify-center mb-4'>
95-
<div className='w-12 h-12 bg-blue-500 rounded-full flex items-center justify-center'>
96-
<svg className='w-6 h-6 text-white' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
97-
<path strokeLinecap='round' strokeLinejoin='round' strokeWidth={2} d='M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z' />
98-
</svg>
99-
</div>
100-
</div>
101-
<h3 className='text-lg font-semibold text-blue-800 mb-2'>📝 博客已迁移</h3>
102-
<p className='text-blue-700 mb-4'>我的博客内容已迁移至新地址,欢迎访问获取最新文章和技术分享</p>
103-
<a
104-
href='https://note.weizwz.com'
105-
target='_blank'
106-
rel='noopener noreferrer'
107-
className='inline-flex items-center px-6 py-3 bg-blue-500 text-white font-medium rounded-full hover:bg-blue-600 transition-colors duration-200'>
108-
<svg className='w-5 h-5 mr-2' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
109-
<path
110-
strokeLinecap='round'
111-
strokeLinejoin='round'
112-
strokeWidth={2}
113-
d='M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14'
114-
/>
115-
</svg>
116-
访问新博客 note.weizwz.com
117-
</a>
118-
</div>
119-
)}
120-
121-
{/* 操作按钮 */}
122-
<div className='flex flex-col sm:flex-row gap-4 justify-center items-center'>
123-
<a
124-
href='/'
125-
className='inline-flex items-center px-8 py-4 bg-gradient-to-r from-blue-500 to-indigo-600 text-white font-semibold rounded-full hover:from-blue-600 hover:to-indigo-700 transition-all duration-200 shadow-lg hover:shadow-xl transform hover:-translate-y-0.5'>
126-
<svg className='w-5 h-5 mr-2' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
127-
<path
128-
strokeLinecap='round'
129-
strokeLinejoin='round'
130-
strokeWidth={2}
131-
d='M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6'
132-
/>
133-
</svg>
134-
返回首页
135-
</a>
136-
</div>
137-
138-
{/* 开发环境错误堆栈 */}
139-
{stack && (
140-
<div className='mt-8 text-left'>
141-
<details className='bg-red-50 border border-red-200 rounded-lg p-4'>
142-
<summary className='cursor-pointer text-red-800 font-medium mb-2'>开发调试信息</summary>
143-
<pre className='text-sm text-red-700 overflow-x-auto'>
144-
<code>{stack}</code>
145-
</pre>
146-
</details>
147-
</div>
148-
)}
149-
150-
{/* 装饰元素 */}
151-
<div className='absolute top-10 left-10 w-20 h-20 bg-blue-200 rounded-full opacity-20 animate-pulse'></div>
152-
<div className='absolute top-20 right-20 w-16 h-16 bg-purple-200 rounded-full opacity-20 animate-pulse' style={{ animationDelay: '1s' }}></div>
153-
<div className='absolute bottom-20 left-20 w-12 h-12 bg-indigo-200 rounded-full opacity-20 animate-pulse' style={{ animationDelay: '2s' }}></div>
154-
<div className='absolute bottom-10 right-10 w-24 h-24 bg-blue-100 rounded-full opacity-20 animate-pulse' style={{ animationDelay: '0.5s' }}></div>
155-
</div>
156-
</main>
157-
</ConfigProvider>
158-
)
159-
}
42+
export { ErrorBoundary } from './components/ErrorBoundary'

app/routes/home.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import type { Article } from "../types/article";
55

66
export function meta({}: Route.MetaArgs) {
77
return [
8-
{ title: "分享编码与科技 - weizwz" },
9-
{ name: "description", content: "weizwz 的个人主页 - 全栈开发者,专注于编码世界与科技生活" },
10-
{ name: "keywords", content: "weizwz, 唯知为之, 唯知笔记, 全栈开发, 前端开发, React, Vue, Nodejs, 个人主页" },
8+
{ title: config.site.title },
9+
{ name: "description", content: config.site.description },
10+
{ name: "keywords", content: config.site.keywords },
1111
];
1212
}
1313

0 commit comments

Comments
 (0)