249 lines
10 KiB
Plaintext
249 lines
10 KiB
Plaintext
|
---
|
||
|
import { getCollection, getEntry, type CollectionEntry } from "astro:content";
|
||
|
import MainLayout from "../../layouts/MainLayout.astro";
|
||
|
import ScrollReveal from "../../components/aceternity/ScrollReveal.astro";
|
||
|
|
||
|
// 定义Props类型
|
||
|
export interface Props {
|
||
|
entry: CollectionEntry<"travel">;
|
||
|
}
|
||
|
|
||
|
// 生成静态路径
|
||
|
export async function getStaticPaths() {
|
||
|
const travels = await getCollection("travel");
|
||
|
return travels.map((entry) => ({
|
||
|
params: { slug: entry.slug },
|
||
|
props: { entry },
|
||
|
}));
|
||
|
}
|
||
|
|
||
|
// 获取当前旅行攻略数据
|
||
|
const { entry } = Astro.props;
|
||
|
const { Content } = await entry.render();
|
||
|
|
||
|
// 获取相关旅行攻略
|
||
|
const allTravels = await getCollection("travel");
|
||
|
const relatedTravels = allTravels
|
||
|
.filter(
|
||
|
(item) =>
|
||
|
item.slug !== entry.slug &&
|
||
|
(item.data.season === entry.data.season ||
|
||
|
item.data.type === entry.data.type ||
|
||
|
item.data.tags.some((tag) => entry.data.tags.includes(tag)))
|
||
|
)
|
||
|
.slice(0, 3);
|
||
|
---
|
||
|
|
||
|
<MainLayout title={`${entry.data.title} - 河北游礼`}>
|
||
|
<!-- 页面标题区域 -->
|
||
|
<div class="relative py-16 bg-gradient-to-br from-color-primary-700 via-color-primary-600 to-color-primary-800 text-white dark:from-color-dark-primary-900 dark:via-color-dark-primary-800 dark:to-color-dark-primary-950">
|
||
|
<div class="absolute inset-0 bg-black/30"></div>
|
||
|
<div class="container mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
|
||
|
<div class="max-w-4xl mx-auto">
|
||
|
<ScrollReveal animation="fade">
|
||
|
<div class="flex flex-wrap items-center gap-2 mb-4">
|
||
|
<a href="/" class="text-white/80 hover:text-white transition-colors">首页</a>
|
||
|
<span class="text-white/60">/</span>
|
||
|
<a href="/travel" class="text-white/80 hover:text-white transition-colors">旅行攻略</a>
|
||
|
<span class="text-white/60">/</span>
|
||
|
<span class="text-white/60">{entry.data.title}</span>
|
||
|
</div>
|
||
|
|
||
|
<h1 class="text-4xl md:text-5xl font-bold mb-4">{entry.data.title}</h1>
|
||
|
|
||
|
<div class="flex flex-wrap items-center gap-4 mb-4">
|
||
|
{entry.data.season && (
|
||
|
<div class="flex items-center text-white/90">
|
||
|
<span class="mr-1">🌤️</span> {entry.data.season}
|
||
|
</div>
|
||
|
)}
|
||
|
|
||
|
{entry.data.type && (
|
||
|
<div class="flex items-center text-white/90">
|
||
|
<span class="mr-1">📋</span> {entry.data.type}
|
||
|
</div>
|
||
|
)}
|
||
|
|
||
|
{entry.data.days && (
|
||
|
<div class="flex items-center text-white/90">
|
||
|
<span class="mr-1">⏱️</span> {entry.data.days}天行程
|
||
|
</div>
|
||
|
)}
|
||
|
|
||
|
{entry.data.difficulty && (
|
||
|
<div class="flex items-center text-white/90">
|
||
|
<span class="mr-1">🏔️</span> 难度:{entry.data.difficulty}
|
||
|
</div>
|
||
|
)}
|
||
|
</div>
|
||
|
|
||
|
<div class="flex flex-wrap gap-2 mb-6">
|
||
|
{entry.data.tags.map((tag) => (
|
||
|
<span class="px-3 py-1 bg-white/20 backdrop-blur-sm text-white text-sm rounded-full">
|
||
|
{tag}
|
||
|
</span>
|
||
|
))}
|
||
|
</div>
|
||
|
|
||
|
<p class="text-xl text-white/90 max-w-3xl">{entry.data.description}</p>
|
||
|
</ScrollReveal>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<!-- 主要内容区域 -->
|
||
|
<div class="py-12 bg-white dark:bg-color-dark-bg">
|
||
|
<div class="container mx-auto px-4 sm:px-6 lg:px-8">
|
||
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-12">
|
||
|
<!-- 左侧内容 -->
|
||
|
<div class="lg:col-span-2">
|
||
|
<ScrollReveal animation="fade">
|
||
|
<div class="prose prose-lg dark:prose-invert max-w-none">
|
||
|
<Content />
|
||
|
</div>
|
||
|
</ScrollReveal>
|
||
|
</div>
|
||
|
|
||
|
<!-- 右侧边栏 -->
|
||
|
<div class="space-y-8">
|
||
|
<!-- 旅行图片 -->
|
||
|
<ScrollReveal animation="slide-up">
|
||
|
<div class="rounded-lg overflow-hidden shadow-md">
|
||
|
<div class="h-64 bg-gray-300 dark:bg-gray-700 flex items-center justify-center">
|
||
|
<span class="text-gray-500 dark:text-gray-400">{entry.data.title} 图片</span>
|
||
|
</div>
|
||
|
</div>
|
||
|
</ScrollReveal>
|
||
|
|
||
|
<!-- 攻略信息卡片 -->
|
||
|
<ScrollReveal animation="slide-up" delay={100}>
|
||
|
<div class="bg-gray-50 dark:bg-color-dark-card rounded-lg shadow-md p-6">
|
||
|
<h3 class="text-xl font-semibold text-gray-900 dark:text-white mb-4">攻略信息</h3>
|
||
|
|
||
|
<div class="space-y-3">
|
||
|
{entry.data.season && (
|
||
|
<div class="flex">
|
||
|
<span class="w-24 flex-shrink-0 text-gray-600 dark:text-gray-400">适宜季节:</span>
|
||
|
<span class="text-gray-900 dark:text-white">{entry.data.season}</span>
|
||
|
</div>
|
||
|
)}
|
||
|
|
||
|
{entry.data.type && (
|
||
|
<div class="flex">
|
||
|
<span class="w-24 flex-shrink-0 text-gray-600 dark:text-gray-400">攻略类型:</span>
|
||
|
<span class="text-gray-900 dark:text-white">{entry.data.type}</span>
|
||
|
</div>
|
||
|
)}
|
||
|
|
||
|
{entry.data.days && (
|
||
|
<div class="flex">
|
||
|
<span class="w-24 flex-shrink-0 text-gray-600 dark:text-gray-400">行程天数:</span>
|
||
|
<span class="text-gray-900 dark:text-white">{entry.data.days}天</span>
|
||
|
</div>
|
||
|
)}
|
||
|
|
||
|
{entry.data.difficulty && (
|
||
|
<div class="flex">
|
||
|
<span class="w-24 flex-shrink-0 text-gray-600 dark:text-gray-400">难度级别:</span>
|
||
|
<span class="text-gray-900 dark:text-white">{entry.data.difficulty}</span>
|
||
|
</div>
|
||
|
)}
|
||
|
|
||
|
<div class="flex">
|
||
|
<span class="w-24 flex-shrink-0 text-gray-600 dark:text-gray-400">旅行主题:</span>
|
||
|
<div class="flex flex-wrap gap-1">
|
||
|
{entry.data.tags.map((tag) => (
|
||
|
<span class="px-2 py-0.5 bg-color-primary-100 text-color-primary-800 text-xs rounded-full dark:bg-color-dark-primary-900/70 dark:text-color-primary-300">
|
||
|
{tag}
|
||
|
</span>
|
||
|
))}
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
{entry.data.pubDate && (
|
||
|
<div class="flex">
|
||
|
<span class="w-24 flex-shrink-0 text-gray-600 dark:text-gray-400">发布时间:</span>
|
||
|
<span class="text-gray-900 dark:text-white">{new Date(entry.data.pubDate).toLocaleDateString('zh-CN')}</span>
|
||
|
</div>
|
||
|
)}
|
||
|
|
||
|
</div>
|
||
|
</div>
|
||
|
</ScrollReveal>
|
||
|
|
||
|
<!-- 旅行小贴士 -->
|
||
|
<ScrollReveal animation="slide-up" delay={200}>
|
||
|
<div class="bg-color-primary-50 dark:bg-color-dark-primary-900/30 rounded-lg shadow-md p-6 border border-color-primary-100 dark:border-color-dark-primary-800">
|
||
|
<h3 class="text-xl font-semibold text-color-primary-800 dark:text-color-primary-300 mb-4">旅行小贴士</h3>
|
||
|
|
||
|
<div class="space-y-3 text-color-primary-700 dark:text-color-primary-300/90">
|
||
|
<div class="flex">
|
||
|
<span class="mr-2">✓</span>
|
||
|
<p>出行前查看天气预报,准备合适的衣物</p>
|
||
|
</div>
|
||
|
<div class="flex">
|
||
|
<span class="mr-2">✓</span>
|
||
|
<p>提前规划路线,预订住宿和交通</p>
|
||
|
</div>
|
||
|
<div class="flex">
|
||
|
<span class="mr-2">✓</span>
|
||
|
<p>携带必要的药品和紧急联系方式</p>
|
||
|
</div>
|
||
|
<div class="flex">
|
||
|
<span class="mr-2">✓</span>
|
||
|
<p>尊重当地风俗习惯,做文明旅行者</p>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
</ScrollReveal>
|
||
|
|
||
|
<!-- 相关攻略 -->
|
||
|
{relatedTravels.length > 0 && (
|
||
|
<ScrollReveal animation="slide-up" delay={300}>
|
||
|
<div class="bg-gray-50 dark:bg-color-dark-card rounded-lg shadow-md p-6">
|
||
|
<h3 class="text-xl font-semibold text-gray-900 dark:text-white mb-4">相关攻略</h3>
|
||
|
|
||
|
<div class="space-y-4">
|
||
|
{relatedTravels.map((travel) => (
|
||
|
<a
|
||
|
href={`/travel/${travel.slug}`}
|
||
|
class="block group"
|
||
|
>
|
||
|
<div class="flex items-start space-x-3">
|
||
|
<div class="w-16 h-16 flex-shrink-0 bg-gray-300 dark:bg-gray-700 rounded flex items-center justify-center">
|
||
|
<span class="text-xs text-gray-500 dark:text-gray-400">图片</span>
|
||
|
</div>
|
||
|
<div>
|
||
|
<h4 class="text-base font-medium text-gray-900 dark:text-white group-hover:text-color-primary-600 dark:group-hover:text-color-primary-400 transition-colors">
|
||
|
{travel.data.title}
|
||
|
</h4>
|
||
|
<div class="flex items-center text-xs text-gray-500 dark:text-gray-400 mt-1 space-x-2">
|
||
|
{travel.data.season && <span>{travel.data.season}</span>}
|
||
|
{travel.data.days && <span>• {travel.data.days}天行程</span>}
|
||
|
</div>
|
||
|
<p class="text-sm text-gray-600 dark:text-gray-400 line-clamp-2 mt-1">
|
||
|
{travel.data.description.substring(0, 60)}...
|
||
|
</p>
|
||
|
</div>
|
||
|
</div>
|
||
|
</a>
|
||
|
))}
|
||
|
</div>
|
||
|
</div>
|
||
|
</ScrollReveal>
|
||
|
)}
|
||
|
|
||
|
<!-- 返回按钮 -->
|
||
|
<ScrollReveal animation="slide-up" delay={400}>
|
||
|
<a
|
||
|
href="/travel"
|
||
|
class="block w-full py-3 text-center bg-color-primary-600 text-white rounded-md hover:bg-color-primary-700 transition-colors dark:bg-color-dark-primary-600 dark:hover:bg-color-dark-primary-500"
|
||
|
>
|
||
|
返回所有攻略
|
||
|
</a>
|
||
|
</ScrollReveal>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
</MainLayout>
|