feat: setup frontend project with bun and dynamic data fetching

- Create new frontend project directory with Next.js 15 + React 19
- Migrate from pnpm to bun for faster package management
- Add TanStack Query + Axios for dynamic data fetching
- Create comprehensive Makefile with development commands
- Setup API layer with query hooks and error handling
- Configure environment variables and bun settings
- Add TypeScript type checking and project documentation
- Update CLAUDE.md with bun-specific development workflow
This commit is contained in:
xujiang
2025-07-08 15:28:26 +08:00
parent d06caee35a
commit 3d197eb7e3
87 changed files with 8329 additions and 0 deletions

91
frontend/lib/queries.ts Normal file
View File

@ -0,0 +1,91 @@
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import api from './api'
// 照片数据类型
export interface Photo {
id: number
src: string
title: string
description: string
category: string
tags: string[]
date: string
exif: {
camera: string
lens: string
settings: string
location: string
}
}
// 查询键
export const queryKeys = {
photos: ['photos'] as const,
photo: (id: number) => ['photo', id] as const,
categories: ['categories'] as const,
}
// 获取所有照片
export const usePhotos = () => {
return useQuery({
queryKey: queryKeys.photos,
queryFn: () => api.get('/photos'),
staleTime: 5 * 60 * 1000, // 5分钟内不重新获取
})
}
// 获取单张照片
export const usePhoto = (id: number) => {
return useQuery({
queryKey: queryKeys.photo(id),
queryFn: () => api.get(`/photos/${id}`),
enabled: !!id,
})
}
// 获取分类列表
export const useCategories = () => {
return useQuery({
queryKey: queryKeys.categories,
queryFn: () => api.get('/categories'),
staleTime: 10 * 60 * 1000, // 10分钟内不重新获取
})
}
// 添加照片
export const useAddPhoto = () => {
const queryClient = useQueryClient()
return useMutation({
mutationFn: (photo: Omit<Photo, 'id'>) => api.post('/photos', photo),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: queryKeys.photos })
},
})
}
// 更新照片
export const useUpdatePhoto = () => {
const queryClient = useQueryClient()
return useMutation({
mutationFn: ({ id, ...photo }: Partial<Photo> & { id: number }) =>
api.put(`/photos/${id}`, photo),
onSuccess: (data, variables) => {
queryClient.invalidateQueries({ queryKey: queryKeys.photos })
queryClient.invalidateQueries({ queryKey: queryKeys.photo(variables.id) })
},
})
}
// 删除照片
export const useDeletePhoto = () => {
const queryClient = useQueryClient()
return useMutation({
mutationFn: (id: number) => api.delete(`/photos/${id}`),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: queryKeys.photos })
},
})
}