8
src/pages/cn/about.astro
Normal file
8
src/pages/cn/about.astro
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
import Layout from "../../layouts/Layout.astro"
|
||||
import About from "../../components/pages/About.astro"
|
||||
---
|
||||
|
||||
<Layout title="About — 暂未上线">
|
||||
<About />
|
||||
</Layout>
|
||||
@ -1,344 +1,8 @@
|
||||
---
|
||||
import Layout from "../../layouts/Layout.astro"
|
||||
import ComingSoon from "../../components/ComingSoon.astro"
|
||||
import { getShows, seriesLabel } from "../../lib/data"
|
||||
import { getLocaleFromUrl, useTranslations } from "../../i18n/utils"
|
||||
|
||||
// SSR 预取走秀数据
|
||||
const shows = await getShows(20)
|
||||
|
||||
// 国际化:根据 URL 前缀(/cn/...)取当前语言与翻译函数
|
||||
const locale = getLocaleFromUrl(Astro.url)
|
||||
const { t } = useTranslations(locale)
|
||||
|
||||
type GalleryItem = {
|
||||
src: string;
|
||||
brand: string;
|
||||
articleId: number;
|
||||
year: number;
|
||||
title: string;
|
||||
collection_type?: string
|
||||
}
|
||||
|
||||
const galleryItems: GalleryItem[] = []
|
||||
for (const s of shows) {
|
||||
if (!s.img) continue
|
||||
galleryItems.push({
|
||||
src: s.img,
|
||||
brand: s.brand,
|
||||
articleId: s.id,
|
||||
year: s.year,
|
||||
title: s.title,
|
||||
collection_type: s.collection_type,
|
||||
})
|
||||
}
|
||||
import Layout from '../../layouts/Layout.astro'
|
||||
import Index from '../../components/Index.astro'
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<div x-data="{ activeTab: 'backstage' }" class="flex flex-col min-h-screen">
|
||||
<!-- 二级分类标签页 -->
|
||||
<div id="tabbar" class="mt-16 md:mt-24">
|
||||
<ul class="flex items-center gap-6 list-none p-0 m-0 overflow-x-auto">
|
||||
<li
|
||||
class="tab text-[42px] font-extrabold cursor-pointer whitespace-nowrap"
|
||||
:class="{ 'is-active': activeTab === 'backstage' }"
|
||||
@click="activeTab = 'backstage'"
|
||||
>
|
||||
A
|
||||
<span class="text-xs uppercase tracking-widest bg-black text-white px-2 py-0.5 rounded-full font-mono font-bold align-super">Live</span>
|
||||
</li>
|
||||
<li
|
||||
class="tab text-[42px] font-extrabold cursor-pointer whitespace-nowrap"
|
||||
:class="{ 'is-active': activeTab === 'shows' }"
|
||||
@click="activeTab = 'shows'"
|
||||
>
|
||||
B
|
||||
<span class="text-xs uppercase tracking-widest bg-zinc-200 text-zinc-600 px-2 py-0.5 rounded-full font-mono font-bold align-super">Soon</span>
|
||||
</li>
|
||||
<li
|
||||
class="tab text-[42px] font-extrabold cursor-pointer whitespace-nowrap"
|
||||
:class="{ 'is-active': activeTab === 'events' }"
|
||||
@click="activeTab = 'events'"
|
||||
>
|
||||
C
|
||||
<span class="text-xs uppercase tracking-widest bg-zinc-200 text-zinc-600 px-2 py-0.5 rounded-full font-mono font-bold align-super">Soon</span>
|
||||
</li>
|
||||
<li
|
||||
class="tab text-[42px] font-extrabold cursor-pointer whitespace-nowrap"
|
||||
:class="{ 'is-active': activeTab === 'street' }"
|
||||
@click="activeTab = 'street'"
|
||||
>
|
||||
D
|
||||
<span class="text-xs uppercase tracking-widest bg-zinc-200 text-zinc-600 px-2 py-0.5 rounded-full font-mono font-bold align-super">Soon</span>
|
||||
</li>
|
||||
<a href="/brands" class="tab text-[42px] font-extrabold cursor-pointer whitespace-nowrap hover:opacity-80">E</a>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 中间内容展示区 -->
|
||||
<main class="grow min-h-[400px] mt-10">
|
||||
<!-- 发布会 面板(默认激活) -->
|
||||
<div class="panel" x-show="activeTab === 'backstage'" x-transition.opacity.duration.300ms>
|
||||
<div class="container px-md md:px-xl text-2xl md:text-2xl-1 font-medium mb-4">Hot Brand</div>
|
||||
|
||||
<!-- 横向滚动画廊 -->
|
||||
{
|
||||
galleryItems.length > 0 && (
|
||||
<section class="hs-section">
|
||||
<div class="hs-stage">
|
||||
<div class="swiper hs-swiper" id="hsSwiper">
|
||||
<div class="swiper-wrapper">
|
||||
{
|
||||
galleryItems.map((g, i) => (
|
||||
<div class="swiper-slide hs-slide">
|
||||
<a class="hs-item" href={`/article?id=${g.articleId}`} data-idx={i}>
|
||||
<!-- Alpine 管理单张图片的加载/失败状态 -->
|
||||
<div
|
||||
class="hs-img-box"
|
||||
x-data="{ loaded: false, error: false }"
|
||||
:class="{ 'is-loaded': loaded || error }"
|
||||
>
|
||||
<div class="hs-skel" aria-hidden="true" x-show="!loaded && !error"></div>
|
||||
<span class="hs-spin" aria-hidden="true" x-show="!loaded && !error">
|
||||
<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>
|
||||
</span>
|
||||
<img
|
||||
src={g.src}
|
||||
alt={g.brand}
|
||||
loading={i < 4 ? "eager" : "lazy"}
|
||||
decoding="async"
|
||||
:class="{ 'is-error': error }"
|
||||
@load="loaded = true; $dispatch('img-ready')"
|
||||
@error="error = true; $dispatch('img-ready')"
|
||||
/>
|
||||
<span class="hs-caption">
|
||||
<span class="hs-cap-brand">{g.brand}</span>
|
||||
<span class="hs-cap-meta">
|
||||
{g.year ? g.year : ""}
|
||||
{g.collection_type ? " · " + seriesLabel(g.collection_type) : ""}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="hs-controls flex justify-between items-center text-[13px] font-bold">
|
||||
<div class="text-[16px] hs-arrow hs-prev cursor-pointer hover:opacity-60 transition-opacity duration-200" role="button" tabindex="0">{t('Previous')}</div>
|
||||
<div class="text-[16px] hs-arrow hs-next cursor-pointer hover:opacity-60 transition-opacity duration-200" role="button" tabindex="0">{t('Next')}</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- 灵感 面板 -->
|
||||
<div class="panel" x-show="activeTab === 'shows'" x-transition.opacity.duration.300ms x-cloak>
|
||||
<ComingSoon module="灵感" moduleZh="Inspiration" note="灵感板块正在筹备中,敬请期待。你可以先浏览已上线的走秀档案。" />
|
||||
</div>
|
||||
|
||||
<!-- 社媒 面板 -->
|
||||
<div class="panel" x-show="activeTab === 'events'" x-transition.opacity.duration.300ms x-cloak>
|
||||
<ComingSoon module="社媒" moduleZh="Social" note="社媒板块正在筹备中,敬请期待。你可以先浏览已上线的走秀档案。" />
|
||||
</div>
|
||||
|
||||
<!-- 街拍 面板 -->
|
||||
<div class="panel" x-show="activeTab === 'street'" x-transition.opacity.duration.300ms x-cloak>
|
||||
<ComingSoon module="街拍" moduleZh="Street" note="街拍板块正在筹备中,敬请期待。你可以先浏览已上线的走秀档案。" />
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<Index />
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
@reference "tailwindcss";
|
||||
|
||||
:root { --brand: #cf5a44; }
|
||||
|
||||
/* 标签基础与激活样式 */
|
||||
.tab {
|
||||
@apply relative pb-[6px] text-[#bbb] transition-colors duration-200 no-underline;
|
||||
}
|
||||
.tab:hover { @apply text-[#666]; }
|
||||
.tab::after {
|
||||
content: "";
|
||||
@apply absolute left-0 right-0 bottom-0 h-[2px] bg-[var(--brand)] scale-x-0 origin-left transition-transform duration-[250ms] ease-in-out;
|
||||
}
|
||||
.tab.is-active { @apply text-black; }
|
||||
.tab.is-active::after { @apply scale-x-100; }
|
||||
|
||||
/* 防闪烁标签 */
|
||||
[x-cloak] { display: none !important; }
|
||||
|
||||
/* 区块标题 */
|
||||
.sec-head { @apply flex items-end justify-between mt-2 mb-6 pb-4 border-b border-[#ececec]; }
|
||||
.sec-title { @apply font-serif text-[28px] leading-none font-extrabold text-[#111]; }
|
||||
.sec-sub { @apply mt-2 text-[12px] uppercase tracking-[.14em] text-[#999] font-mono; }
|
||||
.sec-more { @apply text-[12px] uppercase tracking-[.14em] font-bold no-underline text-[#111] transition-opacity duration-200; }
|
||||
.sec-more:hover { @apply opacity-60; }
|
||||
|
||||
/* 横向画廊样式 */
|
||||
.hs-section { @apply mb-9; }
|
||||
.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;
|
||||
}
|
||||
.hs-controls .hs-arrow { pointer-events: auto; }
|
||||
.hs-arrow { @apply select-none; }
|
||||
.hs-arrow.swiper-button-disabled { pointer-events: none; }
|
||||
|
||||
/* Swiper 尺寸规整 */
|
||||
.hs-swiper {
|
||||
width: 100%;
|
||||
height: clamp(500px, 80vh, 900px);
|
||||
padding: 0 4px;
|
||||
}
|
||||
.hs-swiper .swiper-wrapper { align-items: center; }
|
||||
.hs-swiper .hs-slide {
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.hs-item {
|
||||
display: block;
|
||||
height: auto;
|
||||
width: max-content;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
.hs-img-box {
|
||||
position: relative;
|
||||
display: block;
|
||||
height: clamp(500px, 80vh, 900px);
|
||||
/* aspect-ratio: 3 / 4; */
|
||||
background: #f0f0f0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.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; }
|
||||
.hs-img-box > img.is-error { opacity: 0 !important; }
|
||||
|
||||
/* 加载骨架屏与旋转 Spinner */
|
||||
.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, .hs-img-box { height: clamp(340px, 56vh, 540px); }
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import Swiper from "swiper";
|
||||
import { Navigation, FreeMode, Keyboard } from "swiper/modules";
|
||||
import "swiper/css";
|
||||
import "swiper/css/navigation";
|
||||
import "swiper/css/free-mode";
|
||||
|
||||
// 初始化 Swiper 轮播
|
||||
const el = document.getElementById("hsSwiper");
|
||||
if (el) {
|
||||
const prevBtn = el.parentElement?.querySelector<HTMLElement>(".hs-prev");
|
||||
const nextBtn = el.parentElement?.querySelector<HTMLElement>(".hs-next");
|
||||
|
||||
const swiper = new Swiper(el, {
|
||||
modules: [Navigation, FreeMode, Keyboard],
|
||||
slidesPerView: "auto",
|
||||
spaceBetween: 4,
|
||||
grabCursor: true,
|
||||
speed: 220,
|
||||
freeMode: { enabled: true, sticky: true, momentumBounce: false, momentumRatio: 0.5 },
|
||||
keyboard: { enabled: true },
|
||||
navigation: { prevEl: prevBtn, nextEl: nextBtn, disabledClass: "swiper-button-disabled" },
|
||||
observer: true,
|
||||
observeSlideChildren: true,
|
||||
});
|
||||
|
||||
// 接收 Alpine 发布的图片就绪事件更新布局防闪烁
|
||||
window.addEventListener("img-ready", () => {
|
||||
requestAnimationFrame(() => swiper.update());
|
||||
});
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user