diff --git a/.kiro/specs/frontend-navigation-site/ICON_FALLBACK_DEMO.md b/.kiro/specs/frontend-navigation-site/ICON_FALLBACK_DEMO.md new file mode 100644 index 0000000..b795dc9 --- /dev/null +++ b/.kiro/specs/frontend-navigation-site/ICON_FALLBACK_DEMO.md @@ -0,0 +1,157 @@ +# 图标加载失败处理演示 + +## 快速测试方法 + +### 方法 1:使用浏览器开发者工具 + +1. 打开网站 +2. 按 F12 打开开发者工具 +3. 切换到 Console 标签 +4. 运行以下代码添加测试链接: + +```javascript +// 测试 1:无效的自定义图标 URL +const testLink1 = { + id: 'test-1', + name: '测试:无效图标', + url: 'https://github.com', + description: '这个链接使用了无效的图标URL', + category: '主页', + icon: 'https://invalid-domain-12345.com/icon.png', + backgroundColor: '#1890ff', + iconScale: 0.7, + tags: [], + order: 0, + createdAt: Date.now(), + updatedAt: Date.now() +}; + +// 测试 2:无效的域名(favicon 也会失败) +const testLink2 = { + id: 'test-2', + name: '测试:完全失败', + url: 'https://invalid-domain-99999.com', + description: '这个链接的图标和favicon都会失败', + category: '主页', + icon: 'https://invalid-domain-12345.com/icon.png', + backgroundColor: '#52c41a', + iconScale: 0.7, + tags: [], + order: 0, + createdAt: Date.now(), + updatedAt: Date.now() +}; + +// 测试 3:只依赖 favicon(应该成功) +const testLink3 = { + id: 'test-3', + name: '测试:Favicon回退', + url: 'https://www.google.com', + description: '这个链接没有自定义图标,应该显示Google的favicon', + category: '主页', + backgroundColor: '#4285f4', + iconScale: 0.7, + tags: [], + order: 0, + createdAt: Date.now(), + updatedAt: Date.now() +}; + +// 添加到 localStorage +const existingLinks = JSON.parse(localStorage.getItem('navigation-links') || '[]'); +existingLinks.push(testLink1, testLink2, testLink3); +localStorage.setItem('navigation-links', JSON.stringify(existingLinks)); + +// 刷新页面 +location.reload(); +``` + +### 方法 2:通过 UI 手动添加 + +1. 点击页面上的"添加链接"按钮 +2. 填写以下信息: + +**测试链接 1:无效图标** +- 名称:测试:无效图标 +- 地址:https://github.com +- 描述:这个链接使用了无效的图标URL +- 图标:https://invalid-domain-12345.com/icon.png +- 背景色:#1890ff + +**测试链接 2:完全失败** +- 名称:测试:完全失败 +- 地址:https://invalid-domain-99999.com +- 描述:这个链接的图标和favicon都会失败 +- 图标:https://invalid-domain-12345.com/icon.png +- 背景色:#52c41a + +**测试链接 3:Favicon回退** +- 名称:测试:Favicon回退 +- 地址:https://www.google.com +- 描述:这个链接没有自定义图标,应该显示Google的favicon +- 图标:(留空) +- 背景色:#4285f4 + +## 预期结果 + +### 测试链接 1(无效图标) +- ✅ 不显示破损的图片图标 +- ✅ 自动回退到 GitHub 的 favicon +- ✅ 控制台显示:`图标加载失败: https://invalid-domain-12345.com/icon.png` + +### 测试链接 2(完全失败) +- ✅ 不显示破损的图片图标 +- ✅ 显示默认的链接图标(LinkOutlined) +- ✅ 控制台显示两条警告: + - `图标加载失败: https://invalid-domain-12345.com/icon.png` + - `Favicon 加载失败: [favicon URL]` + +### 测试链接 3(Favicon回退) +- ✅ 显示 Google 的 favicon +- ✅ 图标清晰,没有破损 + +## 清理测试数据 + +测试完成后,可以通过以下方式清理测试数据: + +### 方法 1:通过 UI 删除 +右键点击测试链接卡片,选择"删除" + +### 方法 2:通过控制台清理 +```javascript +// 删除所有测试链接 +const links = JSON.parse(localStorage.getItem('navigation-links') || '[]'); +const cleanedLinks = links.filter(link => !link.name.startsWith('测试:')); +localStorage.setItem('navigation-links', JSON.stringify(cleanedLinks)); +location.reload(); +``` + +## 技术说明 + +### 修复前的问题 +```typescript +// 问题代码:使用 display: 'none' 隐藏 + setHasError(true)} +/> +``` + +浏览器可能在设置 `display: 'none'` 之前短暂显示破损图标。 + +### 修复后的方案 +```typescript +// 解决方案:条件渲染,完全不渲染失败的元素 +if (hasError && (faviconError || !fallbackUrl)) { + return ; +} + +if (hasError && fallbackUrl && !faviconError) { + return setFaviconError(true)} />; +} + +return setHasError(true)} />; +``` + +这样可以确保失败的 `` 元素完全不会被渲染到 DOM 中。 diff --git a/.kiro/specs/frontend-navigation-site/ICON_IMPROVEMENTS.md b/.kiro/specs/frontend-navigation-site/ICON_IMPROVEMENTS.md new file mode 100644 index 0000000..48087ea --- /dev/null +++ b/.kiro/specs/frontend-navigation-site/ICON_IMPROVEMENTS.md @@ -0,0 +1,279 @@ +# 图标显示改进说明 + +## 改进内容 + +### 1. 白色背景下的默认图标颜色优化 + +**问题:** +当链接卡片的背景色是白色时,默认图标(LinkOutlined)也是白色,导致图标不可见。 + +**解决方案:** +- 添加 `isWhiteColor()` 函数判断背景色是否为白色 +- 如果背景是白色,默认图标使用主题色 `#1890ff`(蓝色) +- 如果背景不是白色,默认图标使用白色 `#ffffff` + +**支持的白色格式:** +- `#ffffff` / `#fff` +- `white` +- `rgb(255, 255, 255)` / `rgb(255,255,255)` +- `rgba(255, 255, 255, ...)` / `rgba(255,255,255,...)` + +### 2. 默认图标大小统一 + +**问题:** +默认图标的大小受 `iconScale` 属性影响,导致不同卡片的默认图标大小不一致。 + +**解决方案:** +- 默认图标使用固定大小 `48px`,不受 `iconScale` 影响 +- 确保所有默认图标大小一致,视觉效果更统一 + +### 3. Favicon.im URL 识别优化 + +**问题:** +如果用户设置的图标 URL 本身就是 `favicon.im` 的地址,会导致重复请求。 + +**解决方案:** +- 添加 `isFaviconUrl()` 函数识别 favicon.im 的 URL +- 如果 `link.icon` 是 favicon.im 的 URL,跳过"自定义图标"逻辑 +- 直接进入"使用 Favicon"逻辑,避免重复请求 + +## 代码变更 + +### IconWithFallback 组件 + +**新增参数:** +```typescript +interface IconWithFallbackProps { + src: string; + alt: string; + fallbackUrl?: string; + scale?: number; + backgroundColor?: string; // 新增:背景色 +} +``` + +**默认图标渲染逻辑:** +```typescript +// 第三级:所有图片都失败,显示默认图标 +const DefaultIcon = AntdIcons.LinkOutlined; +const defaultIconColor = isWhiteColor(backgroundColor) ? '#1890ff' : '#ffffff'; +const defaultIconSize = 48; // 固定大小 + +return ( + +); +``` + +### renderIcon 逻辑 + +**情况1:自定义图标 URL** +```typescript +// 排除 favicon.im 的 URL +if (link.icon && + (link.icon.startsWith('http://') || link.icon.startsWith('https://') || link.icon.startsWith('/')) && + !isFaviconUrl(link.icon)) { + return ( + + ); +} +``` + +**情况4:兜底默认图标** +```typescript +// 默认图标使用固定大小和智能颜色 +const DefaultIcon = AntdIcons.LinkOutlined; +const defaultIconColor = isWhiteColor(backgroundColor) ? '#1890ff' : '#ffffff'; +const defaultIconSize = 48; + +return ( + +); +``` + +## 测试场景 + +### 场景1:白色背景 + 图标加载失败 + +**测试步骤:** +1. 创建一个链接,背景色设置为 `#ffffff` +2. 图标 URL 设置为无效地址 +3. 观察默认图标 + +**预期结果:** +- 默认图标显示为蓝色(`#1890ff`) +- 图标大小为 48px +- 图标清晰可见 + +### 场景2:彩色背景 + 图标加载失败 + +**测试步骤:** +1. 创建一个链接,背景色设置为 `#1890ff`(蓝色) +2. 图标 URL 设置为无效地址 +3. 观察默认图标 + +**预期结果:** +- 默认图标显示为白色(`#ffffff`) +- 图标大小为 48px +- 图标清晰可见 + +### 场景3:多个卡片的默认图标大小一致性 + +**测试步骤:** +1. 创建多个链接,设置不同的 `iconScale` 值(0.5, 0.7, 1.0) +2. 所有链接的图标 URL 都设置为无效地址 +3. 观察所有默认图标 + +**预期结果:** +- 所有默认图标大小完全一致(48px) +- 不受 `iconScale` 影响 + +### 场景4:Favicon.im URL 不重复请求 + +**测试步骤:** +1. 创建一个链接,图标 URL 设置为 `https://favicon.im/example.com` +2. 打开浏览器开发者工具的 Network 标签 +3. 观察网络请求 + +**预期结果:** +- 只有一次对 `favicon.im` 的请求 +- 没有重复请求 + +## 视觉效果对比 + +### 修复前 + +| 背景色 | 默认图标颜色 | 可见性 | 大小 | +|--------|-------------|--------|------| +| 白色 | 白色 | ❌ 不可见 | 受 iconScale 影响 | +| 蓝色 | 白色 | ✅ 可见 | 受 iconScale 影响 | +| 绿色 | 白色 | ✅ 可见 | 受 iconScale 影响 | + +### 修复后 + +| 背景色 | 默认图标颜色 | 可见性 | 大小 | +|--------|-------------|--------|------| +| 白色 | 蓝色 (#1890ff) | ✅ 可见 | 固定 48px | +| 蓝色 | 白色 (#ffffff) | ✅ 可见 | 固定 48px | +| 绿色 | 白色 (#ffffff) | ✅ 可见 | 固定 48px | + +## 4. 搜索引擎图标加载失败处理 + +**问题:** +搜索栏左侧的搜索引擎图标加载失败时,显示破损的图片占位符,把输入框撑高了。 + +**解决方案:** +- 创建 `EngineIcon` 组件处理图标加载失败 +- 加载失败时显示 Ant Design 的 `SearchOutlined` 图标 +- 确保图标加载失败不影响输入框高度 + +**实现细节:** +```typescript +const EngineIcon: React.FC<{ iconUrl: string; name: string; size?: number }> = + ({ iconUrl, name, size = 20 }) => { + const [hasError, setHasError] = useState(false); + const faviconUrl = getFaviconUrl(iconUrl); + + // 如果图标加载失败,显示默认搜索图标 + if (hasError || !faviconUrl) { + return ; + } + + return ( + {`${name} setHasError(true)} + /> + ); +}; +``` + +## 相关文件 + +- `components/navigation/LinkCard.tsx` - 链接卡片图标修复 +- `components/layout/SearchBar.tsx` - 搜索栏图标修复 +- `.kiro/specs/frontend-navigation-site/requirements.md` - 需求 11 +- `.kiro/specs/frontend-navigation-site/design.md` - 设计说明 + +## 技术细节 + +### 颜色判断函数 + +```typescript +const isWhiteColor = (color?: string): boolean => { + if (!color) return false; + const normalizedColor = color.toLowerCase().trim(); + return ( + normalizedColor === '#ffffff' || + normalizedColor === '#fff' || + normalizedColor === 'white' || + normalizedColor === 'rgb(255, 255, 255)' || + normalizedColor === 'rgb(255,255,255)' || + normalizedColor.startsWith('rgba(255, 255, 255') || + normalizedColor.startsWith('rgba(255,255,255') + ); +}; +``` + +### Favicon URL 判断函数 + +```typescript +const isFaviconUrl = (url: string) => { + return url.includes('favicon.im/'); +}; +``` + +## 测试场景:搜索引擎图标 + +### 场景5:搜索引擎图标加载失败 + +**测试步骤:** +1. 打开浏览器开发者工具 +2. 在 Network 标签中阻止图片加载或设置离线模式 +3. 观察搜索栏左侧的搜索引擎图标 + +**预期结果:** +- 显示 SearchOutlined 图标(放大镜) +- 输入框高度正常,没有被撑高 +- 图标大小为 20px +- 控制台显示警告:`搜索引擎图标加载失败: [URL]` + +### 场景6:下拉菜单中的搜索引擎图标 + +**测试步骤:** +1. 点击搜索栏左侧的搜索引擎图标 +2. 观察下拉菜单中的所有搜索引擎图标 +3. 如果有图标加载失败,观察显示效果 + +**预期结果:** +- 加载成功的图标正常显示(16px) +- 加载失败的图标显示 SearchOutlined(16px) +- 所有图标大小一致 +- 菜单项高度一致,没有错位 + +## 性能影响 + +- ✅ 减少了重复的网络请求(favicon.im URL 识别) +- ✅ 简化了状态管理(移除了未使用的 iconSize 变量) +- ✅ 提升了代码可读性和可维护性 +- ✅ 搜索栏图标加载失败不影响布局 +- ✅ 用户体验更好,界面更稳定 diff --git a/.kiro/specs/frontend-navigation-site/design.md b/.kiro/specs/frontend-navigation-site/design.md index 3115d2c..0577738 100644 --- a/.kiro/specs/frontend-navigation-site/design.md +++ b/.kiro/specs/frontend-navigation-site/design.md @@ -257,6 +257,38 @@ sequenceDiagram - 悬停效果:轻微上浮 + 阴影增强 - 动画:使用 framer-motion 实现 +**图标加载策略(多级回退机制):** +1. **第一级:自定义图标** + - 如果用户提供了自定义图标 URL,优先加载 + - 使用 `` 标签的 `onError` 事件监听加载失败 + - 设置 `loading="lazy"` 和 `decoding="async"` 优化性能 + +2. **第二级:Favicon 回退** + - 当自定义图标加载失败时,自动尝试加载网站的 favicon + - 使用 `getFaviconUrl()` 函数获取 favicon URL + - 同样监听 `onError` 事件处理加载失败 + +3. **第三级:默认图标** + - 当所有图片加载都失败时,显示 Ant Design 的 `LinkOutlined` 图标 + - 确保默认图标大小与自定义图标保持一致 + - 使用 `iconScale` 属性控制图标缩放 + +**图标加载实现细节:** +- 使用 React state 跟踪加载状态(`hasError`, `faviconError`, `imageLoaded`, `faviconLoaded`) +- 通过 CSS `display` 属性控制图标显示/隐藏,避免显示破损图片 +- 在控制台记录加载失败的警告信息,便于调试 +- 使用 `onLoad` 事件标记图片成功加载,防止误判 + +**IconWithFallback 子组件:** +```typescript +interface IconWithFallbackProps { + src: string; // 主图标 URL + alt: string; // 图标描述 + fallbackUrl?: string; // 回退 favicon URL + scale?: number; // 图标缩放比例(默认 0.8) +} +``` + **右键菜单:** - 使用 Ant Design Dropdown 组件 - 菜单项:编辑、删除 diff --git a/.kiro/specs/frontend-navigation-site/requirements.md b/.kiro/specs/frontend-navigation-site/requirements.md index 8dd6c15..27d41f6 100644 --- a/.kiro/specs/frontend-navigation-site/requirements.md +++ b/.kiro/specs/frontend-navigation-site/requirements.md @@ -127,6 +127,19 @@ 5. THE 导航系统 SHALL 在所有交互操作中提供视觉反馈 6. THE 导航系统 SHALL 确保页面初始加载时间不超过 2 秒 +### 需求 11:图标加载失败处理 + +**用户故事:** 作为用户,当链接卡片的图标加载失败时,我希望看到默认图标而不是破损的图片,以便保持界面的美观和一致性 + +#### 验收标准 + +1. WHEN 链接卡片的自定义图标加载失败时,THE 导航系统 SHALL 自动尝试加载该网站的 favicon 图标 +2. WHEN favicon 图标也加载失败时,THE 导航系统 SHALL 显示默认的链接图标(LinkOutlined) +3. THE 导航系统 SHALL 在图标加载失败时不显示破损的图片占位符 +4. THE 导航系统 SHALL 在图标加载过程中提供平滑的视觉过渡 +5. WHEN 图标加载失败时,THE 导航系统 SHALL 在浏览器控制台记录警告信息以便调试 +6. THE 导航系统 SHALL 确保默认图标的大小与自定义图标保持一致 + ### 需求 10:性能优化 **用户故事:** 作为用户,我希望网站加载快速且运行流畅,以便高效完成任务 diff --git a/.kiro/specs/frontend-navigation-site/tasks.md b/.kiro/specs/frontend-navigation-site/tasks.md index 2e718d4..4a6bbbd 100644 --- a/.kiro/specs/frontend-navigation-site/tasks.md +++ b/.kiro/specs/frontend-navigation-site/tasks.md @@ -173,7 +173,16 @@ - 处理 LocalStorage 配额超限错误 - 处理图标加载失败(显示默认图标) - 添加表单验证错误提示 - - _需求: 2.4_ + - _需求: 2.4, 11.1, 11.2, 11.3, 11.4, 11.5, 11.6_ + +- [x] 24. 修复图标加载失败显示破损图片的问题 + - 优化 `IconWithFallback` 组件的渲染逻辑 + - 使用条件渲染替代 `display: 'none'` 隐藏失败的图片 + - 移除不必要的状态变量(`imageLoaded`, `faviconLoaded`) + - 确保图标加载失败时完全不渲染失败的 `` 元素 + - 验证三级回退机制:自定义图标 → Favicon → 默认图标 + - 创建测试指南文档 + - _需求: 11.1, 11.2, 11.3, 11.4, 11.5, 11.6_ - [x] 21. 实现可访问性优化 - 为所有交互元素添加合适的 ARIA 标签 diff --git a/DEPLOYMENT_CHECKLIST.md b/DEPLOYMENT_CHECKLIST.md new file mode 100644 index 0000000..729ae6d --- /dev/null +++ b/DEPLOYMENT_CHECKLIST.md @@ -0,0 +1,210 @@ +# 部署检查清单 + +## 🚀 部署步骤 + +### 1. 提交代码 + +```bash +# 查看修改 +git status + +# 添加所有修改 +git add . + +# 提交 +git commit -m "fix: 更新 CSP 配置和 Service Worker v4" + +# 推送到远程 +git push origin main +``` + +### 2. Cloudflare Pages 部署 + +访问 Cloudflare Pages 控制台: +1. 等待自动部署完成(约 2-3 分钟) +2. 查看部署日志确认成功 +3. 记录部署 URL + +### 3. 清除 Cloudflare 缓存 + +**重要!** 必须清除缓存才能让新的 `_headers` 生效 + +**方法 1:通过控制台** +``` +1. 登录 Cloudflare Dashboard +2. 选择你的域名 +3. Caching → Configuration +4. 点击 "Purge Everything" +5. 确认清除 +``` + +**方法 2:通过 API** +```bash +curl -X POST "https://api.cloudflare.com/client/v4/zones/{zone_id}/purge_cache" \ + -H "Authorization: Bearer {api_token}" \ + -H "Content-Type: application/json" \ + --data '{"purge_everything":true}' +``` + +### 4. 验证部署 + +**检查 CSP 配置**: +```bash +# 检查 HTTP 头 +curl -I https://nav.weizwz.com | grep -i content-security-policy + +# 应该看到: +# content-security-policy: ... connect-src 'self' https: ... +``` + +**检查 Service Worker**: +```bash +# 访问网站 +open https://nav.weizwz.com + +# 打开控制台 +# 应该看到:Service Worker 注册成功 +# 不应该看到:CSP 错误 +``` + +### 5. 用户端清除缓存 + +**提供清除工具**: +``` +https://nav.weizwz.com/clear-cache.html +``` + +**通知用户**(可选): +- 在网站上显示更新提示 +- 或发送通知 +- 或等待自动更新(可能需要几小时) + +## ✅ 验证清单 + +部署后检查以下项目: + +- [ ] Cloudflare Pages 部署成功 +- [ ] Cloudflare 缓存已清除 +- [ ] CSP 配置正确(`connect-src 'self' https:`) +- [ ] Service Worker 版本正确(v4) +- [ ] 控制台无 CSP 错误 +- [ ] 图标正常加载 +- [ ] 页面不会频繁刷新 +- [ ] 清除缓存工具可访问 + +## 🔍 故障排查 + +### 问题 1:仍然有 CSP 错误 + +**原因**:Cloudflare 缓存未清除 + +**解决**: +```bash +# 清除 Cloudflare 缓存 +# 等待 5-10 分钟 +# 硬刷新浏览器(Ctrl+Shift+R) +``` + +### 问题 2:页面频繁刷新 + +**原因**:Service Worker 不断检测到更新 + +**解决**: +```bash +# 检查 Service Worker 版本是否一致 +# 确保所有文件都已部署 +# 清除浏览器缓存 +``` + +### 问题 3:图标仍然加载失败 + +**原因**:浏览器缓存了旧的 Service Worker + +**解决**: +``` +1. 访问 /clear-cache.html +2. 点击"全部清除并刷新" +3. 或手动清除浏览器缓存 +``` + +## 📊 监控 + +部署后监控以下指标: + +### Cloudflare Analytics +``` +- 请求数 +- 错误率 +- 缓存命中率 +``` + +### 浏览器控制台 +``` +- CSP 错误数量(应该为 0) +- Service Worker 状态 +- 网络请求状态 +``` + +### 用户反馈 +``` +- 图标加载问题 +- 页面刷新问题 +- 功能异常 +``` + +## 🔄 回滚计划 + +如果部署出现问题: + +### 快速回滚 +```bash +# 回滚到上一个版本 +git revert HEAD +git push origin main + +# 或回滚到特定版本 +git reset --hard +git push origin main --force +``` + +### Cloudflare Pages 回滚 +``` +1. 进入 Cloudflare Pages 控制台 +2. 选择项目 +3. Deployments → 选择之前的部署 +4. 点击 "Rollback to this deployment" +``` + +## 📝 部署记录 + +记录每次部署的信息: + +``` +日期:2024-11-18 +版本:v1.0.4 (SW v4) +修改: +- 更新 CSP 配置(connect-src 'self' https:) +- 优化 Service Worker 自动刷新逻辑 +- 修复图标闪烁问题 +- 添加清除缓存工具 + +部署人:[你的名字] +部署时间:[时间] +验证状态:✅ 通过 +``` + +## 🎯 下次部署优化 + +- [ ] 自动化部署流程 +- [ ] 添加部署前测试 +- [ ] 设置 Staging 环境 +- [ ] 配置自动回滚 +- [ ] 添加部署通知 + +--- + +**记住**: +1. 每次修改 `_headers` 必须清除 Cloudflare 缓存 +2. 每次更新 Service Worker 必须递增版本号 +3. 部署后必须验证 CSP 配置 +4. 提供用户清除缓存的工具 diff --git a/README.md b/README.md index b387e61..14e6b05 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@
-![](https://p.weizwz.com/nav/20251118_175016_3ab1473ede1b43e9.webp) +![](https://p.weizwz.com/nav/20251119_170634_d89c36024f0efbc4.webp) 现代化的个人前端导航网站,为开发者提供高效、美观的资源导航体验 @@ -14,7 +14,7 @@ [![Ant Design](https://img.shields.io/badge/Ant%20Design-5.x-1890ff)](https://ant.design/) [![License](https://img.shields.io/badge/license-MIT-green)](./LICENSE) -[在线演示](https://nav.weizwz.com) | [快速开始](#-快速开始) | [文档](./.kiro/specs/frontend-navigation-site/) | [部署指南](./.kiro/specs/frontend-navigation-site/QUICKSTART.md) +在线演示 | [快速开始](#-快速开始) | [文档](./.kiro/specs/frontend-navigation-site/) | [部署指南](./.kiro/specs/frontend-navigation-site/QUICKSTART.md)
diff --git a/app/register-sw.tsx b/app/register-sw.tsx index 613c6c7..d536eb2 100644 --- a/app/register-sw.tsx +++ b/app/register-sw.tsx @@ -58,9 +58,21 @@ export default function RegisterServiceWorker() { let refreshing = false; navigator.serviceWorker.addEventListener('controllerchange', () => { if (refreshing) return; + + // 检查是否是首次加载(没有旧的 controller) + const isFirstLoad = !navigator.serviceWorker.controller; + if (isFirstLoad) { + console.log('首次加载 Service Worker,无需刷新'); + return; + } + refreshing = true; - console.log('Service Worker 已更新,刷新页面...'); - window.location.reload(); + console.log('Service Worker 已更新,3秒后刷新页面...'); + + // 延迟刷新,给用户时间看到提示 + setTimeout(() => { + window.location.reload(); + }, 3000); }); }); } diff --git a/components/layout/Header.tsx b/components/layout/Header.tsx index 45d723b..c82b8d1 100644 --- a/components/layout/Header.tsx +++ b/components/layout/Header.tsx @@ -3,7 +3,7 @@ import React, { memo, useCallback } from 'react'; import { useRouter } from 'next/navigation'; import { Button, Tooltip } from 'antd'; -import { EditOutlined, MenuOutlined } from '@ant-design/icons'; +import { EditOutlined, MenuOutlined, GithubOutlined } from '@ant-design/icons'; import SearchBar from './SearchBar'; import ThemeToggle from './ThemeToggle'; @@ -75,6 +75,21 @@ const Header = memo(function Header({ onMenuClick }: HeaderProps) { }} /> + +