Home hero becomes a 450px flex row with a pinned 240x450 category rail (ShellCategoryMenu `pinned` mode) left of the 960px center-cropped carousel; hovering a top-level category expands the mega-menu over the carousel. The header no longer collapses on scroll, and its category dropdown is now hover-only on non-home pages. OpenSpec change: openspec/changes/pin-home-category-menu BREAKING CHANGE: the header logo/search/cart bar no longer auto-hides past 200px of scroll, and the home page no longer shows the header category dropdown (it is pinned in the hero row instead).
155 lines
3.3 KiB
Vue
155 lines
3.3 KiB
Vue
<script setup lang="ts">
|
||
import { t as pick } from "@vmall/shared";
|
||
import { MOCK_BANNERS, MOCK_PROMOS, MOCK_QUICK_LINKS, homeFloors } from "~/mock/data";
|
||
|
||
const { locale, t } = useI18n();
|
||
const floors = computed(() => homeFloors().filter((f) => f.products.length > 0));
|
||
</script>
|
||
|
||
<template>
|
||
<div>
|
||
<div class="w1200 hero">
|
||
<ShellCategoryMenu pinned />
|
||
<div class="hero-slider">
|
||
<UiCarousel :images="MOCK_BANNERS" :height="450" />
|
||
</div>
|
||
</div>
|
||
|
||
<div class="w1200 strip">
|
||
<ul class="quick">
|
||
<li v-for="q in MOCK_QUICK_LINKS" :key="q.url + pick(q.label, 'en')">
|
||
<NuxtLink :to="q.url">
|
||
<svg viewBox="0 0 24 24" width="26" height="26" fill="currentColor"><path :d="q.glyph" /></svg>
|
||
<span>{{ pick(q.label, locale) }}</span>
|
||
</NuxtLink>
|
||
</li>
|
||
</ul>
|
||
<div class="promos">
|
||
<NuxtLink v-for="p in MOCK_PROMOS" :key="p.image" :to="p.url">
|
||
<img :src="p.image" :alt="t('home.hotPromo')" loading="lazy" />
|
||
</NuxtLink>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section-bg">
|
||
<section v-for="floor in floors" :key="floor.categoryId" class="w1200 floor">
|
||
<header class="floor-head">
|
||
<h2>{{ pick(floor.name, locale) }}</h2>
|
||
<NuxtLink :to="`/search?category=${floor.categoryId}`" class="more">{{ t("home.viewMore") }} ›</NuxtLink>
|
||
</header>
|
||
<div class="floor-body">
|
||
<NuxtLink :to="floor.advUrl" class="floor-adv">
|
||
<img :src="floor.advImage" :alt="pick(floor.name, locale)" loading="lazy" />
|
||
</NuxtLink>
|
||
<div class="floor-grid">
|
||
<UiProductCard v-for="p in floor.products" :key="p.id" :product="p" />
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.hero {
|
||
display: flex;
|
||
height: 450px;
|
||
margin-top: 0;
|
||
background: #fff;
|
||
}
|
||
.hero-slider {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
.strip {
|
||
display: flex;
|
||
gap: 20px;
|
||
margin-top: 20px;
|
||
margin-bottom: 20px;
|
||
}
|
||
.quick {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 1px;
|
||
background: var(--mall-line);
|
||
border: 1px solid var(--mall-line);
|
||
list-style: none;
|
||
margin: 0;
|
||
padding: 0;
|
||
width: 240px;
|
||
}
|
||
.quick li {
|
||
background: #fff;
|
||
text-align: center;
|
||
}
|
||
.quick a {
|
||
display: block;
|
||
padding: 14px 0;
|
||
color: var(--mall-muted);
|
||
text-decoration: none;
|
||
font-size: 12px;
|
||
}
|
||
.quick a:hover {
|
||
color: var(--mall-red);
|
||
}
|
||
.quick svg {
|
||
display: block;
|
||
margin: 0 auto 6px;
|
||
}
|
||
.promos {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 12px;
|
||
}
|
||
.promos img {
|
||
width: 100%;
|
||
height: 160px;
|
||
object-fit: cover;
|
||
display: block;
|
||
}
|
||
.floor {
|
||
margin-bottom: 30px;
|
||
}
|
||
.floor-head {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: baseline;
|
||
border-bottom: 2px solid var(--mall-red);
|
||
margin-bottom: 0;
|
||
}
|
||
.floor-head h2 {
|
||
font-size: 18px;
|
||
margin: 0;
|
||
padding: 10px 0;
|
||
}
|
||
.more {
|
||
font-size: 12px;
|
||
color: var(--mall-muted);
|
||
text-decoration: none;
|
||
}
|
||
.more:hover {
|
||
color: var(--mall-red);
|
||
}
|
||
.floor-body {
|
||
display: flex;
|
||
gap: 0;
|
||
background: #fff;
|
||
}
|
||
.floor-adv img {
|
||
width: 234px;
|
||
height: 614px;
|
||
display: block;
|
||
object-fit: cover;
|
||
}
|
||
.floor-grid {
|
||
flex: 1;
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
gap: 1px;
|
||
background: var(--mall-line);
|
||
}
|
||
.floor-grid :deep(.card) {
|
||
border: 0;
|
||
}
|
||
</style>
|