init ui
This commit is contained in:
63
ui/app/globals.css
Normal file
63
ui/app/globals.css
Normal file
@ -0,0 +1,63 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222.2 84% 4.9%;
|
||||
--primary: 222.2 47.4% 11.2%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
--secondary: 210 40% 96%;
|
||||
--secondary-foreground: 222.2 84% 4.9%;
|
||||
--muted: 210 40% 96%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
--accent: 210 40% 96%;
|
||||
--accent-foreground: 222.2 84% 4.9%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--ring: 222.2 84% 4.9%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
/* Custom scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #c1c1c1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #a8a8a8;
|
||||
}
|
||||
|
||||
/* Smooth transitions */
|
||||
* {
|
||||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow,
|
||||
transform, filter, backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
24
ui/app/layout.tsx
Normal file
24
ui/app/layout.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import type React from "react"
|
||||
import type { Metadata } from "next"
|
||||
import { Inter } from "next/font/google"
|
||||
import "./globals.css"
|
||||
|
||||
const inter = Inter({ subsets: ["latin"] })
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "摄影作品集 - PhotoStudio",
|
||||
description: "专业摄影师作品展示平台,记录世界的美好瞬间",
|
||||
generator: 'v0.dev'
|
||||
}
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<html lang="zh-CN">
|
||||
<body className={inter.className}>{children}</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
270
ui/app/page.tsx
Normal file
270
ui/app/page.tsx
Normal file
@ -0,0 +1,270 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useEffect } from "react"
|
||||
import { PhotoGallery } from "@/components/photo-gallery"
|
||||
import { PhotoModal } from "@/components/photo-modal"
|
||||
import { Navigation } from "@/components/navigation"
|
||||
import { FilterBar } from "@/components/filter-bar"
|
||||
import { LoadingSpinner } from "@/components/loading-spinner"
|
||||
import { TimelineView } from "@/components/timeline-view"
|
||||
import { AboutView } from "@/components/about-view"
|
||||
import { ContactView } from "@/components/contact-view"
|
||||
|
||||
// Mock photo data with EXIF information
|
||||
const mockPhotos = [
|
||||
{
|
||||
id: 1,
|
||||
src: "/placeholder.svg?height=600&width=800",
|
||||
title: "城市夜景",
|
||||
description: "繁华都市的霓虹夜色",
|
||||
category: "urban",
|
||||
tags: ["夜景", "城市", "建筑"],
|
||||
date: "2024-01-15",
|
||||
exif: {
|
||||
camera: "Canon EOS R5",
|
||||
lens: "24-70mm f/2.8",
|
||||
settings: "f/8, 1/60s, ISO 800",
|
||||
location: "上海外滩",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
src: "/placeholder.svg?height=800&width=600",
|
||||
title: "自然风光",
|
||||
description: "山间晨雾缭绕的美景",
|
||||
category: "nature",
|
||||
tags: ["风景", "山脉", "晨雾"],
|
||||
date: "2024-01-20",
|
||||
exif: {
|
||||
camera: "Sony A7R IV",
|
||||
lens: "70-200mm f/4",
|
||||
settings: "f/11, 1/125s, ISO 200",
|
||||
location: "黄山",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
src: "/placeholder.svg?height=600&width=800",
|
||||
title: "人像摄影",
|
||||
description: "自然光下的人像作品",
|
||||
category: "portrait",
|
||||
tags: ["人像", "自然光", "情绪"],
|
||||
date: "2024-02-01",
|
||||
exif: {
|
||||
camera: "Nikon D850",
|
||||
lens: "85mm f/1.4",
|
||||
settings: "f/2.8, 1/200s, ISO 400",
|
||||
location: "工作室",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
src: "/placeholder.svg?height=800&width=800",
|
||||
title: "街头摄影",
|
||||
description: "捕捉城市生活的瞬间",
|
||||
category: "street",
|
||||
tags: ["街头", "生活", "瞬间"],
|
||||
date: "2024-02-10",
|
||||
exif: {
|
||||
camera: "Fujifilm X-T4",
|
||||
lens: "35mm f/1.4",
|
||||
settings: "f/5.6, 1/250s, ISO 800",
|
||||
location: "北京胡同",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
src: "/placeholder.svg?height=600&width=800",
|
||||
title: "建筑摄影",
|
||||
description: "现代建筑的几何美学",
|
||||
category: "architecture",
|
||||
tags: ["建筑", "几何", "现代"],
|
||||
date: "2024-02-15",
|
||||
exif: {
|
||||
camera: "Canon EOS R6",
|
||||
lens: "16-35mm f/2.8",
|
||||
settings: "f/8, 1/100s, ISO 100",
|
||||
location: "深圳",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
src: "/placeholder.svg?height=800&width=600",
|
||||
title: "微距摄影",
|
||||
description: "花朵的细节之美",
|
||||
category: "macro",
|
||||
tags: ["微距", "花卉", "细节"],
|
||||
date: "2024-02-20",
|
||||
exif: {
|
||||
camera: "Sony A7R V",
|
||||
lens: "90mm f/2.8 Macro",
|
||||
settings: "f/5.6, 1/160s, ISO 200",
|
||||
location: "植物园",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
src: "/placeholder.svg?height=600&width=800",
|
||||
title: "日落风景",
|
||||
description: "海边的金色黄昏",
|
||||
category: "nature",
|
||||
tags: ["日落", "海景", "黄昏"],
|
||||
date: "2023-12-10",
|
||||
exif: {
|
||||
camera: "Canon EOS R5",
|
||||
lens: "24-105mm f/4",
|
||||
settings: "f/8, 1/125s, ISO 100",
|
||||
location: "三亚海滩",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
src: "/placeholder.svg?height=800&width=600",
|
||||
title: "古建筑",
|
||||
description: "传统建筑的韵味",
|
||||
category: "architecture",
|
||||
tags: ["古建筑", "传统", "文化"],
|
||||
date: "2023-11-25",
|
||||
exif: {
|
||||
camera: "Sony A7R IV",
|
||||
lens: "50mm f/1.8",
|
||||
settings: "f/5.6, 1/200s, ISO 400",
|
||||
location: "故宫",
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
export default function HomePage() {
|
||||
const [photos, setPhotos] = useState(mockPhotos)
|
||||
const [filteredPhotos, setFilteredPhotos] = useState(mockPhotos)
|
||||
const [selectedPhoto, setSelectedPhoto] = useState(null)
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const [activeCategory, setActiveCategory] = useState("all")
|
||||
const [activeTab, setActiveTab] = useState("gallery")
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading
|
||||
const timer = setTimeout(() => {
|
||||
setIsLoading(false)
|
||||
}, 1500)
|
||||
|
||||
return () => clearTimeout(timer)
|
||||
}, [])
|
||||
|
||||
const handleFilter = (category: string) => {
|
||||
setActiveCategory(category)
|
||||
if (category === "all") {
|
||||
setFilteredPhotos(photos)
|
||||
} else {
|
||||
setFilteredPhotos(photos.filter((photo) => photo.category === category))
|
||||
}
|
||||
}
|
||||
|
||||
const handlePhotoClick = (photo: any) => {
|
||||
setSelectedPhoto(photo)
|
||||
}
|
||||
|
||||
const handleCloseModal = () => {
|
||||
setSelectedPhoto(null)
|
||||
}
|
||||
|
||||
const handlePrevPhoto = () => {
|
||||
if (!selectedPhoto) return
|
||||
const currentPhotos = activeTab === "gallery" ? filteredPhotos : photos
|
||||
const currentIndex = currentPhotos.findIndex((p) => p.id === selectedPhoto.id)
|
||||
const prevIndex = currentIndex > 0 ? currentIndex - 1 : currentPhotos.length - 1
|
||||
setSelectedPhoto(currentPhotos[prevIndex])
|
||||
}
|
||||
|
||||
const handleNextPhoto = () => {
|
||||
if (!selectedPhoto) return
|
||||
const currentPhotos = activeTab === "gallery" ? filteredPhotos : photos
|
||||
const currentIndex = currentPhotos.findIndex((p) => p.id === selectedPhoto.id)
|
||||
const nextIndex = currentIndex < currentPhotos.length - 1 ? currentIndex + 1 : 0
|
||||
setSelectedPhoto(currentPhotos[nextIndex])
|
||||
}
|
||||
|
||||
const handleTabChange = (tab: string) => {
|
||||
setActiveTab(tab)
|
||||
// Reset filters when switching tabs
|
||||
if (tab === "timeline") {
|
||||
setActiveCategory("all")
|
||||
setFilteredPhotos(photos)
|
||||
}
|
||||
}
|
||||
|
||||
const handleContactClick = () => {
|
||||
setActiveTab("contact")
|
||||
}
|
||||
|
||||
const getPageTitle = () => {
|
||||
switch (activeTab) {
|
||||
case "gallery":
|
||||
return "摄影作品集"
|
||||
case "timeline":
|
||||
return "创作时间线"
|
||||
case "about":
|
||||
return "关于我"
|
||||
case "contact":
|
||||
return "联系合作"
|
||||
default:
|
||||
return "摄影作品集"
|
||||
}
|
||||
}
|
||||
|
||||
const getPageDescription = () => {
|
||||
switch (activeTab) {
|
||||
case "gallery":
|
||||
return "用镜头记录世界的美好瞬间,每一张照片都是时光的诗篇"
|
||||
case "timeline":
|
||||
return "按时间顺序回顾摄影创作历程,见证技艺与视角的成长轨迹"
|
||||
case "about":
|
||||
return "分享我的摄影故事,探索光影背后的创作理念与人生感悟"
|
||||
case "contact":
|
||||
return "期待与您合作,共同创造独特而珍贵的视觉记忆"
|
||||
default:
|
||||
return "用镜头记录世界的美好瞬间,每一张照片都是时光的诗篇"
|
||||
}
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return <LoadingSpinner />
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-white">
|
||||
<Navigation activeTab={activeTab} onTabChange={handleTabChange} />
|
||||
|
||||
<main className="container mx-auto px-4 py-8">
|
||||
{(activeTab === "gallery" || activeTab === "timeline") && (
|
||||
<div className="text-center mb-12">
|
||||
<h1 className="text-4xl md:text-6xl font-light text-gray-900 mb-4">{getPageTitle()}</h1>
|
||||
<p className="text-lg text-gray-600 max-w-2xl mx-auto">{getPageDescription()}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === "gallery" && (
|
||||
<>
|
||||
<FilterBar activeCategory={activeCategory} onFilter={handleFilter} />
|
||||
<PhotoGallery photos={filteredPhotos} onPhotoClick={handlePhotoClick} />
|
||||
</>
|
||||
)}
|
||||
|
||||
{activeTab === "timeline" && <TimelineView photos={photos} onPhotoClick={handlePhotoClick} />}
|
||||
|
||||
{activeTab === "about" && <AboutView onContactClick={handleContactClick} />}
|
||||
|
||||
{activeTab === "contact" && <ContactView />}
|
||||
</main>
|
||||
|
||||
{selectedPhoto && (
|
||||
<PhotoModal
|
||||
photo={selectedPhoto}
|
||||
onClose={handleCloseModal}
|
||||
onPrev={handlePrevPhoto}
|
||||
onNext={handleNextPhoto}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user