365 行
9.2 KiB
Vue

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

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

<template>
<div class="page-blog">
<LayoutPageHero
title="旅行攻略"
:subtitle="heroes?.blog || '呼伦贝尔旅行的干货指南,帮你把每一趟出行都过得值'"
:breadcrumbs="[{ text: '攻略' }]"
background-image="/images/seasons/summer-river.jpg"
/>
<!-- TL;DR -->
<section class="page-tldr" aria-label="旅行攻略摘要">
<div class="container">
<div class="page-tldr-inner">
<p>呼伦贝尔旅行攻略合集包含穿搭指南行程规划亲子活动推荐避坑经验等干货内容帮你把每一趟出行都过得值</p>
</div>
</div>
</section>
<section v-if="blogData" class="section">
<div class="container">
<!-- 分类筛选 -->
<div class="blog-filter">
<button
v-for="cat in categories"
:key="cat.key"
class="filter-btn"
:class="{ 'filter-btn--active': activeCategory === cat.key }"
@click="activeCategory = cat.key"
>
{{ cat.label }}
<span class="filter-count">{{ getCategoryCount(cat.key) }}</span>
</button>
</div>
<!-- 文章列表 -->
<div class="blog-grid">
<article
v-for="article in filteredArticles"
:key="article.id"
class="blog-card"
>
<NuxtLink :to="`/blog/${article.id}`" class="blog-card__link">
<div class="blog-card__cover">
<img
:src="article.coverImage"
:alt="article.title"
class="blog-card__img"
loading="lazy"
/>
<span class="blog-card__category">{{ article.category }}</span>
</div>
<div class="blog-card__body">
<div class="blog-card__meta">
<span class="blog-card__date">{{ formatDate(article.date || article.createdAt) }}</span>
<span class="blog-card__author">{{ article.author }}</span>
</div>
<h2 class="blog-card__title">{{ article.title }}</h2>
<p class="blog-card__subtitle">{{ article.subtitle }}</p>
<p class="blog-card__summary">{{ article.summary }}</p>
<div class="blog-card__tags">
<span v-for="tag in article.tags.slice(0, 3)" :key="tag" class="blog-tag">{{ tag }}</span>
</div>
<span class="blog-card__read-more">阅读全文 </span>
</div>
</NuxtLink>
</article>
</div>
<!-- 空状态 -->
<div v-if="filteredArticles.length === 0" class="blog-empty">
<p>暂无该分类的文章</p>
</div>
<!-- CTA -->
<CommonWechatCTA
title="看完攻略,准备出发了吗?"
description="扫码添加定制师微信,根据你的家庭情况和假期安排,推荐合适的行程方案。"
:links="[{ to: '/products', text: '查看定制游产品' }, { to: '/faq', text: '常见问答' }]"
/>
</div>
</section>
</div>
</template>
<script setup>
import { useBreadcrumbSchema } from '~/composables/useJsonLd'
import useScrollReveal from '~/composables/useScrollReveal'
const { data: blogData } = await usePublicApi('blog')
const { data: heroes } = await usePublicApi('heroes')
useHead({
title: '呼伦贝尔旅行攻略 - 亲子游·避坑·穿搭·行程干货 - 呼籁旅行',
meta: [
{ name: 'description', content: '呼籁旅行旅行攻略频道呼伦贝尔亲子游综合指南、几月份去合适、旅行花费明细、避坑指南、草原旅拍穿搭,深耕11年的实用干货。' },
{ name: 'keywords', content: '呼伦贝尔旅游攻略,呼伦贝尔亲子游攻略,呼伦贝尔几月去,呼伦贝尔旅游花费,草原旅拍穿搭' },
{ property: 'og:title', content: '呼伦贝尔旅行攻略 - 呼籁旅行' },
{ property: 'og:description', content: '呼伦贝尔亲子游·避坑指南·穿搭攻略,深耕11年的实用干货。' },
{ property: 'og:type', content: 'website' },
],
link: [
{ rel: 'canonical', href: 'https://1814.love/blog' },
],
})
useBreadcrumbSchema([
{ text: '首页', to: '/' },
{ text: '旅行攻略' },
])
useScrollReveal()
const categories = [
{ key: 'all', label: '全部' },
{ key: '攻略', label: '出行攻略' },
{ key: '亲子', label: '亲子游' },
{ key: '避坑', label: '避坑指南' },
{ key: '穿搭', label: '旅拍穿搭' },
]
const activeCategory = ref('all')
const allArticles = computed(() => blogData.value?.articles || [])
const filteredArticles = computed(() => {
if (activeCategory.value === 'all') return allArticles.value
return allArticles.value.filter(a => a.category === activeCategory.value)
})
function getCategoryCount(key) {
if (key === 'all') return allArticles.value.length
return allArticles.value.filter(a => a.category === key).length
}
function formatDate(dateStr) {
if (!dateStr) return ''
const d = new Date(dateStr.includes('T') ? dateStr : dateStr + 'T00:00:00')
if (isNaN(d.getTime())) return ''
return `${d.getFullYear()}${d.getMonth() + 1}${d.getDate()}`
}
</script>
<style lang="less" scoped>
// 分类筛选
.blog-filter {
display: flex;
flex-wrap: wrap;
gap: @space-sm;
margin-bottom: @space-2xl;
}
.filter-btn {
display: inline-flex;
align-items: center;
gap: @space-xs;
padding: @space-sm @space-md;
background: @color-bg-white;
border: 1px solid @color-border;
border-radius: @border-radius-full;
font-size: @font-size-sm;
color: @color-text-secondary;
cursor: pointer;
transition: all @transition-base;
&:hover {
border-color: @color-primary;
color: @color-primary;
}
&--active {
background: @color-primary;
border-color: @color-primary;
color: #fff;
.filter-count {
background: rgba(255, 255, 255, 0.3);
color: #fff;
}
}
}
.filter-count {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 20px;
height: 20px;
padding: 0 5px;
background: @color-primary-bg;
color: @color-primary;
border-radius: @border-radius-full;
font-size: 11px;
font-weight: @font-weight-medium;
}
// 文章网格
.blog-grid {
display: grid;
grid-template-columns: 1fr;
gap: @space-xl;
.respond-md({
grid-template-columns: 1fr 1fr;
});
.respond-lg({
grid-template-columns: 1fr 1fr 1fr;
});
}
// 文章卡片
.blog-card {
background: @color-bg-white;
border: 1px solid @color-border;
border-radius: @border-radius-lg;
overflow: hidden;
transition: box-shadow @transition-base, transform @transition-base;
&:hover {
box-shadow: @shadow-lg;
transform: translateY(-2px);
.blog-card__img {
transform: scale(1.03);
}
}
&__link {
display: block;
text-decoration: none;
color: inherit;
height: 100%;
}
&__cover {
position: relative;
overflow: hidden;
aspect-ratio: 16 / 9;
background: @color-primary-bg;
}
&__img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform @transition-slow;
}
&__category {
position: absolute;
top: @space-sm;
left: @space-sm;
padding: 3px @space-sm;
background: @color-primary;
color: #fff;
border-radius: @border-radius-sm;
font-size: @font-size-xs;
font-weight: @font-weight-medium;
}
&__body {
padding: @space-lg;
display: flex;
flex-direction: column;
gap: @space-sm;
}
&__meta {
display: flex;
gap: @space-md;
font-size: @font-size-xs;
color: @color-text-muted;
}
&__title {
font-size: @font-size-md;
font-weight: @font-weight-bold;
color: @color-text-primary;
line-height: @line-height-tight;
margin: 0;
}
&__subtitle {
font-size: @font-size-sm;
color: @color-accent;
margin: 0;
line-height: @line-height-tight;
}
&__summary {
font-size: @font-size-sm;
color: @color-text-secondary;
line-height: @line-height-base;
margin: 0;
.text-clamp(3);
}
&__tags {
display: flex;
flex-wrap: wrap;
gap: @space-xs;
margin-top: auto;
padding-top: @space-sm;
}
&__read-more {
display: inline-block;
margin-top: @space-sm;
font-size: @font-size-sm;
color: @color-primary;
font-weight: @font-weight-medium;
transition: color @transition-fast;
.blog-card:hover & {
color: @color-primary-light;
}
}
}
.blog-tag {
display: inline-block;
padding: 2px @space-sm;
background: @color-primary-bg;
color: @color-primary;
border-radius: @border-radius-sm;
font-size: @font-size-xs;
}
// 空状态
.blog-empty {
text-align: center;
padding: @space-3xl;
color: @color-text-muted;
font-size: @font-size-md;
}
// CTA
.blog-cta {
text-align: center;
margin-top: @space-3xl;
padding: @space-2xl;
background: @color-primary-bg;
border-radius: @border-radius-lg;
h2 {
margin-bottom: @space-sm;
}
p {
color: @color-text-secondary;
margin-bottom: @space-xl;
max-width: 500px;
margin-left: auto;
margin-right: auto;
}
}
.blog-cta-links {
display: flex;
justify-content: center;
gap: @space-xl;
flex-wrap: wrap;
}
</style>