# 管理后台模块 - CLAUDE.md 此文件为 Claude Code 在管理后台模块工作时提供指导。 ## 🎯 模块概览 管理后台是摄影作品集项目的管理界面,用于内容管理、用户管理和系统配置。 ### 功能特性 - 📸 照片管理:上传、编辑、删除照片 - 🏷️ 分类管理:相册分类、标签管理 - 👥 用户管理:用户权限、访问控制 - 📊 数据统计:访问统计、照片数据 - ⚙️ 系统配置:站点设置、主题配置 ### 技术栈 - **前端**: React + TypeScript + Vite - **组件**: shadcn/ui + Radix UI - **状态管理**: Zustand / React Query - **路由**: React Router - **认证**: JWT Token - **构建**: Vite + TypeScript ## 📁 目录结构 ``` admin/ ├── CLAUDE.md # 🔍 当前文件 - 管理后台指导 ├── src/ # 源代码目录 │ ├── components/ # 组件目录 │ │ ├── ui/ # shadcn/ui 基础组件 │ │ ├── layout/ # 布局组件 │ │ ├── photo/ # 照片管理组件 │ │ ├── category/ # 分类管理组件 │ │ ├── logs/ # 日志管理组件 │ │ └── common/ # 通用组件 │ ├── pages/ # 页面组件 │ │ ├── dashboard/ # 仪表板 │ │ ├── photos/ # 照片管理 │ │ ├── categories/ # 分类管理 │ │ ├── logs/ # 日志管理 │ │ └── settings/ # 系统设置 │ ├── hooks/ # 自定义 Hooks │ ├── services/ # API 服务 │ ├── store/ # 状态管理 │ ├── utils/ # 工具函数 │ └── types/ # TypeScript 类型定义 ├── public/ # 静态资源 ├── package.json # 项目配置 ├── vite.config.ts # Vite 配置 ├── tsconfig.json # TypeScript 配置 ├── tailwind.config.js # Tailwind 配置 └── README.md # 模块说明 ``` ## 🚀 开发环境配置 ### 环境要求 - **Node.js**: 18+ - **包管理器**: npm/yarn/pnpm - **编辑器**: VS Code (推荐) - **后端依赖**: Golang 后端 API 服务 ### 初始化项目 ```bash # 进入管理后台目录 cd admin/ # 安装依赖 npm install # 启动开发服务器 npm run dev # 构建生产版本 npm run build ``` ### 环境变量配置 ```bash # .env.development VITE_APP_TITLE=摄影作品集管理后台 VITE_API_BASE_URL=http://localhost:8080/api VITE_UPLOAD_URL=http://localhost:8080/api/upload # .env.production VITE_APP_TITLE=摄影作品集管理后台 VITE_API_BASE_URL=https://api.photography.iriver.top VITE_UPLOAD_URL=https://api.photography.iriver.top/upload ``` ## 🏗️ 项目架构 ### 目录结构详解 #### 🧩 组件架构 ``` src/components/ ├── ui/ # shadcn/ui 基础组件 │ ├── button.tsx # 按钮组件 │ ├── input.tsx # 输入框组件 │ ├── table.tsx # 表格组件 │ ├── modal.tsx # 弹窗组件 │ ├── card.tsx # 卡片组件 │ └── form.tsx # 表单组件 ├── layout/ # 布局组件 │ ├── header.tsx # 顶部导航 │ ├── sidebar.tsx # 侧边栏 │ └── main-layout.tsx # 主布局 ├── photo/ # 照片管理组件 │ ├── photo-list.tsx # 照片列表 │ ├── photo-form.tsx # 照片表单 │ ├── photo-upload.tsx # 照片上传 │ └── photo-detail.tsx # 照片详情 ├── category/ # 分类管理组件 │ ├── category-tree.tsx # 分类树 │ ├── category-form.tsx # 分类表单 │ └── category-stats.tsx # 分类统计 ├── logs/ # 日志管理组件 │ ├── log-viewer.tsx # 日志查看器 │ ├── log-filter.tsx # 日志过滤器 │ └── log-detail.tsx # 日志详情 └── common/ # 通用组件 ├── loading.tsx # 加载组件 ├── error-boundary.tsx # 错误边界 └── confirmation.tsx # 确认对话框 ``` #### 📱 页面结构 ``` src/pages/ ├── dashboard/ # 仪表板 │ └── index.tsx # 首页 ├── photos/ # 照片管理 │ ├── index.tsx # 照片列表 │ ├── edit.tsx # 照片编辑 │ └── upload.tsx # 照片上传 ├── categories/ # 分类管理 │ └── index.tsx # 分类管理 ├── tags/ # 标签管理 │ └── index.tsx # 标签管理 ├── logs/ # 日志管理 │ ├── index.tsx # 日志列表 │ └── detail.tsx # 日志详情 └── settings/ # 系统设置 └── index.tsx # 系统设置 ``` #### 🔄 状态管理 ``` src/store/ ├── auth.ts # 认证状态 (Zustand) ├── photo.ts # 照片状态 ├── logs.ts # 日志状态 └── ui.ts # UI 状态 src/hooks/ ├── useAuth.ts # 认证 Hook ├── usePhotos.ts # 照片数据 (React Query) ├── useCategories.ts # 分类数据 ├── useLogs.ts # 日志数据 └── useUpload.ts # 上传功能 ``` ## 🔌 API 集成 ### API 接口规范 ```typescript // src/services/photo.ts interface PhotoAPI { // 获取照片列表 getPhotos(params: PhotoListParams): Promise // 上传照片 uploadPhoto(file: File, metadata: PhotoMetadata): Promise // 更新照片信息 updatePhoto(id: string, data: PhotoUpdateData): Promise // 删除照片 deletePhoto(id: string): Promise // 批量操作 batchUpdatePhotos(ids: string[], data: Partial): Promise } // src/services/category.ts interface CategoryAPI { getCategories(): Promise getCategoryTree(): Promise createCategory(data: CategoryCreateData): Promise updateCategory(id: string, data: CategoryUpdateData): Promise deleteCategory(id: string): Promise } // src/services/logs.ts interface LogsAPI { getLogs(params: LogListParams): Promise getLogDetail(traceId: string): Promise getLogStats(): Promise } ``` ### 数据类型定义 ```typescript // src/types/photo.ts interface Photo { id: string title: string description: string originalFilename: string fileSize: number status: 'processing' | 'published' | 'draft' | 'archived' categories: Category[] tags: Tag[] formats: PhotoFormat[] camera?: string lens?: string iso?: number aperture?: string shutterSpeed?: string focalLength?: string takenAt?: string createdAt: string updatedAt: string } // src/types/category.ts interface Category { id: string name: string slug: string description: string parentId?: string sortOrder: number isActive: boolean photoCount: number createdAt: string updatedAt: string } // src/types/logs.ts interface LogEntry { id: string timestamp: string level: 'error' | 'warn' | 'info' | 'debug' message: string traceId: string userId?: string action?: string details?: Record } ``` ## 🎨 UI 设计规范 ### 设计原则 - **一致性**: 保持界面元素的一致性 - **易用性**: 简洁直观的操作流程 - **响应式**: 适配不同屏幕尺寸 - **可访问性**: 支持键盘导航和屏幕阅读器 ### 主题配置 ```typescript // tailwind.config.js module.exports = { content: ['./src/**/*.{ts,tsx}'], theme: { extend: { colors: { primary: { DEFAULT: '#d4af37', 50: '#fefce8', 500: '#d4af37', 900: '#713f12' }, border: 'hsl(var(--border))', background: 'hsl(var(--background))', foreground: 'hsl(var(--foreground))', muted: 'hsl(var(--muted))', 'muted-foreground': 'hsl(var(--muted-foreground))' }, fontFamily: { sans: ['Inter', 'system-ui', 'sans-serif'] } } }, plugins: [require('tailwindcss-animate')] } // src/lib/utils.ts import { clsx, type ClassValue } from 'clsx' import { twMerge } from 'tailwind-merge' export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) } ``` ## 🔐 权限管理 ### 角色权限设计 ```typescript // src/types/permission.ts interface Permission { id: string name: string code: string description: string } interface Role { id: string name: string permissions: Permission[] } interface User { id: string username: string email: string role: Role isActive: boolean } ``` ### 路由保护 ```typescript // src/components/auth/ProtectedRoute.tsx import { useAuth } from '@/hooks/useAuth' import { Navigate, useLocation } from 'react-router-dom' interface ProtectedRouteProps { children: React.ReactNode requiredRole?: string } export function ProtectedRoute({ children, requiredRole }: ProtectedRouteProps) { const { user, isLoading } = useAuth() const location = useLocation() if (isLoading) { return
Loading...
} if (!user) { return } if (requiredRole && user.role !== requiredRole) { return } return <>{children} } // src/hooks/useAuth.ts import { create } from 'zustand' import { persist } from 'zustand/middleware' interface AuthState { user: User | null token: string | null login: (token: string, user: User) => void logout: () => void isLoggedIn: boolean } export const useAuthStore = create()( persist( (set, get) => ({ user: null, token: null, isLoggedIn: false, login: (token, user) => set({ token, user, isLoggedIn: true }), logout: () => set({ token: null, user: null, isLoggedIn: false }) }), { name: 'auth-storage' } ) ) ``` ## 🚀 构建和部署 ### 开发命令 ```bash # 安装依赖 npm install # 启动开发服务器 npm run dev # 类型检查 npm run type-check # 代码检查 npm run lint # 代码格式化 npm run format # 构建生产版本 npm run build # 预览构建结果 npm run preview ``` ### 部署配置 ```typescript // vite.config.ts import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import path from 'path' export default defineConfig({ plugins: [react()], resolve: { alias: { '@': path.resolve(__dirname, './src') } }, base: '/admin/', // 部署路径 build: { outDir: 'dist', assetsDir: 'static', rollupOptions: { output: { manualChunks: { 'react-vendor': ['react', 'react-dom', 'react-router-dom'], 'ui-vendor': ['@radix-ui/react-dialog', '@radix-ui/react-select'], 'utils-vendor': ['clsx', 'tailwind-merge', 'date-fns'] } } } } }) ``` ## 🔧 开发指南 ### 代码规范 - 使用 TypeScript 进行类型约束 - 遵循 React Hooks 最佳实践 - 组件命名采用 PascalCase - 文件命名采用 kebab-case - 使用 ESLint + Prettier 进行代码格式化 ### 组件开发 ```tsx // src/components/photo/photo-list.tsx import { useState } from 'react' import { usePhotos } from '@/hooks/usePhotos' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Badge } from '@/components/ui/badge' interface PhotoListProps { onPhotoSelect?: (photo: Photo) => void } export function PhotoList({ onPhotoSelect }: PhotoListProps) { const [search, setSearch] = useState('') const [page, setPage] = useState(1) const { data: photosData, isLoading, error } = usePhotos({ search, page, limit: 20 }) if (isLoading) return
Loading...
if (error) return
Error loading photos
return (
{/* 搜索栏 */}
setSearch(e.target.value)} className="max-w-sm" />
{/* 照片网格 */}
{photosData?.photos.map((photo) => ( onPhotoSelect?.(photo)} >
{photo.title}
{photo.status}
))}
) } ``` ### API 请求封装 ```typescript // src/services/api.ts import axios from 'axios' import { useAuthStore } from '@/store/auth' import { toast } from 'sonner' const api = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, timeout: 10000 }) // 请求拦截器 api.interceptors.request.use( (config) => { const { token } = useAuthStore.getState() if (token) { config.headers.Authorization = `Bearer ${token}` } return config }, (error) => Promise.reject(error) ) // 响应拦截器 api.interceptors.response.use( (response) => response.data, (error) => { const message = error.response?.data?.message || '请求失败' toast.error(message) // 401 错误自动跳转登录 if (error.response?.status === 401) { useAuthStore.getState().logout() window.location.href = '/login' } return Promise.reject(error) } ) export default api // src/hooks/usePhotos.ts import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { photoService } from '@/services/photo' export function usePhotos(params: PhotoListParams) { return useQuery({ queryKey: ['photos', params], queryFn: () => photoService.getPhotos(params) }) } export function useCreatePhoto() { const queryClient = useQueryClient() return useMutation({ mutationFn: photoService.createPhoto, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['photos'] }) toast.success('照片创建成功') } }) } ``` ## 🔄 与其他模块的集成 ### 与前端展示的关系 - 管理后台修改的数据通过 API 影响前端展示 - 照片和相册的增删改查直接影响用户访问体验 - 主题设置和配置修改需要前端重新获取 ### 与后端 API 的关系 - 依赖后端提供的 REST API 接口 - 需要后端提供认证和权限控制 - 文件上传需要后端存储支持 ### 与部署模块的关系 - 构建产物需要部署到 Web 服务器 - 需要配置反向代理到管理后台路径 - 与前端项目共享域名和 SSL 证书 ## 🐛 问题排查 ### 常见问题 1. **登录失败**: 检查 API 地址和 Token 存储 2. **图片上传失败**: 检查文件大小和格式限制 3. **权限错误**: 检查用户角色和权限配置 4. **页面空白**: 检查路由配置和组件导入 ### 调试技巧 ```bash # 查看开发环境变量 npm run dev --debug # 查看构建详情 npm run build --debug # 分析构建产物 npm run analyze ``` ## 📈 性能优化 ### 代码分割 ```typescript // 路由懒加载 const PhotoList = () => import('@/views/photos/PhotoList.vue') const AlbumList = () => import('@/views/albums/AlbumList.vue') // 组件懒加载 const LazyComponent = defineAsyncComponent(() => import('./Component.vue')) ``` ### 缓存策略 ```typescript // src/lib/query-client.ts import { QueryClient } from '@tanstack/react-query' export const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: 5 * 60 * 1000, // 5分钟缓存 cacheTime: 10 * 60 * 1000, // 10分钟 refetchOnWindowFocus: false, retry: (failureCount, error: any) => { if (error?.response?.status === 404) return false return failureCount < 3 } } } }) // 使用示例 export function usePhotos(params: PhotoListParams) { return useQuery({ queryKey: ['photos', params], queryFn: () => photoService.getPhotos(params), staleTime: 5 * 60 * 1000 // 5分钟缓存 }) } ``` ## 🔮 未来规划 ### 功能扩展 - 📊 高级数据分析和报表 - 🔄 批量操作和导入导出 - 📱 移动端管理应用 - 🎨 可视化主题编辑器 - 🔌 插件系统支持 ### 技术升级 - 升级到 React 19 最新特性 - 引入 React Server Components - 支持 PWA 离线访问 - 集成 AI 辅助功能 - 支持 Micro Frontend 架构 ## 📚 参考资料 - [React 官方文档](https://react.dev/) - [shadcn/ui 文档](https://ui.shadcn.com/) - [Radix UI 文档](https://radix-ui.com/) - [Tailwind CSS 文档](https://tailwindcss.com/) - [React Query 文档](https://tanstack.com/query/) - [Zustand 文档](https://zustand-demo.pmnd.rs/) - [Vite 配置指南](https://vitejs.dev/) - [TypeScript 最佳实践](https://www.typescriptlang.org/) --- 💡 **开发提示**: 在开始开发前,请确保已经阅读根目录的 CLAUDE.md 文件,了解项目整体架构和模块间的关系。开发过程中遇到问题,可以参考对应模块的 CLAUDE.md 文件寻找解决方案。