YD-WEB/src/views/HomePage.vue
2026-07-27 18:05:58 +08:00

165 lines
8.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="new-home">
<section class="home-hero" @mouseenter="stopBannerRotation" @mouseleave="startBannerRotation">
<div class="home-hero-media" aria-hidden="true">
<div
v-for="(banner, index) in heroBanners"
:key="banner"
class="home-hero-slide"
:class="{ active: index === activeBanner }"
:style="{ backgroundImage: `url('${banner}')` }"
></div>
</div>
<!-- <div class="home-hero-shade"></div> -->
<div class="home-hero-solid"></div>
<div class="home-hero-grid"></div>
<!-- <div class="home-hero-ring ring-one"></div><div class="home-hero-ring ring-two"></div> -->
<div class="container home-hero-content">
<div class="home-eyebrow">YUEDONG · SINCE 2009</div>
<h1>跃出健康<br /><em style="padding-left: 20px;">享受运动</em></h1>
<!-- <p>以跳绳为核心,连接专业装备、国际赛事、教育培训、智慧科技与运动健康,让每一次跃起都更有力量。</p> -->
<div class="home-hero-actions">
<RouterLink class="home-primary-button" to="/about/brand-story">探索跃动 <span>→</span></RouterLink>
<RouterLink class="home-ghost-button" to="/sports/equipment">查看跃动装备</RouterLink>
</div>
</div>
<div class="home-hero-dots" aria-label="首页背景轮播">
<button
v-for="(banner, index) in heroBanners"
:key="`${banner}-dot`"
type="button"
:class="{ active: index === activeBanner }"
:aria-label="`查看第 ${index + 1} 张首页背景图`"
:aria-current="index === activeBanner ? 'true' : undefined"
@click="selectBanner(index)"
></button>
</div>
<div class="home-scroll"><i></i><span>SCROLL TO EXPLORE</span></div>
</section>
<section v-reveal class="home-about container">
<div class="home-honor-carousel" @mouseenter="stopHonorRotation" @mouseleave="startHonorRotation">
<RouterLink
v-for="(honor,index) in honors"
:key="honor.title"
to="/about/brand-story"
class="home-honor-slide"
:class="{ active: index === activeHonor }"
:aria-hidden="index !== activeHonor"
:tabindex="index === activeHonor ? 0 : -1"
>
<img :src="honor.image" :alt="honor.title" />
<div><small>{{ honor.year }} · YUEDONG HONOR</small><strong>{{ honor.title }}</strong></div>
</RouterLink>
<div class="home-honor-controls">
<button type="button" aria-label="上一项荣誉" @click="previousHonor">←</button>
<div>
<button
v-for="(honor,index) in honors"
:key="`${honor.title}-dot`"
type="button"
:class="{ active: index === activeHonor }"
:aria-label="`查看${honor.title}`"
@click="selectHonor(index)"
></button>
</div>
<button type="button" aria-label="下一项荣誉" @click="nextHonor">→</button>
</div>
</div>
<div class="home-about-copy">
<div class="home-section-label">ABOUT YUEDONG</div><h2>从一根跳绳开始,<br />创造运动的更多可能</h2>
<p>跃动体育创立于上海体育大学,是一家提供青少年运动健康促进整体解决方案的企业。我们持续深耕跳绳装备、赛事运营、教育培训与智慧体育,让专业运动走进更多校园、社区与家庭。</p>
<div class="home-about-points"><span>国家体育产业示范项目</span><span>《跳绳》国家标准起草单位</span><span>国际跳绳联合会合作伙伴</span></div>
<RouterLink class="home-text-link" to="/about/brand-story">了解品牌故事 <b>↗</b></RouterLink>
</div>
</section>
<section class="home-products">
<div v-reveal class="container home-section-head light"><div><span>FEATURED EQUIPMENT</span><h2>精选跃动装备</h2></div><RouterLink to="/sports/equipment">查看全部 82 款产品 →</RouterLink></div>
<div class="container home-product-grid">
<RouterLink v-for="(product,index) in featuredProducts" :key="product.id" v-reveal :to="`/sports/equipment/detail/${product.id}`" :style="{ '--delay': `${index * 80}ms` }">
<div class="home-product-image"><img :src="product.images[0]" :alt="product.name" /><span>0{{ index + 1 }}</span></div>
<small>{{ product.category }}</small><h3>{{ product.name }}</h3><b>查看产品详情 ↗</b>
</RouterLink>
</div>
</section>
<section class="home-news">
<div v-reveal class="container home-section-head"><div><span>EVENTS & NEWS</span><h2>竞赛演艺</h2></div><RouterLink to="/sports/competition#event-news">更多赛事动态 →</RouterLink></div>
<div class="container home-news-grid">
<RouterLink v-for="(article,index) in eventArticles" :key="article.title" v-reveal :to="article.to" :style="{ '--delay': `${index * 90}ms` }">
<div class="home-news-image"><img :src="article.image" :alt="article.title" /><span>EVENT 0{{ index + 1 }}</span></div>
<div><small>跃动赛事 · 最新动态</small><h3>{{ article.title }}</h3><p>{{ article.body[0] }}</p><b>阅读全文 →</b></div>
</RouterLink>
</div>
</section>
<section v-reveal class="home-cta">
<div class="home-cta-orbit"></div><div class="container"><small>MOVE WITH YUEDONG</small><h2>与跃动一起,连接更大的运动世界</h2><p>赛事合作、产品定制、教育培训与全球业务合作,期待与你共同创造。</p><RouterLink to="/contact/business">联系我们 <span>→</span></RouterLink></div>
</section>
</div>
</template>
<script setup>
import { onBeforeUnmount, onMounted, ref } from 'vue';
import products from '../data/product-details.json';
import { pages } from '../data/page-content.js';
const featuredProducts = [products[0], products[32], products[58], products[70]].filter((item) => item?.images?.[0]);
const heroBanners = ['/site/assets/images/banners/1.jpg', '/site/assets/images/banners/2.jpg'];
const activeBanner = ref(0);
let bannerTimer;
const stopBannerRotation = () => {
if (bannerTimer) window.clearInterval(bannerTimer);
bannerTimer = undefined;
};
const startBannerRotation = () => {
stopBannerRotation();
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
bannerTimer = window.setInterval(() => {
activeBanner.value = (activeBanner.value + 1) % heroBanners.length;
}, 5500);
};
const selectBanner = (index) => {
activeBanner.value = index;
startBannerRotation();
};
const honors = [
{ year: '2019', title: '国家体育产业示范项目', image: '/site/assets/images/about/about.jpg' },
{ year: '2025', title: '《跳绳》国家标准起草单位', image: '/materials/关于跃动/009-跳绳国家标准-第一起草单位.webp' },
{ year: '2023', title: '国际跳绳联合会合作伙伴', image: '/materials/关于跃动/008-世界跳绳锦标赛官方合作伙伴.webp' }
];
const activeHonor = ref(0);
let honorTimer;
const stopHonorRotation = () => { if (honorTimer) window.clearInterval(honorTimer); honorTimer = undefined; };
const startHonorRotation = () => {
stopHonorRotation();
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
honorTimer = window.setInterval(() => { activeHonor.value = (activeHonor.value + 1) % honors.length; }, 4500);
};
const selectHonor = (index) => { activeHonor.value = index; startHonorRotation(); };
const previousHonor = () => { activeHonor.value = (activeHonor.value - 1 + honors.length) % honors.length; startHonorRotation(); };
const nextHonor = () => { activeHonor.value = (activeHonor.value + 1) % honors.length; startHonorRotation(); };
onMounted(() => {
startBannerRotation();
startHonorRotation();
});
onBeforeUnmount(() => {
stopBannerRotation();
stopHonorRotation();
});
const eventArticles = pages.sports.sections['event-news'].articles.slice(0, 3).map((article, index) => ({ ...article, image: pages.sports.sections['event-news'].gallery[index]?.src || pages.sports.hero?.src, to: `/detail/sports/event-news/${index}` }));
const vReveal = {
mounted(el) {
el.classList.add('reveal');
const observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) { el.classList.add('is-visible'); observer.disconnect(); }
}, { threshold: .1 });
observer.observe(el);
}
};
</script>