完成所有页面,除了文化筛选有问题

This commit is contained in:
lsy 2025-04-10 22:51:22 +08:00
parent 3a93ac41b7
commit 9134f0b67a
25 changed files with 471 additions and 151 deletions

View File

@ -2,18 +2,20 @@
import { defineConfig } from 'astro/config';
import tailwindcss from '@tailwindcss/vite';
import react from '@astrojs/react';
// https://astro.build/config
export default defineConfig({
vite: {
plugins: [tailwindcss()]
},
// 服务器配置
server: {
port: 3000,
host: true
},
// Markdown 配置
markdown: {
syntaxHighlight: 'prism',
@ -23,5 +25,7 @@ export default defineConfig({
langs: [],
wrap: true,
}
}
},
integrations: [react()]
});

View File

@ -9,15 +9,17 @@
"astro": "astro"
},
"dependencies": {
"@astrojs/react": "^4.2.3",
"@studio-freight/lenis": "^1.0.42",
"@tailwindcss/vite": "^4.0.15",
"@types/react": "^19.0.12",
"@types/react-dom": "^19.0.4",
"@types/react": "^19.1.0",
"@types/react-dom": "^19.1.2",
"aceternity-ui": "^0.2.2",
"astro": "^5.5.3",
"echarts": "^5.6.0",
"framer-motion": "^12.5.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"tailwindcss": "^4.0.15"
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,257 @@
import { useEffect, useRef, useState } from 'react';
import * as echarts from 'echarts';
import hebeiJson from '../assets/hebei.json';
// 定义正确的GeoJSON类型
interface GeoJSONFeature {
type: 'Feature';
properties: {
name: string;
[key: string]: any;
};
geometry: any;
}
interface GeoJSONFeatureCollection {
type: 'FeatureCollection';
features: GeoJSONFeature[];
}
interface Article {
title: string;
description: string;
city: string[];
slug: string;
type: string;
}
interface HebeiMapProps {
articles: Article[];
}
// ECharts事件参数类型
interface EChartsClickParams {
name: string;
value?: any;
dataIndex?: number;
seriesIndex?: number;
data?: any;
[key: string]: any;
}
const HebeiMap = ({ articles }: HebeiMapProps) => {
const mapRef = useRef<HTMLDivElement>(null);
const [selectedCity, setSelectedCity] = useState<string>('');
const [filteredArticles, setFilteredArticles] = useState<Article[]>([]);
const [isMobile, setIsMobile] = useState<boolean>(false);
const [chart, setChart] = useState<echarts.ECharts | null>(null);
// 记录上次点击的城市,用于比较
const lastClickedCity = useRef<string>('');
// 检测是否为移动设备
useEffect(() => {
const checkMobile = () => {
const mobile = window.innerWidth < 768;
setIsMobile(mobile);
};
checkMobile();
window.addEventListener('resize', checkMobile);
return () => {
window.removeEventListener('resize', checkMobile);
};
}, []);
// 地图初始化
useEffect(() => {
if (!mapRef.current) return;
// 确保GeoJSON格式正确
const geoJson: GeoJSONFeatureCollection = {
type: 'FeatureCollection',
features: hebeiJson.features.map(feature => ({
type: 'Feature',
properties: feature.properties,
geometry: feature.geometry
}))
};
// 注册地图数据
echarts.registerMap('hebei', geoJson);
// 解决移动端多次初始化问题
if (chart) {
chart.dispose();
}
const newChart = echarts.init(mapRef.current);
setChart(newChart);
const option = {
title: {
text: '河北省地图',
left: 'center',
textStyle: {
fontSize: isMobile ? 14 : 16
}
},
tooltip: {
show: false
},
series: [
{
name: '河北省',
type: 'map',
map: 'hebei',
roam: isMobile, // 移动端允许缩放和平移
zoom: isMobile ? 1.2 : 1, // 移动端稍微放大一点
itemStyle: {
normal: {
areaColor: '#e0f3f8',
borderColor: '#fff'
},
emphasis: {
areaColor: '#74add1'
}
},
label: {
show: true,
fontSize: isMobile ? 10 : 12
},
emphasis: {
label: {
show: true
}
},
data: hebeiJson.features.map(feature => ({
name: feature.properties.name,
value: 0
}))
}
]
};
newChart.setOption(option);
// 点击事件处理 - 修改为点击同一城市时取消选择
const clickHandler = (params: EChartsClickParams) => {
const cityName = params.name.replace('市', '');
// 如果点击的是已选中的城市,则取消选择
if (cityName === lastClickedCity.current) {
lastClickedCity.current = '';
setSelectedCity('');
setFilteredArticles([]);
} else {
// 否则选中新城市并筛选文章
lastClickedCity.current = cityName;
setSelectedCity(cityName);
const cityArticles = articles.filter(article =>
article.city.some(city => city.includes(cityName))
);
setFilteredArticles(cityArticles);
}
};
// 移除之前的点击事件(防止重复绑定)
newChart.off('click');
newChart.on('click', clickHandler);
// 清理函数
return () => {
newChart.off('click');
newChart.dispose();
};
}, [articles, isMobile]); // 从依赖项中移除selectedCity
// 窗口大小变化时调整地图大小
useEffect(() => {
if (chart) {
chart.resize();
}
}, [isMobile, chart]);
// 获取文章类型对应的图标
const getTypeIcon = (type: string) => {
switch (type) {
case 'attractions':
return '🏛️';
case 'culture':
return '🎭';
case 'cuisine':
return '🍜';
case 'travel':
return '🏞️';
default:
return '📄';
}
};
return (
<div className="container mx-auto px-4">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="md:col-span-2">
<div
ref={mapRef}
style={{
height: isMobile ? '400px' : '600px',
width: '100%'
}}
/>
</div>
<div className={`md:col-span-1 ${isMobile && selectedCity ? 'mt-4' : ''}`}>
{selectedCity ? (
<div className="bg-white p-4 rounded-lg shadow">
<div className="flex justify-between items-center mb-4">
<h2 className="text-xl font-bold">{selectedCity}</h2>
<button
onClick={(e) => {
e.stopPropagation(); // 阻止事件冒泡
lastClickedCity.current = ''; // 重置上次点击的城市
setSelectedCity('');
setFilteredArticles([]);
}}
className="text-gray-500 hover:text-gray-700 p-2"
>
</button>
</div>
{filteredArticles.length > 0 ? (
<ul className="space-y-4 max-h-[500px] overflow-y-auto">
{filteredArticles.map((article, index) => (
<li
key={index}
className="border-b pb-2 hover:bg-gray-50 transition-colors cursor-pointer"
onClick={(e) => {
e.stopPropagation(); // 阻止事件冒泡
window.location.href = article.slug;
}}
>
<div className="flex items-start gap-2">
<span className="text-xl">{getTypeIcon(article.type)}</span>
<div>
<h3 className="font-semibold hover:text-blue-600">{article.title}</h3>
<p className="text-gray-600 text-sm">{article.description}</p>
</div>
</div>
</li>
))}
</ul>
) : (
<p className="text-gray-500"></p>
)}
</div>
) : (
<div className="bg-white p-4 rounded-lg shadow">
<p className="text-gray-500 text-center"></p>
</div>
)}
</div>
</div>
</div>
);
};
export default HebeiMap;

View File

@ -2,7 +2,7 @@
import "../styles/global.css";
import ThemeToggle from "../components/ThemeToggle.astro";
import DarkModeTransition from "../components/DarkModeTransition.astro";
import SmoothScroll from "../components/aceternity/SmoothScroll.astro";
import SmoothScroll from "../components/SmoothScroll.astro";
interface Props {
title: string;
@ -49,6 +49,7 @@ const { title, description = "河北游礼宣传网站 - 探索河北的文化
<a href="/culture" class="text-gray-800 hover:text-color-primary-600 dark:text-gray-200 dark:hover:text-color-primary-400 text-shadow-sm">文化</a>
<a href="/cuisine" class="text-gray-800 hover:text-color-primary-600 dark:text-gray-200 dark:hover:text-color-primary-400 text-shadow-sm">美食</a>
<a href="/travel" class="text-gray-800 hover:text-color-primary-600 dark:text-gray-200 dark:hover:text-color-primary-400 text-shadow-sm">旅游攻略</a>
<a href="/map" class="text-gray-800 hover:text-color-primary-600 dark:text-gray-200 dark:hover:text-color-primary-400 text-shadow-sm">地图导航</a>
</nav>
<!-- 主题切换按钮和移动端菜单 -->
@ -72,6 +73,7 @@ const { title, description = "河北游礼宣传网站 - 探索河北的文化
<a href="/culture" class="px-3 py-2 rounded-md text-gray-700 hover:bg-white/20 dark:text-gray-300 dark:hover:bg-white/10">文化</a>
<a href="/cuisine" class="px-3 py-2 rounded-md text-gray-700 hover:bg-white/20 dark:text-gray-300 dark:hover:bg-white/10">美食</a>
<a href="/travel" class="px-3 py-2 rounded-md text-gray-700 hover:bg-white/20 dark:text-gray-300 dark:hover:bg-white/10">旅游攻略</a>
<a href="/map" class="px-3 py-2 rounded-md text-gray-700 hover:bg-white/20 dark:text-gray-300 dark:hover:bg-white/10">地图导航</a>
</div>
</div>
</div>

View File

@ -1,30 +0,0 @@
---
interface Props {
columns?: 1 | 2 | 3 | 4;
gap?: string;
className?: string;
}
const {
columns = 3,
gap = "gap-8",
className = ""
} = Astro.props;
// 根据列数动态设置网格类
const getGridClass = () => {
switch(columns) {
case 1: return 'grid-cols-1';
case 2: return 'grid-cols-1 md:grid-cols-2';
case 3: return 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3';
case 4: return 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4';
default: return 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3';
}
};
const gridClass = getGridClass();
---
<div class={`grid ${gridClass} ${gap} ${className}`}>
<slot />
</div>

View File

@ -1,40 +0,0 @@
---
interface Category {
name: string;
icon?: string;
count?: number;
isActive?: boolean;
}
interface Props {
categories: Category[];
showAllLabel?: string;
allActive?: boolean;
}
const {
categories,
showAllLabel = "全部",
allActive = true
} = Astro.props;
---
<section class="py-8 bg-white dark:bg-color-dark-surface border-b border-gray-200 dark:border-color-dark-border">
<div class="container mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex flex-wrap justify-center gap-4">
<button class={`px-4 py-2 rounded-full font-medium transition-colors ${allActive ? 'bg-color-primary-500 text-white dark:bg-color-dark-primary-600 dark:text-white dark:hover:bg-color-dark-primary-500' : 'bg-gray-100 text-gray-700 hover:bg-gray-200 dark:bg-color-dark-card dark:text-gray-300 dark:hover:bg-color-dark-border'}`}>
{showAllLabel}
</button>
{categories.map(category => (
<button class={`px-4 py-2 rounded-full font-medium transition-colors ${category.isActive ? 'bg-color-primary-500 text-white dark:bg-color-dark-primary-600 dark:text-white dark:hover:bg-color-dark-primary-500' : 'bg-gray-100 text-gray-700 hover:bg-gray-200 dark:bg-color-dark-card dark:text-gray-300 dark:hover:bg-color-dark-border'}`}>
{category.icon && <span class="mr-1">{category.icon}</span>}
{category.name}
{category.count !== undefined && (
<span class="ml-1 text-xs opacity-70">{`(${category.count})`}</span>
)}
</button>
))}
</div>
</div>
</section>

View File

@ -1,18 +0,0 @@
---
interface Props {
title: string;
subtitle?: string;
}
const { title, subtitle } = Astro.props;
---
<section class="py-12 bg-gradient-to-r from-amber-500 to-amber-700 text-white dark:from-amber-800/70 dark:to-amber-950/80">
<div class="container mx-auto px-4 sm:px-6 lg:px-8">
<div class="max-w-4xl mx-auto text-center">
<h1 class="text-4xl font-bold mb-4">{title}</h1>
{subtitle && <p class="text-xl opacity-90">{subtitle}</p>}
<slot />
</div>
</div>
</section>

View File

@ -1,33 +0,0 @@
---
interface Props {
placeholder?: string;
buttonText?: string;
className?: string;
}
const {
placeholder = "搜索...",
buttonText = "搜索",
className = ""
} = Astro.props;
---
<section class={`py-8 bg-white dark:bg-color-dark-bg ${className}`}>
<div class="container mx-auto px-4 sm:px-6 lg:px-8">
<div class="max-w-2xl mx-auto">
<div class="relative">
<input
type="text"
placeholder={placeholder}
class="w-full px-4 py-3 pl-12 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-color-primary-500 focus:border-color-primary-500 shadow-sm dark:bg-color-dark-card dark:border-color-dark-border dark:text-white dark:placeholder-gray-400"
/>
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 absolute left-3 top-3.5 text-gray-400 dark:text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
<button class="absolute right-3 top-3 px-3 py-1 bg-color-primary-500 text-white rounded-md hover:bg-color-primary-600 transition-colors dark:bg-color-dark-primary-600 dark:text-white dark:hover:bg-color-dark-primary-500">
{buttonText}
</button>
</div>
</div>
</div>
</section>

View File

@ -0,0 +1,38 @@
---
title: "衡水老白干火锅 - 酒香四溢的本地特色美食"
description: "衡水老白干火锅是一种独具地方特色的饮食方式,以衡水老白干白酒为汤底,配以牛羊杂、豆制品等,酒香浓郁,辣中带麻,是冬日里驱寒暖胃的绝佳选择。"
category: "地方火锅"
featured: true
image: "/images/cuisine/hengshui-baigan-hotpot.jpg"
city: ["衡水"]
ingredients: ["衡水老白干", "牛杂", "羊杂", "豆腐皮", "白菜", "粉条", "辣椒", "花椒", "葱", "姜", "蒜"]
taste: "香辣浓烈"
cookTime: "1.5小时"
difficulty: "中等"
tags: ["火锅", "衡水美食", "白酒火锅", "地方特色", "冬季美食"]
pubDate: 2025-04-10
---
衡水老白干火锅,是衡水地区冬天非常流行的一道地方美食。不同于普通火锅以清汤或红油为底,这种火锅独具一格地用衡水特产白酒 —— 老白干为汤底,加热时酒香四溢,令人食欲大开。
## 地道食材
火锅的主料通常选用本地现宰的**牛羊杂碎**,经过反复洗净、焯水,再加以**葱姜蒜、花椒、辣椒**等辅料,焖煮入味。配菜方面,**豆腐皮、白菜、粉条**是不可或缺的经典搭配,既吸汤入味,又能平衡肉类的油腻。
## 烹饪方式
老白干火锅的灵魂在于“酒煮”。火锅上桌后,加入一整瓶衡水老白干,点火后酒精会快速挥发,只留下浓郁酒香,并把肉类与香料的味道彻底激发出来,形成一种极具辨识度的香辣口感。
## 口感特点
- **第一口**:酒香扑鼻,带着微醺的刺激感;
- **第二口**:辣味与香味交织,肉质酥烂入味;
- **第三口**:热气腾腾,身体从内而外都暖起来。
## 小贴士
- 初次尝试者可将酒量减少一半,适应其特殊香气;
- 建议搭配**馒头、凉菜**中和辣度;
- 餐后可饮**绿豆汤或酸奶**缓解燥热。
衡水老白干火锅不仅是一道美味,更是一种文化,它把地方酒文化与老百姓的餐桌生活完美融合,是冬天不可错过的一场味觉盛宴。

View File

@ -0,0 +1,32 @@
---
title: "邢台邢窑白瓷 - 古老瓷器的匠心传承"
description: "邢台邢窑白瓷是中国古代瓷器的代表之一,以其精致的制作工艺和洁白的釉色闻名。作为中国四大名窑之一,邢窑白瓷承载着悠久的历史与文化,至今仍影响着瓷器工艺的传承与发展。"
category: "瓷器文化"
featured: true
image: "/images/culture/xingtai-xingyao-ceramics.jpg"
city: ["邢台"]
tags: ["非物质文化遗产", "瓷器", "传统文化", "中国工艺", "历史遗产"]
pubDate: 2025-04-10
---
邢台是邢窑白瓷的发源地,邢窑瓷器距今已有上千年历史。它是中国四大名窑之一,历史上以“白如玉、明如镜、薄如纸、声如磬”而闻名。邢窑的瓷器不仅在中国历史上占据重要地位,也是世界瓷器文化的瑰宝。
## 邢窑的历史与特点
邢窑起源于隋唐时期,盛于宋代,特别是在唐代的中期至宋代初期,邢窑白瓷达到了鼎盛时期。邢窑的白瓷以其洁白如玉的釉色、细腻的质地和精致的制作工艺著称。它的瓷器多为白瓷,釉面光洁透明,色泽洁白如玉,极具艺术价值。
## 邢窑瓷器的工艺
邢窑白瓷的制作工艺非常考究。首先选取上好的瓷土,经过严格的挑选和处理,确保瓷土的纯净。然后采用传统的手工制作工艺,经过高温烧制,使瓷器釉色光洁,质地坚硬。邢窑的瓷器多以碗、盘、瓶、壶等日常器物为主,但也不乏精美的艺术品和祭祀用品。
## 邢台邢窑遗址
今天,邢台的邢窑遗址已经成为国家重点文物保护单位,是研究古代瓷器文化的宝贵资料。游客可以参观邢窑遗址,欣赏到遗址出土的古瓷器以及现代工艺师制作的邢窑仿制品,感受这份千年瓷器文化的魅力。
## 如何体验邢台邢窑文化
- **参观邢窑遗址**:了解邢窑的历史背景和文化底蕴。
- **瓷器制作体验**:在当地陶瓷厂参加瓷器制作体验,亲手制作一件瓷器。
- **购买瓷器**:邢台当地有许多手工瓷器店,可以购买到精美的邢窑瓷器作为纪念。
邢台的邢窑白瓷是中华文化的重要组成部分,是中国瓷器文化的象征。如果你有兴趣了解中国传统瓷器的历史和工艺,不妨亲自前往邢台,体验这片瓷器的发源地,感受它的独特魅力。

View File

@ -0,0 +1,44 @@
---
title: "张家口崇礼三日滑雪之旅 - 冰雪世界里的畅快驰骋"
description: "一条适合冬季出行的滑雪旅游路线,深入张家口崇礼核心滑雪区,覆盖万龙、太舞、云顶三大雪场,体验冬奥同款雪道,享受雪域假期的极致快感。"
season: "冬季"
type: "滑雪游"
featured: true
image: "/images/travel/chongli-winter.jpg"
city: ["张家口", "崇礼"]
days: 3
difficulty: "中等"
tags: ["滑雪", "冬奥", "冰雪旅游", "自然风光", "户外运动"]
pubDate: 2025-04-10
---
张家口崇礼作为2022年冬奥会核心赛区是国内首屈一指的滑雪胜地。这条三日滑雪之旅适合中等体力及以上的滑雪爱好者覆盖最具代表性的三个雪场同时兼顾美食与休闲放松让你的冬日假期动静皆宜、精彩纷呈。
## 第一天:抵达崇礼 · 太舞滑雪度假区
- **上午**从北京乘高铁前往太子城站车程约50分钟抵达后入住太舞滑雪小镇。
- **中午**:在太舞镇内餐厅享用热腾腾的铁锅炖或牛肉汤锅。
- **下午**:在太舞滑雪场体验雪道,初学者可报名滑雪教练课。
- **晚上**:在雪场旁的酒吧或咖啡厅放松,感受冬夜雪镇的静谧。
## 第二天:万龙滑雪场挑战进阶雪道
- **上午**:早餐后乘坐接驳巴士前往万龙滑雪场。
- **全天滑雪体验**:万龙以天然粉雪和长雪期著称,适合中高级玩家挑战不同等级雪道。
- **午餐推荐**:滑雪场内小木屋风格的山顶餐厅,边吃边看雪景。
- **晚上**:返回酒店,可选择泡温泉或在雪场小镇逛夜市。
## 第三天:云顶滑雪公园体验冬奥同款赛道
- **上午**:退房后前往云顶滑雪公园,这里是冬奥自由式滑雪与单板项目的举办地。
- **体验推荐**尝试U型槽、障碍技巧等特色道具雪道。
- **中午**:在云顶小镇内享用快餐或热饮简餐。
- **下午**:搭乘返程高铁返回北京,结束三日雪地之旅。
## 实用建议
- 滑雪衣裤、手套、头盔可现场租赁,建议自带雪镜和保暖内衣。
- 旺季期间(元旦至春节)需提前两周以上预订酒店与雪票。
- 防晒霜和唇膏必备,紫外线强且空气干燥。
崇礼不仅拥有世界级滑雪设施,更是一个可以全身心放松、沉浸在冰雪之中的地方。如果你向往冬天的速度与激情,不妨安排一次崇礼三日之行,为这个冬天留下一段纯白记忆。

View File

@ -1,6 +1,6 @@
---
import { getCollection, getEntry, type CollectionEntry } from "astro:content";
import MainLayout from "../../layouts/MainLayout.astro";
import { getCollection, type CollectionEntry } from "astro:content";
import MainLayout from "../../components/MainLayout.astro";
// 定义Props类型
export interface Props {

View File

@ -1,5 +1,5 @@
---
import MainLayout from "../../layouts/MainLayout.astro";
import MainLayout from "../../components/MainLayout.astro";
import { getCollection, type CollectionEntry } from "astro:content";
// 获取景点内容集合
@ -75,11 +75,6 @@ let filteredAttractions = sortedAttractions;
const totalPages = Math.ceil(filteredAttractions.length / itemsPerPage);
const currentPageAttractions = filteredAttractions.slice((page - 1) * itemsPerPage, page * itemsPerPage);
// 辅助函数用于获取景点的分类从city提取或默认值
const getCategory = (attraction: CollectionEntry<"attractions">) => {
return attraction.data.city && attraction.data.city.length > 0 ? attraction.data.city[0] : '其他景点';
};
---
<MainLayout title="景点 - 河北游礼">

View File

@ -1,6 +1,6 @@
---
import { getCollection, getEntry, type CollectionEntry } from "astro:content";
import MainLayout from "../../layouts/MainLayout.astro";
import { getCollection, type CollectionEntry } from "astro:content";
import MainLayout from "../../components/MainLayout.astro";
// 定义Props类型
export interface Props {

View File

@ -1,5 +1,5 @@
---
import MainLayout from "../../layouts/MainLayout.astro";
import MainLayout from "../../components/MainLayout.astro";
import { getCollection, type CollectionEntry } from "astro:content";
// 获取美食内容集合

View File

@ -1,6 +1,6 @@
---
import { getCollection, getEntry, type CollectionEntry } from "astro:content";
import MainLayout from "../../layouts/MainLayout.astro";
import { getCollection, type CollectionEntry } from "astro:content";
import MainLayout from "../../components/MainLayout.astro";
// 定义Props类型
export interface Props {

View File

@ -1,6 +1,6 @@
---
import { getCollection, type CollectionEntry } from "astro:content";
import MainLayout from "../../layouts/MainLayout.astro";
import MainLayout from "../../components/MainLayout.astro";
// 获取URL参数
const { searchParams } = Astro.url;

View File

@ -1,6 +1,6 @@
---
import MainLayout from "../layouts/MainLayout.astro";
import MorphingText from "../components/aceternity/MorphingText.astro";
import MainLayout from "../components/MainLayout.astro";
import MorphingText from "../components/MorphingText.astro";
// 导航数据
---

View File

@ -0,0 +1,52 @@
---
import MainLayout from "../components/MainLayout.astro";
import HebeiMap from '../components/HebeiMap';
import { getCollection } from 'astro:content';
// 获取所有集合的数据
const attractions = await getCollection('attractions');
const culture = await getCollection('culture');
const cuisine = await getCollection('cuisine');
const travel = await getCollection('travel');
// 合并所有文章并提取需要的字段
const allArticles = [
...attractions.map(entry => ({
title: entry.data.title,
description: entry.data.description,
city: entry.data.city,
slug: `/attractions/${entry.slug}`,
type: 'attractions'
})),
...culture.map(entry => ({
title: entry.data.title,
description: entry.data.description,
city: entry.data.city,
slug: `/culture/${entry.slug}`,
type: 'culture'
})),
...cuisine.map(entry => ({
title: entry.data.title,
description: entry.data.description,
city: entry.data.city,
slug: `/cuisine/${entry.slug}`,
type: 'cuisine'
})),
...travel.map(entry => ({
title: entry.data.title,
description: entry.data.description,
city: entry.data.city,
slug: `/travel/${entry.slug}`,
type: 'travel'
}))
];
---
<MainLayout title="河北地图 - 文章导航">
<main class="pt-16 md:pt-20 pb-4 md:pb-8">
<div class="container mx-auto px-2 md:px-4">
<p class="text-sm md:text-base text-gray-600 text-center mb-6">点击地图上的城市查看相关文章</p>
<HebeiMap client:load articles={allArticles} />
</div>
</main>
</MainLayout>

View File

@ -1,6 +1,6 @@
---
import { getCollection, getEntry, type CollectionEntry } from "astro:content";
import MainLayout from "../../layouts/MainLayout.astro";
import { getCollection, type CollectionEntry } from "astro:content";
import MainLayout from "../../components/MainLayout.astro";
// 定义Props类型
export interface Props {

View File

@ -1,6 +1,6 @@
---
import { getCollection, type CollectionEntry } from "astro:content";
import MainLayout from "../../layouts/MainLayout.astro";
import MainLayout from "../../components/MainLayout.astro";
// 获取所有旅行数据并按发布日期排序
const allTravels = await getCollection("travel");

View File

@ -1,5 +1,19 @@
{
"extends": "astro/tsconfigs/strict",
"include": [".astro/types.d.ts", "**/*"],
"exclude": ["dist"]
}
"include": [
".astro/types.d.ts",
"**/*"
],
"exclude": [
"dist"
],
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "react"
},
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
"components/*": ["./src/components/*"]
}
}