Skip to content

Commit 30bc797

Browse files
committed
feat(core): 添加首页favicon支持及自定义404页面
- 配置文件支持favicon路径设置,支持.ico、.png、.svg等格式 - 实现FaviconManager组件动态管理网站图标 - 改进404.html,增加页面跳转中动画及路径显示 - 新增React 404页面组件,包含中英文多语言支持和操作按钮 - 在路由中添加404路由,保证未匹配路径显示404页面 - 优化404页面样式,实现响应式设计和视觉效果提升
1 parent c4cf815 commit 30bc797

9 files changed

Lines changed: 365 additions & 7 deletions

File tree

config.example.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ site:
1010
description: "这是一个基于 Markdown 的个人主页系统"
1111
author: "Your Name"
1212
baseUrl: "/"
13+
favicon: "/vite.svg" # 网站图标路径,支持 .ico, .png, .svg 等格式
14+
# 示例: "/favicon.ico", "/assets/icon.png", "/logo.svg"
1315

1416
# ========================================
1517
# 个人信息

public/404.html

Lines changed: 85 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,70 @@
22
<html lang="zh-CN">
33
<head>
44
<meta charset="utf-8">
5-
<title>加载中...</title>
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>页面跳转中...</title>
7+
<style>
8+
* {
9+
margin: 0;
10+
padding: 0;
11+
box-sizing: border-box;
12+
}
13+
14+
body {
15+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
16+
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
17+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
18+
min-height: 100vh;
19+
display: flex;
20+
align-items: center;
21+
justify-content: center;
22+
color: white;
23+
}
24+
25+
.container {
26+
text-align: center;
27+
padding: 2rem;
28+
max-width: 500px;
29+
}
30+
31+
.loader {
32+
width: 60px;
33+
height: 60px;
34+
border: 5px solid rgba(255, 255, 255, 0.3);
35+
border-top-color: white;
36+
border-radius: 50%;
37+
animation: spin 1s linear infinite;
38+
margin: 0 auto 2rem;
39+
}
40+
41+
@keyframes spin {
42+
to { transform: rotate(360deg); }
43+
}
44+
45+
h1 {
46+
font-size: 1.5rem;
47+
font-weight: 600;
48+
margin-bottom: 1rem;
49+
opacity: 0.95;
50+
}
51+
52+
p {
53+
font-size: 1rem;
54+
opacity: 0.85;
55+
line-height: 1.6;
56+
}
57+
58+
.path {
59+
margin-top: 1.5rem;
60+
padding: 0.75rem 1rem;
61+
background: rgba(255, 255, 255, 0.1);
62+
border-radius: 8px;
63+
font-family: 'Courier New', monospace;
64+
font-size: 0.875rem;
65+
word-break: break-all;
66+
backdrop-filter: blur(10px);
67+
}
68+
</style>
669
<script>
770
// GitHub Pages SPA 路由解决方案
871
// 简化版:直接将当前路径存储后重定向到首页
@@ -16,18 +79,33 @@
1679
return;
1780
}
1881

82+
// 显示正在跳转的路径
83+
var fullPath = path + search + hash;
84+
document.addEventListener('DOMContentLoaded', function() {
85+
var pathElement = document.getElementById('redirect-path');
86+
if (pathElement) {
87+
pathElement.textContent = fullPath;
88+
}
89+
});
90+
1991
// 将完整路径保存到 sessionStorage
20-
sessionStorage.setItem('redirectPath', path + search + hash);
92+
sessionStorage.setItem('redirectPath', fullPath);
2193

22-
// 重定向到首页
23-
window.location.replace('/');
94+
// 延迟重定向,让用户看到提示信息
95+
setTimeout(function() {
96+
window.location.replace('/');
97+
}, 500);
2498
})();
2599
</script>
26100
</head>
27101
<body>
28-
<div style="font-family: system-ui, -apple-system, sans-serif; text-align: center; padding: 50px;">
29-
<h2>加载中...</h2>
30-
<p>正在跳转到正确的页面</p>
102+
<div class="container">
103+
<div class="loader"></div>
104+
<h1>🚀 页面跳转中...</h1>
105+
<p>正在将您重定向到正确的页面</p>
106+
<div class="path">
107+
<div id="redirect-path"></div>
108+
</div>
31109
</div>
32110
</body>
33111
</html>

public/config.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ site:
66
description: "这是一个基于 Markdown 的个人主页系统"
77
author: "Your Name"
88
baseUrl: "/"
9+
favicon: "/vite.svg" # 网站图标路径,支持 .ico, .png, .svg 等格式
910

1011
# 个人信息
1112
profile:

src/App.jsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import { ConfigProvider, useConfig } from './config/ConfigContext'
44
import { ThemeProvider } from './components/theme/ThemeContext'
55
import { I18nProvider, useI18n } from './i18n/I18nContext'
66
import { Layout } from './components/layout/Layout'
7+
import { FaviconManager } from './components/common/FaviconManager'
78
import { Home } from './pages/Home'
89
import { About } from './pages/About'
910
import { Projects } from './pages/Projects'
1011
import { Posts } from './pages/Posts'
1112
import { Pages } from './pages/Pages'
1213
import { Files } from './pages/Files'
1314
import { News } from './pages/News'
15+
import { NotFound } from './pages/NotFound'
1416
import { DynamicDocumentPage } from './pages/DynamicDocumentPage'
1517
import { generateFolderConfigs } from './utils/folderScanner'
1618
import { getRouterBasename } from './utils/pathUtils'
@@ -107,6 +109,9 @@ function AppContent() {
107109
return (
108110
<I18nProvider>
109111
<ThemeProvider>
112+
{/* Favicon 管理 */}
113+
<FaviconManager />
114+
110115
<BrowserRouter basename={basename}>
111116
{/* 页面标题管理 */}
112117
<DocumentTitleManager folderConfigs={folderConfigs} />
@@ -129,6 +134,9 @@ function AppContent() {
129134
element={<DynamicDocumentPage config={config} />}
130135
/>
131136
))}
137+
138+
{/* 404 页面 - 必须放在最后 */}
139+
<Route path="*" element={<NotFound />} />
132140
</Route>
133141
</Routes>
134142
</BrowserRouter>
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { useEffect } from 'react'
2+
import { useSiteConfig } from '../../config/ConfigContext'
3+
4+
/**
5+
* Favicon 管理器组件
6+
* 根据配置动态更新网站图标
7+
*/
8+
export function FaviconManager() {
9+
const siteConfig = useSiteConfig()
10+
11+
useEffect(() => {
12+
if (!siteConfig?.favicon) {
13+
return
14+
}
15+
16+
const faviconPath = siteConfig.favicon
17+
18+
// 查找或创建 favicon link 元素
19+
let faviconLink = document.querySelector('link[rel="icon"]')
20+
21+
if (!faviconLink) {
22+
faviconLink = document.createElement('link')
23+
faviconLink.rel = 'icon'
24+
document.head.appendChild(faviconLink)
25+
}
26+
27+
// 根据文件扩展名设置正确的 type
28+
const extension = faviconPath.split('.').pop().toLowerCase()
29+
let type = 'image/x-icon' // 默认 .ico 格式
30+
31+
switch (extension) {
32+
case 'png':
33+
type = 'image/png'
34+
break
35+
case 'svg':
36+
type = 'image/svg+xml'
37+
break
38+
case 'jpg':
39+
case 'jpeg':
40+
type = 'image/jpeg'
41+
break
42+
case 'gif':
43+
type = 'image/gif'
44+
break
45+
case 'ico':
46+
type = 'image/x-icon'
47+
break
48+
default:
49+
type = 'image/x-icon'
50+
}
51+
52+
faviconLink.type = type
53+
faviconLink.href = faviconPath
54+
}, [siteConfig?.favicon])
55+
56+
return null // 这是一个纯逻辑组件,不渲染任何内容
57+
}

src/i18n/locales/en.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,4 +168,13 @@ export const en = {
168168
deployment: 'Deployment',
169169
},
170170
},
171+
172+
// 404 Page
173+
notFound: {
174+
title: 'Page Not Found',
175+
description:
176+
'Sorry, the page you are looking for does not exist or has been removed.',
177+
backHome: 'Back to Home',
178+
goBack: 'Go Back',
179+
},
171180
}

src/i18n/locales/zh.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,4 +165,12 @@ export const zh = {
165165
deployment: '部署指南',
166166
},
167167
},
168+
169+
// 404 页面
170+
notFound: {
171+
title: '页面未找到',
172+
description: '抱歉,您访问的页面不存在或已被移除。',
173+
backHome: '返回首页',
174+
goBack: '返回上一页',
175+
},
168176
}

src/pages/NotFound.jsx

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { Link } from 'react-router-dom'
2+
import { useI18n } from '../i18n/I18nContext'
3+
import styles from './NotFound.module.css'
4+
5+
/**
6+
* 404 页面未找到组件
7+
*/
8+
export function NotFound() {
9+
const { t } = useI18n()
10+
11+
return (
12+
<div className={styles.container}>
13+
<div className={styles.content}>
14+
<div className={styles.code}>404</div>
15+
<h1 className={styles.title}>{t('notFound.title')}</h1>
16+
<p className={styles.description}>{t('notFound.description')}</p>
17+
18+
<div className={styles.actions}>
19+
<Link to="/" className={styles.homeButton}>
20+
{t('notFound.backHome')}
21+
</Link>
22+
<button
23+
onClick={() => window.history.back()}
24+
className={styles.backButton}
25+
>
26+
{t('notFound.goBack')}
27+
</button>
28+
</div>
29+
30+
<div className={styles.illustration}>
31+
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
32+
<circle
33+
cx="100"
34+
cy="100"
35+
r="80"
36+
fill="var(--theme-primary-light)"
37+
opacity="0.1"
38+
/>
39+
<path
40+
d="M 70 80 Q 100 60 130 80"
41+
stroke="var(--theme-primary)"
42+
strokeWidth="4"
43+
fill="none"
44+
strokeLinecap="round"
45+
/>
46+
<circle cx="80" cy="90" r="8" fill="var(--theme-primary)" />
47+
<circle cx="120" cy="90" r="8" fill="var(--theme-primary)" />
48+
<path
49+
d="M 70 130 Q 100 145 130 130"
50+
stroke="var(--theme-primary)"
51+
strokeWidth="4"
52+
fill="none"
53+
strokeLinecap="round"
54+
/>
55+
</svg>
56+
</div>
57+
</div>
58+
</div>
59+
)
60+
}

0 commit comments

Comments
 (0)