@@ -0,0 +1,624 @@
|
||||
<template>
|
||||
<div v-if="story" class="page-story-detail">
|
||||
<LayoutPageHero
|
||||
:title="story.title"
|
||||
:subtitle="story.subtitle"
|
||||
:breadcrumbs="[{ text: '客户故事', to: '/stories' }, { text: story.familyType }]"
|
||||
/>
|
||||
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<div class="story-layout">
|
||||
<!-- 主内容 -->
|
||||
<main class="story-main">
|
||||
<!-- 故事元信息 -->
|
||||
<div class="story-meta">
|
||||
<span class="story-meta__family">{{ story.familyType }}</span>
|
||||
<span v-if="story.childrenAge && !story.childrenAge.includes('⚠️')" class="story-meta__children">孩子 {{ story.childrenAge }}</span>
|
||||
<span v-if="story.fromCity && story.fromCity !== '⚠️待填'" class="story-meta__city">来自 {{ story.fromCity }}</span>
|
||||
<span class="story-meta__date">{{ story.travelDate }}</span>
|
||||
<span v-if="story.tripProduct" class="story-meta__product">{{ story.tripProduct }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 小红书来源信息 -->
|
||||
<div v-if="story.author" class="story-source">
|
||||
<span class="story-source__author">{{ story.author }}</span>
|
||||
<span v-if="story.authorPlatform" class="story-source__platform">{{ story.authorPlatform }}</span>
|
||||
<span v-if="story.likes" class="story-source__likes">{{ story.likes }} 赞</span>
|
||||
</div>
|
||||
|
||||
<!-- 配图建议 -->
|
||||
<div v-if="story.imageHints" class="story-image-placeholder">
|
||||
<div class="story-image-placeholder__inner">
|
||||
<span class="story-image-placeholder__icon">📷</span>
|
||||
<p class="story-image-placeholder__hint">{{ story.imageHints }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 故事摘要 -->
|
||||
<div class="story-summary">
|
||||
<p>{{ story.summary }}</p>
|
||||
</div>
|
||||
|
||||
<!-- 小红书搜索提示(直链需要token,引导用户搜索) -->
|
||||
<div v-if="story.authorPlatform === '小红书' && story.title" class="story-source-link">
|
||||
<span class="story-source-link__tip">📱 在小红书搜索「{{ story.title }}」可查看原帖</span>
|
||||
</div>
|
||||
|
||||
<!-- 故事章节 -->
|
||||
<div v-if="story.sections && story.sections.length" class="story-sections">
|
||||
<div
|
||||
v-for="(section, index) in story.sections"
|
||||
:key="index"
|
||||
class="story-section"
|
||||
>
|
||||
<h2 class="story-section__heading">{{ section.heading }}</h2>
|
||||
<p class="story-section__content">{{ section.content }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 印象深刻的瞬间 -->
|
||||
<div v-if="story.keyMoments && story.keyMoments.length" class="story-moments">
|
||||
<h2 class="story-moments__title">印象深刻的瞬间</h2>
|
||||
<div class="story-moments__list">
|
||||
<div
|
||||
v-for="(moment, index) in story.keyMoments"
|
||||
:key="index"
|
||||
class="moment-item"
|
||||
>
|
||||
<span class="moment-item__icon">✨</span>
|
||||
<p class="moment-item__text">{{ moment }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 来源声明 -->
|
||||
<div v-if="story.disclaimer" class="story-disclaimer">
|
||||
<p>{{ story.disclaimer }}</p>
|
||||
</div>
|
||||
|
||||
<!-- 标签 -->
|
||||
<div class="story-tags">
|
||||
<span class="story-tags__label">标签:</span>
|
||||
<span v-for="tag in story.tags" :key="tag" class="story-tag">{{ tag }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 前后导航 -->
|
||||
<div class="story-nav">
|
||||
<NuxtLink
|
||||
v-if="prevStory"
|
||||
:to="`/stories/${prevStory.id}`"
|
||||
class="story-nav__item story-nav__item--prev"
|
||||
>
|
||||
<span class="story-nav__label">上一个故事</span>
|
||||
<span class="story-nav__title">{{ prevStory.title }}</span>
|
||||
</NuxtLink>
|
||||
<NuxtLink
|
||||
v-if="nextStory"
|
||||
:to="`/stories/${nextStory.id}`"
|
||||
class="story-nav__item story-nav__item--next"
|
||||
>
|
||||
<span class="story-nav__label">下一个故事</span>
|
||||
<span class="story-nav__title">{{ nextStory.title }}</span>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- 侧边栏 -->
|
||||
<aside class="story-sidebar">
|
||||
<!-- 咨询 CTA -->
|
||||
<div class="sidebar-card sidebar-card--cta">
|
||||
<h3 class="sidebar-card__title">想要一段类似的旅行?</h3>
|
||||
<p class="sidebar-card__desc">联系呼籁定制师,一对一规划专属家庭行程。</p>
|
||||
<CommonInternalLink to="/contact" text="免费咨询定制师" />
|
||||
</div>
|
||||
|
||||
<!-- 所有故事 -->
|
||||
<div class="sidebar-card">
|
||||
<h3 class="sidebar-card__title">更多家庭故事</h3>
|
||||
<ul class="sidebar-stories">
|
||||
<li
|
||||
v-for="s in detailStories"
|
||||
:key="s.id"
|
||||
class="sidebar-story"
|
||||
:class="{ 'sidebar-story--active': s.id === story.id }"
|
||||
>
|
||||
<NuxtLink :to="`/stories/${s.id}`" class="sidebar-story__link">
|
||||
<span class="sidebar-story__family">{{ s.familyType }}</span>
|
||||
<span class="sidebar-story__title">{{ s.title }}</span>
|
||||
</NuxtLink>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 快速链接 -->
|
||||
<div class="sidebar-card">
|
||||
<h3 class="sidebar-card__title">相关内容</h3>
|
||||
<div class="sidebar-links">
|
||||
<CommonInternalLink to="/reviews" text="查看更多客户评价" />
|
||||
<CommonInternalLink to="/products" text="旅行产品详情" />
|
||||
<CommonInternalLink to="/faq" text="常见问答" />
|
||||
<CommonInternalLink to="/about" text="关于呼籁" />
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- 404 -->
|
||||
<div v-else class="page-not-found">
|
||||
<div class="container">
|
||||
<h1>故事不存在</h1>
|
||||
<NuxtLink to="/stories">返回客户故事列表</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import storiesData from '~/data/stories.json'
|
||||
import { useBreadcrumbSchema, useCustomerStorySchema } from '~/composables/useJsonLd'
|
||||
|
||||
const route = useRoute()
|
||||
const allStories = storiesData.stories
|
||||
// 只有原创故事(有 content 字段)才有详情页
|
||||
const detailStories = allStories.filter(s => s.content)
|
||||
const story = allStories.find(s => s.id === route.params.id)
|
||||
|
||||
if (!story) {
|
||||
throw createError({ statusCode: 404, statusMessage: '故事不存在' })
|
||||
}
|
||||
|
||||
// SEO
|
||||
useHead({
|
||||
title: story.seo?.title || story.title,
|
||||
meta: [
|
||||
{ name: 'description', content: story.seo?.description || story.summary },
|
||||
{ name: 'keywords', content: story.seo?.keywords || (story.tags || []).join(',') },
|
||||
{ name: 'author', content: story.authorName || '呼籁旅行' },
|
||||
{ name: 'article:published_time', content: story.travelDate + '-01T00:00:00+08:00' },
|
||||
{ name: 'geo.region', content: 'CN-NM' },
|
||||
{ name: 'geo.placename', content: '呼伦贝尔' },
|
||||
{ property: 'og:title', content: story.seo?.title || story.title },
|
||||
{ property: 'og:description', content: story.seo?.description || story.summary },
|
||||
{ property: 'og:type', content: 'article' },
|
||||
{ property: 'og:url', content: `https://1814.love/stories/${story.id}` },
|
||||
{ property: 'og:site_name', content: '呼籁旅行' },
|
||||
{ property: 'og:locale', content: 'zh_CN' },
|
||||
],
|
||||
link: [
|
||||
{ rel: 'canonical', href: `https://1814.love/stories/${story.id}` },
|
||||
],
|
||||
})
|
||||
|
||||
useBreadcrumbSchema([
|
||||
{ text: '首页', to: '/' },
|
||||
{ text: '客户故事', to: '/stories' },
|
||||
{ text: story.familyType },
|
||||
{ text: story.title },
|
||||
])
|
||||
|
||||
useCustomerStorySchema(story)
|
||||
|
||||
// 上下篇(仅在有详情页的故事之间切换)
|
||||
const currentIndex = detailStories.findIndex(s => s.id === story.id)
|
||||
const prevStory = currentIndex > 0 ? detailStories[currentIndex - 1] : null
|
||||
const nextStory = currentIndex < detailStories.length - 1 ? detailStories[currentIndex + 1] : null
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.story-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: @space-2xl;
|
||||
|
||||
.respond-lg({
|
||||
grid-template-columns: 1fr 300px;
|
||||
align-items: start;
|
||||
});
|
||||
}
|
||||
|
||||
.story-main {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
// 元信息
|
||||
.story-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: @space-sm;
|
||||
margin-bottom: @space-lg;
|
||||
font-size: @font-size-sm;
|
||||
}
|
||||
|
||||
.story-meta__family {
|
||||
padding: 3px @space-sm;
|
||||
background: @color-primary;
|
||||
color: #fff;
|
||||
border-radius: @border-radius-sm;
|
||||
font-size: @font-size-xs;
|
||||
font-weight: @font-weight-medium;
|
||||
}
|
||||
|
||||
.story-meta__children,
|
||||
.story-meta__city,
|
||||
.story-meta__date {
|
||||
color: @color-text-muted;
|
||||
}
|
||||
|
||||
.story-meta__product {
|
||||
color: @color-primary;
|
||||
font-weight: @font-weight-medium;
|
||||
font-size: @font-size-xs;
|
||||
padding: 2px @space-sm;
|
||||
background: @color-primary-bg;
|
||||
border-radius: @border-radius-full;
|
||||
}
|
||||
|
||||
// 占位提示
|
||||
.story-placeholder-notice {
|
||||
background: #fef9e7;
|
||||
border: 1px solid #f39c12;
|
||||
border-radius: @border-radius-md;
|
||||
padding: @space-md @space-lg;
|
||||
font-size: @font-size-sm;
|
||||
color: #7d6608;
|
||||
margin-bottom: @space-xl;
|
||||
}
|
||||
|
||||
// 配图占位
|
||||
.story-image-placeholder {
|
||||
margin-bottom: @space-xl;
|
||||
border-radius: @border-radius-lg;
|
||||
overflow: hidden;
|
||||
aspect-ratio: 16 / 7;
|
||||
background: @color-primary-bg;
|
||||
border: 2px dashed @color-primary-lighter;
|
||||
|
||||
&__inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
padding: @space-xl;
|
||||
gap: @space-md;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
font-size: 36px;
|
||||
}
|
||||
|
||||
&__hint {
|
||||
font-size: @font-size-sm;
|
||||
color: @color-text-muted;
|
||||
text-align: center;
|
||||
line-height: @line-height-base;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 摘要
|
||||
.story-summary {
|
||||
padding: @space-lg;
|
||||
background: @color-primary-bg;
|
||||
border-left: 4px solid @color-primary;
|
||||
border-radius: 0 @border-radius-md @border-radius-md 0;
|
||||
margin-bottom: @space-xl;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
font-size: @font-size-md;
|
||||
color: @color-text-secondary;
|
||||
line-height: @line-height-base;
|
||||
}
|
||||
}
|
||||
|
||||
// 故事章节
|
||||
.story-sections {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: @space-xl;
|
||||
margin-bottom: @space-2xl;
|
||||
}
|
||||
|
||||
.story-section {
|
||||
&__heading {
|
||||
font-size: @font-size-xl;
|
||||
font-weight: @font-weight-bold;
|
||||
color: @color-text-primary;
|
||||
margin: 0 0 @space-md;
|
||||
padding-bottom: @space-sm;
|
||||
border-bottom: 2px solid @color-primary-bg;
|
||||
}
|
||||
|
||||
&__content {
|
||||
font-size: @font-size-base;
|
||||
color: @color-text-secondary;
|
||||
line-height: @line-height-base;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 印象瞬间
|
||||
.story-moments {
|
||||
background: @color-bg-gray;
|
||||
border-radius: @border-radius-lg;
|
||||
padding: @space-xl;
|
||||
margin-bottom: @space-2xl;
|
||||
|
||||
&__title {
|
||||
font-size: @font-size-lg;
|
||||
font-weight: @font-weight-bold;
|
||||
color: @color-text-primary;
|
||||
margin: 0 0 @space-lg;
|
||||
}
|
||||
|
||||
&__list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: @space-md;
|
||||
}
|
||||
}
|
||||
|
||||
.moment-item {
|
||||
display: flex;
|
||||
gap: @space-md;
|
||||
align-items: flex-start;
|
||||
|
||||
&__icon {
|
||||
font-size: 18px;
|
||||
flex-shrink: 0;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
&__text {
|
||||
font-size: @font-size-sm;
|
||||
color: @color-text-secondary;
|
||||
line-height: @line-height-base;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 标签
|
||||
.story-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: @space-sm;
|
||||
margin-top: @space-2xl;
|
||||
padding-top: @space-lg;
|
||||
border-top: 1px solid @color-border;
|
||||
margin-bottom: @space-xl;
|
||||
}
|
||||
|
||||
.story-tags__label {
|
||||
font-size: @font-size-sm;
|
||||
color: @color-text-muted;
|
||||
}
|
||||
|
||||
.story-tag {
|
||||
padding: 3px @space-sm;
|
||||
background: @color-primary-bg;
|
||||
color: @color-primary;
|
||||
border-radius: @border-radius-sm;
|
||||
font-size: @font-size-xs;
|
||||
}
|
||||
|
||||
// 前后导航
|
||||
.story-nav {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: @space-md;
|
||||
padding-top: @space-lg;
|
||||
border-top: 1px solid @color-border;
|
||||
|
||||
&__item {
|
||||
padding: @space-md;
|
||||
background: @color-bg-white;
|
||||
border: 1px solid @color-border;
|
||||
border-radius: @border-radius-md;
|
||||
text-decoration: none;
|
||||
transition: all @transition-base;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: @space-xs;
|
||||
|
||||
&:hover {
|
||||
border-color: @color-primary;
|
||||
background: @color-primary-bg;
|
||||
}
|
||||
|
||||
&--next {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-size: @font-size-xs;
|
||||
color: @color-text-muted;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-size: @font-size-sm;
|
||||
color: @color-text-primary;
|
||||
line-height: @line-height-tight;
|
||||
.text-clamp(2);
|
||||
}
|
||||
}
|
||||
|
||||
// 侧边栏
|
||||
.story-sidebar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: @space-lg;
|
||||
position: sticky;
|
||||
top: 90px;
|
||||
}
|
||||
|
||||
.sidebar-card {
|
||||
background: @color-bg-white;
|
||||
border: 1px solid @color-border;
|
||||
border-radius: @border-radius-lg;
|
||||
padding: @space-lg;
|
||||
|
||||
&--cta {
|
||||
background: @color-primary-bg;
|
||||
border-color: @color-primary-lighter;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-size: @font-size-base;
|
||||
font-weight: @font-weight-bold;
|
||||
color: @color-text-primary;
|
||||
margin: 0 0 @space-sm;
|
||||
}
|
||||
|
||||
&__desc {
|
||||
font-size: @font-size-sm;
|
||||
color: @color-text-secondary;
|
||||
line-height: @line-height-base;
|
||||
margin: 0 0 @space-md;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-stories {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.sidebar-story {
|
||||
&--active .sidebar-story__link {
|
||||
background: @color-primary-bg;
|
||||
color: @color-primary;
|
||||
}
|
||||
|
||||
&__link {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
padding: @space-sm;
|
||||
border-radius: @border-radius-sm;
|
||||
text-decoration: none;
|
||||
transition: background @transition-fast;
|
||||
|
||||
&:hover {
|
||||
background: @color-primary-bg;
|
||||
}
|
||||
}
|
||||
|
||||
&__family {
|
||||
font-size: @font-size-xs;
|
||||
color: @color-primary;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-size: @font-size-sm;
|
||||
color: @color-text-secondary;
|
||||
line-height: @line-height-tight;
|
||||
.text-clamp(2);
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-links {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: @space-sm;
|
||||
}
|
||||
|
||||
// 小红书来源信息
|
||||
.story-source {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: @space-sm;
|
||||
margin-bottom: @space-lg;
|
||||
|
||||
&__author {
|
||||
font-size: @font-size-sm;
|
||||
font-weight: @font-weight-medium;
|
||||
color: @color-text-secondary;
|
||||
}
|
||||
|
||||
&__platform {
|
||||
font-size: @font-size-xs;
|
||||
padding: 2px @space-sm;
|
||||
background: #ff2442;
|
||||
color: #fff;
|
||||
border-radius: @border-radius-full;
|
||||
font-weight: @font-weight-medium;
|
||||
}
|
||||
|
||||
&__likes {
|
||||
font-size: @font-size-xs;
|
||||
color: @color-text-muted;
|
||||
|
||||
&::before {
|
||||
content: '❤️ ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 查看原文链接
|
||||
.story-source-link {
|
||||
margin-bottom: @space-xl;
|
||||
|
||||
&__tip {
|
||||
font-size: @font-size-sm;
|
||||
color: @color-text-secondary;
|
||||
background: @color-bg-gray;
|
||||
padding: @space-xs @space-sm;
|
||||
border-radius: @border-radius-sm;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&__a {
|
||||
font-size: @font-size-sm;
|
||||
color: #ff2442;
|
||||
text-decoration: none;
|
||||
font-weight: @font-weight-medium;
|
||||
transition: opacity @transition-fast;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 来源声明
|
||||
.story-disclaimer {
|
||||
margin-top: @space-xl;
|
||||
padding: @space-md @space-lg;
|
||||
background: @color-bg-gray;
|
||||
border-radius: @border-radius-md;
|
||||
border-left: 3px solid @color-border;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
font-size: @font-size-xs;
|
||||
color: @color-text-muted;
|
||||
line-height: @line-height-base;
|
||||
}
|
||||
}
|
||||
|
||||
// 404
|
||||
.page-not-found {
|
||||
text-align: center;
|
||||
padding: @space-3xl 0;
|
||||
|
||||
h1 {
|
||||
margin-bottom: @space-lg;
|
||||
}
|
||||
|
||||
a {
|
||||
color: @color-primary;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,265 @@
|
||||
<template>
|
||||
<div class="page-stories">
|
||||
<LayoutPageHero
|
||||
:title="seo.h1"
|
||||
subtitle="来自小红书的真实旅行分享"
|
||||
:breadcrumbs="[{ text: '客户故事' }]"
|
||||
/>
|
||||
|
||||
<!-- TL;DR:告诉 AI/搜索引擎这个页面回答了什么 -->
|
||||
<section class="page-tldr" aria-label="客户故事摘要">
|
||||
<div class="container">
|
||||
<div class="page-tldr-inner">
|
||||
<p>呼籁旅行客户在小红书上的真实旅行分享,记录草原亲子游、家庭旅拍、露营体验等真实出行感受,点击可查看原文。</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 瀑布流卡片墙 -->
|
||||
<section class="section section-stories">
|
||||
<div class="container">
|
||||
<div v-if="readyStories.length > 0" class="stories-wall">
|
||||
<a
|
||||
v-for="story in readyStories"
|
||||
:key="story.id"
|
||||
:href="story.sourceUrl"
|
||||
target="_blank"
|
||||
rel="noopener nofollow"
|
||||
class="story-card reveal"
|
||||
>
|
||||
<div class="story-card__visual">
|
||||
<img
|
||||
v-if="story.coverImage"
|
||||
:src="story.coverImage"
|
||||
:alt="story.title"
|
||||
class="story-card__img"
|
||||
loading="lazy"
|
||||
/>
|
||||
<div v-else class="story-card__img-placeholder" />
|
||||
<!-- 小红书角标 -->
|
||||
<span class="story-card__badge">
|
||||
<svg class="story-card__badge-icon" viewBox="0 0 24 24" width="14" height="14" fill="currentColor">
|
||||
<path d="M7.5 2h9a1.5 1.5 0 011.5 1.5v17a1.5 1.5 0 01-2.3 1.27L12 19l-3.7 2.77A1.5 1.5 0 016 20.5v-17A1.5 1.5 0 017.5 2z"/>
|
||||
</svg>
|
||||
小红书
|
||||
</span>
|
||||
<!-- 家庭类型 -->
|
||||
<span class="story-card__family">{{ story.familyType }}</span>
|
||||
</div>
|
||||
<div class="story-card__body">
|
||||
<h2 class="story-card__title">{{ story.title }}</h2>
|
||||
<p class="story-card__desc">{{ story.subtitle || story.summary }}</p>
|
||||
<div class="story-card__meta">
|
||||
<span v-if="story.author" class="story-card__author">
|
||||
{{ story.author }}
|
||||
</span>
|
||||
<span class="story-card__date">{{ story.travelDate }}</span>
|
||||
</div>
|
||||
<div class="story-card__tags">
|
||||
<span v-for="tag in (story.tags || []).slice(0, 3)" :key="tag" class="story-tag">#{{ tag }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div v-else class="stories-coming-soon reveal">
|
||||
<p class="stories-coming-soon__text">更多真实旅行故事整理中,敬请期待。</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 内链 -->
|
||||
<CommonSectionCTA
|
||||
title="这些故事,可能也会是你的故事"
|
||||
description="联系定制师,开始规划您的呼伦贝尔家庭旅行"
|
||||
:links="[
|
||||
{ to: '/contact', text: '联系定制师规划行程' },
|
||||
{ to: '/reviews', text: '查看更多客户评价' },
|
||||
]"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import storiesData from '~/data/stories.json'
|
||||
import brand from '~/data/brand.json'
|
||||
import contact from '~/data/contact.json'
|
||||
import { useOrganizationSchema, useBreadcrumbSchema, useStoriesListSchema } from '~/composables/useJsonLd'
|
||||
|
||||
const seo = useSEO('stories')
|
||||
useScrollReveal()
|
||||
useOrganizationSchema(brand, contact)
|
||||
useStoriesListSchema(storiesData.stories)
|
||||
useBreadcrumbSchema([
|
||||
{ text: '首页', to: '/' },
|
||||
{ text: '客户故事' },
|
||||
])
|
||||
|
||||
const stories = storiesData.stories
|
||||
|
||||
const readyStories = computed(() =>
|
||||
stories.filter(s => s.summary && !s.summary.includes('⚠️'))
|
||||
)
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.section-stories {
|
||||
background: @color-bg-gray;
|
||||
padding-bottom: @space-3xl;
|
||||
}
|
||||
|
||||
// 瀑布流网格
|
||||
.stories-wall {
|
||||
column-count: 2;
|
||||
column-gap: @space-md;
|
||||
|
||||
.respond-md({
|
||||
column-count: 3;
|
||||
});
|
||||
|
||||
.respond-xl({
|
||||
column-count: 4;
|
||||
column-gap: @space-lg;
|
||||
});
|
||||
}
|
||||
|
||||
// 卡片
|
||||
.story-card {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
break-inside: avoid;
|
||||
margin-bottom: @space-md;
|
||||
background: @color-bg-white;
|
||||
border-radius: @border-radius-lg;
|
||||
overflow: hidden;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
transition: all @transition-base;
|
||||
border: 1px solid transparent;
|
||||
|
||||
.respond-xl({
|
||||
margin-bottom: @space-lg;
|
||||
});
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: @shadow-md;
|
||||
border-color: @color-primary-light;
|
||||
}
|
||||
|
||||
&__visual {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__img {
|
||||
width: 100%;
|
||||
display: block;
|
||||
object-fit: cover;
|
||||
// 不固定高度,让图片保持原始比例实现瀑布流效果
|
||||
}
|
||||
|
||||
&__img-placeholder {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
background: @color-primary-bg;
|
||||
}
|
||||
|
||||
// 角标(小红书/原创)
|
||||
&__badge {
|
||||
position: absolute;
|
||||
top: @space-sm;
|
||||
right: @space-sm;
|
||||
background: rgba(255, 46, 46, 0.9);
|
||||
color: #fff;
|
||||
font-size: 11px;
|
||||
padding: 2px @space-sm;
|
||||
border-radius: @border-radius-full;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
font-weight: @font-weight-medium;
|
||||
backdrop-filter: blur(4px);
|
||||
|
||||
}
|
||||
|
||||
&__badge-icon {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
// 家庭类型
|
||||
&__family {
|
||||
position: absolute;
|
||||
bottom: @space-sm;
|
||||
left: @space-sm;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
color: #fff;
|
||||
font-size: @font-size-xs;
|
||||
padding: 2px @space-sm;
|
||||
border-radius: @border-radius-full;
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
&__body {
|
||||
padding: @space-md;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: @space-xs;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-size: @font-size-base;
|
||||
font-weight: @font-weight-bold;
|
||||
color: @color-text-primary;
|
||||
margin: 0;
|
||||
line-height: @line-height-tight;
|
||||
.text-clamp(2);
|
||||
}
|
||||
|
||||
&__desc {
|
||||
font-size: @font-size-sm;
|
||||
color: @color-text-secondary;
|
||||
margin: 0;
|
||||
line-height: @line-height-base;
|
||||
.text-clamp(2);
|
||||
}
|
||||
|
||||
&__meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: @space-sm;
|
||||
font-size: @font-size-xs;
|
||||
color: @color-text-muted;
|
||||
}
|
||||
|
||||
&__author {
|
||||
font-weight: @font-weight-medium;
|
||||
color: @color-text-secondary;
|
||||
}
|
||||
|
||||
&__tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: @space-xs;
|
||||
margin-top: @space-xs;
|
||||
}
|
||||
}
|
||||
|
||||
.story-tag {
|
||||
font-size: 11px;
|
||||
color: @color-primary;
|
||||
background: @color-primary-bg;
|
||||
padding: 1px @space-sm;
|
||||
border-radius: @border-radius-full;
|
||||
}
|
||||
|
||||
.stories-coming-soon {
|
||||
text-align: center;
|
||||
padding: @space-3xl 0;
|
||||
&__text {
|
||||
font-size: @font-size-lg;
|
||||
color: @color-text-secondary;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
在新工单中引用
屏蔽一个用户