diff --git a/package.json b/package.json index 6871174..4a94c17 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,6 @@ "preview": "vite preview" }, "dependencies": { - "tailwindcss": "^4.1.4", - "vue": "^3.5.13", "@ant-design-vue/pro-layout": "^3.2.5", "@ant-design/icons-vue": "^7.0.1", "@fortawesome/fontawesome-free": "^6.7.2", @@ -21,6 +19,9 @@ "lucide-vue-next": "^0.507.0", "pinia": "^2.1.7", "swiper": "^11.0.5", + "tailwindcss": "^4.1.4", + "three": "^0.182.0", + "vue": "^3.5.13", "vue-router": "^4.2.5" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 45e372f..4fb62da 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,6 +41,9 @@ importers: tailwindcss: specifier: ^4.1.4 version: 4.1.4 + three: + specifier: ^0.182.0 + version: 0.182.0 vue: specifier: ^3.5.13 version: 3.5.13 @@ -299,36 +302,42 @@ packages: engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm-musl@2.5.1': resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [musl] '@parcel/watcher-linux-arm64-glibc@2.5.1': resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm64-musl@2.5.1': resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [musl] '@parcel/watcher-linux-x64-glibc@2.5.1': resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-x64-musl@2.5.1': resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [musl] '@parcel/watcher-win32-arm64@2.5.1': resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} @@ -386,56 +395,67 @@ packages: resolution: {integrity: sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.40.0': resolution: {integrity: sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.40.0': resolution: {integrity: sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.40.0': resolution: {integrity: sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loongarch64-gnu@4.40.0': resolution: {integrity: sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-powerpc64le-gnu@4.40.0': resolution: {integrity: sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.40.0': resolution: {integrity: sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.40.0': resolution: {integrity: sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.40.0': resolution: {integrity: sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.40.0': resolution: {integrity: sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.40.0': resolution: {integrity: sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-win32-arm64-msvc@4.40.0': resolution: {integrity: sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==} @@ -493,24 +513,28 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-arm64-musl@4.1.4': resolution: {integrity: sha512-X3As2xhtgPTY/m5edUtddmZ8rCruvBvtxYLMw9OsZdH01L2gS2icsHRwxdU0dMItNfVmrBezueXZCHxVeeb7Aw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@tailwindcss/oxide-linux-x64-gnu@4.1.4': resolution: {integrity: sha512-2VG4DqhGaDSmYIu6C4ua2vSLXnJsb/C9liej7TuSO04NK+JJJgJucDUgmX6sn7Gw3Cs5ZJ9ZLrnI0QRDOjLfNQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-x64-musl@4.1.4': resolution: {integrity: sha512-v+mxVgH2kmur/X5Mdrz9m7TsoVjbdYQT0b4Z+dr+I4RvreCNXyCFELZL/DO0M1RsidZTrm6O1eMnV6zlgEzTMQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@tailwindcss/oxide-wasm32-wasi@4.1.4': resolution: {integrity: sha512-2TLe9ir+9esCf6Wm+lLWTMbgklIjiF0pbmDnwmhR9MksVOq+e8aP3TSsXySnBDDvTTVd/vKu1aNttEGj3P6l8Q==} @@ -828,24 +852,28 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.29.2: resolution: {integrity: sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.29.2: resolution: {integrity: sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.29.2: resolution: {integrity: sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-arm64-msvc@1.29.2: resolution: {integrity: sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==} @@ -982,6 +1010,9 @@ packages: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} engines: {node: '>=6'} + three@0.182.0: + resolution: {integrity: sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ==} + throttle-debounce@5.0.2: resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==} engines: {node: '>=12.22'} @@ -1864,6 +1895,8 @@ snapshots: tapable@2.2.1: {} + three@0.182.0: {} + throttle-debounce@5.0.2: {} tinyglobby@0.2.13: diff --git a/src/App.vue b/src/App.vue index 525dba6..cb1bb48 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,36 +1,15 @@ - - - ❅ - + + - + diff --git a/src/components/AlbumGallery.vue b/src/components/AlbumGallery.vue new file mode 100644 index 0000000..61fe8fd --- /dev/null +++ b/src/components/AlbumGallery.vue @@ -0,0 +1,297 @@ + + + + {{ title }} + + + + + + + + + + + + + + + + + + + + + + + THE END · 美好已全部呈现 + + + + + + + + diff --git a/src/components/CinematicSlideshow.vue b/src/components/CinematicSlideshow.vue new file mode 100644 index 0000000..07225c5 --- /dev/null +++ b/src/components/CinematicSlideshow.vue @@ -0,0 +1,509 @@ + + + + + + + + + + + + + + + + + + + + + + + + + {{ title }} + {{ formatDate(currentPhoto.created_at) }} + + + + + + + + + + + + + + + + + + + + + + 放映设置 + + + + + 单张时长 + {{ duration / 1000 }}秒 + + + + + + + + + + PPT 转场风格 + + + {{ label }} + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ formatNumber(currentIndex + 1) }} + / + {{ formatNumber(photos.length) }} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/PhotoPlanet.vue b/src/components/PhotoPlanet.vue new file mode 100644 index 0000000..338fe16 --- /dev/null +++ b/src/components/PhotoPlanet.vue @@ -0,0 +1,488 @@ + + + + + + + + + + + + 正在构建影像宇宙... {{ loadingProgress }}% + 已加载 {{ loadedCount }} / {{ totalCount }} 碎片 + + + + + + + + + + 返回 + + 3D 影像星球 + + + + {{ isMobile ? '单指旋转 · 双指缩放' : '拖拽旋转 · 滚轮缩放' }} + + + + + + + + + + + + + + + diff --git a/src/components/PhotoWall.vue b/src/components/PhotoWall.vue new file mode 100644 index 0000000..4485467 --- /dev/null +++ b/src/components/PhotoWall.vue @@ -0,0 +1,324 @@ + + + + + + + + + {{ title }} + + + + + + + + + + + + + + + + + {{ formatDate(photo.created_at) }} + + + + + + + + + + + + + THE END · 故事未完待续 + + + + + + + + diff --git a/src/components/SnowEffect.vue b/src/components/SnowEffect.vue new file mode 100644 index 0000000..e183a92 --- /dev/null +++ b/src/components/SnowEffect.vue @@ -0,0 +1,231 @@ + + + + + + + + + + + + diff --git a/src/components/SwipeSlideshow.vue b/src/components/SwipeSlideshow.vue new file mode 100644 index 0000000..8a6e1f8 --- /dev/null +++ b/src/components/SwipeSlideshow.vue @@ -0,0 +1,283 @@ + + + + + + + + + + {{ currentIndex + 1 }} / {{ photos.length }} + + + + + + 放映 + + + + + + + + + + + + + + + + 切换效果 + + + + + + {{ label }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/views/index/detail.vue b/src/views/index/detail.vue index 2612921..171f04b 100644 --- a/src/views/index/detail.vue +++ b/src/views/index/detail.vue @@ -1,607 +1,291 @@ - - - 我们的相册 + - - - - 加载照片中... + + + + 正在读取美好瞬间... - - - - 返回相册列表 - + + + + + + + + 返回 + + + + + - - {{ albumData.title }} - + + + + + + 返回 + + + + + - - - - - - - - - - - - - - - - - - - - - - + currentIndex = val" /> - - - - - - - - - - - + + currentIndex = val" + /> + + + - - - diff --git a/src/views/index/index.vue b/src/views/index/index.vue index 34f9a6d..e28307f 100644 --- a/src/views/index/index.vue +++ b/src/views/index/index.vue @@ -1,40 +1,223 @@ - 我们的相册 + + + 影像集 - - - 加载中... + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - {{ album.title }} - {{ formatDate(album.created_at) }} - - {{ album.photo_count }} 张照片 + + + + 正在加载相册... + + + + + + 暂无相册,去创建一个吧 + + + + + + + + + + + + + + {{ album.title }} + + {{ formatDate(album.created_at) }} + {{ album.photo_count }} 张 + + + - + + + + + + + + + + + + {{ album.title }} + {{ album.photo_count }} Photos + + {{ album.description || '暂无描述...' }} + + + + + + + + + + + + + {{ formatDate(album.created_at) }} + + + + + + {{ album.title }} + {{ album.photo_count }} 张照片 + + + + + + + + + + + + + + + + + {{ album.title }} + + {{ formatDate(album.created_at) }} + {{ album.photo_count }} 张 + + + + + + + 左右滑动切换卡片 + + + + + + + + + + + + + {{ album.title }} + {{ album.photo_count }} 张照片 + + + + + + 左右滑动选择 · 点击进入 + + + + @@ -45,48 +228,57 @@ import { ref, onMounted } from 'vue' import { useRouter } from 'vue-router' import { albumListApi } from "@/api/request/api.js" +// Swiper +import { Swiper, SwiperSlide } from 'swiper/vue'; +import { EffectCoverflow, EffectCards, Pagination } from 'swiper/modules'; +import 'swiper/css'; +import 'swiper/css/effect-coverflow'; +import 'swiper/css/effect-cards'; +import 'swiper/css/pagination'; + const router = useRouter() const albums = ref([]) const loading = ref(true) +const viewMode = ref('grid') // 'grid' | 'list' | 'timeline' | 'stack' | 'coverflow' +const swiperModules = [EffectCoverflow, EffectCards, Pagination] + +const getImgUrl = (path) => path ? `http://i.nailaoyun.cn${path}` : '' const getList = async () => { loading.value = true try { const res = await albumListApi() - albums.value = res + albums.value = res || [] } catch (error) { console.error('获取相册列表失败:', error) - // Fallback to empty array on error albums.value = [] } finally { loading.value = false } } -const onImageLoad = (event) => { - event.target.classList.add('loaded') -} - -const onImageError = (event) => { - // Fallback to placeholder on error - event.target.src = '/placeholder.svg?height=300&width=400&text=加载失败' - event.target.classList.add('error') -} - const formatDate = (dateString) => { + if (!dateString) return '' const date = new Date(dateString) - return date.toLocaleDateString('zh-CN', { - year: 'numeric', - month: 'long', - day: 'numeric' - }) + return date.toLocaleDateString('zh-CN', { year: 'numeric', month: 'long', day: 'numeric' }) } const goToAlbum = (albumId) => { router.push(`/detail/${albumId}`) } +// 切换模式并持久化 +const changeViewMode = (mode) => { + viewMode.value = mode + localStorage.setItem('album_view_mode', mode) +} + onMounted(() => { + // 读取缓存的视图模式 + const savedMode = localStorage.getItem('album_view_mode') + if (savedMode) { + viewMode.value = savedMode + } getList() }) @@ -97,209 +289,470 @@ onMounted(() => { min-height: 100vh; position: relative; z-index: 2; + padding-bottom: 4rem; } .container { max-width: 1400px; margin: 0 auto; - padding: 3rem; + padding: 2rem; +} + +/* 头部布局 */ +.header-section { + display: flex; + flex-direction: column; + align-items: center; + margin-bottom: 3rem; position: relative; } .main-title { color: var(--light-color); - text-align: center; - margin-bottom: 3rem; font-weight: 300; - letter-spacing: 3px; + letter-spacing: 4px; font-size: 2.5rem; text-transform: uppercase; - position: relative; - padding-bottom: 1rem; - text-shadow: 0 0 10px rgba(230, 57, 70, 0.3); + margin-bottom: 1.5rem; + text-shadow: 0 0 20px rgba(230, 57, 70, 0.4); } -.main-title::after { - content: ''; +/* 视图切换按钮组 */ +.view-mode-group { + display: flex; + background: rgba(255, 255, 255, 0.05); + border-radius: 30px; + padding: 4px; + border: 1px solid rgba(255, 255, 255, 0.1); + backdrop-filter: blur(10px); + /* 移动端支持横向滑动 */ + overflow-x: auto; + max-width: 100%; +} + +.group-btn { + background: transparent; + border: none; + color: rgba(255, 255, 255, 0.5); + width: 44px; + height: 36px; + border-radius: 20px; + cursor: pointer; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + display: flex; + align-items: center; + justify-content: center; + font-size: 1.1rem; + flex-shrink: 0; /* 防止挤压 */ +} + +.group-btn:hover { color: #fff; background: rgba(255, 255, 255, 0.1); } +.group-btn.active { background: #e63946; color: white; box-shadow: 0 2px 10px rgba(230, 57, 70, 0.4); } + +/* --- 模式 A: 网格视图 --- */ +.album-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 2rem; +} + +.album-card { + position: relative; + border-radius: 12px; + overflow: hidden; + cursor: pointer; + transition: transform 0.3s ease, box-shadow 0.3s ease; + background: #1a1a1a; + border: 1px solid rgba(255,255,255,0.05); +} + +.album-card:hover { + transform: translateY(-8px); + box-shadow: 0 15px 30px rgba(0, 0, 0, 0.4); + border-color: #e63946; +} + +.album-cover { + aspect-ratio: 4/3; + position: relative; + overflow: hidden; +} + +.cover-image { + width: 100%; + height: 100%; + object-fit: cover; + transition: transform 0.5s ease; +} + +.album-card:hover .cover-image { transform: scale(1.1); } + +.card-overlay { position: absolute; + inset: 0; + background: linear-gradient(to top, rgba(0,0,0,0.9) 0%, rgba(0,0,0,0.2) 50%, transparent 100%); + display: flex; + flex-direction: column; + justify-content: flex-end; + padding: 1.5rem; + opacity: 0.9; +} + +.card-title { + color: #fff; + margin: 0 0 0.5rem 0; + font-size: 1.2rem; + font-weight: 600; + text-shadow: 0 2px 4px rgba(0,0,0,0.5); +} + +.card-meta { + display: flex; + justify-content: space-between; + font-size: 0.85rem; + color: rgba(255,255,255,0.7); +} + +/* --- 模式 B: 列表视图 --- */ +.album-list { + display: flex; + flex-direction: column; + gap: 1.5rem; + max-width: 1000px; + margin: 0 auto; +} + +.list-item { + display: flex; + background: rgba(20, 20, 20, 0.6); + border: 1px solid rgba(255,255,255,0.05); + border-radius: 12px; + overflow: hidden; + cursor: pointer; + transition: all 0.3s ease; + height: 180px; +} + +.list-item:hover { + background: rgba(30, 30, 30, 0.8); + border-color: #e63946; + transform: translateX(10px); +} + +.list-cover { + width: 280px; + height: 100%; + flex-shrink: 0; +} + +.list-cover img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.list-info { + flex: 1; + padding: 1.5rem; + display: flex; + flex-direction: column; + justify-content: space-between; +} + +.info-header { + display: flex; + justify-content: space-between; + align-items: center; +} + +.list-title { + color: #fff; + font-size: 1.4rem; + margin: 0; +} + +.badge { + background: rgba(230, 57, 70, 0.2); + color: #e63946; + padding: 4px 10px; + border-radius: 12px; + font-size: 0.8rem; + font-weight: 600; +} + +.list-desc { + color: rgba(255,255,255,0.6); + font-size: 0.95rem; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + margin: 0.5rem 0; +} + +.list-footer { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: auto; +} + +.list-date { + color: rgba(255,255,255,0.4); + font-size: 0.85rem; +} + +.enter-btn { + background: transparent; + border: 1px solid rgba(255,255,255,0.2); + color: #fff; + padding: 6px 16px; + border-radius: 20px; + cursor: pointer; + transition: all 0.3s; + font-size: 0.85rem; +} + +.list-item:hover .enter-btn { + background: #e63946; + border-color: #e63946; +} + +/* --- 模式 C: 时光轴 (Timeline) --- */ +.album-timeline { + position: relative; + max-width: 1000px; + margin: 0 auto; + padding: 2rem 0; +} + +/* 垂直线 */ +.timeline-line { + position: absolute; + top: 0; bottom: 0; left: 50%; + width: 2px; + background: rgba(255,255,255,0.1); transform: translateX(-50%); - width: 100px; - height: 3px; - background: var(--primary-color); - box-shadow: 0 0 10px rgba(230, 57, 70, 0.5); } +.timeline-item { + position: relative; + margin-bottom: 4rem; + width: 50%; + padding: 0 40px; + box-sizing: border-box; + cursor: pointer; + transition: transform 0.3s ease; +} + +.timeline-item:hover { transform: translateY(-5px); } + +.timeline-item.left { left: 0; text-align: right; } +.timeline-item.right { left: 50%; text-align: left; } + +/* 节点 */ +.timeline-dot { + position: absolute; + top: 20px; + width: 16px; + height: 16px; + border-radius: 50%; + background: #e63946; + border: 4px solid #1a1a1a; + box-shadow: 0 0 0 2px rgba(230, 57, 70, 0.5); + z-index: 2; +} +.timeline-item.left .timeline-dot { right: -8px; } +.timeline-item.right .timeline-dot { left: -8px; } + +/* 日期 */ +.timeline-date { + position: absolute; + top: 18px; + color: #e63946; + font-weight: 600; + font-size: 0.9rem; + letter-spacing: 1px; +} +.timeline-item.left .timeline-date { right: -120px; text-align: left; width: 100px; } +.timeline-item.right .timeline-date { left: -120px; text-align: right; width: 100px; } + +/* 内容卡片 */ +.timeline-content { + background: rgba(30,30,30,0.6); + border: 1px solid rgba(255,255,255,0.05); + border-radius: 12px; + overflow: hidden; + display: flex; + flex-direction: column; + transition: border-color 0.3s; +} +.timeline-item:hover .timeline-content { border-color: #e63946; } + +.timeline-cover { + width: 100%; + height: 180px; +} +.timeline-cover img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.timeline-info { + padding: 1rem; +} +.timeline-info h3 { color: #fff; margin: 0 0 5px 0; font-size: 1.1rem; } +.timeline-info .count { color: rgba(255,255,255,0.5); font-size: 0.8rem; } + +/* --- 模式 D: 卡片堆叠 (Stack) --- */ +.album-stack { + display: flex; + justify-content: center; + align-items: center; + min-height: 500px; + flex-direction: column; +} + +.stack-swiper { + width: 320px; + height: 460px; +} + +.stack-card { + width: 100%; + height: 100%; + border-radius: 16px; + background: #222; + overflow: hidden; + border: 1px solid rgba(255,255,255,0.1); + box-shadow: 0 20px 50px rgba(0,0,0,0.5); +} + +.stack-cover { + height: 320px; + overflow: hidden; +} +.stack-cover img { width: 100%; height: 100%; object-fit: cover; } + +.stack-info { + padding: 1.5rem; + background: linear-gradient(to bottom, #2a2a2a, #222); + height: calc(100% - 320px); + display: flex; + flex-direction: column; + justify-content: center; +} +.stack-info h3 { color: #fff; font-size: 1.4rem; margin-bottom: 0.5rem; } +.stack-meta { color: #e63946; font-size: 0.9rem; display: flex; justify-content: space-between; } + +/* --- 模式 E: Cover Flow --- */ +.album-coverflow { + padding: 3rem 0; + text-align: center; +} + +.coverflow-swiper { + width: 100%; + padding-top: 50px; + padding-bottom: 50px; +} + +.coverflow-swiper .swiper-slide { + background-position: center; + background-size: cover; + width: 300px; + height: 400px; + /* 倒影效果 (CSS Reflect) */ + -webkit-box-reflect: below 1px linear-gradient(transparent, transparent 70%, rgba(0,0,0,0.3)); +} + +.slide-inner { + width: 100%; + height: 100%; + position: relative; + border-radius: 10px; + overflow: hidden; + box-shadow: 0 10px 20px rgba(0,0,0,0.5); + cursor: pointer; +} + +.slide-inner img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.slide-info { + position: absolute; + bottom: 0; left: 0; right: 0; + padding: 1rem; + background: rgba(0,0,0,0.7); + color: #fff; + transform: translateY(100%); + transition: transform 0.3s; +} + +.swiper-slide-active .slide-info { + transform: translateY(0); +} + +.hint-text { + margin-top: 2rem; + color: rgba(255,255,255,0.5); + font-size: 0.9rem; +} +.coverflow-hint { + margin-top: 2rem; + color: rgba(255,255,255,0.5); + font-size: 0.9rem; +} + +/* Loading */ .loading-container { display: flex; flex-direction: column; align-items: center; justify-content: center; - min-height: 300px; + min-height: 400px; color: var(--light-color); } .loading-spinner { - width: 40px; - height: 40px; - border: 3px solid rgba(168, 218, 220, 0.3); + width: 40px; height: 40px; + border: 3px solid rgba(230, 57, 70, 0.3); border-top-color: #e63946; border-radius: 50%; - border-top-color: var(--secondary-color); - animation: spin 1s ease-in-out infinite; + animation: spin 1s infinite linear; margin-bottom: 1rem; } -@keyframes spin { - to { - transform: rotate(360deg); - } -} +@keyframes spin { 100% { transform: rotate(360deg); } } -.album-selection { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); - gap: 3rem; - margin-top: 3rem; -} +/* Transitions */ +.fade-up-enter-active, .fade-up-leave-active { transition: all 0.4s ease; } +.fade-up-enter-from, .fade-up-leave-to { opacity: 0; transform: translateY(20px); } -.album-card { - background: rgba(29, 0, 0, 0.7); - border-radius: 16px; - overflow: hidden; - box-shadow: var(--card-shadow); - transition: var(--transition); - cursor: pointer; - position: relative; - backdrop-filter: blur(12px); - -webkit-backdrop-filter: blur(12px); - border: 1px solid rgba(230, 57, 70, 0.2); - transform: translateY(0); -} - -.album-card:hover { - transform: translateY(-10px); - box-shadow: var(--card-hover-shadow); - border-color: var(--primary-color); -} - -.album-cover { - height: 240px; - position: relative; - transition: var(--transition); - background-color: rgba(20, 0, 0, 0.6); - overflow: hidden; -} - -/* Added optimized image styles */ -.cover-image { - width: 100%; - height: 100%; - object-fit: cover; - transition: all 0.3s ease; - opacity: 0; - transform: scale(1.1); -} - -.cover-image.loaded { - opacity: 1; - transform: scale(1); -} - -.cover-image.error { - opacity: 0.5; - filter: grayscale(100%); -} - -.album-card:hover .cover-image { - transform: scale(1.05); -} - -.album-overlay { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: linear-gradient(to top, rgba(0, 0, 0, 0.8), transparent); - display: flex; - align-items: flex-end; - justify-content: center; - padding: 2rem; - opacity: 1; - transition: var(--transition); -} - -.album-info { - padding: 2rem; - text-align: center; -} - -.album-title { - font-size: 1.4rem; - font-weight: 600; - margin-bottom: 0.5rem; - color: var(--light-color); - letter-spacing: 1px; - text-shadow: 0 0 5px rgba(0, 0, 0, 0.5); -} - -.album-date { - font-size: 0.9rem; - color: var(--secondary-color); - letter-spacing: 1px; -} - -/* Added photo count styling */ -.album-count { - font-size: 0.8rem; - color: var(--secondary-color); - opacity: 0.8; - margin-top: 0.3rem; -} - -@media (max-width: 1024px) { - .container { - padding: 2rem; - } - - .album-selection { - grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); - gap: 2rem; - } -} +.fade-scale-enter-active, .fade-scale-leave-active { transition: all 0.4s ease; } +.fade-scale-enter-from, .fade-scale-leave-to { opacity: 0; transform: scale(0.9); } +/* Mobile Responsive */ @media (max-width: 768px) { - .container { - padding: 1.5rem; - } + .header-section { margin-bottom: 2rem; } + .main-title { font-size: 1.8rem; margin-bottom: 1rem; } - .main-title { - font-size: 2rem; - margin-bottom: 2rem; - } + .list-item { height: auto; flex-direction: column; } + .list-cover { width: 100%; height: 200px; } + .list-item:hover { transform: translateY(-5px); } - .album-selection { - grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); - gap: 1.5rem; - } + .coverflow-swiper .swiper-slide { width: 240px; height: 320px; } - .album-cover { - height: 200px; - } -} - -@media (max-width: 480px) { - .container { - padding: 1rem; - } - - .main-title { - font-size: 1.8rem; - } - - .album-selection { - grid-template-columns: 1fr; - } + /* 时光轴移动端适配 */ + .timeline-line { left: 20px; } + .timeline-item { width: 100%; padding-left: 50px; padding-right: 0; margin-bottom: 2rem; } + .timeline-item.left, .timeline-item.right { left: 0; text-align: left; } + .timeline-dot { left: 12px !important; right: auto !important; } + .timeline-date { position: relative; top: 0; left: 0 !important; right: auto !important; text-align: left !important; margin-bottom: 0.5rem; display: block; width: auto; } }
加载照片中...
正在读取美好瞬间...
加载中...
{{ formatDate(album.created_at) }}
{{ album.photo_count }} 张照片
正在加载相册...
暂无相册,去创建一个吧
{{ album.description || '暂无描述...' }}