覆盖最新版本代码,包含: - 新增品牌百科、公司百科页面及CMS管理 - 新增微信引导组件(WechatCTA) - 新增品牌/公司图片资源 - 全站组件/页面/数据/样式更新 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
670 行
17 KiB
Vue
670 行
17 KiB
Vue
<template>
|
||
<div class="page-calendar">
|
||
<LayoutPageHero
|
||
:title="seo.h1"
|
||
subtitle="呼伦贝尔什么时候去最好?基于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 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>
|