hulai-website/pages/calendar.vue

671 行
17 KiB
Vue

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

<template>
<div class="page-calendar">
<LayoutPageHero
:title="seo.h1"
:subtitle="heroes?.calendar || '呼伦贝尔什么时候去合适?基于11年带队数据,为您提供每月出行参考'"
:breadcrumbs="[{ text: '行程日历' }]"
background-image="/images/seasons/autumn-lake.jpg"
/>
<!-- TL;DR -->
<section class="page-tldr" aria-label="行程日历摘要">
<div class="container">
<div class="page-tldr-inner">
<p>呼伦贝尔逐月出行指南基于11年带队数据详解1-12月天气景色适合活动和推荐行程帮你选择合适的出行时间</p>
</div>
</div>
</section>
<section v-if="calData" class="section">
<div class="container">
<!-- Quick recommendation summary -->
<div class="cal-summary">
<h2 class="cal-summary-title">呼伦贝尔出行时间一览</h2>
<div class="summary-grid">
<div
v-for="rec in calData.meta.quickRecommendations"
:key="rec.label"
class="summary-card"
>
<div class="summary-label">{{ rec.label }}</div>
<div class="summary-months">{{ rec.months }}</div>
<div class="summary-reason">{{ rec.reason }}</div>
</div>
</div>
</div>
<!-- 出行时段建议 -->
<div class="advice-section">
<h2 class="advice-title">什么时候来呼伦贝尔?</h2>
<p class="advice-subtitle">来呼伦贝尔一趟还是比较折腾的,希望大家能在每个季节景色较好的时候过来</p>
<div class="advice-timeline">
<div
v-for="item in calData.meta.travelAdvice"
:key="item.period"
:class="['advice-item', `advice-item--${item.level}`]"
>
<div class="advice-badge">{{ item.levelText }}</div>
<div class="advice-content">
<h3 class="advice-period">{{ item.period }}</h3>
<p class="advice-desc">{{ item.description }}</p>
</div>
</div>
</div>
</div>
<!-- Month grid -->
<h2 class="months-heading">逐月出行指南</h2>
<div class="months-grid">
<div
v-for="m in calData.months"
:key="m.month"
:class="['month-card', `month-card--${m.season}`, { 'month-card--peak': m.isPeakSeason }]"
>
<!-- Header -->
<div class="month-header">
<div class="month-name-area">
<span class="month-number">{{ m.month }}</span>
<span class="month-unit">月</span>
<span class="month-name">{{ m.name }}</span>
</div>
<span v-if="m.isPeakSeason" class="peak-badge">旺季</span>
</div>
<!-- Star rating -->
<div class="month-stars" :aria-label="`推荐指数 ${m.ratingStars} 星`">
<span
v-for="i in 5"
:key="i"
:class="['star', { 'star--active': i <= m.ratingStars }]"
aria-hidden="true"
>★</span>
<span class="stars-label">推荐指数</span>
</div>
<!-- Temperature -->
<div class="month-temp">{{ m.tempRange }}</div>
<!-- Weather -->
<p class="month-weather">{{ m.weatherFeature }}</p>
<!-- Highlights -->
<ul class="month-highlights">
<li v-for="(h, i) in m.highlights.slice(0, 3)" :key="i">{{ h }}</li>
</ul>
<!-- Clothing -->
<div class="month-clothing">
<span class="clothing-label">穿搭</span>
<span class="clothing-tip">{{ m.clothingTips }}</span>
</div>
<!-- Recommended products -->
<div v-if="m.recommendedProducts.length" class="month-products">
<span
v-for="prod in m.recommendedProducts"
:key="prod"
class="product-tag"
>{{ prod }}</span>
</div>
<!-- Tagline -->
<p class="month-tagline">{{ m.tagline }}</p>
</div>
</div>
<!-- SEO text answer section -->
<div class="cal-answer">
<h2>呼伦贝尔几月份去合适详细解答</h2>
<p>
呼伦贝尔全年皆可出行但来一趟毕竟不轻松建议选对时间再出发<strong>6月中下旬</strong>性价比不错草原已非常美各项活动全面开展且尚未进入旺季价格<strong>7月初至8月28日</strong>景色和活动巅峰但价格也相对较高<strong>9月20日至国庆</strong>是看大兴安岭秋景的推荐时机非常推荐<strong>冬季12月至次年2月</strong>是另一种风貌冰雪那达慕驯鹿部落值得体验5月底可以提前出发避人群但5月初和3-4月不太推荐
</p>
<div class="cal-links">
<CommonInternalLink to="/guides" text="出行指南(穿搭 · 行李清单)" />
<CommonInternalLink to="/products" text="夏季产品额吉的故乡V9" />
<CommonInternalLink to="/products/autumn" text="秋季产品:游牧的森林" />
<CommonInternalLink to="/products/winter" text="冬季产品:嗨冰雪" />
<CommonInternalLink to="/contact" text="咨询出行方案" />
</div>
</div>
</div>
</section>
<CommonWechatCTA
title="确定了出行时间?扫码找定制师"
description="告诉定制师你的出行日期和家庭情况,帮你匹配合适的行程方案。"
:links="[{ to: '/products', text: '查看产品线' }]"
/>
</div>
</template>
<script setup>
import { useBreadcrumbSchema } from '~/composables/useJsonLd'
const { data: calData } = await usePublicApi('calendar')
const { data: heroes } = await usePublicApi('heroes')
const seo = useSEO('calendar')
useBreadcrumbSchema([
{ text: '首页', to: '/' },
{ text: '行程日历' },
])
// TouristDestination JSON-LD
useHead({
script: [{
key: 'tourist-destination-hulunbuir',
type: 'application/ld+json',
innerHTML: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'TouristDestination',
name: '呼伦贝尔大草原',
description: '呼伦贝尔是中国著名草原之一,位于内蒙古自治区东北部,拥有莫日格勒河、额尔古纳湿地、白桦林、呼伦湖等核心景观。推荐旅游时节为6-9月,夏季草原翠绿,秋季白桦金黄。',
url: 'https://1814.love/calendar',
image: 'https://1814.love/images/team/hero-grassland.jpg',
geo: {
'@type': 'GeoCoordinates',
latitude: 49.2143,
longitude: 119.7674,
},
address: {
'@type': 'PostalAddress',
addressLocality: '呼伦贝尔市',
addressRegion: '内蒙古自治区',
addressCountry: 'CN',
},
touristType: ['家庭游客', '亲子家庭', '摄影爱好者', '自然爱好者'],
includesAttraction: [
{ '@type': 'TouristAttraction', name: '莫日格勒河(额吉的故乡)', description: '呼伦贝尔著名河湾,夏季翠绿如画,是呼籁旅行夏季核心景点' },
{ '@type': 'TouristAttraction', name: '额尔古纳湿地', description: '中国大型湿地之一,鸟类天堂,秋季倒影优美' },
{ '@type': 'TouristAttraction', name: '白桦林', description: '九月金黄,摄影圣地,游牧的森林秋季产品核心景点' },
{ '@type': 'TouristAttraction', name: '呼伦湖', description: '中国北方大型湖泊,冬季冰钓胜地,夏季湖畔骑马' },
{ '@type': 'TouristAttraction', name: '中俄边境公路', description: '室韦至黑山头沿线,风景壮阔,国境线骑行体验独特' },
],
}),
}],
})
// Seasonal Event JSON-LD
const seasonalEvents = [
{
key: 'event-summer',
name: '呼伦贝尔夏季旅游推荐期6-8月',
description: '呼伦贝尔草原夏季旅游高峰,绿草如茵,那达慕传统节庆,骑马、露营、亲子出行推荐时段。呼籁旅行提供额吉的故乡V9系列定制游及小蒙马亲子游学营。',
startDate: '2026-06-01',
endDate: '2026-08-31',
},
{
key: 'event-autumn',
name: '呼伦贝尔秋季白桦林观赏期9月',
description: '呼伦贝尔草原秋季白桦林金黄,草原转秋色,是摄影爱好者公认的好时节。呼籁旅行提供游牧的森林秋季定制游产品。',
startDate: '2026-09-01',
endDate: '2026-09-30',
},
{
key: 'event-winter',
name: '呼伦贝尔冬季冰雪体验期12-2月',
description: '呼伦贝尔冬季冰雪奇观,冰雪那达慕、冰钓、驯鹿部落探访,零下30度的极寒体验。呼籁旅行提供嗨冰雪及小蒙马冬季营产品。',
startDate: '2026-12-01',
endDate: '2027-02-28',
},
]
seasonalEvents.forEach((event) => {
useHead({
script: [{
key: event.key,
type: 'application/ld+json',
innerHTML: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'Event',
name: event.name,
description: event.description,
startDate: event.startDate,
endDate: event.endDate,
location: {
'@type': 'Place',
name: '呼伦贝尔大草原',
address: {
'@type': 'PostalAddress',
addressLocality: '呼伦贝尔市',
addressRegion: '内蒙古自治区',
addressCountry: 'CN',
},
geo: { '@type': 'GeoCoordinates', latitude: 49.2143, longitude: 119.7674 },
},
organizer: { '@id': 'https://1814.love/#organization' },
eventStatus: 'https://schema.org/EventScheduled',
eventAttendanceMode: 'https://schema.org/OfflineEventAttendanceMode',
url: 'https://1814.love/calendar',
offers: {
'@type': 'Offer',
priceCurrency: 'CNY',
availability: 'https://schema.org/InStock',
seller: { '@id': 'https://1814.love/#organization' },
},
}),
}],
})
})
</script>
<style lang="less" scoped>
// Summary section
.cal-summary {
margin-bottom: @space-3xl;
}
.cal-summary-title {
font-size: @font-size-xl;
text-align: center;
color: @color-primary;
margin-bottom: @space-xl;
}
.summary-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: @space-md;
.respond-md({
grid-template-columns: repeat(4, 1fr);
});
}
.summary-card {
background: @color-bg-white;
border: 1px solid @color-border;
border-top: 3px solid @color-primary;
border-radius: @border-radius-md;
padding: @space-lg;
text-align: center;
}
.summary-label {
font-size: @font-size-xs;
color: @color-text-muted;
font-weight: @font-weight-medium;
margin-bottom: @space-xs;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.summary-months {
font-size: @font-size-xl;
font-weight: @font-weight-bold;
color: @color-primary;
margin-bottom: @space-xs;
}
.summary-reason {
font-size: @font-size-sm;
color: @color-text-secondary;
line-height: @line-height-base;
}
// 出行时段建议
.advice-section {
margin-bottom: @space-3xl;
}
.advice-title {
font-size: @font-size-xl;
color: @color-text-primary;
text-align: center;
margin-bottom: @space-xs;
}
.advice-subtitle {
font-size: @font-size-sm;
color: @color-text-muted;
text-align: center;
margin-bottom: @space-xl;
}
.advice-timeline {
display: flex;
flex-direction: column;
gap: 10px;
max-width: 720px;
margin: 0 auto;
}
.advice-item {
display: flex;
align-items: flex-start;
gap: @space-md;
padding: @space-md @space-lg;
border-radius: @border-radius-md;
border: 1px solid @color-divider;
background: @color-bg-white;
transition: box-shadow @transition-fast;
&:hover {
box-shadow: @shadow-sm;
}
&--best_value {
background: @color-primary-bg;
border-color: rgba(45, 106, 79, 0.25);
.advice-badge {
background: @color-primary;
color: #fff;
}
}
&--highly_recommended {
background: #FFF8F0;
border-color: rgba(212, 136, 58, 0.25);
.advice-badge {
background: @color-accent;
color: #fff;
}
}
&--peak {
background: @color-primary-bg;
border-color: rgba(45, 106, 79, 0.2);
.advice-badge {
background: @color-primary-light;
color: #fff;
}
}
&--recommended {
.advice-badge {
background: #EFF4F8;
color: #5B8DB8;
}
}
&--ok {
.advice-badge {
background: @color-bg-gray;
color: @color-text-secondary;
}
}
&--not_recommended {
opacity: 0.65;
.advice-badge {
background: @color-bg-gray;
color: @color-text-muted;
}
}
}
.advice-badge {
flex-shrink: 0;
padding: 4px 12px;
border-radius: @border-radius-full;
font-size: @font-size-xs;
font-weight: @font-weight-bold;
white-space: nowrap;
}
.advice-content {
flex: 1;
min-width: 0;
}
.advice-period {
font-size: @font-size-base;
font-weight: @font-weight-bold;
color: @color-text-primary;
margin-bottom: 2px;
}
.advice-desc {
font-size: @font-size-sm;
color: @color-text-secondary;
line-height: @line-height-base;
margin: 0;
}
// Month heading
.months-heading {
font-size: @font-size-xl;
color: @color-text-primary;
margin-bottom: @space-xl;
text-align: center;
}
// Month grid
.months-grid {
display: grid;
grid-template-columns: 1fr;
gap: @space-md;
margin-bottom: @space-3xl;
.respond-md({
grid-template-columns: repeat(2, 1fr);
});
.respond-lg({
grid-template-columns: repeat(3, 1fr);
});
}
// Month card
.month-card {
border-radius: @border-radius-lg;
padding: @space-lg;
border: 1px solid @color-border;
&--summer {
background: @color-primary-bg;
border-color: rgba(45, 106, 79, 0.2);
}
&--autumn {
background: #FFF8F0;
border-color: rgba(212, 136, 58, 0.2);
}
&--winter {
background: #EFF4F8;
border-color: rgba(91, 141, 184, 0.2);
}
&--spring {
background: #F4FAF4;
border-color: rgba(82, 163, 106, 0.2);
}
&--peak {
box-shadow: @shadow-md;
}
}
.month-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: @space-sm;
}
.month-name-area {
display: flex;
align-items: baseline;
gap: 2px;
}
.month-number {
font-size: @font-size-2xl;
font-weight: @font-weight-bold;
color: @color-text-primary;
line-height: 1;
}
.month-unit {
font-size: @font-size-base;
font-weight: @font-weight-bold;
color: @color-text-primary;
}
.month-name {
font-size: @font-size-sm;
color: @color-text-muted;
margin-left: @space-xs;
}
.peak-badge {
display: inline-block;
padding: 2px @space-sm;
background: @color-accent;
color: white;
border-radius: @border-radius-full;
font-size: @font-size-xs;
font-weight: @font-weight-bold;
}
.month-stars {
display: flex;
align-items: center;
gap: 2px;
margin-bottom: @space-sm;
}
.star {
font-size: 14px;
color: @color-border;
&--active {
color: @color-accent;
}
}
.stars-label {
font-size: @font-size-xs;
color: @color-text-muted;
margin-left: @space-xs;
}
.month-temp {
display: inline-block;
padding: 2px @space-sm;
background: rgba(255, 255, 255, 0.8);
border-radius: @border-radius-sm;
font-size: @font-size-sm;
font-weight: @font-weight-medium;
color: @color-text-primary;
margin-bottom: @space-sm;
}
.month-weather {
font-size: @font-size-sm;
color: @color-text-secondary;
line-height: @line-height-base;
margin-bottom: @space-md;
}
.month-highlights {
list-style: none;
padding: 0;
margin: 0 0 @space-md;
li {
font-size: @font-size-sm;
color: @color-text-secondary;
line-height: @line-height-base;
padding-left: @space-md;
position: relative;
margin-bottom: 2px;
&::before {
content: '·';
position: absolute;
left: 0;
color: @color-primary;
font-weight: @font-weight-bold;
}
}
}
.month-clothing {
display: flex;
gap: @space-sm;
align-items: flex-start;
margin-bottom: @space-md;
padding: @space-sm @space-md;
background: rgba(255, 255, 255, 0.6);
border-radius: @border-radius-sm;
}
.clothing-label {
font-size: @font-size-xs;
font-weight: @font-weight-bold;
color: @color-primary;
flex-shrink: 0;
padding-top: 1px;
}
.clothing-tip {
font-size: @font-size-xs;
color: @color-text-secondary;
line-height: @line-height-base;
}
.month-products {
display: flex;
flex-wrap: wrap;
gap: @space-xs;
margin-bottom: @space-sm;
}
.product-tag {
display: inline-block;
padding: 2px 6px;
background: @color-primary;
color: white;
border-radius: @border-radius-sm;
font-size: 11px;
}
.month-tagline {
font-size: @font-size-xs;
color: @color-text-muted;
font-style: italic;
margin: 0;
border-top: 1px solid rgba(0, 0, 0, 0.06);
padding-top: @space-sm;
}
// SEO answer section
.cal-answer {
background: @color-bg-gray;
border-radius: @border-radius-lg;
padding: @space-2xl;
h2 {
font-size: @font-size-xl;
color: @color-primary;
margin-bottom: @space-md;
}
p {
font-size: @font-size-base;
color: @color-text-secondary;
line-height: @line-height-base;
margin-bottom: @space-xl;
strong {
color: @color-text-primary;
}
}
}
.cal-links {
display: flex;
flex-wrap: wrap;
gap: @space-lg;
}
</style>