update
Some checks failed
deploy / deploy (push) Has been cancelled

This commit is contained in:
toom1996
2026-08-15 01:07:33 +08:00
parent 8d65eb8850
commit 58f9f95f8e
8 changed files with 1383 additions and 600 deletions

View File

@ -5,11 +5,15 @@ import tailwindcss from '@tailwindcss/vite';
import alpinejs from '@astrojs/alpinejs'; import alpinejs from '@astrojs/alpinejs';
// https://astro.build/config // https://astro.build/config
export default defineConfig({ export default defineConfig({
vite: { vite: {
plugins: [tailwindcss()] plugins: [tailwindcss()]
}, },
build: {
// 示例:在构建过程中生 成`page.html` 而不是 `page/index.html`。
format: 'file'
},
integrations: [alpinejs()] integrations: [alpinejs()]
}); });

869
dev.log Normal file
View File

@ -0,0 +1,869 @@
> test@0.0.1 dev
> astro dev --port 4345
20:06:59 [vite] connected.
20:07:00 [types] Generated 0ms
20:07:00 [vite] connected.
 astro  v7.1.6 ready in 1053 ms
┃ Local http://localhost:4345/
┃ Network use --host to expose
20:07:00 watching for file changes...
20:07:09 [200] /shows 2667ms
20:07:21 [watch] dom_modal.html
20:07:43 [200] HEAD /shows 492ms
20:07:43 [200] /shows 276ms
20:25:27 [200] /shows 672ms
20:44:17 [200] /shows 587ms
20:46:17 [200] /article 22ms
00:32:00 Configuration file updated. Restarting...
00:32:00 [watch] /src/styles/global.css
00:32:00 [vite] connected.
00:32:01 [vite] connected.
00:32:01 [vite] Re-optimizing dependencies because vite config has changed
00:32:01 [vite] server restarted.
00:32:02 [200] /shows 618ms
00:32:03 [vite] dependency optimized: alpinejs
11:23:01 [200] /shows 584ms
11:28:36 [200] /shows 943ms
11:36:18 [200] / 13271ms
11:38:37 [watch] /src/styles/global.css
11:38:38 [200] /shows 777ms
11:43:14 [watch] /src/styles/global.css
11:43:15 [200] /shows 564ms
11:45:36 [watch] /src/styles/global.css
11:45:36 [200] /shows 551ms
11:46:20 [watch] /src/styles/global.css
11:46:21 [200] /shows 610ms
11:46:42 [200] / 18ms
11:46:54 [watch] home.html
11:46:55 [200] / 4ms
11:47:06 [200] /shows 7236ms
11:47:07 [watch] home.html
11:48:14 [200] / 4ms
11:49:30 [200] / 4ms
11:49:30 [watch] home.html
11:49:53 [200] HEAD / 3ms
11:49:53 [200] / 3ms
11:54:04 [200] / 4ms
11:57:39 [watch] src/pages/index.astro
11:57:40 [200] / 30ms
11:57:40 [200] / 30ms
11:57:47 [watch] src/pages/index.astro
11:57:47 [200] / 19ms
11:58:05 [watch] src/pages/index.astro
11:58:05 [200] / 20ms
11:58:05 [200] / 20ms
11:58:05 [200] / 2ms
11:58:30 [watch] /src/pages/index.astro?astro&type=style&index=1&lang.css, /src/pages/index.astro?astro&type=style&index=0&lang.css
11:59:05 [watch] /src/pages/index.astro?astro&type=style&index=1&lang.css, /src/pages/index.astro?astro&type=style&index=0&lang.css
11:59:16 [watch] /src/pages/index.astro?astro&type=style&index=1&lang.css, /src/pages/index.astro?astro&type=style&index=0&lang.css
11:59:29 [watch] /src/pages/index.astro?astro&type=style&index=1&lang.css, /src/pages/index.astro?astro&type=style&index=0&lang.css
11:59:34 [watch] /src/pages/index.astro?astro&type=style&index=1&lang.css, /src/pages/index.astro?astro&type=style&index=0&lang.css
12:00:11 [watch] /src/pages/index.astro?astro&type=style&index=1&lang.css, /src/pages/index.astro?astro&type=style&index=0&lang.css
12:00:17 [watch] /src/pages/index.astro?astro&type=style&index=1&lang.css, /src/pages/index.astro?astro&type=style&index=0&lang.css
12:00:36 [200] / 17ms
12:00:48 [200] / 2ms
12:02:03 [200] HEAD / 3ms
12:02:03 [200] / 1ms
12:03:52 [200] / 2ms
12:05:24 [watch] src/pages/index.astro
12:05:24 [200] / 22ms
12:05:24 [200] / 22ms
12:05:24 [200] / 1ms
12:05:25 [200] / 2ms
12:05:28 [watch] /src/pages/index.astro?astro&type=style&index=1&lang.css, /src/pages/index.astro?astro&type=style&index=0&lang.css
12:05:37 [watch] src/pages/index.astro
12:05:38 [200] / 30ms
12:05:38 [200] / 30ms
12:05:38 [200] / 4ms
12:05:38 [200] / 3ms
12:05:47 [200] / 1ms
12:06:32 [200] HEAD / 2ms
12:06:33 [200] / 1ms
12:19:05 [watch] /src/pages/index.astro?astro&type=style&index=1&lang.css, /src/pages/index.astro?astro&type=style&index=0&lang.css
12:19:15 [watch] /src/pages/index.astro?astro&type=style&index=1&lang.css, /src/pages/index.astro?astro&type=style&index=0&lang.css
12:19:32 [watch] src/pages/index.astro
12:19:32 [200] / 20ms
12:19:41 [200] / 1ms
12:20:15 [200] / 3ms
12:20:52 [200] HEAD / 2ms
13:08:58 [200] / 4ms
13:09:57 [200] / 3ms
13:11:55 [watch] /src/pages/index.astro?astro&type=style&index=1&lang.css, /src/pages/index.astro?astro&type=style&index=0&lang.css
13:11:59 [watch] /src/pages/index.astro?astro&type=style&index=1&lang.css, /src/pages/index.astro?astro&type=style&index=0&lang.css
13:12:07 [200] / 21ms
13:13:05 [200] HEAD / 1ms
13:13:05 [200] / 2ms
15:52:35 [200] / 4ms
17:07:01 [200] / 2ms
17:07:15 [200] / 2ms
17:29:37 Configuration file updated. Restarting...
17:29:37 [vite] program reload
17:29:37 [ERROR] [vite] An error happened during full reload
Failed to load url astro:server-app.js (resolved id: astro:server-app.js). Does the file exist?
Error: Failed to load url astro:server-app.js (resolved id: astro:server-app.js). Does the file exist?
at loadAndTransform (file:///D:/project/test/test/node_modules/vite/dist/node/chunks/node.js:20583:31)
17:29:37 [vite] connected.
17:29:42 [vite] connected.
17:29:42 [vite] Re-optimizing dependencies because lockfile has changed
17:29:42 [vite] server restarted.
17:32:09 [200] / 1965ms
17:32:19 [vite] dependencies optimized: swiper, swiper/modules
17:53:05 [200] / 34ms
17:53:05 [200] / 3ms
17:53:05 [vite] dependency optimized: alpinejs
17:53:47 [200] / 3ms
17:53:57 [200] / 2ms
17:54:12 [200] / 2ms
17:54:59 [200] / 2ms
17:55:40 [200] / 2ms
17:55:52 [200] HEAD / 3ms
17:55:53 [200] / 2ms
17:56:49 [200] / 2ms
17:59:43 [watch] /src/pages/index.astro?astro&type=style&index=0&lang.css, /src/pages/index.astro?astro&type=style&index=1&lang.css
17:59:50 [watch] /src/pages/index.astro?astro&type=style&index=0&lang.css, /src/pages/index.astro?astro&type=style&index=1&lang.css
18:00:06 [watch] src/pages/index.astro
18:00:07 [200] / 28ms
18:00:07 [200] / 28ms
18:00:11 [200] / 2ms
18:00:49 [200] / 1ms
18:01:05 [200] HEAD / 1ms
18:01:05 [200] / 1ms
18:10:32 [200] / 2ms
18:10:40 [200] / 1ms
18:11:25 [200] / 1ms
18:12:24 [200] / 2ms
18:21:35 [200] / 3ms
18:21:56 [200] / 2ms
18:24:11 [watch] /src/pages/index.astro?astro&type=style&index=0&lang.css, /src/pages/index.astro?astro&type=style&index=1&lang.css
18:24:48 [200] / 26ms
18:24:55 [200] / 3ms
18:25:02 [200] / 1ms
18:25:17 [watch] src/pages/index.astro
[
{
id: 52634,
brand: '5000',
title: 'Spring 2026 Ready-to-Wear',
season: 'AW26',
year: 2026,
img: 'https://assets.vogue.com/photos/68c84730627c7d40ac52aa17/master/w_2560,c_limit/00001-5000-spring-2026-ready-to-wear-credit-brand.jpg',
count: 30,
gallery: [
'https://assets.vogue.com/photos/68c84730627c7d40ac52aa17/master/w_2560,c_limit/00001-5000-spring-2026-ready-to-wear-credit-brand.jpg',
'https://assets.vogue.com/photos/68c8473052c290f6844f15a4/master/w_2560,c_limit/00002-5000-spring-2026-ready-to-wear-credit-brand.jpg',
'https://assets.vogue.com/photos/68c84730c00a8da1b9585e04/master/w_2560,c_limit/00003-5000-spring-2026-ready-to-wear-credit-brand.jpg',
'https://assets.vogue.com/photos/68c847330865a9bf107a6248/master/w_2560,c_limit/00004-5000-spring-2026-ready-to-wear-credit-brand.jpg',
'https://assets.vogue.com/photos/68c84733cc5a262b887823c6/master/w_2560,c_limit/00005-5000-spring-2026-ready-to-wear-credit-brand.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52633,
brand: '5000',
title: 'Fall 2025 Ready-to-Wear',
season: 'AW25',
year: 2025,
img: 'https://assets.vogue.com/photos/67a7ec6c540e35479b3d337c/master/w_2560,c_limit/00001-5000-fall-2025-ready-to-wear-credit-gorunway.jpg',
count: 27,
gallery: [
'https://assets.vogue.com/photos/67a7ec6c540e35479b3d337c/master/w_2560,c_limit/00001-5000-fall-2025-ready-to-wear-credit-gorunway.jpg',
'https://assets.vogue.com/photos/67a7ec6cee822deb1e8416c6/master/w_2560,c_limit/00002-5000-fall-2025-ready-to-wear-credit-gorunway.jpg',
'https://assets.vogue.com/photos/67a7ec6d551d29a948ad941e/master/w_2560,c_limit/00003-5000-fall-2025-ready-to-wear-credit-gorunway.jpg',
'https://assets.vogue.com/photos/67a7ec72ee822deb1e8416c8/master/w_2560,c_limit/00004-5000-fall-2025-ready-to-wear-credit-gorunway.jpg',
'https://assets.vogue.com/photos/67a7ec726ccfc8cbda1f1936/master/w_2560,c_limit/00005-5000-fall-2025-ready-to-wear-credit-gorunway.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52632,
brand: '5000',
title: 'Spring 2025 Ready-to-Wear',
season: 'AW25',
year: 2025,
img: 'https://assets.vogue.com/photos/66e1c75bd788bc9ca9d9acba/master/w_2560,c_limit/00001-5000-spring-2025-ready-to-wear-credit-brand.jpg',
count: 22,
gallery: [
'https://assets.vogue.com/photos/66e1c75bd788bc9ca9d9acba/master/w_2560,c_limit/00001-5000-spring-2025-ready-to-wear-credit-brand.jpg',
'https://assets.vogue.com/photos/66e1c772bc1a3e72a02f841a/master/w_2560,c_limit/00002-5000-spring-2025-ready-to-wear-credit-brand.jpg',
'https://assets.vogue.com/photos/66e1c772bc1a3e72a02f8419/master/w_2560,c_limit/00003-5000-spring-2025-ready-to-wear-credit-brand.jpg',
'https://assets.vogue.com/photos/66e1c775c22905cb0212f62b/master/w_2560,c_limit/00004-5000-spring-2025-ready-to-wear-credit-brand.jpg',
'https://assets.vogue.com/photos/66e1c79d0e2401b690216a7b/master/w_2560,c_limit/00005-5000-spring-2025-ready-to-wear-credit-brand.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52631,
brand: '4SDesigns',
title: 'Fall 2021 Menswear',
season: 'AW21',
year: 2021,
img: 'https://assets.vogue.com/photos/6027d24c0a9cf1d42cece5cd/master/w_2560,c_limit/00001-4SDESIGNS-MENSWEAR-FALL-21.jpg',
count: 34,
gallery: [
'https://assets.vogue.com/photos/6027d24c0a9cf1d42cece5cd/master/w_2560,c_limit/00001-4SDESIGNS-MENSWEAR-FALL-21.jpg',
'https://assets.vogue.com/photos/6027d244327fc00a43425432/master/w_2560,c_limit/00002-4SDESIGNS-MENSWEAR-FALL-21.jpg',
'https://assets.vogue.com/photos/6027d243fbb177114cbf582a/master/w_2560,c_limit/00003-4SDESIGNS-MENSWEAR-FALL-21.jpg',
'https://assets.vogue.com/photos/6027d247257ad32ccce37ce6/master/w_2560,c_limit/00004-4SDESIGNS-MENSWEAR-FALL-21.jpg',
'https://assets.vogue.com/photos/6027d2476da2d9ae5efbd828/master/w_2560,c_limit/00005-4SDESIGNS-MENSWEAR-FALL-21.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52630,
brand: '4SDesigns',
title: 'Spring 2021 Menswear',
season: 'AW21',
year: 2021,
img: 'https://assets.vogue.com/photos/5f5bc247c287ced7cb397bed/master/w_2560,c_limit/00001-4SDESIGN-MENS-Spring-2021.jpg',
count: 37,
gallery: [
'https://assets.vogue.com/photos/5f5bc247c287ced7cb397bed/master/w_2560,c_limit/00001-4SDESIGN-MENS-Spring-2021.jpg',
'https://assets.vogue.com/photos/5f5bc1d35b20143972317da5/master/w_2560,c_limit/00002-4SDESIGN-MENS-Spring-2021.jpg',
'https://assets.vogue.com/photos/5f5bc1df3c1d7b6a190becdd/master/w_2560,c_limit/00003-4SDESIGN-MENS-Spring-2021.jpg',
'https://assets.vogue.com/photos/5f5bc2425b20143972317da9/master/w_2560,c_limit/00004-4SDESIGN-MENS-Spring-2021.jpg',
'https://assets.vogue.com/photos/5f5bc213ad0b360706e648f7/master/w_2560,c_limit/00005-4SDESIGN-MENS-Spring-2021.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52629,
brand: '4SDesigns',
title: 'Spring 2022 Menswear',
season: 'AW22',
year: 2022,
img: 'https://assets.vogue.com/photos/60d1cbf645d4cb14fb3dc280/master/w_2560,c_limit/00001-4SDesigns-Mens-SS22-credit-brand.jpg',
count: 37,
gallery: [
'https://assets.vogue.com/photos/60d1cbf645d4cb14fb3dc280/master/w_2560,c_limit/00001-4SDesigns-Mens-SS22-credit-brand.jpg',
'https://assets.vogue.com/photos/60d1cbfb827cc425d00d5309/master/w_2560,c_limit/00002-4SDesigns-Mens-SS22-credit-brand.jpg',
'https://assets.vogue.com/photos/60d1cbfc43dc9f0bcf2cdf82/master/w_2560,c_limit/00003-4SDesigns-Mens-SS22-credit-brand.jpg',
'https://assets.vogue.com/photos/60d1cbfa31f3da1aa8f2b184/master/w_2560,c_limit/00004-4SDesigns-Mens-SS22-credit-brand.jpg',
'https://assets.vogue.com/photos/60d1cbff31f3da1aa8f2b186/master/w_2560,c_limit/00005-4SDesigns-Mens-SS22-credit-brand.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52628,
brand: '3.1 Phillip Lim',
title: 'Spring 2007 Ready-to-Wear',
season: 'AW07',
year: 2007,
img: 'https://assets.vogue.com/photos/55c6517108298d8be221a938/master/w_2560,c_limit/00010m.jpg',
count: 64,
gallery: [
'https://assets.vogue.com/photos/55c6517108298d8be221a938/master/w_2560,c_limit/00010m.jpg',
'https://assets.vogue.com/photos/55c6517108298d8be221a939/master/w_2560,c_limit/00020m.jpg',
'https://assets.vogue.com/photos/55c6517108298d8be221a93a/master/w_2560,c_limit/00030m.jpg',
'https://assets.vogue.com/photos/55c6517108298d8be221a93b/master/w_2560,c_limit/00040m.jpg',
'https://assets.vogue.com/photos/55c6517108298d8be221a93c/master/w_2560,c_limit/00050m.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52627,
brand: '4SDesigns',
title: 'Spring 2023 Menswear',
season: 'AW23',
year: 2023,
img: 'https://assets.vogue.com/photos/62a8a07f3f1b1935680d3bd7/master/w_2560,c_limit/00001-4SDesigns-mens-spring-2023-credit-brand.jpg',
count: 38,
gallery: [
'https://assets.vogue.com/photos/62a8a07f3f1b1935680d3bd7/master/w_2560,c_limit/00001-4SDesigns-mens-spring-2023-credit-brand.jpg',
'https://assets.vogue.com/photos/62a8a07e7fdb5f476a38ea63/master/w_2560,c_limit/00002-4SDesigns-mens-spring-2023-credit-brand.jpg',
'https://assets.vogue.com/photos/62a8a08298c7d4eb9d6771d8/master/w_2560,c_limit/00003-4SDesigns-mens-spring-2023-credit-brand.jpg',
'https://assets.vogue.com/photos/62a8a08abbd4a39a9da9c2dc/master/w_2560,c_limit/00004-4SDesigns-mens-spring-2023-credit-brand.jpg',
'https://assets.vogue.com/photos/62a8a093dd731c1b9956b7ce/master/w_2560,c_limit/00005-4SDesigns-mens-spring-2023-credit-brand.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52626,
brand: '4SDesigns',
title: 'Spring 2024 Menswear',
season: 'AW24',
year: 2024,
img: 'https://assets.vogue.com/photos/64972b08c2a3004981ecefa1/master/w_2560,c_limit/00001-4sdesigns-spring-2024-menswear-credit-brand.jpg',
count: 32,
gallery: [
'https://assets.vogue.com/photos/64972b08c2a3004981ecefa1/master/w_2560,c_limit/00001-4sdesigns-spring-2024-menswear-credit-brand.jpg',
'https://assets.vogue.com/photos/64972b06cebc6648cb42b423/master/w_2560,c_limit/00002-4sdesigns-spring-2024-menswear-credit-brand.jpg',
'https://assets.vogue.com/photos/64972b07117ea73b4611cc16/master/w_2560,c_limit/00003-4sdesigns-spring-2024-menswear-credit-brand.jpg',
'https://assets.vogue.com/photos/64972b10f496fd5039bb28f5/master/w_2560,c_limit/00004-4sdesigns-spring-2024-menswear-credit-brand.jpg',
'https://assets.vogue.com/photos/64972b166d2b1f02bbf79dfa/master/w_2560,c_limit/00005-4sdesigns-spring-2024-menswear-credit-brand.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52625,
brand: '4SDesigns',
title: 'Fall 2023 Menswear',
season: 'AW23',
year: 2023,
img: 'https://assets.vogue.com/photos/63c531d0d6dc24d26b516f7e/master/w_2560,c_limit/00001-4sdesigns-fall-2023-menswear-credit-brand.jpg',
count: 30,
gallery: [
'https://assets.vogue.com/photos/63c531d0d6dc24d26b516f7e/master/w_2560,c_limit/00001-4sdesigns-fall-2023-menswear-credit-brand.jpg',
'https://assets.vogue.com/photos/63c531d23cfdf376930a346a/master/w_2560,c_limit/00002-4sdesigns-fall-2023-menswear-credit-brand.jpg',
'https://assets.vogue.com/photos/63c531cf1944dc168acdfb98/master/w_2560,c_limit/00003-4sdesigns-fall-2023-menswear-credit-brand.jpg',
'https://assets.vogue.com/photos/63c531d43b8c841452deb23d/master/w_2560,c_limit/00004-4sdesigns-fall-2023-menswear-credit-brand.jpg',
'https://assets.vogue.com/photos/63c531d4a426ef92a40afb03/master/w_2560,c_limit/00005-4sdesigns-fall-2023-menswear-credit-brand.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52624,
brand: '3.1 Phillip Lim',
title: 'Fall 2007 Ready-to-Wear',
season: 'AW07',
year: 2007,
img: 'https://assets.vogue.com/photos/55c6517708298d8be222187c/master/w_2560,c_limit/00010m.jpg',
count: 70,
gallery: [
'https://assets.vogue.com/photos/55c6517708298d8be222187c/master/w_2560,c_limit/00010m.jpg',
'https://assets.vogue.com/photos/55c6517708298d8be222187d/master/w_2560,c_limit/00020m.jpg',
'https://assets.vogue.com/photos/55c6517708298d8be222187e/master/w_2560,c_limit/00030m.jpg',
'https://assets.vogue.com/photos/55c6517708298d8be222187f/master/w_2560,c_limit/00040m.jpg',
'https://assets.vogue.com/photos/55c6517708298d8be2221880/master/w_2560,c_limit/00050m.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52623,
brand: '4SDesigns',
title: 'Fall 2022 Menswear',
season: 'AW22',
year: 2022,
img: 'https://assets.vogue.com/photos/61e32026e135cfda761c9c1c/master/w_2560,c_limit/00001-4SDesigns-Mens-Fall-22-credit-brand.jpg',
count: 39,
gallery: [
'https://assets.vogue.com/photos/61e32026e135cfda761c9c1c/master/w_2560,c_limit/00001-4SDesigns-Mens-Fall-22-credit-brand.jpg',
'https://assets.vogue.com/photos/61e32029b1c1870aceac9214/master/w_2560,c_limit/00002-4SDesigns-Mens-Fall-22-credit-brand.jpg',
'https://assets.vogue.com/photos/61e3202cb1c1870aceac9216/master/w_2560,c_limit/00003-4SDesigns-Mens-Fall-22-credit-brand.jpg',
'https://assets.vogue.com/photos/61e3202e1be17143b2387ab5/master/w_2560,c_limit/00004-4SDesigns-Mens-Fall-22-credit-brand.jpg',
'https://assets.vogue.com/photos/61e32032a668411b428595ee/master/w_2560,c_limit/00005-4SDesigns-Mens-Fall-22-credit-brand.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52622,
brand: '3.1 Phillip Lim',
title: 'Fall 2006 Ready-to-Wear',
season: 'AW06',
year: 2006,
img: 'https://assets.vogue.com/photos/55c6516608298d8be220ec9d/master/w_2560,c_limit/01m.jpg',
count: 20,
gallery: [
'https://assets.vogue.com/photos/55c6516608298d8be220ec9d/master/w_2560,c_limit/01m.jpg',
'https://assets.vogue.com/photos/55c6516608298d8be220ec9e/master/w_2560,c_limit/02m.jpg',
'https://assets.vogue.com/photos/55c6516608298d8be220ec9f/master/w_2560,c_limit/03m.jpg',
'https://assets.vogue.com/photos/55c6516608298d8be220eca0/master/w_2560,c_limit/05m.jpg',
'https://assets.vogue.com/photos/55c6516608298d8be220eca1/master/w_2560,c_limit/06m.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52621,
brand: '3.1 Phillip Lim',
title: 'Fall 2008 Ready-to-Wear',
season: 'AW08',
year: 2008,
img: 'https://assets.vogue.com/photos/55c6518908298d8be2236856/master/w_2560,c_limit/00010m.jpg',
count: 106,
gallery: [
'https://assets.vogue.com/photos/55c6518908298d8be2236856/master/w_2560,c_limit/00010m.jpg',
'https://assets.vogue.com/photos/55c6518908298d8be2236857/master/w_2560,c_limit/00020m.jpg',
'https://assets.vogue.com/photos/55c6518908298d8be2236858/master/w_2560,c_limit/00030m.jpg',
'https://assets.vogue.com/photos/55c6518908298d8be2236859/master/w_2560,c_limit/00040m.jpg',
'https://assets.vogue.com/photos/55c6518908298d8be223685a/master/w_2560,c_limit/00050m.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52620,
brand: '4SDesigns',
title: 'Fall 2024 Menswear',
season: 'AW24',
year: 2024,
img: 'https://assets.vogue.com/photos/65a8fbbbe58c6b2c313c49c2/master/w_2560,c_limit/00001-4sdesigns-fall-2024-menswear-credit-brand.jpg',
count: 25,
gallery: [
'https://assets.vogue.com/photos/65a8fbbbe58c6b2c313c49c2/master/w_2560,c_limit/00001-4sdesigns-fall-2024-menswear-credit-brand.jpg',
'https://assets.vogue.com/photos/65a8fbbcd46c3066d0e3a561/master/w_2560,c_limit/00002-4sdesigns-fall-2024-menswear-credit-brand.jpg',
'https://assets.vogue.com/photos/65a8fbbdcb58e8ab7ad97d8c/master/w_2560,c_limit/00003-4sdesigns-fall-2024-menswear-credit-brand.jpg',
'https://assets.vogue.com/photos/65a8fbbfdf79593d6bd9add0/master/w_2560,c_limit/00004-4sdesigns-fall-2024-menswear-credit-brand.jpg',
'https://assets.vogue.com/photos/65a8fbc0d46c3066d0e3a563/master/w_2560,c_limit/00005-4sdesigns-fall-2024-menswear-credit-brand.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52619,
brand: '3.1 Phillip Lim',
title: 'Spring 2008 Ready-to-Wear',
season: 'AW08',
year: 2008,
img: 'https://assets.vogue.com/photos/55c6518008298d8be222c795/master/w_2560,c_limit/00010m.jpg',
count: 109,
gallery: [
'https://assets.vogue.com/photos/55c6518008298d8be222c795/master/w_2560,c_limit/00010m.jpg',
'https://assets.vogue.com/photos/55c6518008298d8be222c796/master/w_2560,c_limit/00020m.jpg',
'https://assets.vogue.com/photos/55c6518008298d8be222c797/master/w_2560,c_limit/00030m.jpg',
'https://assets.vogue.com/photos/55c6518008298d8be222c798/master/w_2560,c_limit/00040m.jpg',
'https://assets.vogue.com/photos/55c6518008298d8be222c799/master/w_2560,c_limit/00050m.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52618,
brand: '3.1 Phillip Lim',
title: 'Resort 2008',
season: 'AW08',
year: 2008,
img: 'https://assets.vogue.com/photos/55c6517d08298d8be222920d/master/w_2560,c_limit/01m.jpg',
count: 16,
gallery: [
'https://assets.vogue.com/photos/55c6517d08298d8be222920d/master/w_2560,c_limit/01m.jpg',
'https://assets.vogue.com/photos/55c6517d08298d8be222920e/master/w_2560,c_limit/02m.jpg',
'https://assets.vogue.com/photos/55c6517d08298d8be222920f/master/w_2560,c_limit/03m.jpg',
'https://assets.vogue.com/photos/55c6517d08298d8be2229210/master/w_2560,c_limit/04m.jpg',
'https://assets.vogue.com/photos/55c6517d08298d8be2229211/master/w_2560,c_limit/05m.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52617,
brand: '3.1 Phillip Lim',
title: 'Spring 2009 Ready-to-Wear',
season: 'AW09',
year: 2009,
img: 'https://assets.vogue.com/photos/55c6519708298d8be224625e/master/w_2560,c_limit/00010fullscreen.jpg',
count: 105,
gallery: [
'https://assets.vogue.com/photos/55c6519708298d8be224625e/master/w_2560,c_limit/00010fullscreen.jpg',
'https://assets.vogue.com/photos/55c6519708298d8be224625f/master/w_2560,c_limit/00020fullscreen.jpg',
'https://assets.vogue.com/photos/55c6519708298d8be2246260/master/w_2560,c_limit/00030fullscreen.jpg',
'https://assets.vogue.com/photos/55c6519708298d8be2246261/master/w_2560,c_limit/00040fullscreen.jpg',
'https://assets.vogue.com/photos/55c6519708298d8be2246262/master/w_2560,c_limit/00050fullscreen.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52616,
brand: '3.1 Phillip Lim',
title: 'Fall 2009 Ready-to-Wear',
season: 'AW09',
year: 2009,
img: 'https://assets.vogue.com/photos/55c6519f08298d8be2250311/master/w_2560,c_limit/00010fullscreen.jpg',
count: 145,
gallery: [
'https://assets.vogue.com/photos/55c6519f08298d8be2250311/master/w_2560,c_limit/00010fullscreen.jpg',
'https://assets.vogue.com/photos/55c6519f08298d8be2250312/master/w_2560,c_limit/00020fullscreen.jpg',
'https://assets.vogue.com/photos/55c6519f08298d8be2250313/master/w_2560,c_limit/00030fullscreen.jpg',
'https://assets.vogue.com/photos/55c6519f08298d8be2250314/master/w_2560,c_limit/00040fullscreen.jpg',
'https://assets.vogue.com/photos/55c6519f08298d8be2250315/master/w_2560,c_limit/00050fullscreen.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52615,
brand: '3.1 Phillip Lim',
title: 'Resort 2010',
season: 'AW10',
year: 2010,
img: 'https://assets.vogue.com/photos/55c651a508298d8be2257404/master/w_2560,c_limit/01fullscreen.jpg',
count: 27,
gallery: [
'https://assets.vogue.com/photos/55c651a508298d8be2257404/master/w_2560,c_limit/01fullscreen.jpg',
'https://assets.vogue.com/photos/55c651a508298d8be2257405/master/w_2560,c_limit/02fullscreen.jpg',
'https://assets.vogue.com/photos/55c651a508298d8be2257406/master/w_2560,c_limit/03fullscreen.jpg',
'https://assets.vogue.com/photos/55c651a508298d8be2257407/master/w_2560,c_limit/04fullscreen.jpg',
'https://assets.vogue.com/photos/55c651a508298d8be2257408/master/w_2560,c_limit/05fullscreen.jpg'
],
collection_type: '',
season_code: ''
}
]
18:25:17 [200] / 24ms
[
{
id: 52634,
brand: '5000',
title: 'Spring 2026 Ready-to-Wear',
season: 'AW26',
year: 2026,
img: 'https://assets.vogue.com/photos/68c84730627c7d40ac52aa17/master/w_2560,c_limit/00001-5000-spring-2026-ready-to-wear-credit-brand.jpg',
count: 30,
gallery: [
'https://assets.vogue.com/photos/68c84730627c7d40ac52aa17/master/w_2560,c_limit/00001-5000-spring-2026-ready-to-wear-credit-brand.jpg',
'https://assets.vogue.com/photos/68c8473052c290f6844f15a4/master/w_2560,c_limit/00002-5000-spring-2026-ready-to-wear-credit-brand.jpg',
'https://assets.vogue.com/photos/68c84730c00a8da1b9585e04/master/w_2560,c_limit/00003-5000-spring-2026-ready-to-wear-credit-brand.jpg',
'https://assets.vogue.com/photos/68c847330865a9bf107a6248/master/w_2560,c_limit/00004-5000-spring-2026-ready-to-wear-credit-brand.jpg',
'https://assets.vogue.com/photos/68c84733cc5a262b887823c6/master/w_2560,c_limit/00005-5000-spring-2026-ready-to-wear-credit-brand.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52633,
brand: '5000',
title: 'Fall 2025 Ready-to-Wear',
season: 'AW25',
year: 2025,
img: 'https://assets.vogue.com/photos/67a7ec6c540e35479b3d337c/master/w_2560,c_limit/00001-5000-fall-2025-ready-to-wear-credit-gorunway.jpg',
count: 27,
gallery: [
'https://assets.vogue.com/photos/67a7ec6c540e35479b3d337c/master/w_2560,c_limit/00001-5000-fall-2025-ready-to-wear-credit-gorunway.jpg',
'https://assets.vogue.com/photos/67a7ec6cee822deb1e8416c6/master/w_2560,c_limit/00002-5000-fall-2025-ready-to-wear-credit-gorunway.jpg',
'https://assets.vogue.com/photos/67a7ec6d551d29a948ad941e/master/w_2560,c_limit/00003-5000-fall-2025-ready-to-wear-credit-gorunway.jpg',
'https://assets.vogue.com/photos/67a7ec72ee822deb1e8416c8/master/w_2560,c_limit/00004-5000-fall-2025-ready-to-wear-credit-gorunway.jpg',
'https://assets.vogue.com/photos/67a7ec726ccfc8cbda1f1936/master/w_2560,c_limit/00005-5000-fall-2025-ready-to-wear-credit-gorunway.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52632,
brand: '5000',
title: 'Spring 2025 Ready-to-Wear',
season: 'AW25',
year: 2025,
img: 'https://assets.vogue.com/photos/66e1c75bd788bc9ca9d9acba/master/w_2560,c_limit/00001-5000-spring-2025-ready-to-wear-credit-brand.jpg',
count: 22,
gallery: [
'https://assets.vogue.com/photos/66e1c75bd788bc9ca9d9acba/master/w_2560,c_limit/00001-5000-spring-2025-ready-to-wear-credit-brand.jpg',
'https://assets.vogue.com/photos/66e1c772bc1a3e72a02f841a/master/w_2560,c_limit/00002-5000-spring-2025-ready-to-wear-credit-brand.jpg',
'https://assets.vogue.com/photos/66e1c772bc1a3e72a02f8419/master/w_2560,c_limit/00003-5000-spring-2025-ready-to-wear-credit-brand.jpg',
'https://assets.vogue.com/photos/66e1c775c22905cb0212f62b/master/w_2560,c_limit/00004-5000-spring-2025-ready-to-wear-credit-brand.jpg',
'https://assets.vogue.com/photos/66e1c79d0e2401b690216a7b/master/w_2560,c_limit/00005-5000-spring-2025-ready-to-wear-credit-brand.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52631,
brand: '4SDesigns',
title: 'Fall 2021 Menswear',
season: 'AW21',
year: 2021,
img: 'https://assets.vogue.com/photos/6027d24c0a9cf1d42cece5cd/master/w_2560,c_limit/00001-4SDESIGNS-MENSWEAR-FALL-21.jpg',
count: 34,
gallery: [
'https://assets.vogue.com/photos/6027d24c0a9cf1d42cece5cd/master/w_2560,c_limit/00001-4SDESIGNS-MENSWEAR-FALL-21.jpg',
'https://assets.vogue.com/photos/6027d244327fc00a43425432/master/w_2560,c_limit/00002-4SDESIGNS-MENSWEAR-FALL-21.jpg',
'https://assets.vogue.com/photos/6027d243fbb177114cbf582a/master/w_2560,c_limit/00003-4SDESIGNS-MENSWEAR-FALL-21.jpg',
'https://assets.vogue.com/photos/6027d247257ad32ccce37ce6/master/w_2560,c_limit/00004-4SDESIGNS-MENSWEAR-FALL-21.jpg',
'https://assets.vogue.com/photos/6027d2476da2d9ae5efbd828/master/w_2560,c_limit/00005-4SDESIGNS-MENSWEAR-FALL-21.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52630,
brand: '4SDesigns',
title: 'Spring 2021 Menswear',
season: 'AW21',
year: 2021,
img: 'https://assets.vogue.com/photos/5f5bc247c287ced7cb397bed/master/w_2560,c_limit/00001-4SDESIGN-MENS-Spring-2021.jpg',
count: 37,
gallery: [
'https://assets.vogue.com/photos/5f5bc247c287ced7cb397bed/master/w_2560,c_limit/00001-4SDESIGN-MENS-Spring-2021.jpg',
'https://assets.vogue.com/photos/5f5bc1d35b20143972317da5/master/w_2560,c_limit/00002-4SDESIGN-MENS-Spring-2021.jpg',
'https://assets.vogue.com/photos/5f5bc1df3c1d7b6a190becdd/master/w_2560,c_limit/00003-4SDESIGN-MENS-Spring-2021.jpg',
'https://assets.vogue.com/photos/5f5bc2425b20143972317da9/master/w_2560,c_limit/00004-4SDESIGN-MENS-Spring-2021.jpg',
'https://assets.vogue.com/photos/5f5bc213ad0b360706e648f7/master/w_2560,c_limit/00005-4SDESIGN-MENS-Spring-2021.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52629,
brand: '4SDesigns',
title: 'Spring 2022 Menswear',
season: 'AW22',
year: 2022,
img: 'https://assets.vogue.com/photos/60d1cbf645d4cb14fb3dc280/master/w_2560,c_limit/00001-4SDesigns-Mens-SS22-credit-brand.jpg',
count: 37,
gallery: [
'https://assets.vogue.com/photos/60d1cbf645d4cb14fb3dc280/master/w_2560,c_limit/00001-4SDesigns-Mens-SS22-credit-brand.jpg',
'https://assets.vogue.com/photos/60d1cbfb827cc425d00d5309/master/w_2560,c_limit/00002-4SDesigns-Mens-SS22-credit-brand.jpg',
'https://assets.vogue.com/photos/60d1cbfc43dc9f0bcf2cdf82/master/w_2560,c_limit/00003-4SDesigns-Mens-SS22-credit-brand.jpg',
'https://assets.vogue.com/photos/60d1cbfa31f3da1aa8f2b184/master/w_2560,c_limit/00004-4SDesigns-Mens-SS22-credit-brand.jpg',
'https://assets.vogue.com/photos/60d1cbff31f3da1aa8f2b186/master/w_2560,c_limit/00005-4SDesigns-Mens-SS22-credit-brand.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52628,
brand: '3.1 Phillip Lim',
title: 'Spring 2007 Ready-to-Wear',
season: 'AW07',
year: 2007,
img: 'https://assets.vogue.com/photos/55c6517108298d8be221a938/master/w_2560,c_limit/00010m.jpg',
count: 64,
gallery: [
'https://assets.vogue.com/photos/55c6517108298d8be221a938/master/w_2560,c_limit/00010m.jpg',
'https://assets.vogue.com/photos/55c6517108298d8be221a939/master/w_2560,c_limit/00020m.jpg',
'https://assets.vogue.com/photos/55c6517108298d8be221a93a/master/w_2560,c_limit/00030m.jpg',
'https://assets.vogue.com/photos/55c6517108298d8be221a93b/master/w_2560,c_limit/00040m.jpg',
'https://assets.vogue.com/photos/55c6517108298d8be221a93c/master/w_2560,c_limit/00050m.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52627,
brand: '4SDesigns',
title: 'Spring 2023 Menswear',
season: 'AW23',
year: 2023,
img: 'https://assets.vogue.com/photos/62a8a07f3f1b1935680d3bd7/master/w_2560,c_limit/00001-4SDesigns-mens-spring-2023-credit-brand.jpg',
count: 38,
gallery: [
'https://assets.vogue.com/photos/62a8a07f3f1b1935680d3bd7/master/w_2560,c_limit/00001-4SDesigns-mens-spring-2023-credit-brand.jpg',
'https://assets.vogue.com/photos/62a8a07e7fdb5f476a38ea63/master/w_2560,c_limit/00002-4SDesigns-mens-spring-2023-credit-brand.jpg',
'https://assets.vogue.com/photos/62a8a08298c7d4eb9d6771d8/master/w_2560,c_limit/00003-4SDesigns-mens-spring-2023-credit-brand.jpg',
'https://assets.vogue.com/photos/62a8a08abbd4a39a9da9c2dc/master/w_2560,c_limit/00004-4SDesigns-mens-spring-2023-credit-brand.jpg',
'https://assets.vogue.com/photos/62a8a093dd731c1b9956b7ce/master/w_2560,c_limit/00005-4SDesigns-mens-spring-2023-credit-brand.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52626,
brand: '4SDesigns',
title: 'Spring 2024 Menswear',
season: 'AW24',
year: 2024,
img: 'https://assets.vogue.com/photos/64972b08c2a3004981ecefa1/master/w_2560,c_limit/00001-4sdesigns-spring-2024-menswear-credit-brand.jpg',
count: 32,
gallery: [
'https://assets.vogue.com/photos/64972b08c2a3004981ecefa1/master/w_2560,c_limit/00001-4sdesigns-spring-2024-menswear-credit-brand.jpg',
'https://assets.vogue.com/photos/64972b06cebc6648cb42b423/master/w_2560,c_limit/00002-4sdesigns-spring-2024-menswear-credit-brand.jpg',
'https://assets.vogue.com/photos/64972b07117ea73b4611cc16/master/w_2560,c_limit/00003-4sdesigns-spring-2024-menswear-credit-brand.jpg',
'https://assets.vogue.com/photos/64972b10f496fd5039bb28f5/master/w_2560,c_limit/00004-4sdesigns-spring-2024-menswear-credit-brand.jpg',
'https://assets.vogue.com/photos/64972b166d2b1f02bbf79dfa/master/w_2560,c_limit/00005-4sdesigns-spring-2024-menswear-credit-brand.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52625,
brand: '4SDesigns',
title: 'Fall 2023 Menswear',
season: 'AW23',
year: 2023,
img: 'https://assets.vogue.com/photos/63c531d0d6dc24d26b516f7e/master/w_2560,c_limit/00001-4sdesigns-fall-2023-menswear-credit-brand.jpg',
count: 30,
gallery: [
'https://assets.vogue.com/photos/63c531d0d6dc24d26b516f7e/master/w_2560,c_limit/00001-4sdesigns-fall-2023-menswear-credit-brand.jpg',
'https://assets.vogue.com/photos/63c531d23cfdf376930a346a/master/w_2560,c_limit/00002-4sdesigns-fall-2023-menswear-credit-brand.jpg',
'https://assets.vogue.com/photos/63c531cf1944dc168acdfb98/master/w_2560,c_limit/00003-4sdesigns-fall-2023-menswear-credit-brand.jpg',
'https://assets.vogue.com/photos/63c531d43b8c841452deb23d/master/w_2560,c_limit/00004-4sdesigns-fall-2023-menswear-credit-brand.jpg',
'https://assets.vogue.com/photos/63c531d4a426ef92a40afb03/master/w_2560,c_limit/00005-4sdesigns-fall-2023-menswear-credit-brand.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52624,
brand: '3.1 Phillip Lim',
title: 'Fall 2007 Ready-to-Wear',
season: 'AW07',
year: 2007,
img: 'https://assets.vogue.com/photos/55c6517708298d8be222187c/master/w_2560,c_limit/00010m.jpg',
count: 70,
gallery: [
'https://assets.vogue.com/photos/55c6517708298d8be222187c/master/w_2560,c_limit/00010m.jpg',
'https://assets.vogue.com/photos/55c6517708298d8be222187d/master/w_2560,c_limit/00020m.jpg',
'https://assets.vogue.com/photos/55c6517708298d8be222187e/master/w_2560,c_limit/00030m.jpg',
'https://assets.vogue.com/photos/55c6517708298d8be222187f/master/w_2560,c_limit/00040m.jpg',
'https://assets.vogue.com/photos/55c6517708298d8be2221880/master/w_2560,c_limit/00050m.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52623,
brand: '4SDesigns',
title: 'Fall 2022 Menswear',
season: 'AW22',
year: 2022,
img: 'https://assets.vogue.com/photos/61e32026e135cfda761c9c1c/master/w_2560,c_limit/00001-4SDesigns-Mens-Fall-22-credit-brand.jpg',
count: 39,
gallery: [
'https://assets.vogue.com/photos/61e32026e135cfda761c9c1c/master/w_2560,c_limit/00001-4SDesigns-Mens-Fall-22-credit-brand.jpg',
'https://assets.vogue.com/photos/61e32029b1c1870aceac9214/master/w_2560,c_limit/00002-4SDesigns-Mens-Fall-22-credit-brand.jpg',
'https://assets.vogue.com/photos/61e3202cb1c1870aceac9216/master/w_2560,c_limit/00003-4SDesigns-Mens-Fall-22-credit-brand.jpg',
'https://assets.vogue.com/photos/61e3202e1be17143b2387ab5/master/w_2560,c_limit/00004-4SDesigns-Mens-Fall-22-credit-brand.jpg',
'https://assets.vogue.com/photos/61e32032a668411b428595ee/master/w_2560,c_limit/00005-4SDesigns-Mens-Fall-22-credit-brand.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52622,
brand: '3.1 Phillip Lim',
title: 'Fall 2006 Ready-to-Wear',
season: 'AW06',
year: 2006,
img: 'https://assets.vogue.com/photos/55c6516608298d8be220ec9d/master/w_2560,c_limit/01m.jpg',
count: 20,
gallery: [
'https://assets.vogue.com/photos/55c6516608298d8be220ec9d/master/w_2560,c_limit/01m.jpg',
'https://assets.vogue.com/photos/55c6516608298d8be220ec9e/master/w_2560,c_limit/02m.jpg',
'https://assets.vogue.com/photos/55c6516608298d8be220ec9f/master/w_2560,c_limit/03m.jpg',
'https://assets.vogue.com/photos/55c6516608298d8be220eca0/master/w_2560,c_limit/05m.jpg',
'https://assets.vogue.com/photos/55c6516608298d8be220eca1/master/w_2560,c_limit/06m.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52621,
brand: '3.1 Phillip Lim',
title: 'Fall 2008 Ready-to-Wear',
season: 'AW08',
year: 2008,
img: 'https://assets.vogue.com/photos/55c6518908298d8be2236856/master/w_2560,c_limit/00010m.jpg',
count: 106,
gallery: [
'https://assets.vogue.com/photos/55c6518908298d8be2236856/master/w_2560,c_limit/00010m.jpg',
'https://assets.vogue.com/photos/55c6518908298d8be2236857/master/w_2560,c_limit/00020m.jpg',
'https://assets.vogue.com/photos/55c6518908298d8be2236858/master/w_2560,c_limit/00030m.jpg',
'https://assets.vogue.com/photos/55c6518908298d8be2236859/master/w_2560,c_limit/00040m.jpg',
'https://assets.vogue.com/photos/55c6518908298d8be223685a/master/w_2560,c_limit/00050m.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52620,
brand: '4SDesigns',
title: 'Fall 2024 Menswear',
season: 'AW24',
year: 2024,
img: 'https://assets.vogue.com/photos/65a8fbbbe58c6b2c313c49c2/master/w_2560,c_limit/00001-4sdesigns-fall-2024-menswear-credit-brand.jpg',
count: 25,
gallery: [
'https://assets.vogue.com/photos/65a8fbbbe58c6b2c313c49c2/master/w_2560,c_limit/00001-4sdesigns-fall-2024-menswear-credit-brand.jpg',
'https://assets.vogue.com/photos/65a8fbbcd46c3066d0e3a561/master/w_2560,c_limit/00002-4sdesigns-fall-2024-menswear-credit-brand.jpg',
'https://assets.vogue.com/photos/65a8fbbdcb58e8ab7ad97d8c/master/w_2560,c_limit/00003-4sdesigns-fall-2024-menswear-credit-brand.jpg',
'https://assets.vogue.com/photos/65a8fbbfdf79593d6bd9add0/master/w_2560,c_limit/00004-4sdesigns-fall-2024-menswear-credit-brand.jpg',
'https://assets.vogue.com/photos/65a8fbc0d46c3066d0e3a563/master/w_2560,c_limit/00005-4sdesigns-fall-2024-menswear-credit-brand.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52619,
brand: '3.1 Phillip Lim',
title: 'Spring 2008 Ready-to-Wear',
season: 'AW08',
year: 2008,
img: 'https://assets.vogue.com/photos/55c6518008298d8be222c795/master/w_2560,c_limit/00010m.jpg',
count: 109,
gallery: [
'https://assets.vogue.com/photos/55c6518008298d8be222c795/master/w_2560,c_limit/00010m.jpg',
'https://assets.vogue.com/photos/55c6518008298d8be222c796/master/w_2560,c_limit/00020m.jpg',
'https://assets.vogue.com/photos/55c6518008298d8be222c797/master/w_2560,c_limit/00030m.jpg',
'https://assets.vogue.com/photos/55c6518008298d8be222c798/master/w_2560,c_limit/00040m.jpg',
'https://assets.vogue.com/photos/55c6518008298d8be222c799/master/w_2560,c_limit/00050m.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52618,
brand: '3.1 Phillip Lim',
title: 'Resort 2008',
season: 'AW08',
year: 2008,
img: 'https://assets.vogue.com/photos/55c6517d08298d8be222920d/master/w_2560,c_limit/01m.jpg',
count: 16,
gallery: [
'https://assets.vogue.com/photos/55c6517d08298d8be222920d/master/w_2560,c_limit/01m.jpg',
'https://assets.vogue.com/photos/55c6517d08298d8be222920e/master/w_2560,c_limit/02m.jpg',
'https://assets.vogue.com/photos/55c6517d08298d8be222920f/master/w_2560,c_limit/03m.jpg',
'https://assets.vogue.com/photos/55c6517d08298d8be2229210/master/w_2560,c_limit/04m.jpg',
'https://assets.vogue.com/photos/55c6517d08298d8be2229211/master/w_2560,c_limit/05m.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52617,
brand: '3.1 Phillip Lim',
title: 'Spring 2009 Ready-to-Wear',
season: 'AW09',
year: 2009,
img: 'https://assets.vogue.com/photos/55c6519708298d8be224625e/master/w_2560,c_limit/00010fullscreen.jpg',
count: 105,
gallery: [
'https://assets.vogue.com/photos/55c6519708298d8be224625e/master/w_2560,c_limit/00010fullscreen.jpg',
'https://assets.vogue.com/photos/55c6519708298d8be224625f/master/w_2560,c_limit/00020fullscreen.jpg',
'https://assets.vogue.com/photos/55c6519708298d8be2246260/master/w_2560,c_limit/00030fullscreen.jpg',
'https://assets.vogue.com/photos/55c6519708298d8be2246261/master/w_2560,c_limit/00040fullscreen.jpg',
'https://assets.vogue.com/photos/55c6519708298d8be2246262/master/w_2560,c_limit/00050fullscreen.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52616,
brand: '3.1 Phillip Lim',
title: 'Fall 2009 Ready-to-Wear',
season: 'AW09',
year: 2009,
img: 'https://assets.vogue.com/photos/55c6519f08298d8be2250311/master/w_2560,c_limit/00010fullscreen.jpg',
count: 145,
gallery: [
'https://assets.vogue.com/photos/55c6519f08298d8be2250311/master/w_2560,c_limit/00010fullscreen.jpg',
'https://assets.vogue.com/photos/55c6519f08298d8be2250312/master/w_2560,c_limit/00020fullscreen.jpg',
'https://assets.vogue.com/photos/55c6519f08298d8be2250313/master/w_2560,c_limit/00030fullscreen.jpg',
'https://assets.vogue.com/photos/55c6519f08298d8be2250314/master/w_2560,c_limit/00040fullscreen.jpg',
'https://assets.vogue.com/photos/55c6519f08298d8be2250315/master/w_2560,c_limit/00050fullscreen.jpg'
],
collection_type: '',
season_code: ''
},
{
id: 52615,
brand: '3.1 Phillip Lim',
title: 'Resort 2010',
season: 'AW10',
year: 2010,
img: 'https://assets.vogue.com/photos/55c651a508298d8be2257404/master/w_2560,c_limit/01fullscreen.jpg',
count: 27,
gallery: [
'https://assets.vogue.com/photos/55c651a508298d8be2257404/master/w_2560,c_limit/01fullscreen.jpg',
'https://assets.vogue.com/photos/55c651a508298d8be2257405/master/w_2560,c_limit/02fullscreen.jpg',
'https://assets.vogue.com/photos/55c651a508298d8be2257406/master/w_2560,c_limit/03fullscreen.jpg',
'https://assets.vogue.com/photos/55c651a508298d8be2257407/master/w_2560,c_limit/04fullscreen.jpg',
'https://assets.vogue.com/photos/55c651a508298d8be2257408/master/w_2560,c_limit/05fullscreen.jpg'
],
collection_type: '',
season_code: ''
}
]
18:25:17 [200] / 2ms

42
package-lock.json generated
View File

@ -13,6 +13,7 @@
"@types/alpinejs": "^3.13.11", "@types/alpinejs": "^3.13.11",
"alpinejs": "^3.16.1", "alpinejs": "^3.16.1",
"astro": "^7.1.6", "astro": "^7.1.6",
"swiper": "^11.2.10",
"tailwindcss": "^4.3.3" "tailwindcss": "^4.3.3"
}, },
"engines": { "engines": {
@ -21,7 +22,7 @@
}, },
"node_modules/@astrojs/alpinejs": { "node_modules/@astrojs/alpinejs": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmmirror.com/@astrojs/alpinejs/-/alpinejs-1.0.0.tgz", "resolved": "https://registry.npmjs.org/@astrojs/alpinejs/-/alpinejs-1.0.0.tgz",
"integrity": "sha512-xmLFD8MBunXCG39nS3dmINQfdhqlDNbHAYyw8n6jfLGL/AFeyYOtz6p1EtXX8NEbGLHk7mUOHoaC3j1WudUYoQ==", "integrity": "sha512-xmLFD8MBunXCG39nS3dmINQfdhqlDNbHAYyw8n6jfLGL/AFeyYOtz6p1EtXX8NEbGLHk7mUOHoaC3j1WudUYoQ==",
"license": "MIT", "license": "MIT",
"peerDependencies": { "peerDependencies": {
@ -521,6 +522,16 @@
"tslib": "^2.4.0" "tslib": "^2.4.0"
} }
}, },
"node_modules/@emnapi/runtime": {
"version": "1.11.3",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz",
"integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==",
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@emnapi/wasi-threads": { "node_modules/@emnapi/wasi-threads": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmmirror.com/@emnapi/wasi-threads/-/wasi-threads-2.0.1.tgz", "resolved": "https://registry.npmmirror.com/@emnapi/wasi-threads/-/wasi-threads-2.0.1.tgz",
@ -2444,10 +2455,9 @@
}, },
"node_modules/@types/alpinejs": { "node_modules/@types/alpinejs": {
"version": "3.13.11", "version": "3.13.11",
"resolved": "https://registry.npmmirror.com/@types/alpinejs/-/alpinejs-3.13.11.tgz", "resolved": "https://registry.npmjs.org/@types/alpinejs/-/alpinejs-3.13.11.tgz",
"integrity": "sha512-3KhGkDixCPiLdL3Z/ok1GxHwLxEWqQOKJccgaQL01wc0EVM2tCTaqlC3NIedmxAXkVzt/V6VTM8qPgnOHKJ1MA==", "integrity": "sha512-3KhGkDixCPiLdL3Z/ok1GxHwLxEWqQOKJccgaQL01wc0EVM2tCTaqlC3NIedmxAXkVzt/V6VTM8qPgnOHKJ1MA==",
"license": "MIT", "license": "MIT"
"peer": true
}, },
"node_modules/@types/estree": { "node_modules/@types/estree": {
"version": "1.0.9", "version": "1.0.9",
@ -2520,10 +2530,9 @@
}, },
"node_modules/alpinejs": { "node_modules/alpinejs": {
"version": "3.16.1", "version": "3.16.1",
"resolved": "https://registry.npmmirror.com/alpinejs/-/alpinejs-3.16.1.tgz", "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.16.1.tgz",
"integrity": "sha512-QXcW8MK9JoG8GZ7vCt2cXJlcEPIA7Xk/7IQ1+L2iLp7sXcIxct0PrgidmHzK97gD9QlfUjHXPxujdZ1mC2PYVg==", "integrity": "sha512-QXcW8MK9JoG8GZ7vCt2cXJlcEPIA7Xk/7IQ1+L2iLp7sXcIxct0PrgidmHzK97gD9QlfUjHXPxujdZ1mC2PYVg==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@vue/reactivity": "~3.1.1" "@vue/reactivity": "~3.1.1"
} }
@ -3059,7 +3068,6 @@
"integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==",
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"esbuild": "bin/esbuild" "esbuild": "bin/esbuild"
}, },
@ -4394,6 +4402,25 @@
"url": "https://opencollective.com/svgo" "url": "https://opencollective.com/svgo"
} }
}, },
"node_modules/swiper": {
"version": "11.2.10",
"resolved": "https://registry.npmjs.org/swiper/-/swiper-11.2.10.tgz",
"integrity": "sha512-RMeVUUjTQH+6N3ckimK93oxz6Sn5la4aDlgPzB+rBrG/smPdCTicXyhxa+woIpopz+jewEloiEE3lKo1h9w2YQ==",
"funding": [
{
"type": "patreon",
"url": "https://www.patreon.com/swiperjs"
},
{
"type": "open_collective",
"url": "http://opencollective.com/swiper"
}
],
"license": "MIT",
"engines": {
"node": ">= 4.7.0"
}
},
"node_modules/tailwindcss": { "node_modules/tailwindcss": {
"version": "4.3.3", "version": "4.3.3",
"resolved": "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-4.3.3.tgz", "resolved": "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-4.3.3.tgz",
@ -4739,7 +4766,6 @@
"resolved": "https://registry.npmmirror.com/vite/-/vite-8.2.0.tgz", "resolved": "https://registry.npmmirror.com/vite/-/vite-8.2.0.tgz",
"integrity": "sha512-pn+CFpM0lwDeKwmOq1ZaBK/9sjorZcgqxki6MbY/jPEVd9vichIlmlD4HmQ5wdP5EgqQCFRaACBxMC7uEGc6lQ==", "integrity": "sha512-pn+CFpM0lwDeKwmOq1ZaBK/9sjorZcgqxki6MbY/jPEVd9vichIlmlD4HmQ5wdP5EgqQCFRaACBxMC7uEGc6lQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"lightningcss": "^1.33.0", "lightningcss": "^1.33.0",
"picomatch": "^4.0.5", "picomatch": "^4.0.5",

View File

@ -17,6 +17,7 @@
"@types/alpinejs": "^3.13.11", "@types/alpinejs": "^3.13.11",
"alpinejs": "^3.16.1", "alpinejs": "^3.16.1",
"astro": "^7.1.6", "astro": "^7.1.6",
"swiper": "^11.2.10",
"tailwindcss": "^4.3.3" "tailwindcss": "^4.3.3"
} }
} }

View File

@ -66,15 +66,21 @@ async function request<T>(path: string, params?: URLSearchParams): Promise<T> {
} }
// 侧边栏品牌清单(含 article_count 计数) // 侧边栏品牌清单(含 article_count 计数)
// 支持 keyword(模糊搜索 name/show_name)/ letter(A-Z 或 OTHER=中文)/ featured(精选)参数,
// 用于品牌筛选弹窗的「搜索 + A-Z 跳转 + 热门推荐」三种按需加载,避免一次拉全量。
export async function getBrands(opts?: { export async function getBrands(opts?: {
size?: number size?: number
only_with_articles?: number only_with_articles?: number
featured?: number featured?: number
keyword?: string
letter?: string
}): Promise<BrandEntry[]> { }): Promise<BrandEntry[]> {
const qs = new URLSearchParams() const qs = new URLSearchParams()
if (opts?.size) qs.set("size", String(opts.size)) if (opts?.size) qs.set("size", String(opts.size))
if (opts?.only_with_articles) qs.set("only_with_articles", String(opts.only_with_articles)) if (opts?.only_with_articles != null) qs.set("only_with_articles", String(opts.only_with_articles))
if (opts?.featured) qs.set("featured", String(opts.featured)) if (opts?.featured != null) qs.set("featured", String(opts.featured))
if (opts?.keyword) qs.set("keyword", opts.keyword)
if (opts?.letter) qs.set("letter", opts.letter)
const j = await request<{ data?: any[] }>("/public/brands", qs) const j = await request<{ data?: any[] }>("/public/brands", qs)
return (j.data ?? []).map((b: any) => ({ return (j.data ?? []).map((b: any) => ({
id: Number(b.id), id: Number(b.id),

View File

@ -186,6 +186,7 @@ export function loadArticlesWithImages(size = 40): Promise<ArticleCard[]> {
if (!API_BASE) return [] if (!API_BASE) return []
try { try {
const res = await fetch(`${API_BASE}/api/public/articles?page=1&size=${size}&with_images=5`) const res = await fetch(`${API_BASE}/api/public/articles?page=1&size=${size}&with_images=5`)
console.log(res)
if (!res.ok) return [] if (!res.ok) return []
const json: any = await res.json() const json: any = await res.json()
const items: RawArticle[] = json?.data ?? [] const items: RawArticle[] = json?.data ?? []

View File

@ -6,68 +6,20 @@ import { getShows, seriesLabel } from "../lib/data"
// 默认 tab(Shows)走 SSR:构建期从接口预取真实数据与多图,首屏立即可见。 // 默认 tab(Shows)走 SSR:构建期从接口预取真实数据与多图,首屏立即可见。
// 其余 tab(Backstage / Events / Street)点击时才由前端请求接口动态渲染,避免一次性拉全量拖慢首屏。 // 其余 tab(Backstage / Events / Street)点击时才由前端请求接口动态渲染,避免一次性拉全量拖慢首屏。
const shows = await getShows(20) const shows = await getShows(20)
// 精选轮播:取前 5 场秀,做成「左图右文」卡片式轮播(Featured Carousel) // 横向滚动走秀画廊:扁平化所有 show 的图片(cover + gallery 去重),
const slides = shows.slice(0, 5) // 每张卡片 = 一个文章(show)的封面,不展开 gallery,避免同一文章重复出现。
// 右侧品牌栏:跨分页拉取「与轮播不同品牌」的其他精选品牌,按品牌去重取 2 个 type GalleryItem = { src: string; brand: string; articleId: number; year: number; title: string; collection_type?: string }
const API_BASE = (import.meta.env.PUBLIC_API_BASE || "").replace(/\/$/, "") const galleryItems: GalleryItem[] = []
const slideBrands = new Set(slides.map(s => s.brand)) for (const s of shows) {
const rail: typeof shows = [] if (!s.img) continue
const railSeen = new Set<string>() galleryItems.push({
if (API_BASE) { src: s.img,
for (let pg = 1; pg <= 6 && rail.length < 6; pg++) { brand: s.brand,
try { articleId: s.id,
const rr = await fetch(`${API_BASE}/api/public/articles?page=${pg}&size=40&with_images=5`) year: s.year,
if (!rr.ok) break title: s.title,
const rj = await rr.json() collection_type: s.collection_type,
const items: any[] = rj?.data ?? [] })
for (const r of items) {
const b = r.brand_name || "Unknown"
if (slideBrands.has(b) || railSeen.has(b)) continue
railSeen.add(b)
rail.push({
id: r.id, brand: b, title: r.title || "",
collection_type: r.collection_type, season_code: r.season_code,
year: Number(r.year) || 0,
count: r.image_count || 0, img: r.cover || "",
gallery: (r.images || []).map((im: any) => im.image || "").filter(Boolean).slice(0, 5),
})
if (rail.length >= 2) break
}
} catch {}
}
}
const railHot = [...rail].sort((a, b) => (b.count || 0) - (a.count || 0))
// 最新取前 2;最热取 count 最高的 2;若两者完全相同(库内品牌少),改取最新之外的其它品牌,保证切换有可见差异
const latestSet = rail.slice(0, 2)
let hotSet = railHot.slice(0, 2)
if (hotSet.length === 2 && latestSet.length === 2 &&
hotSet[0].brand === latestSet[0].brand && hotSet[1].brand === latestSet[1].brand) {
const lb = new Set(latestSet.map((r) => r.brand))
const others = rail.filter((r) => !lb.has(r.brand)).slice(0, 2)
if (others.length === 2) hotSet = others
}
// 拼装右侧卡片的文案字段
function railEyebrow(r: typeof shows[number]) {
const m = (r.title || "").match(/(\d{4}).*?(Spring|Summer|Fall|Autumn|Winter|Resort|Pre-Fall|Cruise)/i)
const season = m ? `${m[1]} ${m[2]}` : (r.season_code || "")
const yr = r.year ? `${r.year}` : ""
return [yr || (m ? m[1] : ""), season].filter(Boolean).join(" · ").toUpperCase() || "Featured Runway"
}
function railTagline(r: typeof shows[number]) {
return `${seriesLabel(r.collection_type)}${r.season_code ? " · " + r.season_code : ""}`
}
function railDesc(r: typeof shows[number]) {
const yr = r.year || ((r.title || "").match(/\b(19|20)\d{2}\b/) || [""])[0]
const seasonTxt = (() => {
const m = (r.title || "").match(/(Spring|Summer|Fall|Autumn|Winter|Resort|Pre-Fall|Cruise)/i)
if (m) {
const map: Record<string, string> = { Spring: "春", Summer: "夏", Fall: "秋冬", Autumn: "秋冬", Winter: "冬", Resort: "度假", "Pre-Fall": "早秋", Cruise: "度假" }
return (map[m[1].toLowerCase().replace(/(^|\s)\w/g, (s) => s.toUpperCase())] || map[m[1]] || "") + (yr || "")
}
return yr ? `${yr} 发布` : ""
})()
return { seasonTxt: seasonTxt || (yr ? `${yr} 发布` : "") }
} }
--- ---
@ -104,150 +56,39 @@ function railDesc(r: typeof shows[number]) {
<a href="/shows" class="sec-more">全部走秀 →</a> <a href="/shows" class="sec-more">全部走秀 →</a>
</div> </div>
<!-- 系列快速筛选(前端本地过滤,data-ct 对应 collection_type) --> <!-- 横向滚动画廊:左右滑动浏览;图片加载前骨架占位防偏移 -->
<div class="seriesbar" id="seriesbar"> {galleryItems.length > 0 && (
<button class="schip on" data-ct="">全部</button> <section class="hs-section">
<button class="schip" data-ct="rtw">成衣</button> <div class="hs-stage">
<button class="schip" data-ct="menswear">男装</button> <div class="swiper hs-swiper" id="hsSwiper">
<button class="schip" data-ct="couture">高定</button> <div class="swiper-wrapper">
<button class="schip" data-ct="resort">度假</button> {galleryItems.map((g, i) => (
<button class="schip" data-ct="pre_fall">早秋</button> <div class="swiper-slide hs-slide">
</div> <a class="hs-item" href={`/article?id=${g.articleId}`} data-idx={i}>
<div class="hs-img-box">
<!-- 精选:左轮播(图上文下)+ 右品牌栏(横版卡片 ×2) --> <div class="hs-skel" aria-hidden="true"></div>
{slides.length > 0 && ( <span class="hs-spin" aria-hidden="true">
<section class="hero-carousel" id="heroCarousel" data-count={slides.length}> <svg viewBox="0 0 24 24" width="22" height="22"><circle cx="12" cy="12" r="9" fill="none" stroke="currentColor" stroke-width="1.5" stroke-dasharray="40 22" class="hs-ring" /></svg>
<div class="hc-stage"> </span>
<div class="hc-rotator"> <img src={g.src} alt={g.brand} loading={i < 4 ? "eager" : "lazy"} decoding="async" />
{slides.map((s, i) => ( <span class="hs-caption">
<div class="hc-slide" data-index={i} data-ct={s.collection_type || "rtw"} style={i === 0 ? "" : "display:none"}> <span class="hs-cap-brand">{g.brand}</span>
<a class="hc-media" href={`/article?id=${s.id}`} aria-label={s.brand}> <span class="hs-cap-meta">{g.year ? g.year : ""}{g.collection_type ? " · " + seriesLabel(g.collection_type) : ""}</span>
<img src={s.gallery[0] || s.img} alt={s.brand} class="hc-img" loading={i === 0 ? "eager" : "lazy"} /> </span>
<span class="prod-tag tl">Featured</span>
</a>
<div class="hc-text">
<div class="hc-info">
<span class="hc-eyebrow">{((s.title || "").match(/(\d{4}).*?(Spring|Summer|Fall|Autumn|Winter|Resort|Pre-Fall|Cruise)/i) || []).slice(1,3).join(" ") || s.season_code || "Featured Runway"}</span>
<div class="hc-head">
<h2 class="hc-brand">{s.brand}</h2>
<p class="hc-tagline">{seriesLabel(s.collection_type)}{s.season_code ? " · " + s.season_code : ""}</p>
</div>
<div class="hc-bar-row">
<a class="hc-bar" href={`/article?id=${s.id}`}>
<span>Brand Analysis</span>
<span class="hc-bar-plus">+</span>
</a>
<span class="hc-meta-count">{s.count} Look{s.count > 1 ? "s" : ""}</span>
</div>
</div>
<div class="hc-controls">
<span class="hc-index"><span class="hc-cur">{String(i + 1).padStart(2, "0")}</span> / {String(slides.length).padStart(2, "0")}</span>
<div class="hc-nav">
<button class="hc-arrow hc-prev" type="button" aria-label="上一张">←</button>
<div class="hc-dots">
{slides.map((_, di) => (
<button class="hc-dot" type="button" data-i={di} aria-label={`第 ${di + 1} 张`} class:list={["hc-dot", di === 0 && "on"]}></button>
))}
</div> </div>
<button class="hc-arrow hc-next" type="button" aria-label="下一张">→</button>
</div>
</div>
</div>
</div>
))}
</div>
{rail.length > 0 && (
<aside class="hc-rail">
<div class="hc-rail-head">
<div class="hc-rail-tabs">
<button type="button" class="on" data-sort="latest">最新</button>
<button type="button" data-sort="hot">最热</button>
</div>
</div>
<div class="hc-rail-list" id="railList">
<div class="rail-set" data-set="latest">
{latestSet.map((r) => (
<article class="rail-card">
<div class="rail-text">
<span class="rail-eyebrow">{railEyebrow(r)}</span>
<h3 class="rail-brand">{r.brand}</h3>
<p class="rail-tag">{railTagline(r)}</p>
<a class="rail-bar" href={`/article?id=${r.id}`}>
<span>Brand Analysis</span>
<span class="rail-bar-plus">+</span>
</a>
<div class="rail-desc">{r.brand}:{railDesc(r).seasonTxt || "近期新作"} <span class="rail-dot">·</span> {r.count} 张</div>
</div>
<a class="rail-thumb" href={`/article?id=${r.id}`} aria-label={r.brand}>
<img src={r.gallery[0] || r.img} alt={r.brand} loading="lazy" />
</a> </a>
</article> </div>
))}
</div>
<div class="rail-set" data-set="hot" style="display:none">
{hotSet.map((r) => (
<article class="rail-card">
<div class="rail-text">
<span class="rail-eyebrow">{railEyebrow(r)}</span>
<h3 class="rail-brand">{r.brand}</h3>
<p class="rail-tag">{railTagline(r)}</p>
<a class="rail-bar" href={`/article?id=${r.id}`}>
<span>Brand Analysis</span>
<span class="rail-bar-plus">+</span>
</a>
<div class="rail-desc">{r.brand}:{railDesc(r).seasonTxt || "近期新作"} <span class="rail-dot">·</span> {r.count} 张</div>
</div>
<a class="rail-thumb" href={`/article?id=${r.id}`} aria-label={r.brand}>
<img src={r.gallery[0] || r.img} alt={r.brand} loading="lazy" />
</a>
</article>
))} ))}
</div> </div>
</div> </div>
</aside> <div class="hs-controls flex justify-between items-center text-[13px] font-bold">
)} <div class="hs-arrow hs-prev cursor-pointer hover:opacity-60 transition-opacity duration-200" role="button" tabindex="0">Previous</div>
<div class="hs-arrow hs-next cursor-pointer hover:opacity-60 transition-opacity duration-200" role="button" tabindex="0">Next</div>
</div>
</div> </div>
</section> </section>
)} )}
<!-- 卡片网格(轮播之外的剩余 shows) -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6" id="showgrid">
{shows.slice(5).map((s) => (
<article class="prod-card group" data-article-id={s.id} data-ct={s.collection_type || "rtw"}>
<div class="prod-main">
<img src={s.gallery[0] || s.img} alt={s.brand} class="prod-main-img" loading="lazy" />
<span class="prod-tag tl">Runway</span>
{s.count > 1 && <span class="prod-tag tr">{s.count} Pics</span>}
<a href={`/article?id=${s.id}`} class="prod-viewall">查看全部 {s.count} 张 →</a>
</div>
<div class="prod-thumbs">
{( s.gallery.length ? s.gallery : [s.img] ).map((g, gi) => (
<button class="prod-thumb" type="button" aria-label={`查看第 ${gi + 1} 张`}>
<img src={g} alt="" loading="lazy" />
</button>
))}
{s.count > (s.gallery.length || 1) && (
<a href={`/article?id=${s.id}`} class="prod-more">+{s.count - (s.gallery.length || 1)}</a>
)}
</div>
<div class="prod-info">
<div class="prod-name">{s.brand}</div>
<div class="prod-meta">
<span class="prod-chip">{seriesLabel(s.collection_type)}</span>
{s.season_code && <span class="prod-chip">{s.season_code}</span>}
</div>
<div class="prod-sub">{s.title}</div>
<div class="prod-foot">
<a href={`/article?id=${s.id}`} class="prod-cta">查看 {s.count} 张 →</a>
</div>
</div>
</article>
))}
</div>
<div class="showmore-wrap">
<button id="showmore" class="showmore" type="button">查看更多走秀 ↓</button>
</div>
<div class="empty" id="showempty" hidden>该系列暂无可展示的走秀</div>
</div> </div>
<!-- 灵感 面板(暂未上线) --> <!-- 灵感 面板(暂未上线) -->
@ -265,12 +106,6 @@ function railDesc(r: typeof shows[number]) {
<ComingSoon module="街拍" moduleZh="Street" note="街拍板块正在筹备中,敬请期待。你可以先浏览已上线的走秀档案。" /> <ComingSoon module="街拍" moduleZh="Street" note="街拍板块正在筹备中,敬请期待。你可以先浏览已上线的走秀档案。" />
</div> </div>
</main> </main>
<!-- 底部/两侧切换按钮(保持你原来的) -->
<div class="flex justify-between items-center text-[13px] font-bold mt-12">
<div class="cursor-pointer hover:opacity-60 transition-opacity duration-200">Previous</div>
<div class="cursor-pointer hover:opacity-60 transition-opacity duration-200">Next</div>
</div>
</Layout> </Layout>
<style is:global> <style is:global>
@ -296,83 +131,8 @@ function railDesc(r: typeof shows[number]) {
to { opacity: 1; transform: none; } to { opacity: 1; transform: none; }
} }
/* 卡片悬停上浮 + 阴影(恢复此前被误删的规则) */
.prod-card:hover { @apply -translate-y-1 shadow-[0_16px_38px_rgba(0,0,0,0.13)]; }
/* 主图容器:固定 3:4 比例 + 骨架占位,图片未加载时尺寸恒定、显示加载动画 */
.prod-main {
@apply relative w-full aspect-[3/4] overflow-hidden bg-[#f0f0f0];
background-image: linear-gradient(100deg, #f0f0f0 30%, #e6e6e6 50%, #f0f0f0 70%);
background-size: 200% 100%;
animation: img-shimmer 1.3s ease-in-out infinite;
}
.prod-main.is-loaded { animation: none; }
/* 主图:绝对铺满容器并裁切,保证不同分辨率图展示一致、容器高度恒定 */
.prod-main-img {
@apply absolute inset-0 w-full h-full object-cover block origin-center;
transition: transform .55s ease, opacity .22s ease;
}
/* 缩略图容器:固定 3:4 比例 + 骨架占位 */
.prod-thumb {
@apply bg-[#f0f0f0];
background-image: linear-gradient(100deg, #f0f0f0 30%, #e6e6e6 50%, #f0f0f0 70%);
background-size: 200% 100%;
animation: img-shimmer 1.3s ease-in-out infinite;
}
.prod-thumb.is-loaded { animation: none; }
@keyframes img-shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }
/* (.js 门控的淡入与 is-error 占位规则已移至下方 <style is:global>,避免组件作用域破坏 .js 选择器) */ /* (.js 门控的淡入与 is-error 占位规则已移至下方 <style is:global>,避免组件作用域破坏 .js 选择器) */
/* ===== 卡片 + 缩略图轮播 混合布局 ===== */
:root { --brand: #cf5a44; } :root { --brand: #cf5a44; }
.prod-card {
@apply flex flex-col bg-white border border-[#ececec] rounded-[6px] overflow-hidden transition-[transform,box-shadow] duration-300 will-change-transform;
}
.prod-card:hover .prod-main-img { @apply scale-105; }
.prod-card:hover .prod-main { @apply shadow-[0_12px_26px_rgba(0,0,0,0.18)]; }
.prod-tag {
@apply absolute z-[2] font-mono text-[10px] uppercase tracking-[.18em] px-[8px] py-[4px];
}
.prod-tag.tl { @apply top-[10px] left-[10px] bg-white text-black; }
.prod-tag.tr { @apply top-[10px] right-[10px] bg-black text-white flex gap-[5px] items-center; }
.prod-tag.tr::before { content: ""; @apply w-[6px] h-[6px] rounded-full bg-white; }
.prod-thumbs {
@apply flex gap-2 px-[10px] pt-[10px] pb-[6px] overflow-x-auto;
scrollbar-width: none; -ms-overflow-style: none;
}
.prod-thumbs::-webkit-scrollbar { display: none; }
.prod-thumb {
@apply flex-none w-[78px] aspect-[3/4] h-auto p-0 m-0 border-2 border-transparent bg-[#f2f2f2] cursor-pointer rounded-[4px] overflow-hidden opacity-75 transition-[border-color,opacity,transform] duration-200;
}
.prod-thumb img { @apply w-full h-full object-cover block; }
.prod-thumb:hover { @apply opacity-100; }
.prod-thumb.active { @apply border-[var(--brand)] opacity-100; }
.prod-info { @apply px-[12px] pt-[8px] pb-[14px] flex flex-col gap-[6px] flex-1; }
.prod-name { @apply text-[16px] font-extrabold leading-[1.2] text-[#111]; }
.prod-sub { @apply text-[11px] uppercase tracking-[.14em] text-[#999] font-mono; }
.prod-tags { @apply flex gap-[6px] flex-wrap; }
.prod-badge {
@apply text-[10px] uppercase tracking-[.08em] px-[8px] py-[3px] border border-[#ddd] rounded-full text-[#666] bg-white;
}
.prod-badge.new { @apply bg-[#111] text-white border-[#111]; }
.prod-badge.hot { @apply bg-[var(--brand)] text-white border-[var(--brand)]; }
.prod-foot { @apply flex items-center justify-between mt-auto pt-[4px]; }
.prod-price { @apply text-[15px] font-extrabold tracking-[.02em] text-[#111]; }
.prod-cta { @apply text-[11px] uppercase tracking-[.14em] font-bold no-underline; color: var(--brand); }
.prod-card:hover .prod-cta { @apply underline; }
/* 缩略图条末尾的"更多"入口:提示还有更多图片,点击进入详情看全部 */
.prod-more {
@apply flex-none w-[78px] aspect-[3/4] h-auto flex items-center justify-center bg-[var(--brand)] text-white no-underline rounded-[4px] text-[13px] font-extrabold tracking-[.02em] transition-[filter,transform] duration-200;
}
.prod-more:hover { @apply brightness-[1.08] -translate-y-[2px]; }
/* 主图悬停时浮现"查看全部 N 张"浮层(桌面 hover 生效,移动端不拦截 pinch) */
.prod-viewall {
@apply absolute inset-0 z-[3] flex items-end justify-center pb-[16px] text-white text-[12px] font-bold tracking-[.14em] uppercase no-underline opacity-0 pointer-events-none transition-opacity duration-[250ms] ease-[ease];
background: linear-gradient(to top, rgba(0,0,0,.6), rgba(0,0,0,0) 55%);
}
.prod-card:hover .prod-viewall { @apply opacity-100 pointer-events-auto; }
/* 标签页激活态:品牌色下划线动画 */ /* 标签页激活态:品牌色下划线动画 */
.tab { @apply relative pb-[6px]; } .tab { @apply relative pb-[6px]; }
@ -403,88 +163,144 @@ function railDesc(r: typeof shows[number]) {
.sec-more { @apply text-[12px] uppercase tracking-[.14em] font-bold no-underline text-[#111] transition-opacity duration-200; } .sec-more { @apply text-[12px] uppercase tracking-[.14em] font-bold no-underline text-[#111] transition-opacity duration-200; }
.sec-more:hover { @apply opacity-60; } .sec-more:hover { @apply opacity-60; }
/* 系列快速筛选 */ /* 横向滚动画廊(Runway Gallery):水平 flex 容器,每张图按统一卡片尺寸展示、
.seriesbar { @apply flex items-center gap-2 overflow-x-auto mb-7 pb-1; scrollbar-width: none; -ms-overflow-style: none; } 左右滑动浏览。图片用 object-fit:cover 适配卡片框(不再因原图宽高比过宽);
.seriesbar::-webkit-scrollbar { display: none; } 加载前骨架 shimmer 占位,加载完图片淡入、骨架淡出。 */
.schip { .hs-section { @apply mb-9; }
@apply flex-none font-mono text-[11px] uppercase tracking-[.12em] px-[14px] py-[6px] rounded-full border border-[#ddd] bg-white text-[#666] cursor-pointer transition-colors duration-200; .hs-head {
@apply flex items-end justify-between gap-4 pb-4 mb-4 border-b border-[#ececec];
} }
.schip:hover { @apply text-[#111] border-[#111]; } .hs-title { @apply font-serif text-[28px] leading-none font-extrabold text-[#111] m-0 tracking-[-.01em]; }
.schip.on { @apply bg-[#111] text-white border-[#111]; } .hs-sub { @apply font-mono text-[12px] uppercase tracking-[.14em] text-[#999] mt-2 mb-0; }
.hs-stage { @apply relative; }
.hs-controls {
position: absolute;
top: 50%;
left: 0;
right: 0;
transform: translateY(-50%);
z-index: 6;
padding: 0 8px;
pointer-events: none; /* 容器不挡拖拽,仅按钮可点 */
mix-blend-mode: difference; /* 随背景反相,始终可见 */
color: #fff; /* 白色源色经 difference 后随底图反相 */
}
.hs-controls .hs-arrow { pointer-events: auto; }
.hs-arrow { @apply select-none; } /* 仅保留 Swiper 绑定所需的类,视觉完全由内联类名控制 */
.hs-arrow.swiper-button-disabled { pointer-events: none; } /* 禁用时保持原色,仅不可点击 */
/* 精选 feat group:橄榄绿 editorial 重排(保留左大图轮播 + 右品牌栏) */ /* Swiper 容器:高度恒定(内容区固定);宽度由 slide 自适应撑开 */
.hero-carousel { @apply relative mb-9; } .hs-swiper {
.hc-stage { @apply relative flex flex-col; } width: 100%;
@media (min-width: 768px) { .hc-stage { flex-direction: row; gap: 32px; align-items: stretch; } } height: clamp(500px, 80vh, 900px); /* 固定高度,不随图片尺寸变化(已调高) */
.hc-rotator { display: flex; flex-direction: column; min-height: 0; } padding: 0 4px;
@media (min-width: 768px) { .hc-rotator { flex: 1 1 0%; min-height: 560px; } }
.hc-slide { display: flex; flex-direction: column; flex: 1 1 0%; min-height: 0; gap: 20px; }
.hc-media {
position: relative; flex: 1 1 0%; min-height: 320px; width: 100%; overflow: hidden;
border-radius: 2px; background: #f0f0f0; display: block; text-decoration: none;
} }
@media (min-width: 768px) { .hc-media { min-height: 480px; } } .hs-swiper .swiper-wrapper { align-items: center; } /* 卡片在固定高度的容器内垂直居中 */
.hc-img { @apply absolute inset-0 w-full h-full object-cover block origin-center transition-transform duration-[600ms] ease-out; }
.hc-media:hover .hc-img { @apply scale-[1.03]; }
.hc-text { @apply flex flex-col gap-[18px] flex-shrink-0; }
.hc-info { @apply flex flex-col gap-3; }
.hc-eyebrow { @apply block font-mono text-[11px] uppercase tracking-[.24em] text-[#8a8455]; }
.hc-head { @apply flex items-baseline gap-4 flex-wrap; }
.hc-brand { @apply font-serif font-extrabold text-[#14110d] m-0 tracking-[-.02em] leading-[1.0]; font-size: 34px; }
@media (min-width: 768px) { .hc-brand { font-size: 46px; } }
.hc-tagline { @apply font-mono text-[11px] uppercase tracking-[.18em] text-[#8a8455] m-0; }
.hc-bar-row { @apply flex items-center gap-4 pt-[14px] border-t border-[#e6e2d8]; }
.hc-bar {
@apply inline-flex items-center justify-between gap-4 px-4 py-[10px] text-white font-mono text-[10px] uppercase tracking-[.2em] font-bold no-underline border transition-colors duration-200;
background: #8a8455; border-color: #8a8455;
}
.hc-bar:hover { @apply no-underline; background: #fff; color: #8a8455; }
.hc-bar-plus { @apply text-[16px] leading-none font-light; }
.hc-meta-count { @apply font-mono text-[11px] uppercase tracking-[.16em] text-[#9a937f]; }
.hc-controls { @apply flex items-center justify-between pt-[14px] border-t border-[#e6e2d8]; }
.hc-index { @apply font-mono text-[12px] tracking-[.2em] text-[#9a937f]; }
.hc-index .hc-cur { @apply font-bold; color: #8a8455; }
.hc-nav { @apply flex items-center gap-4; }
.hc-arrow {
@apply w-9 h-9 rounded-full flex items-center justify-center cursor-pointer bg-white no-underline select-none transition-colors duration-200;
border: 1px solid #8a8455; color: #8a8455;
}
.hc-arrow:hover { background: #8a8455; color: #fff; }
.hc-dots { @apply flex gap-2 items-center; }
.hc-dot { @apply w-2 h-2 rounded-full border-0 p-0 cursor-pointer transition-colors duration-200; background: #dcd8cc; }
.hc-dot.on { background: #8a8455; }
/* 右侧品牌栏(横版卡片,图左文右,×2 等高) */ /* 每张 slide:尺寸完全由内部图片决定(slidesPerView:"auto" 取自身宽) */
.hc-rail { @apply flex flex-col min-h-0; } .hs-swiper .hs-slide {
@media (min-width: 768px) { .hc-rail { width: 34%; flex-shrink: 0; border-left: 1px solid #e6e2d8; padding-left: 24px; } } width: auto;
.hc-rail-head { @apply mb-4; } height: auto;
.hc-rail-tabs { @apply flex gap-3; }
.hc-rail-tabs button { @apply font-mono text-[11px] uppercase tracking-[.14em] bg-none border-0 p-0 cursor-pointer transition-colors duration-200; color: #b8b2a2; }
.hc-rail-tabs button.on { @apply font-bold pb-[2px] border-b; color: #8a8455; border-color: #8a8455; }
.hc-rail-list { @apply flex flex-col flex-1 overflow-y-auto min-h-0; scrollbar-width: thin; scrollbar-color: #dcd8cc #fff; }
.hc-rail-list::-webkit-scrollbar { @apply w-[6px]; }
.hc-rail-list::-webkit-scrollbar-thumb { @apply bg-[#dcd8cc] rounded-full; }
.rail-set { @apply list-none m-0 p-0 flex flex-col flex-1 min-h-0 gap-4; }
.rail-card { @apply flex flex-row-reverse flex-1 min-h-0 gap-3 items-stretch; }
.rail-text { @apply w-[42%] flex flex-col justify-between min-w-0 py-[2px]; }
.rail-eyebrow { @apply block font-mono text-[10px] uppercase tracking-[.16em] text-[#8a8455]; }
.rail-brand { @apply font-serif font-extrabold text-[#14110d] m-0 mt-[2px] leading-[1.02] tracking-[-.01em]; font-size: 22px; }
.rail-tag { @apply font-mono text-[10px] uppercase tracking-[.12em] m-0 mt-[2px]; color: #9a937f; }
.rail-bar {
@apply flex items-center justify-between w-full mt-3 px-3 py-[8px] text-white font-mono text-[10px] uppercase tracking-[.16em] font-bold no-underline border transition-colors duration-200;
background: #8a8455; border-color: #8a8455;
} }
.rail-bar:hover { @apply no-underline; background: #fff; color: #8a8455; }
.rail-bar-plus { @apply text-[14px] leading-none font-light; } .hs-item {
.rail-desc { @apply font-mono text-[10px] mt-2 leading-[1.5] tracking-[.02em]; color: #9a937f; } display: block;
.rail-dot { @apply mx-[3px]; color: #ccc; } height: auto; /* 高度交给图片框,按图片比例自适应 */
.rail-thumb { width: max-content; /* 宽度收缩到图片自身宽度 */
position: relative; flex: 1; min-height: 160px; border-radius: 2px; overflow: hidden; text-decoration: none;
background: #f0f0f0; display: block; text-decoration: none; color: inherit;
}
.hs-img-box {
position: relative;
display: block;
/* 加载前默认占位:统一高度的 3:2 横向占位,避免塌缩;加载后由 JS 按真实比例写入精确 px */
height: clamp(500px, 80vh, 900px);
/* aspect-ratio: 3 / 2; */
background: #f0f0f0;
overflow: hidden;
}
/* 图片:填满图片框;图片框的比例由 JS 按真实宽高比设定,故完整显示、不裁切、不变形 */
.hs-img-box > img {
width: 100%;
height: 100%;
display: block;
position: relative;
z-index: 1;
opacity: 0;
transition: opacity 0.4s ease;
}
.hs-img-box.is-loaded > img { opacity: 1; }
/* 骨架占位:绝对覆盖 img-box,shimmer 动画;加载完淡出(与图片淡入同步) */
.hs-skel {
position: absolute;
inset: 0;
z-index: 0;
background-image: linear-gradient(100deg, #f0f0f0 30%, #e6e6e6 50%, #f0f0f0 70%);
background-size: 200% 100%;
animation: hs-shimmer 1.3s ease-in-out infinite;
transition: opacity 0.35s ease;
}
.hs-img-box.is-loaded .hs-skel { opacity: 0; }
/* 加载中旋转环(细线灰调) */
.hs-spin {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
color: #bdbdbd;
pointer-events: none;
z-index: 2;
transition: opacity 0.3s ease;
}
.hs-img-box.is-loaded .hs-spin { opacity: 0; }
.hs-spin svg { display: block; }
.hs-ring { transform-origin: 12px 12px; animation: hs-rotate 0.8s linear infinite; }
@keyframes hs-rotate { to { transform: rotate(360deg); } }
@keyframes hs-shimmer {
from { background-position: 200% 0; }
to { background-position: -200% 0; }
}
/* 悬停浮现品牌标签 */
.hs-caption {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 28px 16px 12px;
background: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent);
color: white;
display: flex;
justify-content: space-between;
align-items: flex-end;
gap: 12px;
opacity: 0;
transition: opacity 0.25s ease;
z-index: 3;
font-family: monospace;
pointer-events: none;
}
.hs-item:hover .hs-caption { opacity: 1; }
.hs-cap-brand {
font-size: 13px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
}
.hs-cap-meta {
font-size: 11px;
opacity: 0.85;
letter-spacing: 0.12em;
}
@media (max-width: 768px) {
.hs-swiper { height: clamp(340px, 56vh, 540px); }
.hs-img-box { height: clamp(340px, 56vh, 540px); }
.hs-head { flex-direction: column; align-items: flex-start; gap: 12px; }
} }
@media (min-width: 768px) { .rail-thumb { min-height: 0; } }
.rail-thumb img { @apply absolute inset-0 w-full h-full object-cover transition-transform duration-[600ms] ease-out; }
.rail-thumb:hover img { @apply scale-[1.04]; }
/* 卡片上的系列 / 季节标签 */ /* 卡片上的系列 / 季节标签 */
.prod-meta { @apply flex gap-2 flex-wrap mt-1; } .prod-meta { @apply flex gap-2 flex-wrap mt-1; }
@ -493,28 +309,24 @@ function railDesc(r: typeof shows[number]) {
} }
.empty { @apply text-center text-[13px] text-[#999] py-16 font-mono uppercase tracking-[.12em]; } .empty { @apply text-center text-[13px] text-[#999] py-16 font-mono uppercase tracking-[.12em]; }
/* 查看更多(首页走秀栏内联分页) */ /* (图片堆方案已移除,改由横向画廊展示多图) */
.showmore-wrap { @apply flex justify-center mt-10; }
.showmore {
@apply font-mono uppercase tracking-[.16em] text-[12px] px-[28px] py-[14px] border border-[#111] bg-white text-[#111] rounded-full cursor-pointer transition-colors duration-200;
}
.showmore:hover { @apply bg-[#111] text-white; }
.showmore[disabled] { @apply opacity-40 cursor-default; }
/* (图片堆方案已移除,改由卡片内缩略图条展示多图) */
</style> </style>
<style is:global> <style is:global>
/* JS 启用时主图/缩略图先透明,加载完成淡入;失败保留占位骨架(is:global 避免组件作用域误伤 .js 选择器) */ /* JS 启用时画廊图片先透明,加载完成淡入;失败保留占位骨架(is:global 避免组件作用域误伤 .js 选择器) */
.js .prod-main-img { @apply opacity-0; } .js .hs-img-box > img { opacity: 0; }
.js .prod-main-img.is-loaded { @apply opacity-100; } .js .hs-img-box.is-loaded > img { opacity: 1; }
.js .prod-thumb img { @apply opacity-0; } .hs-img-box > img.is-error { opacity: 0 !important; }
.js .prod-thumb img.is-loaded { @apply opacity-100; }
.prod-main-img.is-error, .prod-thumb img.is-error { opacity: 0 !important; }
</style> </style>
<script> <script>
import Swiper from "swiper";
import { Navigation, FreeMode, Keyboard } from "swiper/modules";
import "swiper/css";
import "swiper/css/navigation";
import "swiper/css/free-mode";
// 标签页交互:默认 Shows 为 SSR;点击其它标签时按需请求接口渲染(懒加载) // 标签页交互:默认 Shows 为 SSR;点击其它标签时按需请求接口渲染(懒加载)
const API_BASE = (import.meta.env.PUBLIC_API_BASE || "http://localhost:8090").replace(/\/$/, ""); const API_BASE = (import.meta.env.PUBLIC_API_BASE || "http://localhost:8090").replace(/\/$/, "");
const tabs = Array.from(document.querySelectorAll<HTMLElement>("#tabbar .tab")); const tabs = Array.from(document.querySelectorAll<HTMLElement>("#tabbar .tab"));
@ -599,59 +411,59 @@ function railDesc(r: typeof shows[number]) {
</article>`; </article>`;
} }
function initCardInteractions(root: ParentNode) { // function initCardInteractions(root: ParentNode) {
root.querySelectorAll<HTMLElement>(".prod-card").forEach((card) => { // root.querySelectorAll<HTMLElement>(".prod-card").forEach((card) => {
const main = card.querySelector<HTMLImageElement>(".prod-main-img"); // const main = card.querySelector<HTMLImageElement>(".prod-main-img");
const thumbs = Array.from(card.querySelectorAll<HTMLElement>(".prod-thumb")); // const thumbs = Array.from(card.querySelectorAll<HTMLElement>(".prod-thumb"));
if (!main || !thumbs.length) return; // if (!main || !thumbs.length) return;
thumbs[0].classList.add("active"); // thumbs[0].classList.add("active");
thumbs.forEach((t) => { // thumbs.forEach((t) => {
t.addEventListener("click", (e) => { // t.addEventListener("click", (e) => {
e.preventDefault(); // e.preventDefault();
e.stopPropagation(); // e.stopPropagation();
const img = t.querySelector("img"); // const img = t.querySelector("img");
const src = img?.getAttribute("src") || ""; // const src = img?.getAttribute("src") || "";
if (!src || main.getAttribute("src") === src) return; // if (!src || main.getAttribute("src") === src) return;
main.classList.remove("is-loaded"); // main.classList.remove("is-loaded");
main.parentElement?.classList.remove("is-loaded"); // main.parentElement?.classList.remove("is-loaded");
main.style.opacity = "0"; // main.style.opacity = "0";
window.setTimeout(() => { // window.setTimeout(() => {
main.setAttribute("src", src); // main.setAttribute("src", src);
main.style.opacity = "1"; // main.style.opacity = "1";
}, 180); // }, 180);
thumbs.forEach((x) => x.classList.remove("active")); // thumbs.forEach((x) => x.classList.remove("active"));
t.classList.add("active"); // t.classList.add("active");
}); // });
}); // });
main.addEventListener("load", () => { // main.addEventListener("load", () => {
main.classList.add("is-loaded"); // main.classList.add("is-loaded");
main.parentElement?.classList.add("is-loaded"); // main.parentElement?.classList.add("is-loaded");
}); // });
enablePinch(card.querySelector<HTMLElement>(".prod-main")); // enablePinch(card.querySelector<HTMLElement>(".prod-main"));
}); // });
root.querySelectorAll<HTMLImageElement>(".prod-main-img, .prod-thumb img").forEach(watchImg); // root.querySelectorAll<HTMLImageElement>(".prod-main-img, .prod-thumb img").forEach(watchImg);
} // }
async function loadPanel(name: string) { // async function loadPanel(name: string) {
const panel = panels.find((p) => p.getAttribute("data-panel") === name); // const panel = panels.find((p) => p.getAttribute("data-panel") === name);
if (!panel || panel.getAttribute("data-loaded") === "true" || panel.getAttribute("data-loading") === "true") return; // if (!panel || panel.getAttribute("data-loaded") === "true" || panel.getAttribute("data-loading") === "true") return;
panel.setAttribute("data-loading", "true"); // panel.setAttribute("data-loading", "true");
const grid = panel.querySelector<HTMLElement>(".grid"); // const grid = panel.querySelector<HTMLElement>(".grid");
if (grid) grid.innerHTML = skeletonHTML(8); // if (grid) grid.innerHTML = skeletonHTML(8);
try { // try {
const all = await ensurePanelData(); // const all = await ensurePanelData();
const [a, b] = PANEL_SLICE[name] || [0, 8]; // const [a, b] = PANEL_SLICE[name] || [0, 8];
const items = all.slice(a, b); // const items = all.slice(a, b);
if (grid) { // if (grid) {
grid.innerHTML = items.map((it, i) => cardHTML(it, i, LABEL[name] || "Show")).join(""); // grid.innerHTML = items.map((it, i) => cardHTML(it, i, LABEL[name] || "Show")).join("");
initCardInteractions(grid); // initCardInteractions(grid);
} // }
panel.setAttribute("data-loaded", "true"); // panel.setAttribute("data-loaded", "true");
} catch { // } catch {
if (grid) grid.innerHTML = `<div class="text-sm text-gray-400 p-6">内容加载失败,请重试</div>`; // if (grid) grid.innerHTML = `<div class="text-sm text-gray-400 p-6">内容加载失败,请重试</div>`;
} // }
panel.setAttribute("data-loading", "false"); // panel.setAttribute("data-loading", "false");
} // }
tabs.forEach((tab) => { tabs.forEach((tab) => {
// Brands 是导航型标签(<a href="/brands">):点击直接跳转品牌档案页,不切换面板 // Brands 是导航型标签(<a href="/brands">):点击直接跳转品牌档案页,不切换面板
@ -669,80 +481,6 @@ function railDesc(r: typeof shows[number]) {
}); });
}); });
// 首页发布会面板:系列快速筛选(前端本地过滤,按 data-ct 显示/隐藏)
let homeFType = ""
const seriesApply = (function () {
const bar = document.getElementById("seriesbar");
if (!bar) return () => {};
const grid = document.getElementById("showgrid");
const empty = document.getElementById("showempty");
const chips = Array.from(bar.querySelectorAll<HTMLButtonElement>(".schip"));
function apply(ct: string) {
homeFType = ct;
const ok = (el: Element | null) => !!el && (!ct || (el.getAttribute("data-ct") || "") === ct);
let vis = 0;
if (grid) grid.querySelectorAll<HTMLElement>(".prod-card").forEach((c) => {
const m = ok(c); c.style.display = m ? "" : "none"; if (m) vis++;
});
// 精选轮播(Featured)始终展示,不参与系列筛选隐藏
if (empty) empty.hidden = vis > 0;
}
chips.forEach((b) => b.addEventListener("click", () => {
chips.forEach((x) => x.classList.toggle("on", x === b));
apply(b.getAttribute("data-ct") || "");
}));
return apply;
})();
// 首页发布会面板:查看更多(前端分页追加,带系列筛选参数)
(function () {
let homePage = 2;
let homeBusy = false;
const btn = document.getElementById("showmore") as HTMLButtonElement | null;
if (!btn) return;
btn.addEventListener("click", async () => {
if (homeBusy) return;
homeBusy = true;
const orig = btn.textContent;
btn.textContent = "加载中…";
btn.disabled = true;
try {
const p = new URLSearchParams({ page: String(homePage), size: "8", with_images: "5" });
if (homeFType) p.set("collection_type", homeFType);
const res = await fetch(`${API_BASE}/api/public/articles?${p.toString()}`);
const json = await res.json();
const rows = json?.data ?? [];
const grid = document.getElementById("showgrid");
if (grid && rows.length) {
const html = rows.map((r: any) => {
const g = (r.images || []).map((im: any) => toAbs(im.image || "")).filter(Boolean).slice(0, 5);
const it = {
id: r.id, brand: r.brand_name || "Unknown", title: r.title || "",
season: r.year ? "AW" + String(r.year).slice(-2) : "",
season_code: r.season_code || "", collection_type: r.collection_type || "",
year: r.year, img: toAbs(r.cover || ""), count: r.image_count || 0, gallery: g,
};
return cardHTML(it, 99, "Runway");
}).join("");
grid.insertAdjacentHTML("beforeend", html);
initCardInteractions(grid);
seriesApply(homeFType);
homePage++;
if (rows.length < 8) { btn.textContent = "没有更多了"; return; }
} else {
btn.textContent = "没有更多了"; return;
}
btn.textContent = orig;
btn.disabled = false;
} catch {
btn.textContent = orig;
btn.disabled = false;
} finally {
homeBusy = false;
}
});
})();
// 启用 JS 标记:用于渐进增强的占位符淡入(无 JS 时图片直接显示,不影响可用性) // 启用 JS 标记:用于渐进增强的占位符淡入(无 JS 时图片直接显示,不影响可用性)
document.documentElement.classList.add("js"); document.documentElement.classList.add("js");
@ -766,7 +504,7 @@ function railDesc(r: typeof shows[number]) {
} }
// 初始化 SSR 的 Shows 面板卡片交互(缩略图切换 / 占位淡入 / 双指缩放) // 初始化 SSR 的 Shows 面板卡片交互(缩略图切换 / 占位淡入 / 双指缩放)
initCardInteractions(document); // initCardInteractions(document);
// 移动端双指缩放主图(pinch-to-zoom)+ 单指平移 // 移动端双指缩放主图(pinch-to-zoom)+ 单指平移
function enablePinch(box: HTMLElement | null) { function enablePinch(box: HTMLElement | null) {
@ -810,55 +548,51 @@ function railDesc(r: typeof shows[number]) {
}); });
} }
// 首页精选轮播(左图右文卡片式) // 横向滚动画廊(Swiper):容器高度固定,按每张图片原始宽高比自适应该卡尺寸
(function () { (function () {
const root = document.getElementById("heroCarousel"); const el = document.getElementById("hsSwiper");
if (!root) return; if (!el) return;
const slides = Array.from(root.querySelectorAll<HTMLElement>(".hc-slide"));
const total = slides.length;
if (total <= 1) {
root.querySelectorAll<HTMLElement>(".hc-controls").forEach((c) => (c.style.display = "none"));
return;
}
let idx = 0;
let timer: number | undefined;
const dots = Array.from(root.querySelectorAll<HTMLButtonElement>(".hc-dot"));
function go(n: number) {
idx = (n + total) % total;
slides.forEach((s, i) => { s.style.display = i === idx ? "" : "none"; });
// 每屏各自带一套 controls,只高亮当前可见 slide 内的圆点
const visDots = slides[idx].querySelectorAll<HTMLElement>(".hc-dot");
visDots.forEach((d, i) => d.classList.toggle("on", i === idx));
}
function start() { stop(); timer = window.setInterval(() => go(idx + 1), 5000); }
function stop() { if (timer) window.clearInterval(timer); }
root.querySelectorAll<HTMLButtonElement>(".hc-prev").forEach((b) =>
b.addEventListener("click", () => { go(idx - 1); start(); })
);
root.querySelectorAll<HTMLButtonElement>(".hc-next").forEach((b) =>
b.addEventListener("click", () => { go(idx + 1); start(); })
);
dots.forEach((d) =>
d.addEventListener("click", () => { go(Number(d.dataset.i)); start(); })
);
root.addEventListener("mouseenter", stop);
root.addEventListener("mouseleave", start);
start();
})();
// 右侧品牌栏:最新 / 最热 切换(事件委托,稳健绑定) let swiper: any = null;
(function () { let updateScheduled = false;
const wrap = document.querySelector(".hc-rail-tabs"); // 图片尺寸随加载/视口变化,需在下一帧通知 Swiper 重新测量布局(防偏移)
const railList = document.getElementById("railList"); const scheduleUpdate = () => {
if (!wrap || !railList) return; if (updateScheduled) return;
const sets = railList.querySelectorAll<HTMLElement>(".rail-set"); updateScheduled = true;
wrap.addEventListener("click", (e) => { requestAnimationFrame(() => { updateScheduled = false; if (swiper) swiper.update(); });
const btn = (e.target as HTMLElement).closest("button"); };
if (!btn) return;
const sort = btn.dataset.sort; // 图片加载:按真实比例设定尺寸 + 完成淡入;失败保留灰底占位
if (!sort) return; const imgs = el.querySelectorAll<HTMLImageElement>(".hs-img-box img");
wrap.querySelectorAll("button").forEach((b) => b.classList.toggle("on", b === btn)); imgs.forEach((img) => {
sets.forEach((s) => { s.style.display = s.dataset.set === sort ? "" : "none"; }); const box = img.closest(".hs-img-box") as HTMLElement | null;
const done = () => {img.classList.add("is-loaded"); box?.classList.add("is-loaded"); scheduleUpdate(); };
const fail = () => {
if (box) { box.style.width = "360px"; box.style.height = (el.clientHeight || 600) + "px"; }
img.style.display = "none";
box?.classList.add("is-loaded");
scheduleUpdate();
};
if (img.complete) { if (img.naturalWidth > 0) done(); else fail(); }
else { img.addEventListener("load", done, { once: true }); img.addEventListener("error", fail, { once: true }); }
});
// 箭头按钮位于容器外的 header 内,直接传元素以确保正确绑定
const prevBtn = el.parentElement?.querySelector<HTMLElement>(".hs-prev") || document.querySelector(".hs-prev");
const nextBtn = el.parentElement?.querySelector<HTMLElement>(".hs-next") || document.querySelector(".hs-next");
swiper = new Swiper(el, {
modules: [Navigation, FreeMode, Keyboard],
slidesPerView: "auto", // 每张 slide 取自身宽度(由图片宽高比决定)
spaceBetween: 4, // slide 间距
grabCursor: true, // 可拖拽光标
speed: 220, // 箭头/吸附过渡时长(ms),调小让拖拽松手后更快吸附
freeMode: { enabled: true, sticky: true, momentumBounce: false, momentumRatio: 0.5 }, // 自由拖拽 + 松手粘性吸附(momentumRatio 调小减短滑行)
keyboard: { enabled: true },
navigation: { prevEl: prevBtn, nextEl: nextBtn, disabledClass: "swiper-button-disabled" },
updateOnImagesReady: true, // 图片就绪后自动 update
observer: true,
observeSlideChildren: true,
}); });
})(); })();

View File

@ -20,15 +20,6 @@ if (!yearChips.length) yearChips = yearSeed
--- ---
<Layout title="All Shows — Portfolio Studio"> <Layout title="All Shows — Portfolio Studio">
<!-- 首屏绘制前同步侧边栏折叠状态,杜绝刷新时的闪烁 -->
<script is:inline>
try {
if (localStorage.getItem("showsaside-collapsed") === "1") {
document.documentElement.classList.add("showsaside-collapsed")
}
} catch (e) {}
</script>
<div class="showsroot w-full" x-data="showsPage()"> <div class="showsroot w-full" x-data="showsPage()">
<!-- 顶部:极简眉头 --> <!-- 顶部:极简眉头 -->
<header class="showshead"> <header class="showshead">
@ -36,7 +27,7 @@ if (!yearChips.length) yearChips = yearSeed
<nav class="showscrumb" aria-label="Breadcrumb"> <nav class="showscrumb" aria-label="Breadcrumb">
<a href="/">Home</a> <a href="/">Home</a>
<span class="sep" aria-hidden="true">/</span> <span class="sep" aria-hidden="true">/</span>
<a href="/shows">Runway Archive</a> <a href="/shows">All Shows</a>
<span class="sep" aria-hidden="true">/</span> <span class="sep" aria-hidden="true">/</span>
<span class="cur" aria-current="page" x-text="crumbYear"></span> <span class="cur" aria-current="page" x-text="crumbYear"></span>
</nav> </nav>
@ -57,17 +48,12 @@ if (!yearChips.length) yearChips = yearSeed
<!-- 主体:左 sidebar + 右 4 列商品 grid --> <!-- 主体:左 sidebar + 右 4 列商品 grid -->
<div class="showsbody"> <div class="showsbody">
<!-- LEFT SIDEBAR --> <!-- LEFT SIDEBAR -->
<aside class="showsaside" :class="{ 'is-collapsed': asideCollapsed }"> <aside class="showsaside">
<div class="showsaside-head"> <div class="showsaside-head">
<h2 class="showsaside-h">REFINE</h2> <h2 class="showsaside-h">REFINE</h2>
<button class="showsaside-toggle" type="button" aria-label="折叠/展开筛选" @click="toggleAside()">
<svg viewBox="0 0 12 12" width="11" height="11" aria-hidden="true">
<path d="M7.5 3l-4 3 4 3" stroke="currentColor" fill="none" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
</div> </div>
<div class="showsaside-body" x-show="!asideCollapsed"> <div class="showsaside-body">
<!-- Designer 多选 --> <!-- Designer 多选 -->
<section class="showsg"> <section class="showsg">
<header class="showsg-h" @click="toggleGroup('designers')"> <header class="showsg-h" @click="toggleGroup('designers')">
@ -103,7 +89,7 @@ if (!yearChips.length) yearChips = yearSeed
</template> </template>
</li> </li>
</ul> </ul>
<a class="showsmore" x-show="hasMoreBrands && !keyword" @click="brandsExpanded = !brandsExpanded" x-text="brandsExpanded ? 'Show fewer ▴' : `Show all ${allBrands.length} designers ▾`"></a> <a class="showsmore" @click="openBrandModal()" x-text="'浏览全部品牌 +'"></a>
</div> </div>
</section> </section>
@ -194,7 +180,7 @@ if (!yearChips.length) yearChips = yearSeed
<!-- Empty State --> <!-- Empty State -->
<template x-if="!loading && items.length === 0"> <template x-if="!loading && items.length === 0">
<div class="showsempty">No shows match the current filters · 暂无符合条件的秀场</div> <div class="showsempty">No shows match the current filters</div>
</template> </template>
<!-- Card List --> <!-- Card List -->
@ -235,6 +221,59 @@ if (!yearChips.length) yearChips = yearSeed
</div> </div>
</main> </main>
</div> </div>
<!-- 品牌筛选弹窗:搜索 + A-Z 跳转 + 热门推荐,按需加载,避免一次拉全量 -->
<div class="showsmodal" x-show="modalOpen" x-cloak>
<div class="showsmodal-mask" @click="closeBrandModal()"></div>
<div class="showsmodal-panel">
<header class="showsmodal-head">
<h3>全部品牌</h3>
<button class="showsmodal-x" type="button" @click="closeBrandModal()" aria-label="关闭">✕</button>
</header>
<div class="showsmodal-search">
<input
class="showsmodal-input"
type="text"
x-model="modalKeyword"
@input.debounce.300ms="modalSearch()"
placeholder="搜索品牌名…"
autocomplete="off"
/>
</div>
<div class="showsmodal-az">
<template x-for="l in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ#'.split('')" :key="l">
<button class="az" type="button" :class="{ active: modalLetter === l }" @click="modalPickLetter(l)" x-text="l"></button>
</template>
</div>
<div class="showsmodal-body">
<div class="showsmodal-hint" x-show="!modalKeyword && !modalLetter">热门推荐</div>
<div class="showsmodal-loading" x-show="modalLoading" x-html="SPIN_SVG"></div>
<ul class="showsmodal-list">
<template x-for="b in modalResults" :key="b.id">
<li>
<label class="showsmodal-row">
<input
type="checkbox"
:value="b.id"
:checked="modalSelected.includes(b.id)"
:disabled="!modalSelected.includes(b.id) && modalMaxReached"
@change="toggleModalBrand(b.id)"
/>
<span class="showsmodal-name" x-text="b.name"></span>
<span class="showsmodal-ct" x-text="b.article_count ? b.article_count : ''"></span>
</label>
</li>
</template>
<li class="showsmodal-empty" x-show="!modalLoading && modalResults.length === 0">未找到匹配的品牌</li>
</ul>
</div>
<footer class="showsmodal-foot">
<span class="showsmodal-counter" x-text="`已选 ${modalSelected.length} / 最多 5 个`"></span>
<button class="showsmodal-ok" type="button" @click="confirmBrandModal()">确定</button>
</footer>
</div>
</div>
</div> </div>
</Layout> </Layout>
@ -286,10 +325,6 @@ if (!yearChips.length) yearChips = yearSeed
.showsaside { .showsaside {
@apply flex-none w-[240px] bg-white sticky top-5 max-h-[calc(100vh-40px)] rounded-none transition-[flex-basis] duration-250 ease-in-out flex flex-col max-md:static max-md:max-h-none max-md:w-full; @apply flex-none w-[240px] bg-white sticky top-5 max-h-[calc(100vh-40px)] rounded-none transition-[flex-basis] duration-250 ease-in-out flex flex-col max-md:static max-md:max-h-none max-md:w-full;
} }
html.showsaside-collapsed .showsaside,
.showsaside.is-collapsed {
@apply flex-none w-[44px];
}
.showsaside-head { .showsaside-head {
@apply flex items-center justify-between p-[14px_14px_12px] border-b border-black shrink-0; @apply flex items-center justify-between p-[14px_14px_12px] border-b border-black shrink-0;
@ -297,25 +332,10 @@ if (!yearChips.length) yearChips = yearSeed
.showsaside-h { .showsaside-h {
@apply text-[11px] tracking-[0.26em] uppercase font-bold m-0 leading-none transition-opacity duration-200; @apply text-[11px] tracking-[0.26em] uppercase font-bold m-0 leading-none transition-opacity duration-200;
} }
html.showsaside-collapsed .showsaside-h,
.showsaside.is-collapsed .showsaside-h {
@apply opacity-0 pointer-events-none w-0 overflow-hidden;
}
.showsaside-toggle {
@apply appearance-none bg-white border-[1.5px] border-black w-[22px] h-[22px] inline-flex items-center justify-center cursor-pointer rounded-none text-black p-0 transition-transform duration-250 ease-in-out hover:bg-black hover:text-white;
}
html.showsaside-collapsed .showsaside-toggle,
.showsaside.is-collapsed .showsaside-toggle {
@apply ml-auto rotate-180;
}
.showsaside-body { .showsaside-body {
@apply p-[14px_14px_18px] overflow-y-auto flex-1 min-h-0 [scrollbar-width:thin] [scrollbar-color:#ccc_#fff]; @apply p-[14px_14px_18px] overflow-y-auto flex-1 min-h-0 [scrollbar-width:thin] [scrollbar-color:#ccc_#fff];
} }
html.showsaside-collapsed .showsaside-body {
@apply hidden;
}
.showsg { .showsg {
@apply border-t border-[#e6e6e6] py-[12px] pb-[6px] first-of-type:border-t-0 first-of-type:pt-1; @apply border-t border-[#e6e6e6] py-[12px] pb-[6px] first-of-type:border-t-0 first-of-type:pt-1;
@ -479,11 +499,62 @@ if (!yearChips.length) yearChips = yearSeed
@keyframes hbxProg { 0% { left: -38%; } 100% { left: 100%; } } @keyframes hbxProg { 0% { left: -38%; } 100% { left: 100%; } }
@keyframes hbxShim { 0%, 100% { opacity: .85; } 50% { opacity: .55; } } @keyframes hbxShim { 0%, 100% { opacity: .85; } 50% { opacity: .55; } }
/* ===== 品牌筛选弹窗 ===== */
.showsmodal {
@apply fixed inset-0 z-[60] flex items-center justify-center p-[16px];
}
.showsmodal-mask {
@apply absolute inset-0 bg-black/45;
}
.showsmodal-panel {
@apply relative w-full max-w-[680px] max-h-[86vh] bg-white flex flex-col border border-black;
}
.showsmodal-head {
@apply flex items-center justify-between px-[16px] py-[13px] border-b border-black shrink-0;
}
.showsmodal-head h3 {
@apply m-0 text-[13px] tracking-[0.2em] uppercase font-bold;
}
.showsmodal-x {
@apply appearance-none bg-white border border-black w-[24px] h-[24px] inline-flex items-center justify-center cursor-pointer text-[12px] leading-none;
}
.showsmodal-x:hover { @apply bg-black text-white; }
.showsmodal-search { @apply px-[16px] pt-[12px] pb-[8px] shrink-0; }
.showsmodal-input {
@apply w-full border border-black px-[10px] py-[8px] text-[13px] outline-none;
}
.showsmodal-input:focus { @apply ring-1 ring-black; }
.showsmodal-az {
@apply flex flex-wrap gap-[4px] px-[16px] pb-[10px] border-b border-[#e6e6e6] shrink-0;
}
.showsmodal-az .az {
@apply appearance-none bg-white border border-[#ccc] w-[22px] h-[22px] text-[11px] cursor-pointer leading-none;
}
.showsmodal-az .az.active { @apply bg-black text-white border-black; }
.showsmodal-body { @apply flex-1 min-h-0 overflow-y-auto px-[16px] py-[8px]; }
.showsmodal-hint {
@apply text-[11px] tracking-[0.2em] uppercase text-[#888] py-[6px] pb-[8px];
}
.showsmodal-loading { @apply flex justify-center py-[28px] text-[#bdbdbd]; }
.showsmodal-list { @apply list-none m-0 p-0; }
.showsmodal-row {
@apply flex items-center gap-[8px] w-full cursor-pointer py-[7px] border-b border-[#f0f0f0] text-[13px];
}
.showsmodal-name { @apply flex-1 truncate; }
.showsmodal-ct { @apply text-[#999] text-[11px] ml-auto; }
.showsmodal-empty { @apply py-[28px] text-center text-[#999] text-[13px]; }
.showsmodal-foot {
@apply flex items-center justify-between px-[16px] py-[12px] border-t border-black shrink-0 text-[12px];
}
.showsmodal-ok {
@apply appearance-none bg-black text-white border border-black px-[22px] py-[8px] cursor-pointer text-[12px] tracking-[0.1em];
}
</style> </style>
<script> <script>
import Alpine from "alpinejs" import Alpine from "alpinejs"
import { getArticles, toAbs, type ArticleItem, type BrandEntry } from "../lib/api" import { getArticles, getBrands, toAbs, type ArticleItem, type BrandEntry } from "../lib/api"
const ICON_USER = `<svg viewBox="0 0 12 12" width="9" height="9" aria-hidden="true"><circle cx="6" cy="4" r="2" stroke="currentColor" fill="none" stroke-width="1"/><path d="M2 10c0-2 1.8-3 4-3s4 1 4 3" stroke="currentColor" fill="none" stroke-width="1" stroke-linecap="round"/></svg>` const ICON_USER = `<svg viewBox="0 0 12 12" width="9" height="9" aria-hidden="true"><circle cx="6" cy="4" r="2" stroke="currentColor" fill="none" stroke-width="1"/><path d="M2 10c0-2 1.8-3 4-3s4 1 4 3" stroke="currentColor" fill="none" stroke-width="1" stroke-linecap="round"/></svg>`
const ICON_DATE = `<svg viewBox="0 0 12 12" width="9" height="9" aria-hidden="true"><rect x="2" y="3" width="8" height="7" stroke="currentColor" fill="none" stroke-width="1"/><path d="M2 6h8M4 2v2M8 2v2" stroke="currentColor" fill="none" stroke-width="1" stroke-linecap="round"/></svg>` const ICON_DATE = `<svg viewBox="0 0 12 12" width="9" height="9" aria-hidden="true"><rect x="2" y="3" width="8" height="7" stroke="currentColor" fill="none" stroke-width="1"/><path d="M2 6h8M4 2v2M8 2v2" stroke="currentColor" fill="none" stroke-width="1" stroke-linecap="round"/></svg>`
@ -499,7 +570,6 @@ if (!yearChips.length) yearChips = yearSeed
ICON_USER, ICON_USER,
ICON_DATE, ICON_DATE,
SPIN_SVG, SPIN_SVG,
asideCollapsed: localStorage.getItem("showsaside-collapsed") === "1",
collapsedGroups: { collapsedGroups: {
designers: localStorage.getItem("showsg-designers") === "1", designers: localStorage.getItem("showsg-designers") === "1",
collection: localStorage.getItem("showsg-collection") === "1", collection: localStorage.getItem("showsg-collection") === "1",
@ -513,6 +583,17 @@ if (!yearChips.length) yearChips = yearSeed
keyword: "", keyword: "",
brandsExpanded: false, brandsExpanded: false,
// 品牌筛选弹窗状态
modalOpen: false,
modalKeyword: "",
modalLetter: "",
modalResults: [] as BrandEntry[],
modalLoading: false,
modalSelected: [] as number[],
get modalMaxReached() {
return this.modalSelected.length >= 5
},
items: [] as (ArticleItem & { _imgLoaded?: boolean })[], items: [] as (ArticleItem & { _imgLoaded?: boolean })[],
loading: false, loading: false,
loadingMore: false, loadingMore: false,
@ -620,12 +701,6 @@ if (!yearChips.length) yearChips = yearSeed
}, },
// 事件处理方法 // 事件处理方法
toggleAside() {
this.asideCollapsed = !this.asideCollapsed
localStorage.setItem("showsaside-collapsed", this.asideCollapsed ? "1" : "")
document.documentElement.classList.toggle("showsaside-collapsed", this.asideCollapsed)
},
toggleGroup(group: "designers" | "collection" | "season" | "year") { toggleGroup(group: "designers" | "collection" | "season" | "year") {
this.collapsedGroups[group] = !this.collapsedGroups[group] this.collapsedGroups[group] = !this.collapsedGroups[group]
localStorage.setItem(`showsg-${group}`, this.collapsedGroups[group] ? "1" : "") localStorage.setItem(`showsg-${group}`, this.collapsedGroups[group] ? "1" : "")
@ -642,6 +717,73 @@ if (!yearChips.length) yearChips = yearSeed
this.loadPage(1, false) this.loadPage(1, false)
}, },
// ===== 品牌筛选弹窗方法 =====
async openBrandModal() {
this.modalSelected = [...this.selectedBrands]
this.modalKeyword = ""
this.modalLetter = ""
this.modalOpen = true
await this.modalLoadHot()
},
closeBrandModal() {
this.modalOpen = false
},
async modalLoadHot() {
this.modalLoading = true
try {
const list = await getBrands({ featured: 1, size: 50 })
this.modalResults = list.length ? list : this.allBrands.slice(0, 50)
} catch {
this.modalResults = this.allBrands.slice(0, 50)
} finally {
this.modalLoading = false
}
},
async modalSearch() {
const kw = this.modalKeyword.trim()
if (kw) {
this.modalLetter = ""
this.modalLoading = true
try {
this.modalResults = await getBrands({ keyword: kw, size: 60 })
} catch {
this.modalResults = []
} finally {
this.modalLoading = false
}
} else if (this.modalLetter) {
await this.modalPickLetter(this.modalLetter)
} else {
await this.modalLoadHot()
}
},
async modalPickLetter(l: string) {
this.modalLetter = l
this.modalKeyword = ""
this.modalLoading = true
try {
const letter = l === "#" ? "OTHER" : l
this.modalResults = await getBrands({ letter, size: 200 })
} catch {
this.modalResults = []
} finally {
this.modalLoading = false
}
},
toggleModalBrand(id: number) {
const idx = this.modalSelected.indexOf(id)
if (idx >= 0) this.modalSelected.splice(idx, 1)
else {
if (this.modalSelected.length >= 5) return
this.modalSelected.push(id)
}
},
confirmBrandModal() {
this.selectedBrands = [...this.modalSelected]
this.modalOpen = false
this.refreshFilter()
},
clearAll() { clearAll() {
this.selectedBrands = [] this.selectedBrands = []
this.selectedCt = [] this.selectedCt = []