覆盖最新版本代码,包含: - 新增品牌百科、公司百科页面及CMS管理 - 新增微信引导组件(WechatCTA) - 新增品牌/公司图片资源 - 全站组件/页面/数据/样式更新 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
364 行
9.1 KiB
Vue
364 行
9.1 KiB
Vue
<template>
|
||
<div class="page-blog">
|
||
<LayoutPageHero
|
||
title="旅行攻略"
|
||
subtitle="呼伦贝尔旅行的干货指南,帮你把每一趟出行都过得值"
|
||
: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')
|
||
|
||
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>
|