From 9376a67052f9b2c0fdda8306cfdedd0e50d82bd3 Mon Sep 17 00:00:00 2001 From: iriver Date: Wed, 9 Jul 2025 00:13:41 +0800 Subject: [PATCH] init doc --- CLAUDE.md | 57 +++- docs/README.md | 46 +++ docs/UI设计需求文档.md | 175 ++++++++++++ docs/api/README.md | 25 ++ docs/deployment/README.md | 36 +++ docs/design/README.md | 18 ++ docs/development/README.md | 29 ++ docs/user-guide/README.md | 21 ++ docs/前端开发文档.md | 524 ++++++++++++++++++++++++++++++++++ docs/后端开发文档.md | 543 +++++++++++++++++++++++++++++++++++ docs/测试需求文档.md | 562 +++++++++++++++++++++++++++++++++++++ 11 files changed, 2028 insertions(+), 8 deletions(-) create mode 100644 docs/README.md create mode 100644 docs/UI设计需求文档.md create mode 100644 docs/api/README.md create mode 100644 docs/deployment/README.md create mode 100644 docs/design/README.md create mode 100644 docs/development/README.md create mode 100644 docs/user-guide/README.md create mode 100644 docs/前端开发文档.md create mode 100644 docs/后端开发文档.md create mode 100644 docs/测试需求文档.md diff --git a/CLAUDE.md b/CLAUDE.md index 195bebc..c6af08e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -52,10 +52,20 @@ cd frontend && make quick - `make install` - 安装依赖 - `make dev` - 启动开发服务器 - `make build` - 构建生产版本 -- `make clean` - 清理构建文件 +- `make start` - 启动生产服务器 +- `make clean` - 清理构建文件和缓存 - `make status` - 检查项目状态 - `make add PACKAGE=name` - 添加依赖 +- `make add-dev PACKAGE=name` - 添加开发依赖 - `make remove PACKAGE=name` - 移除依赖 +- `make type-check` - 运行 TypeScript 类型检查 +- `make lint` - 运行代码检查 +- `make format` - 格式化代码 +- `make setup` - 设置开发环境 +- `make deploy-prep` - 准备生产部署 +- `make quick` - 快速启动(安装依赖并启动开发服务器) +- `make update` - 更新所有依赖 +- `make reinstall` - 清理并重新安装依赖 ## 项目结构 @@ -90,10 +100,19 @@ photography/ - `components/navigation.tsx` - 粘性导航,带移动菜单 - `components/photo-modal.tsx` - 照片详情模态框,带导航 - `components/timeline-view.tsx` - 基于时间轴的照片展示 +- `components/timeline-stats.tsx` - 时间轴统计信息 - `components/filter-bar.tsx` - 照片分类过滤 +- `components/about-view.tsx` - 关于页面组件 +- `components/contact-view.tsx` - 联系页面组件 +- `components/theme-provider.tsx` - 主题提供者(深色模式支持) +- `components/providers/query-provider.tsx` - TanStack Query 提供者 +- `components/loading-spinner.tsx` - 加载动画组件 - `components/ui/` - 来自 shadcn/ui 的可重用 UI 组件 - `lib/api.ts` - 使用 axios 的 API 配置 - `lib/queries.ts` - 用于数据获取的 TanStack Query hooks +- `lib/utils.ts` - 工具函数 +- `hooks/use-mobile.tsx` - 移动端检测钩子 +- `hooks/use-toast.ts` - 通知钩子 ## 数据结构 @@ -121,7 +140,14 @@ interface Photo { - `GET /api/photos` - 获取所有照片 - `GET /api/photos/:id` - 获取单个照片 - `GET /api/categories` - 获取分类列表 -- 标准 CRUD 操作用于照片管理 +- `POST /api/photos` - 添加新照片 +- `PUT /api/photos/:id` - 更新照片信息 +- `DELETE /api/photos/:id` - 删除照片 + +启动模拟 API 服务器: +```bash +cd frontend && node mock-api.js +``` ## 环境变量 @@ -152,11 +178,19 @@ NEXT_PUBLIC_API_URL=http://localhost:3001/api ## 开发流程 1. 所有开发都在 `frontend/` 目录中进行 -2. 使用 `make dev` 或 `bun run dev` 启动开发服务器 -3. 使用 `make lint` 检查代码规范问题 -4. 使用 `make type-check` 运行 TypeScript 检查 -5. 部署前使用 `make build` 构建 -6. 使用 `make status` 检查项目健康状况 +2. 首次设置:`make setup` 创建环境变量文件 +3. 使用 `make dev` 或 `bun run dev` 启动开发服务器 +4. 使用 `make lint` 检查代码规范问题 +5. 使用 `make type-check` 运行 TypeScript 检查 +6. 使用 `make format` 格式化代码 +7. 部署前使用 `make deploy-prep` 进行完整检查和构建 +8. 使用 `make status` 检查项目健康状况 + +### 快速开始工作流程 +```bash +cd frontend +make quick # 安装依赖并启动开发服务器 +``` ## Bun 特定说明 @@ -181,4 +215,11 @@ NEXT_PUBLIC_API_URL=http://localhost:3001/api ### 开发环境配置 - 构建时忽略 TypeScript 和 ESLint 错误(适用于开发环境) - 图像未优化设置,便于开发调试 -- 端口自动检测(如果 3000 被占用,会尝试 3001、3002 等) \ No newline at end of file +- 端口自动检测(如果 3000 被占用,会尝试 3001、3002 等) +- 使用 Prettier 进行代码格式化 +- 支持通过 `make format` 统一代码风格 + +## 文件锁定和包管理 +- 使用 `bun.lockb` 而非 `package-lock.json` 作为锁定文件 +- 项目名称:`my-v0-project`(可能需要更新为更合适的名称) +- 所有包管理操作都应通过 Makefile 命令执行,确保使用 bun \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..375d014 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,46 @@ +# 产品文档 + +本目录包含摄影作品集项目的完整产品文档。 + +## 文档结构 + +### 📐 Design(设计文档) +- UI/UX 设计规范 +- 组件设计系统 +- 交互设计文档 +- 品牌指南和视觉规范 + +### 🔌 API(API 文档) +- API 接口文档 +- 数据结构定义 +- 接口调用示例 +- 错误码说明 + +### 👥 User Guide(用户指南) +- 用户操作手册 +- 功能使用说明 +- 常见问题解答 +- 最佳实践指南 + +### 🛠️ Development(开发文档) +- 开发环境搭建 +- 代码规范和约定 +- 开发工作流程 +- 测试指南 + +### 🚀 Deployment(部署文档) +- 部署配置说明 +- 环境变量配置 +- 服务器配置要求 +- 部署流程和脚本 + +## 文档维护 + +请在相应的子目录中维护各类文档,确保文档的及时更新和准确性。 + +## 贡献指南 + +1. 在相应目录下创建或更新文档 +2. 使用 Markdown 格式编写 +3. 确保文档结构清晰、内容准确 +4. 添加适当的图片和示例代码 \ No newline at end of file diff --git a/docs/UI设计需求文档.md b/docs/UI设计需求文档.md new file mode 100644 index 0000000..f777c93 --- /dev/null +++ b/docs/UI设计需求文档.md @@ -0,0 +1,175 @@ +# 摄影作品集网站 - UI设计需求文档 + +## 1. 项目概述 + +### 1.1 项目定位 +个人摄影作品展示网站,专注于高品质的视觉呈现和用户体验。 + +### 1.2 设计目标 +- **极简美学**:突出作品本身,减少视觉干扰 +- **专业感**:体现摄影师的专业水准 +- **沉浸式体验**:让用户专注于作品欣赏 +- **响应式设计**:全设备完美适配 + +## 2. 设计风格指南 + +### 2.1 色彩方案 +- **主色调**:深色主题(#1a1a1a, #2d2d2d) +- **辅助色**:纯白文字(#ffffff) +- **强调色**:温暖金色(#d4af37)用于按钮和链接 +- **背景色**:渐变深灰(#0f0f0f 到 #1e1e1e) + +### 2.2 字体系统 +- **标题字体**:Modern Sans(如 Inter, SF Pro) +- **正文字体**:清晰易读的无衬线字体 +- **字重层次**:Light(300), Regular(400), Medium(500), Bold(700) + +### 2.3 视觉层次 +- **一级标题**:32px-48px,Bold +- **二级标题**:24px-32px,Medium +- **正文**:16px-18px,Regular +- **说明文字**:14px,Light + +## 3. 页面布局设计 + +### 3.1 首页布局 +``` +[导航栏 - 固定在顶部] +[英雄区域 - 全屏背景图/视频] + - 摄影师姓名 + - 简短介绍 + - CTA按钮 +[精选作品网格 - 3x2] +[关于简介预览] +[页脚] +``` + +### 3.2 作品集页面 +``` +[面包屑导航] +[分类筛选器 - 水平排列] +[作品网格展示] + - 瀑布流布局 + - 悬停效果 + - 快速预览 +[分页导航] +``` + +### 3.3 作品详情页 +``` +[返回按钮] +[大图展示区域] + - 支持缩放 + - 前后导航 +[作品信息侧栏] + - 标题、描述 + - 拍摄参数 + - 时间地点 +[相关推荐] +``` + +### 3.4 时间线页面 +``` +[年份导航] +[时间线主体] + - 垂直时间轴 + - 按月份分组 + - 缩略图预览 + - 作品数量统计 +``` + +## 4. 交互设计规范 + +### 4.1 悬停效果 +- **图片悬停**:轻微放大(1.05x) + 蒙层显示标题 +- **按钮悬停**:颜色渐变 + 轻微阴影 +- **导航悬停**:下划线动画 + +### 4.2 加载动画 +- **页面加载**:优雅的骨架屏 +- **图片加载**:从模糊到清晰的渐进式加载 +- **切换动画**:平滑的页面过渡 + +### 4.3 响应式交互 +- **移动端**:手势滑动支持 +- **桌面端**:键盘快捷键支持 +- **触摸优化**:合适的点击区域大小 + +## 5. 组件设计规范 + +### 5.1 导航组件 +```css +.navigation { + position: fixed; + top: 0; + backdrop-filter: blur(10px); + background: rgba(26, 26, 26, 0.9); + padding: 20px; + z-index: 1000; +} +``` + +### 5.2 图片网格组件 +- **网格间距**:16px-24px +- **图片比例**:保持原始比例 +- **最小尺寸**:移动端 150px,桌面端 250px +- **最大列数**:桌面4列,平板3列,手机2列 + +### 5.3 模态框组件 +- **背景蒙层**:rgba(0, 0, 0, 0.8) +- **关闭方式**:ESC键 + 点击蒙层 + 关闭按钮 +- **内容区域**:最大宽度90vw,最大高度90vh + +## 6. 移动端适配 + +### 6.1 断点设置 +- **手机**:< 768px +- **平板**:768px - 1024px +- **桌面**:> 1024px +- **大屏**:> 1440px + +### 6.2 移动端优化 +- **触摸友好**:按钮最小44px +- **滑动操作**:支持左右滑动浏览 +- **下拉刷新**:原生滑动体验 +- **底部导航**:重要功能快速访问 + +## 7. 性能优化设计 + +### 7.1 图片优化 +- **懒加载**:视口外图片延迟加载 +- **渐进式**:先显示缩略图再加载高清 +- **格式优化**:WebP格式优先,降级JPG +- **尺寸适配**:根据设备提供合适尺寸 + +### 7.2 加载体验 +- **骨架屏**:内容加载前的占位 +- **进度指示**:长时间加载的进度反馈 +- **错误状态**:网络异常的友好提示 + +## 8. 辅助功能设计 + +### 8.1 可访问性 +- **键盘导航**:所有功能键盘可操作 +- **屏幕阅读器**:适当的ARIA标签 +- **对比度**:满足WCAG 2.1 AA标准 +- **焦点指示**:清晰的焦点样式 + +### 8.2 多语言支持 +- **语言切换**:顶部导航显眼位置 +- **文字方向**:支持RTL语言 +- **日期格式**:本地化日期显示 + +## 9. 设计交付物 + +### 9.1 设计稿要求 +- **高保真原型**:Figma/Sketch源文件 +- **设计规范**:详细的设计系统文档 +- **交互说明**:动效和交互的详细说明 +- **响应式方案**:各断点的适配方案 + +### 9.2 开发协作 +- **组件库**:可复用的UI组件 +- **图标资源**:SVG格式图标集 +- **字体文件**:web font资源 +- **样式指南**:CSS变量和类名规范 \ No newline at end of file diff --git a/docs/api/README.md b/docs/api/README.md new file mode 100644 index 0000000..9e93a0f --- /dev/null +++ b/docs/api/README.md @@ -0,0 +1,25 @@ +# API 文档 + +本目录包含摄影作品集项目的 API 接口文档。 + +## 目录结构 + +- `endpoints.md` - API 端点详细说明 +- `data-models.md` - 数据模型定义 +- `authentication.md` - 认证机制说明 +- `examples.md` - API 调用示例 +- `error-codes.md` - 错误码说明 + +## API 基础信息 + +- 基础 URL: `http://localhost:3001/api` +- 数据格式: JSON +- 认证方式: 待实现 +- 版本: v1 + +## 主要端点 + +- `/photos` - 照片相关操作 +- `/categories` - 分类管理 +- `/upload` - 文件上传(待实现) +- `/user` - 用户管理(待实现) \ No newline at end of file diff --git a/docs/deployment/README.md b/docs/deployment/README.md new file mode 100644 index 0000000..6b64304 --- /dev/null +++ b/docs/deployment/README.md @@ -0,0 +1,36 @@ +# 部署文档 + +本目录包含摄影作品集项目的部署相关文档。 + +## 目录结构 + +- `environments.md` - 环境配置说明 +- `vercel-deployment.md` - Vercel 部署指南 +- `docker-deployment.md` - Docker 部署指南 +- `ci-cd.md` - CI/CD 配置说明 +- `monitoring.md` - 监控和日志配置 +- `backup.md` - 备份策略 + +## 部署准备 + +在部署前,请确保完成以下步骤: + +1. 代码检查: `make lint` +2. 类型检查: `make type-check` +3. 构建测试: `make build` +4. 完整部署准备: `make deploy-prep` + +## 环境变量 + +确保在部署环境中配置以下环境变量: + +```bash +NEXT_PUBLIC_API_URL=your-api-url +``` + +## 支持的部署平台 + +- Vercel (推荐) +- Netlify +- Docker +- 传统服务器部署 \ No newline at end of file diff --git a/docs/design/README.md b/docs/design/README.md new file mode 100644 index 0000000..0777566 --- /dev/null +++ b/docs/design/README.md @@ -0,0 +1,18 @@ +# 设计文档 + +本目录包含摄影作品集项目的设计相关文档。 + +## 目录结构 + +- `ui-design.md` - UI 设计规范 +- `component-system.md` - 组件设计系统 +- `interaction-design.md` - 交互设计文档 +- `brand-guide.md` - 品牌指南 +- `assets/` - 设计资源文件夹 + +## 设计原则 + +- 简洁优雅的视觉风格 +- 响应式设计,适配多种设备 +- 突出摄影作品的展示效果 +- 良好的用户体验和交互流程 \ No newline at end of file diff --git a/docs/development/README.md b/docs/development/README.md new file mode 100644 index 0000000..4b53039 --- /dev/null +++ b/docs/development/README.md @@ -0,0 +1,29 @@ +# 开发文档 + +本目录包含摄影作品集项目的开发相关文档。 + +## 目录结构 + +- `setup.md` - 开发环境搭建 +- `coding-standards.md` - 代码规范 +- `architecture.md` - 架构设计说明 +- `components.md` - 组件开发指南 +- `testing.md` - 测试指南 +- `troubleshooting.md` - 常见问题解决 + +## 技术栈 + +- **前端**: Next.js 15 + React 19 + TypeScript +- **样式**: Tailwind CSS + shadcn/ui +- **状态管理**: TanStack Query + React Hooks +- **包管理**: bun +- **构建工具**: Next.js 内置构建系统 + +## 开发流程 + +1. 环境设置: `make setup` +2. 安装依赖: `make install` +3. 启动开发: `make dev` +4. 代码检查: `make lint` +5. 类型检查: `make type-check` +6. 代码格式化: `make format` \ No newline at end of file diff --git a/docs/user-guide/README.md b/docs/user-guide/README.md new file mode 100644 index 0000000..7227db6 --- /dev/null +++ b/docs/user-guide/README.md @@ -0,0 +1,21 @@ +# 用户指南 + +本目录包含摄影作品集项目的用户使用指南。 + +## 目录结构 + +- `getting-started.md` - 快速入门指南 +- `photo-management.md` - 照片管理功能 +- `gallery-features.md` - 画廊功能说明 +- `timeline-usage.md` - 时间轴使用方法 +- `theme-settings.md` - 主题设置说明 +- `faq.md` - 常见问题解答 + +## 主要功能 + +- 📸 照片画廊浏览 +- 📅 时间轴查看 +- 🏷️ 分类筛选 +- 🔍 照片详情查看 +- 🌙 深色模式切换 +- 📱 移动端适配 \ No newline at end of file diff --git a/docs/前端开发文档.md b/docs/前端开发文档.md new file mode 100644 index 0000000..00d26d6 --- /dev/null +++ b/docs/前端开发文档.md @@ -0,0 +1,524 @@ +# 摄影作品集网站 - 前端开发文档 + +## 1. 技术架构 + +### 1.1 技术栈选择 +- **框架**:Next.js 14 (App Router) +- **样式**:Tailwind CSS + CSS Modules +- **状态管理**:Zustand + React Query +- **图片处理**:Sharp + Next.js Image +- **动画**:Framer Motion +- **UI组件**:Headless UI + 自定义组件 + +### 1.2 项目结构 +``` +src/ +├── app/ # App Router页面 +│ ├── layout.tsx # 根布局 +│ ├── page.tsx # 首页 +│ ├── gallery/ # 作品集页面 +│ ├── timeline/ # 时间线页面 +│ └── about/ # 关于页面 +├── components/ # 可复用组件 +│ ├── ui/ # 基础UI组件 +│ ├── gallery/ # 作品集相关组件 +│ ├── timeline/ # 时间线组件 +│ └── layout/ # 布局组件 +├── hooks/ # 自定义Hooks +├── lib/ # 工具函数 +├── stores/ # 状态管理 +├── types/ # TypeScript类型 +└── styles/ # 全局样式 +``` + +## 2. 核心功能实现 + +### 2.1 图片管理系统 + +#### 多格式图片处理 +```typescript +// lib/imageUtils.ts +export interface ImageFormats { + raw?: string; // RAW文件路径 + jpg: string; // JPG文件路径 + webp?: string; // WebP优化版本 + thumb: string; // 缩略图 +} + +export interface PhotoData { + id: string; + title: string; + description?: string; + date: string; + formats: ImageFormats; + metadata: { + camera?: string; + lens?: string; + settings?: { + iso: number; + aperture: string; + shutter: string; + focal: string; + }; + }; + collections: string[]; +} +``` + +#### 响应式图片组件 +```typescript +// components/ui/ResponsiveImage.tsx +interface ResponsiveImageProps { + photo: PhotoData; + sizes: string; + priority?: boolean; + className?: string; + onClick?: () => void; +} + +export function ResponsiveImage({ photo, sizes, priority, className, onClick }: ResponsiveImageProps) { + return ( +
+ {photo.title} +
+ ); +} +``` + +### 2.2 作品集网格系统 + +#### 自适应网格布局 +```typescript +// components/gallery/PhotoGrid.tsx +export function PhotoGrid({ photos }: { photos: PhotoData[] }) { + return ( +
+ {photos.map((photo, index) => ( +
+ openLightbox(photo)} + /> +
+ ))} +
+ ); +} +``` + +#### 虚拟化长列表 +```typescript +// hooks/useVirtualGrid.ts +export function useVirtualGrid(items: PhotoData[], itemHeight: number) { + const [visibleItems, setVisibleItems] = useState([]); + const [startIndex, setStartIndex] = useState(0); + + const handleScroll = useCallback((scrollTop: number, containerHeight: number) => { + const newStartIndex = Math.floor(scrollTop / itemHeight); + const endIndex = Math.min( + newStartIndex + Math.ceil(containerHeight / itemHeight) + 2, + items.length + ); + + setStartIndex(newStartIndex); + setVisibleItems(items.slice(newStartIndex, endIndex)); + }, [items, itemHeight]); + + return { visibleItems, startIndex, handleScroll }; +} +``` + +### 2.3 时间线功能 + +#### 时间线数据结构 +```typescript +// types/timeline.ts +export interface TimelineGroup { + year: number; + months: { + [month: number]: { + photos: PhotoData[]; + count: number; + }; + }; +} + +export interface TimelineData { + groups: TimelineGroup[]; + totalPhotos: number; + yearRange: [number, number]; +} +``` + +#### 时间线组件 +```typescript +// components/timeline/Timeline.tsx +export function Timeline({ data }: { data: TimelineData }) { + const [selectedYear, setSelectedYear] = useState(data.yearRange[1]); + + return ( +
+ {/* 年份导航 */} + g.year)} + selectedYear={selectedYear} + onYearSelect={setSelectedYear} + /> + + {/* 时间线内容 */} +
+ {data.groups + .filter(group => group.year === selectedYear) + .map(group => ( + + ))} +
+
+ ); +} +``` + +### 2.4 灯箱组件 + +#### 全功能图片查看器 +```typescript +// components/ui/Lightbox.tsx +export function Lightbox() { + const { isOpen, currentPhoto, photos, currentIndex } = useLightboxStore(); + const [isZoomed, setIsZoomed] = useState(false); + const [position, setPosition] = useState({ x: 0, y: 0 }); + + if (!isOpen || !currentPhoto) return null; + + return ( + + {/* 图片区域 */} +
+ +
+ + {/* 导航控件 */} + + + {/* 信息面板 */} + +
+ ); +} +``` + +## 3. 性能优化策略 + +### 3.1 图片优化 + +#### 渐进式加载 +```typescript +// hooks/useProgressiveImage.ts +export function useProgressiveImage(src: string, placeholderSrc: string) { + const [currentSrc, setCurrentSrc] = useState(placeholderSrc); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const img = new Image(); + img.src = src; + img.onload = () => { + setCurrentSrc(src); + setLoading(false); + }; + }, [src]); + + return { src: currentSrc, loading }; +} +``` + +#### 智能预加载 +```typescript +// hooks/useImagePreloader.ts +export function useImagePreloader(photos: PhotoData[], currentIndex: number) { + useEffect(() => { + // 预加载当前图片的前后各2张 + const preloadRange = 2; + const start = Math.max(0, currentIndex - preloadRange); + const end = Math.min(photos.length, currentIndex + preloadRange + 1); + + for (let i = start; i < end; i++) { + if (i !== currentIndex) { + const img = new Image(); + img.src = photos[i].formats.jpg; + } + } + }, [photos, currentIndex]); +} +``` + +### 3.2 状态管理优化 + +#### Zustand Store设计 +```typescript +// stores/galleryStore.ts +interface GalleryState { + photos: PhotoData[]; + collections: Collection[]; + currentCollection: string | null; + loading: boolean; + error: string | null; + + // Actions + setPhotos: (photos: PhotoData[]) => void; + setCurrentCollection: (id: string | null) => void; + addPhotos: (photos: PhotoData[]) => void; +} + +export const useGalleryStore = create((set, get) => ({ + photos: [], + collections: [], + currentCollection: null, + loading: false, + error: null, + + setPhotos: (photos) => set({ photos }), + setCurrentCollection: (id) => set({ currentCollection: id }), + addPhotos: (newPhotos) => set((state) => ({ + photos: [...state.photos, ...newPhotos] + })), +})); +``` + +### 3.3 代码分割 + +#### 动态导入 +```typescript +// app/gallery/page.tsx +const LazyPhotoGrid = dynamic(() => import('@/components/gallery/PhotoGrid'), { + loading: () => , + ssr: false +}); + +const LazyLightbox = dynamic(() => import('@/components/ui/Lightbox'), { + ssr: false +}); +``` + +## 4. 移动端优化 + +### 4.1 触摸手势 +```typescript +// hooks/useSwipeGesture.ts +export function useSwipeGesture(onSwipeLeft: () => void, onSwipeRight: () => void) { + const touchStart = useRef<{ x: number; y: number } | null>(null); + const touchEnd = useRef<{ x: number; y: number } | null>(null); + + const handleTouchStart = (e: TouchEvent) => { + touchEnd.current = null; + touchStart.current = { + x: e.targetTouches[0].clientX, + y: e.targetTouches[0].clientY, + }; + }; + + const handleTouchEnd = () => { + if (!touchStart.current || !touchEnd.current) return; + + const distance = touchStart.current.x - touchEnd.current.x; + const isLeftSwipe = distance > 50; + const isRightSwipe = distance < -50; + + if (isLeftSwipe) onSwipeLeft(); + if (isRightSwipe) onSwipeRight(); + }; + + return { handleTouchStart, handleTouchEnd }; +} +``` + +### 4.2 移动端导航 +```typescript +// components/layout/MobileNavigation.tsx +export function MobileNavigation() { + const [isOpen, setIsOpen] = useState(false); + + return ( + <> + {/* 汉堡菜单按钮 */} + + + {/* 侧边导航 */} + + {isOpen && ( + + + + )} + + + ); +} +``` + +## 5. SEO优化 + +### 5.1 元数据管理 +```typescript +// app/gallery/[collection]/page.tsx +export async function generateMetadata({ params }: { params: { collection: string } }) { + const collection = await getCollection(params.collection); + + return { + title: `${collection.name} | 摄影作品集`, + description: collection.description, + openGraph: { + title: collection.name, + description: collection.description, + images: [collection.coverImage], + }, + }; +} +``` + +### 5.2 结构化数据 +```typescript +// components/seo/StructuredData.tsx +export function PhotoStructuredData({ photo }: { photo: PhotoData }) { + const structuredData = { + '@context': 'https://schema.org', + '@type': 'Photograph', + name: photo.title, + description: photo.description, + contentUrl: photo.formats.jpg, + thumbnail: photo.formats.thumb, + dateCreated: photo.date, + creator: { + '@type': 'Person', + name: '摄影师姓名', + }, + }; + + return ( +