Some checks failed
Deploy Frontend / deploy (push) Failing after 1m39s
- 安装 ESLint 和 eslint-config-next 依赖 - 创建 .eslintrc.json 配置文件,使用 Next.js 严格模式 - 修复 TypeScript 代码中的类型错误: - 移除 any 类型,使用具体的类型定义 - 修复未使用的函数参数和变量 - 优化组件类型定义 - 现在 CI/CD 可以成功通过 lint 检查
163 lines
5.5 KiB
TypeScript
163 lines
5.5 KiB
TypeScript
"use client"
|
||
|
||
import { useEffect } from "react"
|
||
import Image from "next/image"
|
||
import { X, ChevronLeft, ChevronRight, Camera, MapPin, Calendar } from "lucide-react"
|
||
import { Button } from "@/components/ui/button"
|
||
import { Badge } from "@/components/ui/badge"
|
||
import { Card, CardContent } from "@/components/ui/card"
|
||
|
||
import { Photo } from '@/lib/queries'
|
||
|
||
interface PhotoModalProps {
|
||
photo: Photo
|
||
onClose: () => void
|
||
onPrev: () => void
|
||
onNext: () => void
|
||
}
|
||
|
||
export function PhotoModal({ photo, onClose, onPrev, onNext }: PhotoModalProps) {
|
||
useEffect(() => {
|
||
const handleKeyDown = (e: KeyboardEvent) => {
|
||
switch (e.key) {
|
||
case "Escape":
|
||
onClose()
|
||
break
|
||
case "ArrowLeft":
|
||
onPrev()
|
||
break
|
||
case "ArrowRight":
|
||
onNext()
|
||
break
|
||
}
|
||
}
|
||
|
||
document.addEventListener("keydown", handleKeyDown)
|
||
document.body.style.overflow = "hidden"
|
||
|
||
return () => {
|
||
document.removeEventListener("keydown", handleKeyDown)
|
||
document.body.style.overflow = "unset"
|
||
}
|
||
}, [onClose, onPrev, onNext])
|
||
|
||
return (
|
||
<div className="fixed inset-0 z-50 bg-black/95 flex items-center justify-center p-4">
|
||
{/* Close button */}
|
||
<Button
|
||
variant="ghost"
|
||
size="sm"
|
||
className="absolute top-4 right-4 z-10 text-white hover:bg-white/10"
|
||
onClick={onClose}
|
||
>
|
||
<X className="h-6 w-6" />
|
||
</Button>
|
||
|
||
{/* Navigation buttons */}
|
||
<Button
|
||
variant="ghost"
|
||
size="sm"
|
||
className="absolute left-4 top-1/2 -translate-y-1/2 z-10 text-white hover:bg-white/10"
|
||
onClick={onPrev}
|
||
>
|
||
<ChevronLeft className="h-8 w-8" />
|
||
</Button>
|
||
|
||
<Button
|
||
variant="ghost"
|
||
size="sm"
|
||
className="absolute right-4 top-1/2 -translate-y-1/2 z-10 text-white hover:bg-white/10"
|
||
onClick={onNext}
|
||
>
|
||
<ChevronRight className="h-8 w-8" />
|
||
</Button>
|
||
|
||
<div className="w-full max-w-7xl mx-auto grid lg:grid-cols-3 gap-6 h-full max-h-[90vh]">
|
||
{/* Main image */}
|
||
<div className="lg:col-span-2 relative flex items-center justify-center">
|
||
<div className="relative w-full h-full max-h-[80vh] flex items-center justify-center">
|
||
<Image
|
||
src={photo.src || "/placeholder.svg"}
|
||
alt={photo.title}
|
||
width={1200}
|
||
height={800}
|
||
className="max-w-full max-h-full object-contain"
|
||
priority
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Photo information */}
|
||
<div className="lg:col-span-1 overflow-y-auto">
|
||
<Card className="bg-white/95 backdrop-blur-sm">
|
||
<CardContent className="p-6">
|
||
<div className="space-y-6">
|
||
{/* Title and description */}
|
||
<div>
|
||
<h2 className="text-2xl font-light text-gray-900 mb-2">{photo.title}</h2>
|
||
<p className="text-gray-600 leading-relaxed">{photo.description}</p>
|
||
</div>
|
||
|
||
{/* Tags */}
|
||
<div>
|
||
<h3 className="text-sm font-medium text-gray-900 mb-2">标签</h3>
|
||
<div className="flex flex-wrap gap-2">
|
||
{photo.tags.map((tag: string) => (
|
||
<Badge key={tag} variant="secondary">
|
||
{tag}
|
||
</Badge>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Date and location */}
|
||
<div className="grid grid-cols-1 gap-4">
|
||
<div className="flex items-center gap-2 text-gray-600">
|
||
<Calendar className="h-4 w-4" />
|
||
<span className="text-sm">拍摄时间:{photo.date}</span>
|
||
</div>
|
||
<div className="flex items-center gap-2 text-gray-600">
|
||
<MapPin className="h-4 w-4" />
|
||
<span className="text-sm">拍摄地点:{photo.exif.location}</span>
|
||
</div>
|
||
</div>
|
||
|
||
{/* EXIF information */}
|
||
<div>
|
||
<h3 className="text-sm font-medium text-gray-900 mb-3 flex items-center gap-2">
|
||
<Camera className="h-4 w-4" />
|
||
拍摄参数
|
||
</h3>
|
||
<div className="space-y-2 text-sm text-gray-600">
|
||
<div className="flex justify-between">
|
||
<span>相机:</span>
|
||
<span className="font-medium">{photo.exif.camera}</span>
|
||
</div>
|
||
<div className="flex justify-between">
|
||
<span>镜头:</span>
|
||
<span className="font-medium">{photo.exif.lens}</span>
|
||
</div>
|
||
<div className="flex justify-between">
|
||
<span>参数:</span>
|
||
<span className="font-medium">{photo.exif.settings}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Keyboard shortcuts */}
|
||
<div className="pt-4 border-t border-gray-200">
|
||
<h3 className="text-sm font-medium text-gray-900 mb-2">快捷键</h3>
|
||
<div className="text-xs text-gray-500 space-y-1">
|
||
<div>ESC - 关闭</div>
|
||
<div>← → - 切换图片</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|