管理后台

This commit is contained in:
xujiang
2025-07-09 17:50:29 +08:00
parent 0651b6626a
commit 5f2152c7a6
40 changed files with 3839 additions and 795 deletions

View File

@ -1,6 +1,6 @@
import React, { useState } from 'react'
import { useState } from 'react'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Card, CardContent } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Badge } from '@/components/ui/badge'
@ -9,23 +9,21 @@ import { Checkbox } from '@/components/ui/checkbox'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'
import {
PhotoIcon,
PlusIcon,
SearchIcon,
FilterIcon,
MoreVerticalIcon,
EditIcon,
TrashIcon,
EyeIcon,
DownloadIcon,
GridIcon,
ListIcon
Camera,
Plus,
Search,
MoreVertical,
Edit,
Trash,
Eye,
Grid,
List
} from 'lucide-react'
import { useNavigate } from 'react-router-dom'
import { toast } from 'sonner'
import { photoService } from '@/services/photoService'
import { categoryService } from '@/services/categoryService'
import { tagService } from '@/services/tagService'
type ViewMode = 'grid' | 'list'
@ -73,11 +71,6 @@ export default function Photos() {
queryFn: () => categoryService.getCategories()
})
// 获取标签列表
const { data: tags } = useQuery({
queryKey: ['tags-all'],
queryFn: () => tagService.getAllTags()
})
// 删除照片
const deletePhotoMutation = useMutation({
@ -194,7 +187,7 @@ export default function Photos() {
</p>
</div>
<Button onClick={() => navigate('/photos/upload')} className="flex items-center gap-2">
<PlusIcon className="h-4 w-4" />
<Plus className="h-4 w-4" />
</Button>
</div>
@ -205,7 +198,7 @@ export default function Photos() {
<div className="flex flex-col lg:flex-row gap-4">
{/* 搜索 */}
<div className="flex-1 relative">
<SearchIcon className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground h-4 w-4" />
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground h-4 w-4" />
<Input
placeholder="搜索照片..."
value={filters.search}
@ -251,14 +244,14 @@ export default function Photos() {
size="sm"
onClick={() => setViewMode('grid')}
>
<GridIcon className="h-4 w-4" />
<Grid className="h-4 w-4" />
</Button>
<Button
variant={viewMode === 'list' ? 'default' : 'ghost'}
size="sm"
onClick={() => setViewMode('list')}
>
<ListIcon className="h-4 w-4" />
<List className="h-4 w-4" />
</Button>
</div>
</div>
@ -342,7 +335,7 @@ export default function Photos() {
<CardContent className="p-4">
<div className="relative mb-4">
<div className="aspect-square bg-gray-100 rounded-lg flex items-center justify-center">
<PhotoIcon className="h-12 w-12 text-gray-400" />
<Camera className="h-12 w-12 text-gray-400" />
</div>
{/* 复选框 */}
@ -359,20 +352,20 @@ export default function Photos() {
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="sm" className="h-8 w-8 p-0 bg-white">
<MoreVerticalIcon className="h-4 w-4" />
<MoreVertical className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => navigate(`/photos/${photo.id}`)}>
<EyeIcon className="h-4 w-4 mr-2" />
<Eye className="h-4 w-4 mr-2" />
</DropdownMenuItem>
<DropdownMenuItem onClick={() => navigate(`/photos/${photo.id}/edit`)}>
<EditIcon className="h-4 w-4 mr-2" />
<Edit className="h-4 w-4 mr-2" />
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleDeletePhoto(photo.id)}>
<TrashIcon className="h-4 w-4 mr-2" />
<Trash className="h-4 w-4 mr-2" />
</DropdownMenuItem>
</DropdownMenuContent>
@ -410,7 +403,7 @@ export default function Photos() {
/>
<div className="h-16 w-16 bg-gray-100 rounded flex items-center justify-center flex-shrink-0">
<PhotoIcon className="h-8 w-8 text-gray-400" />
<Camera className="h-8 w-8 text-gray-400" />
</div>
<div className="flex-1 min-w-0">
@ -434,20 +427,20 @@ export default function Photos() {
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="sm">
<MoreVerticalIcon className="h-4 w-4" />
<MoreVertical className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => navigate(`/photos/${photo.id}`)}>
<EyeIcon className="h-4 w-4 mr-2" />
<Eye className="h-4 w-4 mr-2" />
</DropdownMenuItem>
<DropdownMenuItem onClick={() => navigate(`/photos/${photo.id}/edit`)}>
<EditIcon className="h-4 w-4 mr-2" />
<Edit className="h-4 w-4 mr-2" />
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleDeletePhoto(photo.id)}>
<TrashIcon className="h-4 w-4 mr-2" />
<Trash className="h-4 w-4 mr-2" />
</DropdownMenuItem>
</DropdownMenuContent>
@ -500,13 +493,13 @@ export default function Photos() {
<Card>
<CardContent className="py-12">
<div className="text-center">
<PhotoIcon className="h-16 w-16 mx-auto mb-4 text-gray-300" />
<Camera className="h-16 w-16 mx-auto mb-4 text-gray-300" />
<h3 className="text-lg font-medium mb-2"></h3>
<p className="text-muted-foreground mb-4">
</p>
<Button onClick={() => navigate('/photos/upload')}>
<PlusIcon className="h-4 w-4 mr-2" />
<Plus className="h-4 w-4 mr-2" />
</Button>
</div>