From 36969a4fa0a3ced2a6615c5424d4027682adecfe Mon Sep 17 00:00:00 2001 From: lychang Date: Sun, 19 Jul 2026 23:56:28 +0800 Subject: [PATCH] update --- Cargo.lock | 204 ++++++++++++++ assets/album.html | 27 +- assets/css/album.css | 419 ++++++++++++++++++++++------ assets/css/style.css | 162 ++++++++++- assets/index.html | 42 +-- assets/js/album.js | 12 +- assets/js/app.js | 136 +++++++-- docs/pipeline-architecture.md | 226 +++++++++++++++ docs/pipeline-spec.md | 124 ++++++++ src/db/metadata.rs | 15 + src/engine/person_merger.rs | 13 +- src/engine/pipeline.rs | 512 +++++++++++++++++++++++++++++----- src/engine/tag_extractor.rs | 110 ++++++-- src/engine/title_parser.rs | 130 +++++++-- src/main.rs | 119 ++++---- src/routes/pipeline.rs | 24 +- 16 files changed, 1925 insertions(+), 350 deletions(-) create mode 100644 docs/pipeline-architecture.md create mode 100644 docs/pipeline-spec.md diff --git a/Cargo.lock b/Cargo.lock index fa82d82..f4e04f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -214,6 +214,12 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + [[package]] name = "ahash" version = "0.7.8" @@ -426,6 +432,15 @@ dependencies = [ "shlex", ] +[[package]] +name = "cedarwood" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d910bedd62c24733263d0bed247460853c9d22e8956bd4cd964302095e04e90" +dependencies = [ + "smallvec", +] + [[package]] name = "cfg-if" version = "1.0.4" @@ -606,6 +621,12 @@ dependencies = [ "hybrid-array", ] +[[package]] +name = "dary_heap" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b1e3a325bc115f096c8b77bbf027a7c2592230e70be2d985be950d3d5e60ebe" + [[package]] name = "der" version = "0.7.10" @@ -877,6 +898,15 @@ dependencies = [ "slab", ] +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -970,6 +1000,17 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + [[package]] name = "hashbrown" version = "0.17.1" @@ -1265,6 +1306,7 @@ dependencies = [ "chrono", "config", "image", + "jieba-rs", "lazy_static", "mimalloc", "rand 0.8.7", @@ -1286,6 +1328,39 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35a84fd5aa25fae5c0f4a33d9cac2ca017fc622cbd089be2229993514990f870" +[[package]] +name = "include-flate" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48f173716febb1ad596c16ea5637b5f1790ea32de8e627493ff82bc73b0876ce" +dependencies = [ + "include-flate-codegen", + "include-flate-compress", +] + +[[package]] +name = "include-flate-codegen" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a7875b62a72ad3f3203cdd8950d4cf9947db036030b974b8b37ceae90c8d8c0" +dependencies = [ + "include-flate-compress", + "proc-macro-error3", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "include-flate-compress" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fbb9c5ccb9a5b67b4afa2974c27e5507ea1bf6d22828cef418e4dfaeca51dd" +dependencies = [ + "libflate", + "zstd", +] + [[package]] name = "indexmap" version = "2.14.0" @@ -1308,6 +1383,30 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jieba-macros" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c676b32a471d3cfae8dac2ad2f8334cd52e53377733cca8c1fb0a5062fec192" +dependencies = [ + "phf_codegen", +] + +[[package]] +name = "jieba-rs" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5dd552bbb95d578520ee68403bf8aaf0dbbb2ce55b0854d019f9350ad61040a" +dependencies = [ + "cedarwood", + "fxhash", + "include-flate", + "jieba-macros", + "lazy_static", + "phf", + "regex", +] + [[package]] name = "jobserver" version = "0.1.35" @@ -1367,6 +1466,30 @@ version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +[[package]] +name = "libflate" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd96e993e5f3368b0cb8497dae6c860c22af8ff18388c61c6c0b86c58d86b5df" +dependencies = [ + "adler32", + "crc32fast", + "dary_heap", + "libflate_lz77", + "no_std_io2", +] + +[[package]] +name = "libflate_lz77" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff7a10e427698aef6eef269482776debfef63384d30f13aad39a1a95e0e098fd" +dependencies = [ + "hashbrown 0.16.1", + "no_std_io2", + "rle-decode-fast", +] + [[package]] name = "libm" version = "0.2.16" @@ -1524,6 +1647,15 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "no_std_io2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418abd1b6d34fbf6cae440dc874771b0525a604428704c76e48b29a5e67b8003" +dependencies = [ + "memchr", +] + [[package]] name = "nom" version = "7.1.3" @@ -1702,6 +1834,44 @@ dependencies = [ "pest", ] +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand 0.8.7", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project-lite" version = "0.2.17" @@ -1778,6 +1948,28 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "proc-macro-error-attr3" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34e4dd828515431dd6c4a030d26f7eaed7dd4778226e9d2bb968d65ca4ec3d4d" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error3" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee475e440453418ff1335189eddf7101ba502cd818ab7ae04209bc83aa925aa" +dependencies = [ + "proc-macro-error-attr3", + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -1963,6 +2155,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rle-decode-fast" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" + [[package]] name = "ron" version = "0.7.1" @@ -2220,6 +2418,12 @@ version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" +[[package]] +name = "siphasher" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + [[package]] name = "slab" version = "0.4.12" diff --git a/assets/album.html b/assets/album.html index 0ab87bd..6521e9f 100644 --- a/assets/album.html +++ b/assets/album.html @@ -15,10 +15,12 @@ 返回首页

加载中...

-
- 正在加载... +
+
+ 正在加载... +
+
-
@@ -38,13 +40,6 @@ 来自: 相册名称
- - 1 / 1 - @@ -70,7 +65,19 @@ 0 KB
+
+ + 1 / 1 + +
+ diff --git a/assets/css/album.css b/assets/css/album.css index ad7ed89..7672fb4 100644 --- a/assets/css/album.css +++ b/assets/css/album.css @@ -88,25 +88,103 @@ body { .album-info { display: inline-flex; align-items: center; - gap: 16px; - color: var(--text-light); - font-size: 0.95rem; - padding: 10px 20px; - background: var(--bg-surface); - border-radius: var(--radius-full); - border: 1px solid var(--border-gray); + gap: 6px; + color: #475569; + font-size: 0.85rem; + font-weight: 500; + line-height: 1.2; + padding: 6px 14px; + background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%); + border-radius: 20px; + border: 1px solid #cbd5e1; + cursor: default; + user-select: none; + height: auto; + min-height: 0; } -.album-info i { color: var(--secondary-blue); } +.album-info i { color: #64748b; } -/* ============ 相册标签区(新:分类着色) ============ */ -.album-meta-tags { +/* 照片数量 + 标签 合并到一行 */ +.album-header-row { display: flex; flex-wrap: wrap; + align-items: center; justify-content: center; + gap: 12px 16px; + margin: 12px auto 24px; + max-width: 1100px; +} + +.album-header-row .album-info { + margin: 0; + flex: 0 0 auto; + width: auto; + max-width: none; +} + +/* ============ 相册标签区(简洁版:与照片数量同行) ============ */ +.album-header-row .album-meta-tags { + display: flex; + flex-wrap: wrap; + justify-content: flex-start; + gap: 8px; + margin: 0; + max-width: none; + min-width: 0; +} + +/* 标签分组 */ +.tag-group { + margin-bottom: 14px; + background: var(--bg-surface); + border-radius: var(--radius-card); + border: 1px solid var(--border-gray); + padding: 12px 16px; + transition: all 0.2s ease; + box-shadow: var(--shadow-sm); +} + +.tag-group:hover { + box-shadow: var(--shadow-md); + border-color: #cbd5e1; +} + +.tag-group-header { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 10px; + padding-bottom: 8px; + border-bottom: 1px dashed var(--border-gray); + font-size: 0.85rem; + font-weight: 600; + color: var(--text-light); +} + +.tag-group-header i { + font-size: 0.9rem; + color: var(--secondary-blue); +} + +.tag-group-header span { + color: var(--text-light); +} + +.tag-group-count { + margin-left: auto; + background: var(--bg-hover); + padding: 2px 8px; + border-radius: 10px; + font-size: 0.75rem; + font-weight: 600; + color: var(--text-muted); +} + +.tag-group-chips { + display: flex; + flex-wrap: wrap; gap: 8px; - margin: 20px auto 32px; - max-width: 900px; } .meta-chip { @@ -114,13 +192,31 @@ body { align-items: center; gap: 6px; padding: 6px 14px; - border-radius: var(--radius-full); + border-radius: 20px; font-size: 0.85rem; font-weight: 500; + line-height: 1.2; cursor: pointer; transition: all 0.2s ease; user-select: none; border: 1px solid transparent; + position: relative; + overflow: hidden; +} + +.meta-chip::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent); + transition: left 0.5s ease; +} + +.meta-chip:hover::before { + left: 100%; } .meta-chip:hover { @@ -130,13 +226,14 @@ body { .meta-chip:active { transform: translateY(0); } -.meta-chip i { font-size: 0.75rem; } +.meta-chip i { font-size: 0.75rem; opacity: 0.9; } /* 人物标签 */ .meta-chip-person { - background: linear-gradient(135deg, rgba(139, 92, 246, 0.1) 0%, rgba(236, 72, 153, 0.1) 100%); + background: linear-gradient(135deg, rgba(139, 92, 246, 0.12) 0%, rgba(236, 72, 153, 0.12) 100%); color: var(--accent-purple); - border-color: rgba(139, 92, 246, 0.2); + border-color: rgba(139, 92, 246, 0.25); + font-weight: 600; } .meta-chip-person:hover { @@ -147,54 +244,120 @@ body { /* 分类标签(按类别着色) */ .meta-chip-tag-style { - background: #f0f9ff; + background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%); color: #0284c7; border-color: #bae6fd; } -.meta-chip-tag-style:hover { background: #0284c7; color: #fff; border-color: #0284c7; } +.meta-chip-tag-style:hover { background: linear-gradient(135deg, #0284c7 0%, #0369a1 100%); color: #fff; border-color: transparent; } .meta-chip-tag-outfit { - background: #fdf2f8; + background: linear-gradient(135deg, #fdf2f8 0%, #fce7f3 100%); color: #db2777; border-color: #fbcfe8; } -.meta-chip-tag-outfit:hover { background: #db2777; color: #fff; border-color: #db2777; } +.meta-chip-tag-outfit:hover { background: linear-gradient(135deg, #db2777 0%, #be185d 100%); color: #fff; border-color: transparent; } .meta-chip-tag-scene { - background: #f0fdf4; + background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%); color: #15803d; border-color: #bbf7d0; } -.meta-chip-tag-scene:hover { background: #15803d; color: #fff; border-color: #15803d; } +.meta-chip-tag-scene:hover { background: linear-gradient(135deg, #15803d 0%, #166534 100%); color: #fff; border-color: transparent; } .meta-chip-tag-body { - background: #fef3c7; + background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%); color: #b45309; border-color: #fde68a; } -.meta-chip-tag-body:hover { background: #b45309; color: #fff; border-color: #b45309; } +.meta-chip-tag-body:hover { background: linear-gradient(135deg, #b45309 0%, #92400e 100%); color: #fff; border-color: transparent; } .meta-chip-tag-mood { - background: #faf5ff; + background: linear-gradient(135deg, #faf5ff 0%, #f3e8ff 100%); color: #7c3aed; border-color: #e9d5ff; } -.meta-chip-tag-mood:hover { background: #7c3aed; color: #fff; border-color: #7c3aed; } +.meta-chip-tag-mood:hover { background: linear-gradient(135deg, #7c3aed 0%, #6d28d9 100%); color: #fff; border-color: transparent; } .meta-chip-tag-topic { - background: #eff6ff; + background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%); color: #0369a1; border-color: #bfdbfe; } -.meta-chip-tag-topic:hover { background: #0369a1; color: #fff; border-color: #0369a1; } +.meta-chip-tag-topic:hover { background: linear-gradient(135deg, #0369a1 0%, #075985 100%); color: #fff; border-color: transparent; } + +/* jieba 提取的新分类 */ +.meta-chip-tag-noun { + background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%); + color: #1d4ed8; + border-color: #bfdbfe; +} +.meta-chip-tag-noun:hover { background: linear-gradient(135deg, #1d4ed8 0%, #1e40af 100%); color: #fff; border-color: transparent; } + +.meta-chip-tag-proper { + background: linear-gradient(135deg, #e0e7ff 0%, #c7d2fe 100%); + color: #4338ca; + border-color: #c7d2fe; +} +.meta-chip-tag-proper:hover { background: linear-gradient(135deg, #4338ca 0%, #3730a3 100%); color: #fff; border-color: transparent; } + +.meta-chip-tag-location { + background: linear-gradient(135deg, #ecfdf5 0%, #d1fae5 100%); + color: #047857; + border-color: #a7f3d0; +} +.meta-chip-tag-location:hover { background: linear-gradient(135deg, #047857 0%, #065f46 100%); color: #fff; border-color: transparent; } + +.meta-chip-tag-organization { + background: linear-gradient(135deg, #fef9c3 0%, #fef08a 100%); + color: #a16207; + border-color: #fde68a; +} +.meta-chip-tag-organization:hover { background: linear-gradient(135deg, #a16207 0%, #854d0e 100%); color: #fff; border-color: transparent; } + +.meta-chip-tag-noun_verb { + background: linear-gradient(135deg, #fce7f3 0%, #fbcfe8 100%); + color: #be185d; + border-color: #f9a8d4; +} +.meta-chip-tag-noun_verb:hover { background: linear-gradient(135deg, #be185d 0%, #9d174d 100%); color: #fff; border-color: transparent; } + +.meta-chip-tag-adj { + background: linear-gradient(135deg, #f3e8ff 0%, #e9d5ff 100%); + color: #6d28d9; + border-color: #ddd6fe; +} +.meta-chip-tag-adj:hover { background: linear-gradient(135deg, #6d28d9 0%, #5b21b6 100%); color: #fff; border-color: transparent; } + +.meta-chip-tag-verb { + background: linear-gradient(135deg, #fed7aa 0%, #fdba74 100%); + color: #c2410c; + border-color: #fed7aa; +} +.meta-chip-tag-verb:hover { background: linear-gradient(135deg, #c2410c 0%, #9a3412 100%); color: #fff; border-color: transparent; } + +.meta-chip-tag-chinese { + background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%); + color: #475569; + border-color: #cbd5e1; +} +.meta-chip-tag-chinese:hover { background: linear-gradient(135deg, #475569 0%, #334155 100%); color: #fff; border-color: transparent; } + +.meta-chip-tag-english { + background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%); + color: #64748b; + border-color: #cbd5e1; +} +.meta-chip-tag-english:hover { background: linear-gradient(135deg, #64748b 0%, #475569 100%); color: #fff; border-color: transparent; } /* 默认标签 */ +.meta-chip-tag-default, .meta-chip-tag { background: var(--bg-hover); color: var(--text-light); border-color: var(--border-gray); } -.meta-chip-tag:hover { background: var(--text-light); color: #fff; border-color: var(--text-light); } +.meta-chip-tag-default:hover, +.meta-chip-tag:hover { background: linear-gradient(135deg, #64748b 0%, #475569 100%); color: #fff; border-color: transparent; } /* ============ 瀑布流布局(核心) ============ */ .images-grid { @@ -214,7 +377,6 @@ body { box-shadow: var(--shadow-sm); background: var(--bg-surface); transition: all 0.3s ease; - cursor: pointer; border: 1px solid var(--border-gray); break-inside: avoid; page-break-inside: avoid; @@ -233,6 +395,7 @@ body { display: block; object-fit: contain; transition: transform 0.4s ease; + cursor: pointer; } .image-item:hover img { @@ -249,6 +412,7 @@ body { align-items: center; gap: 8px; border-top: 1px solid var(--border-gray); + cursor: default; } .image-caption small { @@ -368,7 +532,7 @@ body { justify-content: center; opacity: 0; visibility: hidden; - transition: all 0.3s ease; + transition: all 0.25s ease; } .preview-modal.active { opacity: 1; visibility: visible; } @@ -377,44 +541,48 @@ body { position: absolute; top: 0; left: 0; width: 100%; height: 100%; - background: rgba(15, 23, 42, 0.9); - backdrop-filter: blur(8px); + background: rgba(0, 0, 0, 0.92); + backdrop-filter: blur(4px); } .preview-container { position: relative; - background: var(--bg-surface); - border-radius: var(--radius-lg); - box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); - max-width: 92vw; - max-height: 90vh; - width: 1200px; - height: 800px; + width: 100vw; + height: 100vh; + max-width: 100vw; + max-height: 100vh; display: flex; flex-direction: column; - transform: scale(0.92); - transition: transform 0.3s ease; + background: transparent; + border-radius: 0; + box-shadow: none; + transform: scale(1); } .preview-modal.active .preview-container { transform: scale(1); } +/* 顶部栏 - 浮在图片上方 */ .preview-header { + position: absolute; + top: 0; left: 0; right: 0; + z-index: 10; display: flex; justify-content: space-between; align-items: center; - padding: 16px 20px; - border-bottom: 1px solid var(--border-gray); + padding: 16px 24px; + background: linear-gradient(to bottom, rgba(0,0,0,0.7) 0%, transparent 100%); flex-shrink: 0; + border: none; } .preview-title h3 { - font-size: 1.1rem; + font-size: 1rem; font-weight: 600; - color: var(--text-dark); + color: #f1f5f9; margin: 0 0 4px 0; } -.preview-info { font-size: 0.85rem; color: var(--text-light); } +.preview-info { font-size: 0.8rem; color: #94a3b8; } .preview-actions { display: flex; @@ -423,51 +591,60 @@ body { } .preview-nav { - width: 36px; height: 36px; - border: 1px solid var(--border-gray); - border-radius: var(--radius); - background: var(--bg-surface); - color: var(--text-dark); + width: 40px; height: 40px; + border: 1px solid rgba(255,255,255,0.15); + border-radius: 50%; + background: rgba(255,255,255,0.08); + color: #f1f5f9; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.2s ease; + backdrop-filter: blur(10px); } .preview-nav:hover:not(:disabled) { - background: var(--bg-hover); - border-color: var(--secondary-blue); - color: var(--secondary-blue); + background: rgba(255,255,255,0.2); + border-color: rgba(255,255,255,0.3); + color: #fff; } -.preview-nav:disabled { opacity: 0.4; cursor: not-allowed; } +.preview-nav:disabled { opacity: 0.3; cursor: not-allowed; } -.preview-counter { font-size: 0.85rem; color: var(--text-light); font-weight: 500; } +.preview-counter { + font-size: 0.9rem; + color: #cbd5e1; + font-weight: 500; + padding: 0 4px; +} .preview-close { - width: 36px; height: 36px; - border: none; - border-radius: var(--radius); - background: var(--bg-hover); - color: var(--text-dark); + width: 40px; height: 40px; + border: 1px solid rgba(255,255,255,0.15); + border-radius: 50%; + background: rgba(255,255,255,0.08); + color: #f1f5f9; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.2s ease; + backdrop-filter: blur(10px); } -.preview-close:hover { background: #ef4444; color: #fff; } +.preview-close:hover { background: rgba(239, 68, 68, 0.8); color: #fff; border-color: rgba(239, 68, 68, 0.6); } +/* 图片内容区 - 占满全屏 */ .preview-content { - flex: 1; - padding: 0; + position: absolute; + top: 0; left: 0; right: 0; bottom: 0; + padding: 60px 80px; overflow: hidden; display: flex; align-items: center; justify-content: center; - background: #0f172a; + background: transparent; } .preview-image-container { @@ -482,8 +659,12 @@ body { .preview-image { max-width: 100%; max-height: 100%; + width: auto; + height: auto; object-fit: contain; - transition: opacity 0.3s ease; + transition: opacity 0.3s ease, transform 0.3s ease; + box-shadow: 0 25px 80px rgba(0, 0, 0, 0.5); + border-radius: 4px; } .preview-loading { @@ -494,54 +675,107 @@ body { flex-direction: column; align-items: center; gap: 8px; - color: var(--text-light); + color: #94a3b8; } -.preview-loading i { font-size: 1.8rem; color: var(--secondary-blue); } +.preview-loading i { font-size: 2rem; color: #3b82f6; } +/* 底部栏 - 浮在图片上方 */ .preview-footer { + position: absolute; + bottom: 0; left: 0; right: 0; + z-index: 10; display: flex; justify-content: space-between; align-items: center; - padding: 12px 20px; - border-top: 1px solid var(--border-gray); + gap: 20px; + padding: 16px 24px; + background: linear-gradient(to top, rgba(0,0,0,0.75) 0%, transparent 100%); flex-shrink: 0; + border: none; } -.preview-meta { display: flex; gap: 16px; } +.preview-meta { display: flex; gap: 16px; flex: 1; } .preview-meta .meta-item { display: flex; align-items: center; gap: 6px; - font-size: 0.85rem; - color: var(--text-light); + font-size: 0.8rem; + color: #cbd5e1; } -.preview-controls { display: flex; gap: 10px; } +/* 底部居中的切换控制栏 */ +.preview-center-controls { + display: flex; + align-items: center; + gap: 16px; + flex: 1; + justify-content: center; +} + +.preview-center-controls .preview-nav { + width: 40px; + height: 40px; + border: 1px solid rgba(255,255,255,0.2); + border-radius: 50%; + background: rgba(255,255,255,0.1); + color: #f1f5f9; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s ease; + backdrop-filter: blur(10px); + font-size: 0.9rem; +} + +.preview-center-controls .preview-nav:hover:not(:disabled) { + background: rgba(255,255,255,0.25); + border-color: rgba(255,255,255,0.4); + transform: scale(1.08); +} + +.preview-center-controls .preview-nav:disabled { + opacity: 0.3; + cursor: not-allowed; +} + +.preview-center-controls .preview-counter { + font-size: 0.9rem; + color: #e2e8f0; + font-weight: 500; + min-width: 60px; + text-align: center; +} + +.preview-controls { display: flex; gap: 10px; flex: 1; justify-content: flex-end; } .preview-action { - padding: 7px 14px; - border: 1px solid var(--border-gray); - border-radius: var(--radius); - background: var(--bg-surface); - color: var(--text-dark); + padding: 8px 16px; + border: 1px solid rgba(255,255,255,0.15); + border-radius: 20px; + background: rgba(255,255,255,0.08); + color: #f1f5f9; cursor: pointer; - font-size: 0.85rem; + font-size: 0.8rem; font-weight: 500; transition: all 0.2s ease; display: flex; align-items: center; gap: 6px; + backdrop-filter: blur(10px); } -.preview-action:hover { background: var(--bg-hover); } +.preview-action:hover { background: rgba(255,255,255,0.2); } -.preview-remove { border-color: #fecaca; color: #dc2626; } -.preview-remove:hover { background: #fef2f2; border-color: #fca5a5; } +.preview-action-close:hover { + background: rgba(239, 68, 68, 0.7); + border-color: rgba(239, 68, 68, 0.5); +} -.preview-action.favorited { border-color: #fbcfe8; color: #ec4899; background: #fdf2f8; } -.preview-action.favorited:hover { background: #fce7f3; border-color: #f9a8d4; } +.preview-action.favorited { border-color: rgba(236, 72, 153, 0.5); color: #f9a8d4; background: rgba(236, 72, 153, 0.15); } +.preview-action.favorited:hover { background: rgba(236, 72, 153, 0.25); } /* 暗色模式 */ @media (prefers-color-scheme: dark) { @@ -554,6 +788,15 @@ body { --text-light: #cbd5e1; --text-muted: #94a3b8; } - .preview-container { background: var(--bg-surface); } - .preview-nav, .preview-close, .preview-action { background: var(--bg-hover); color: var(--text-dark); border-color: var(--border-gray); } +} + +/* 移动端适配 */ +@media (max-width: 768px) { + .preview-content { padding: 40px 10px 70px; } + .preview-header, .preview-footer { padding: 12px 16px; } + .preview-footer { flex-wrap: wrap; gap: 12px; } + .preview-meta { order: 2; justify-content: center; } + .preview-center-controls { order: 1; width: 100%; } + .preview-controls { order: 3; justify-content: center; } + .preview-center-controls .preview-nav { width: 36px; height: 36px; } } diff --git a/assets/css/style.css b/assets/css/style.css index df13b87..9d90491 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -107,30 +107,56 @@ input { font-family: inherit; } .sidebar.collapsed .brand-text, .sidebar.collapsed .nav-item span, .sidebar.collapsed .sidebar-section-title span, +.sidebar.collapsed .sidebar-section-title .section-arrow, .sidebar.collapsed .sidebar-tags, -.sidebar.collapsed .collapse-btn span { +.sidebar.collapsed .sidebar-people, +.sidebar.collapsed .sidebar-section-content, +.sidebar.collapsed .sidebar-divider { opacity: 0; pointer-events: none; + display: none; +} + +.sidebar.collapsed .sidebar-section { + padding: var(--spacer-md) 0; + gap: var(--spacer-xs); } .sidebar.collapsed .sidebar-section-title { justify-content: center; + padding: var(--spacer-sm) 0; + margin-bottom: 0; +} + +.sidebar.collapsed .sidebar-section-group { + margin-bottom: var(--spacer-sm); } .sidebar.collapsed .nav-item { justify-content: center; padding-left: 0; padding-right: 0; + margin: 0 var(--spacer-xs); +} + +.sidebar.collapsed .sidebar-brand { + padding: var(--spacer-lg) 0; + justify-content: center; } .sidebar.collapsed .collapse-btn { justify-content: center; + padding: var(--spacer-md) 0; } .sidebar.collapsed .collapse-btn i { transform: rotate(180deg); } +.sidebar.collapsed .sidebar-nav { + padding: var(--spacer-md) var(--spacer-xs); +} + /* 品牌 Logo */ .sidebar-brand { display: flex; @@ -228,6 +254,7 @@ input { font-family: inherit; } display: flex; flex-direction: column; min-height: 0; + gap: var(--spacer-sm); } .sidebar-section::-webkit-scrollbar { @@ -238,6 +265,12 @@ input { font-family: inherit; } border-radius: 2px; } +/* 可折叠区块组 */ +.sidebar-section-group { + display: flex; + flex-direction: column; +} + .sidebar-section-title { display: flex; align-items: center; @@ -251,7 +284,49 @@ input { font-family: inherit; } margin-bottom: var(--spacer-sm); } -.sidebar-section-title i { +.sidebar-section-title.collapsible { + cursor: pointer; + border-radius: var(--radius); + transition: background var(--transition-fast); + user-select: none; +} + +.sidebar-section-title.collapsible:hover { + background: var(--bg-hover); + color: var(--text-light); +} + +.section-arrow { + margin-left: auto; + font-size: 0.7rem; + transition: transform var(--transition-base); + opacity: 0.6; +} + +.sidebar-section-group.collapsed .section-arrow { + transform: rotate(-90deg); +} + +.sidebar-section-content { + overflow: hidden; + max-height: 600px; + transition: max-height 0.28s cubic-bezier(0.4, 0, 0.2, 1), + opacity 0.2s ease, + margin 0.2s ease; + opacity: 1; + margin-bottom: var(--spacer-sm); +} + +.sidebar-section-group.collapsed .sidebar-section-content { + max-height: 0; + opacity: 0; + margin-bottom: 0; + transition: max-height 0.2s cubic-bezier(0.4, 0, 0.2, 1), + opacity 0.15s ease, + margin 0.15s ease; +} + +.sidebar-section-title i:first-child { width: 20px; text-align: center; font-size: 0.85rem; @@ -309,24 +384,95 @@ input { font-family: inherit; } color: var(--white); } +/* 侧边栏人物列表 */ +.sidebar-people { + display: flex; + flex-direction: column; + gap: var(--spacer-xs); + transition: opacity var(--transition-base); +} + +.sidebar-person { + display: flex; + align-items: center; + gap: var(--spacer-sm); + padding: var(--spacer-sm) var(--spacer-lg); + border-radius: var(--radius); + cursor: pointer; + transition: all var(--transition-fast); + font-size: 0.9rem; + color: var(--text-light); +} + +.sidebar-person:hover { + background: var(--bg-hover); + color: var(--text-dark); +} + +.sidebar-person.active { + background: rgba(139, 92, 246, 0.1); + color: var(--accent-purple); + font-weight: 500; +} + +.sidebar-person-avatar { + width: 28px; + height: 28px; + border-radius: 50%; + background: #6366f1; + color: var(--white); + display: flex; + align-items: center; + justify-content: center; + font-size: 0.8rem; + font-weight: 600; + flex-shrink: 0; +} + +.sidebar-person-info { + display: flex; + flex-direction: column; + flex: 1; + min-width: 0; +} + +.sidebar-person-name { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-size: 0.9rem; +} + +.sidebar-person-count { + font-size: 0.75rem; + color: var(--text-muted); +} + +.sidebar-person.active .sidebar-person-count { + color: var(--accent-purple); + opacity: 0.8; +} + /* 侧边栏底部 */ .sidebar-footer { padding: var(--spacer-md); border-top: 1px solid var(--border-gray); flex-shrink: 0; + margin-top: auto; } .collapse-btn { display: flex; align-items: center; - gap: var(--spacer-md); + justify-content: center; width: 100%; - padding: var(--spacer-md) var(--spacer-lg); + padding: var(--spacer-md); border-radius: var(--radius); color: var(--text-light); - font-size: 0.9rem; + font-size: 1rem; font-weight: 500; transition: all var(--transition-fast); + cursor: pointer; } .collapse-btn:hover { @@ -338,12 +484,6 @@ input { font-family: inherit; } width: 20px; text-align: center; transition: transform var(--transition-base); - flex-shrink: 0; -} - -.collapse-btn span { - transition: opacity var(--transition-base); - white-space: nowrap; } /* ============ 主内容区 ============ */ diff --git a/assets/index.html b/assets/index.html index f4f5421..c14d769 100644 --- a/assets/index.html +++ b/assets/index.html @@ -37,22 +37,38 @@ + + + -