practice_code/web/graduation/src/pages/attractions/index.astro

1235 lines
56 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
import MainLayout from "../../layouts/MainLayout.astro";
import { getCollection, type CollectionEntry } from "astro:content";
// 获取景点内容集合
const attractions = await getCollection("attractions");
// 按照日期排序
const sortByDate = <T extends { data: { pubDate?: Date | string, updatedDate?: Date | string } }>(a: T, b: T): number => {
return new Date(b.data.pubDate || b.data.updatedDate || 0).getTime() -
new Date(a.data.pubDate || a.data.updatedDate || 0).getTime();
};
// 按发布日期排序
const sortedAttractions = [...attractions].sort(sortByDate);
// 提取所有标签
const allTags: {name: string, count: number}[] = [];
sortedAttractions.forEach((attraction: CollectionEntry<"attractions">) => {
attraction.data.tags.forEach((tag: string) => {
const existingTag = allTags.find(t => t.name === tag);
if (existingTag) {
existingTag.count++;
} else {
allTags.push({ name: tag, count: 1 });
}
});
});
// 按照标签出现次数排序
allTags.sort((a, b) => b.count - a.count);
// 获取所有分类并计数 (使用可选链以防属性不存在)
const categories: {name: string, count: number}[] = [];
sortedAttractions.forEach((attraction: CollectionEntry<"attractions">) => {
// 从city或title中提取分类信息因为原数据模型似乎没有category字段
const category = attraction.data.city && attraction.data.city.length > 0 ? attraction.data.city[0] : '其他景点';
const existingCategory = categories.find(c => c.name === category);
if (existingCategory) {
existingCategory.count++;
} else {
categories.push({ name: category, count: 1 });
}
});
// 按照分类出现次数排序
categories.sort((a, b) => b.count - a.count);
// 获取所有城市并计数 (从city中提取城市信息)
const cities: {name: string, count: number}[] = [];
sortedAttractions.forEach((attraction: CollectionEntry<"attractions">) => {
if (attraction.data.city && attraction.data.city.length > 0) {
// 直接使用数组中的第一个城市,或默认值
const city = attraction.data.city[0] || '其他地区';
const existingCity = cities.find(c => c.name === city);
if (existingCity) {
existingCity.count++;
} else {
cities.push({ name: city, count: 1 });
}
}
});
// 按照城市出现次数排序
cities.sort((a, b) => b.count - a.count);
// 从URL获取查询参数 - 在静态站点中只用于预填充输入框
const searchQuery = ''; // 对于静态生成的站点,我们在客户端处理搜索
const tagQuery = ''; // 标签筛选参数
const cityQuery = ''; // 城市筛选参数
// 分页逻辑
const itemsPerPage = 9;
const page = 1; // 对于静态生成的页面,我们默认显示第一页,客户端再处理分页
// 搜索和筛选逻辑 - 在静态站点中,所有筛选都在客户端完成
const selectedCategory = '';
const selectedCity = '';
const selectedTags: string[] = [];
const sortBy: 'date' | 'name' = 'date';
// 对于静态生成的页面,我们提供所有景点数据,客户端再进行筛选
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] : '其他景点';
};
// 辅助函数用于获取景点的城市从city提取或默认值
const getCity = (attraction: CollectionEntry<"attractions">) => {
return attraction.data.city && attraction.data.city.length > 0 ?
attraction.data.city[attraction.data.city.length - 1] : '其他地区';
};
---
<MainLayout title="景点 - 河北游礼">
<!-- 摄影探索风格头部 - 更鲜艳的色彩方案 -->
<div class="relative overflow-hidden">
<!-- 背景效果 - 景观照片效果和彩色渐变叠加 -->
<div class="absolute inset-0 bg-gradient-to-br from-primary-400/50 via-primary-500/30 to-accent-400/40 dark:from-primary-900/60 dark:via-primary-900/50 dark:to-accent-900/60 opacity-70 dark:opacity-60"></div>
<div class="absolute inset-0 bg-[url('/images/texture-light.jpg')] dark:bg-[url('/images/texture-dark.jpg')] mix-blend-overlay opacity-30 bg-cover bg-center"></div>
<div class="container mx-auto px-4 py-20 relative z-10">
<div class="max-w-5xl mx-auto text-center">
<!-- 彩色相机取景框效果 -->
<div class="inline-block relative">
<div class="absolute -inset-1 border-2 border-primary-400/70 dark:border-primary-500/50 rounded-sm"></div>
<div class="absolute -inset-3 border border-accent-400/30 dark:border-accent-500/30 rounded-sm"></div>
<div class="absolute -inset-5 border border-secondary-400/20 dark:border-secondary-500/20 rounded-sm"></div>
<h1 class="text-4xl xs:text-5xl sm:text-6xl md:text-7xl font-serif font-light tracking-tight text-slate-800 dark:text-white leading-none mb-4 sm:mb-6">
<span class="inline-block transform -rotate-1 text-transparent bg-clip-text bg-gradient-to-r from-primary-600 to-primary-600 dark:from-primary-400 dark:to-primary-400">河北</span>
<span class="inline-block mx-1 sm:mx-2 text-3xl xs:text-4xl sm:text-5xl opacity-70 text-amber-500 dark:text-amber-400">·</span>
<span class="inline-block transform rotate-1 text-transparent bg-clip-text bg-gradient-to-r from-accent-600 to-accent-600 dark:from-accent-400 dark:to-accent-400">景观</span>
</h1>
<!-- 彩色相机参数显示 -->
<div class="flex flex-wrap justify-center items-center mt-4 mb-4 sm:mb-6 text-[10px] xs:text-xs tracking-widest font-mono bg-white/30 dark:bg-black/30 backdrop-blur-sm px-3 sm:px-4 py-1 rounded-full text-slate-700 dark:text-slate-300">
<span class="text-primary-600 dark:text-primary-400">ISO 100</span>
<span class="mx-2 sm:mx-3 text-slate-500">|</span>
<span class="text-secondary-600 dark:text-secondary-400">f/2.8</span>
<span class="mx-2 sm:mx-3 text-slate-500">|</span>
<span class="text-primary-600 dark:text-primary-400">1/250s</span>
<span class="mx-2 sm:mx-3 text-slate-500">|</span>
<span class="text-accent-600 dark:text-accent-400">24-70mm</span>
</div>
</div>
<p class="text-base sm:text-lg text-slate-700 dark:text-slate-200 max-w-2xl mx-auto font-light leading-relaxed mt-3 sm:mt-4 px-3 drop-shadow-sm">
通过镜头捕捉河北的自然与人文之美,每一处景点都是一幅值得细细品味的画作
</p>
<!-- 彩色取景器元素 -->
<div class="mt-8 sm:mt-12 mb-2 flex justify-center">
<div class="px-4 sm:px-5 py-1.5 sm:py-2 bg-gradient-to-r from-primary-500/80 to-accent-500/80 dark:from-primary-700/80 dark:to-accent-700/80 text-white rounded-full text-xs sm:text-sm tracking-wider font-mono inline-flex items-center space-x-1 sm:space-x-2 shadow-md sm:shadow-lg shadow-primary-500/20 dark:shadow-primary-700/20">
<svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5 sm:h-4 sm:w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 13a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
<span>EXPLORE</span>
</div>
</div>
</div>
</div>
</div>
<!-- 主内容区 - 摄影展览风格 -->
<div class="bg-gradient-to-b from-white to-primary-50 dark:from-black dark:to-primary-950/30 text-gray-900 dark:text-white py-6 sm:py-10">
<div class="container mx-auto px-4">
<!-- 移动端筛选按钮 -->
<div class="lg:hidden mb-4 flex justify-between items-center">
<button id="mobile-filter-toggle" class="bg-primary-500 text-white px-4 py-2 rounded-full inline-flex items-center space-x-2 shadow-md active:shadow-sm transition-shadow">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
</svg>
<span>筛选</span>
</button>
<!-- 移动端搜索框 -->
<form id="search-form-mobile" class="relative flex-grow mx-3" method="get" action="/attractions">
<input
type="text"
name="search"
placeholder="搜索景点..."
class="w-full text-slate-700 dark:text-gray-200 bg-white/90 dark:bg-primary-900/50 border border-primary-300 dark:border-primary-700 rounded-full py-2 pl-3 pr-10 focus:outline-none focus:border-primary-500 placeholder-gray-400/80 dark:placeholder-gray-500/80 text-sm"
/>
<button type="submit" class="absolute right-3 top-1/2 transform -translate-y-1/2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-primary-500 dark:text-primary-400" 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>
</form>
</div>
<!-- 移动端筛选抽屉 - 默认隐藏 -->
<div id="mobile-filter-drawer" class="lg:hidden fixed inset-0 z-50 transform translate-x-full transition-transform duration-300 ease-in-out">
<div class="absolute inset-0 bg-black/50 backdrop-blur-sm" id="mobile-filter-backdrop"></div>
<div class="absolute right-0 top-0 bottom-0 w-4/5 max-w-sm bg-white dark:bg-gray-900 shadow-xl overflow-y-auto">
<div class="p-4 border-b border-gray-200 dark:border-gray-800 flex justify-between items-center">
<h3 class="text-lg font-medium text-gray-800 dark:text-white">筛选</h3>
<button id="mobile-filter-close" class="rounded-full p-2 text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div class="p-3 space-y-4">
<!-- 城市筛选 - 移动端 -->
<div>
<h3 class="text-base font-medium text-gray-700 dark:text-gray-300 mb-2">按城市浏览</h3>
<div class="flex flex-wrap gap-2">
{cities.slice(0, 8).map(city => (
<div class="mobile-city-tag px-3 py-2 bg-secondary-100 dark:bg-secondary-900/30 text-secondary-800 dark:text-secondary-200 text-sm rounded-full cursor-pointer hover:bg-secondary-200 dark:hover:bg-secondary-800/50 transition-colors">
{city.name}
<span class="ml-1 text-xs text-secondary-500 dark:text-secondary-400">({city.count})</span>
</div>
))}
</div>
</div>
<!-- 标签筛选 - 移动端 -->
<div>
<h3 class="text-base font-medium text-gray-700 dark:text-gray-300 mb-2">特色标签</h3>
<div class="flex flex-wrap gap-2">
{allTags.slice(0, 15).map(tag => (
<div class="mobile-tag-item px-3 py-2 bg-accent-100 dark:bg-accent-900/30 text-accent-800 dark:text-accent-200 text-sm rounded-full cursor-pointer hover:bg-accent-200 dark:hover:bg-accent-800/50 transition-colors">
#{tag.name}
<span class="ml-1 text-xs text-accent-500 dark:text-accent-400">({tag.count})</span>
</div>
))}
</div>
</div>
<!-- 底部应用按钮 -->
<div class="border-t border-gray-200 dark:border-gray-800 pt-3 flex justify-end">
<button id="mobile-filter-apply" class="bg-primary-500 text-white px-6 py-2 rounded-full shadow-md">
应用筛选
</button>
</div>
</div>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-12 gap-4 sm:gap-8">
<!-- 左侧滤镜区域 - 彩色摄影参数风格 - 在桌面端显示 -->
<div class="hidden lg:block lg:col-span-3">
<div class="sticky top-16 space-y-3 sm:space-y-6">
<div class="bg-white/80 dark:bg-primary-950/40 backdrop-blur-sm p-4 sm:p-5 border border-primary-100 dark:border-primary-800/50 rounded-lg shadow-lg sm:shadow-xl shadow-primary-100/50 dark:shadow-primary-900/20">
<!-- 彩色搜索框 -->
<form id="search-form" class="mb-4 sm:mb-6 max-w-xl mx-auto" method="get" action="/attractions">
<div class="flex items-center">
<div class="relative flex-grow">
<input
type="text"
name="search"
placeholder="搜索景点名称、描述、标签..."
class="block w-full text-slate-700 dark:text-gray-200 bg-white/90 dark:bg-primary-900/50 border border-primary-300 dark:border-primary-700 rounded-full py-1.5 sm:py-2 pl-3 sm:pl-4 pr-10 focus:outline-none focus:border-primary-500 placeholder-gray-400/80 dark:placeholder-gray-500/80 text-sm"
/>
<div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 sm:h-5 sm:w-5 text-primary-500 dark:text-primary-400" 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>
</div>
</div>
</div>
</form>
<!-- 城市筛选 - 彩色光圈设置 -->
<div class="mt-4 sm:mt-6">
<!-- 城市标签 -->
<h3 class="text-lg font-medium text-gray-700 dark:text-gray-200 mb-2">按城市浏览</h3>
<div class="flex flex-wrap gap-2">
{cities.slice(0, 6).map(city => (
<div class="desktop-city-tag px-3 py-1 bg-secondary-100 dark:bg-secondary-900/30 text-secondary-800 dark:text-secondary-200 text-sm rounded-full cursor-pointer hover:bg-secondary-200 dark:hover:bg-secondary-800/50 transition-colors">
{city.name}
<span class="ml-1 text-xs text-secondary-500 dark:text-secondary-400">({city.count})</span>
</div>
))}
</div>
</div>
<!-- 标签筛选 - 彩色胶片风格 -->
<div class="mt-4 sm:mt-6">
<!-- 标签云 -->
<h3 class="text-lg font-medium text-gray-700 dark:text-gray-200 mb-2">特色标签</h3>
<div class="flex flex-wrap gap-2">
{allTags.slice(0, 12).map(tag => (
<div class="desktop-tag-item px-3 py-1 bg-accent-100 dark:bg-accent-900/30 text-accent-800 dark:text-accent-200 text-sm rounded-full cursor-pointer hover:bg-accent-200 dark:hover:bg-accent-800/50 transition-colors">
#{tag.name}
<span class="ml-1 text-xs text-accent-500 dark:text-accent-400">({tag.count})</span>
</div>
))}
</div>
</div>
</div>
<!-- 自定义筛选器提示 - 彩色相机操作引导 -->
<div class="p-4 border border-primary-100 dark:border-primary-900/50 bg-gradient-to-br from-white to-primary-50 dark:from-primary-950/20 dark:to-primary-950/20 mt-4 rounded-lg">
<div class="flex items-start space-x-3">
<div class="text-primary-500 dark:text-primary-400">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<p class="text-sm text-gray-700 dark:text-primary-200/80 leading-relaxed">
筛选条件就像相机参数,调整它们以找到最适合你的河北景观视角。
</p>
</div>
</div>
</div>
</div>
<!-- 右侧景点展示区 - 彩色摄影展览风格 -->
<div class="lg:col-span-9">
<!-- 当前筛选状态 - 彩色胶片信息风格 -->
<div id="filter-status" class="mb-4 sm:mb-8 bg-gradient-to-r from-primary-50 to-secondary-50 dark:from-primary-950/30 dark:to-secondary-950/30 p-3 sm:p-4 border-l-4 border-primary-300 dark:border-primary-700 rounded-r-lg hidden">
<div class="flex flex-wrap items-center gap-2 sm:gap-3 text-xs sm:text-sm text-primary-700 dark:text-primary-300">
<div class="font-mono text-[10px] xs:text-xs tracking-wider text-primary-500 dark:text-primary-400 bg-primary-100 dark:bg-primary-900/50 px-1.5 sm:px-2 py-0.5 sm:py-1 rounded">FILTER</div>
{/* 搜索查询 - 客户端处理 */}
<div id="search-filter" class="px-2 sm:px-3 py-0.5 sm:py-1 bg-primary-100 dark:bg-primary-800/30 rounded-full text-xs sm:text-sm flex items-center space-x-1 hidden truncate max-w-[40%] sm:max-w-none">
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 sm:h-4 sm:w-4 flex-shrink-0 text-primary-500 dark:text-primary-400" 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>
<span id="search-filter-text" class="truncate">搜索: </span>
</div>
{/* 标签筛选 - 客户端处理 */}
<div id="tag-filter" class="px-2 sm:px-3 py-0.5 sm:py-1 bg-accent-100 dark:bg-accent-800/30 rounded-full text-xs sm:text-sm flex items-center space-x-1 hidden truncate max-w-[40%] sm:max-w-none">
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 sm:h-4 sm:w-4 flex-shrink-0 text-accent-500 dark:text-accent-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" />
</svg>
<span id="tag-filter-text" class="truncate">标签: </span>
</div>
{/* 城市筛选 - 客户端处理 */}
<div id="city-filter" class="px-2 sm:px-3 py-0.5 sm:py-1 bg-secondary-100 dark:bg-secondary-800/30 rounded-full text-xs sm:text-sm flex items-center space-x-1 hidden truncate max-w-[40%] sm:max-w-none">
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 sm:h-4 sm:w-4 flex-shrink-0 text-secondary-500 dark:text-secondary-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
<span id="city-filter-text" class="truncate">城市: </span>
</div>
<button
id="reset-filters"
class="ml-auto text-primary-500 hover:text-primary-700 dark:hover:text-primary-300 text-xs sm:text-sm flex items-center space-x-0.5 sm:space-x-1 bg-white/80 dark:bg-black/30 px-2 sm:px-3 py-0.5 sm:py-1 rounded-full"
>
<span>重置</span>
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 sm:h-4 sm:w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
<!-- 景点展示 - 彩色摄影展览风格 -->
<div class="flex flex-wrap gap-3 sm:gap-4 md:gap-6" id="attractions-grid">
{currentPageAttractions.map((attraction, index) => {
// 为每个卡片生成不同的颜色方案
const colorSchemes = [
{ from: 'from-primary-900/80', via: 'via-primary-900/60', to: 'to-transparent', border: 'border-primary-200 dark:border-primary-800', hover: 'group-hover:border-primary-300 dark:group-hover:border-primary-700', badges: 'bg-primary-900/70 text-primary-100' },
{ from: 'from-secondary-900/80', via: 'via-primary-900/60', to: 'to-transparent', border: 'border-secondary-200 dark:border-secondary-800', hover: 'group-hover:border-secondary-300 dark:group-hover:border-secondary-700', badges: 'bg-secondary-900/70 text-secondary-100' },
{ from: 'from-accent-900/80', via: 'via-accent-900/60', to: 'to-transparent', border: 'border-accent-200 dark:border-accent-800', hover: 'group-hover:border-accent-300 dark:group-hover:border-accent-700', badges: 'bg-accent-900/70 text-accent-100' }
];
const colorScheme = colorSchemes[index % colorSchemes.length];
return (
<div class="w-full sm:w-[calc(50%-8px)] md:w-[calc(50%-12px)] lg:w-[calc(33.333%-16px)]">
<a href={`/attractions/${attraction.slug}`} class="group">
<div class={`attraction-card relative bg-white dark:bg-gray-900 overflow-hidden border ${colorScheme.border} ${colorScheme.hover} transition-all duration-300 h-full shadow-lg hover:shadow-xl dark:shadow-none rounded-lg`}>
<!-- 景点图片 - 彩色摄影展览风格 -->
<div class="aspect-[4/5] relative overflow-hidden rounded-t-lg">
<div class="absolute inset-0 bg-gradient-to-br from-primary-100 via-primary-100 to-secondary-100 dark:from-primary-900/30 dark:via-primary-900/30 dark:to-secondary-900/30 flex items-center justify-center">
<span class="text-primary-400 dark:text-primary-500">{attraction.data.title}</span>
</div>
<!-- 景点信息悬浮层 - 彩色相片信息卡片 -->
<div class={`absolute inset-0 flex flex-col justify-end p-4 bg-gradient-to-t ${colorScheme.from} ${colorScheme.via} ${colorScheme.to} opacity-90 group-hover:opacity-100 transition-opacity`}>
<div class="transform translate-y-0 group-hover:translate-y-0 transition-transform duration-300">
<div class="space-y-2">
<!-- 类别标签 -->
<div class="flex items-center space-x-2 text-xs text-white">
<div class={`attraction-city px-2 py-1 ${colorScheme.badges} backdrop-blur-sm inline-flex items-center space-x-1 rounded-full`}>
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
<span>{attraction.data.city || '其他地区'}</span>
</div>
</div>
<!-- 景点名称 -->
<h3 class="attraction-title text-white text-base sm:text-lg font-light leading-tight drop-shadow-md">{attraction.data.title}</h3>
<!-- 景点描述 - 悬停时显示 -->
<p class="attraction-desc text-primary-100 dark:text-primary-100 text-xs sm:text-sm line-clamp-2 opacity-0 group-hover:opacity-100 transition-opacity duration-300 delay-100">
{attraction.data.description}
</p>
<!-- 标签 - 彩色快速信息 -->
<div class="attraction-tags flex flex-wrap gap-0.5 sm:gap-1 pt-1 opacity-80 group-hover:opacity-100 transition-opacity">
{attraction.data.tags.slice(0, 3).map((tag: string, i: number) => {
const tagColors = ['bg-primary-500/40', 'bg-secondary-500/40', 'bg-accent-500/40'];
return (
<span class={`px-1.5 py-0.5 text-[10px] ${tagColors[i % tagColors.length]} text-white backdrop-blur-sm rounded-sm`}>
#{tag}
</span>
);
})}
{attraction.data.tags.length > 3 && (
<span class="px-1.5 py-0.5 text-[10px] bg-white/20 text-white backdrop-blur-sm rounded-sm">
+{attraction.data.tags.length - 3}
</span>
)}
</div>
</div>
</div>
</div>
</div>
<!-- 底部探索提示 - 彩色胶片边缘信息 -->
<div class="p-3 border-t border-gray-100 dark:border-gray-800 bg-gradient-to-r from-white to-primary-50/50 dark:from-gray-900 dark:to-primary-950/30 flex justify-between items-center rounded-b-lg">
<span class="text-xs text-primary-500 dark:text-primary-400 font-mono tracking-wider">EXPLORE</span>
<div class="flex items-center text-xs text-gray-600 dark:text-gray-400 group-hover:text-primary-700 dark:group-hover:text-primary-300 transition-colors">
<span class="mr-1">查看详情</span>
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 transform group-hover:translate-x-1 transition-transform" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3" />
</svg>
</div>
</div>
</div>
</a>
</div>
);
})}
</div>
<!-- 无搜索结果提示 - 独立放置由JS控制显示 -->
<div id="no-results-message" class="hidden container mx-auto p-8 text-center">
<div class="max-w-md mx-auto bg-white dark:bg-gray-800 rounded-lg shadow-md p-8 border border-gray-100 dark:border-gray-700">
<svg xmlns="http://www.w3.org/2000/svg" class="h-16 w-16 mx-auto text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1">
<path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
<h3 class="mt-4 text-xl font-semibold text-gray-800 dark:text-white">未找到匹配结果</h3>
<p id="search-term-message" class="mt-2 text-gray-600 dark:text-gray-300">
抱歉,未找到相关景点。请尝试其他关键词或浏览所有景点。
</p>
<div class="mt-6">
<a href="/attractions" class="text-primary-600 hover:text-primary-700 dark:text-primary-400 dark:hover:text-primary-300 font-medium transition-colors">
查看所有景点 &rarr;
</a>
</div>
</div>
</div>
<!-- 分页控件 - 彩色胶片编号风格 -->
{totalPages > 1 && (
<div class="mt-8 sm:mt-12 md:mt-16 flex justify-center">
<div class="inline-flex bg-white dark:bg-primary-950/30 border border-primary-200 dark:border-primary-800 shadow-md sm:shadow-lg dark:shadow-primary-900/20 rounded-xl overflow-hidden text-xs sm:text-sm">
<a
href={page > 1 ? `/attractions?page=${page - 1}` : '#'}
class={`px-2 sm:px-4 py-1.5 sm:py-2 border-r border-primary-200 dark:border-primary-800 flex items-center space-x-0.5 sm:space-x-1 ${
page > 1
? 'text-primary-600 dark:text-primary-400 hover:text-primary-900 dark:hover:text-primary-200 hover:bg-primary-50 dark:hover:bg-primary-900/30'
: 'text-primary-300 dark:text-primary-700 cursor-not-allowed'
}`}
>
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 sm:h-4 sm:w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
<span>上一页</span>
</a>
{Array.from({ length: Math.min(totalPages, 5) }, (_, i) => {
const pageNum = i + 1;
return (
<a
href={`/attractions?page=${pageNum}`}
class={`w-8 sm:w-10 flex items-center justify-center border-r border-primary-200 dark:border-primary-800 ${
pageNum === page
? 'bg-gradient-to-br from-primary-500 to-secondary-500 dark:from-primary-600 dark:to-secondary-600 text-white font-medium'
: 'text-primary-600 dark:text-primary-400 hover:text-primary-800 dark:hover:text-primary-200 hover:bg-primary-50 dark:hover:bg-primary-900/30'
}`}
>
{pageNum}
</a>
);
})}
{totalPages > 5 && (
<span class="w-6 sm:w-10 flex items-center justify-center border-r border-primary-200 dark:border-primary-800 text-primary-400 dark:text-primary-600">...</span>
)}
{totalPages > 5 && (
<a
href={`/attractions?page=${totalPages}`}
class="w-8 sm:w-10 flex items-center justify-center border-r border-primary-200 dark:border-primary-800 text-primary-600 dark:text-primary-400 hover:text-primary-800 dark:hover:text-primary-200 hover:bg-primary-50 dark:hover:bg-primary-900/30"
>
{totalPages}
</a>
)}
<a
href={page < totalPages ? `/attractions?page=${page + 1}` : '#'}
class={`px-2 sm:px-4 py-1.5 sm:py-2 flex items-center space-x-0.5 sm:space-x-1 ${
page < totalPages
? 'text-primary-600 dark:text-primary-400 hover:text-primary-900 dark:hover:text-primary-200 hover:bg-primary-50 dark:hover:bg-primary-900/30'
: 'text-primary-300 dark:text-primary-700 cursor-not-allowed'
}`}
>
<span>下一页</span>
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 sm:h-4 sm:w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
</a>
</div>
</div>
)}
<!-- 页脚引言 - 彩色摄影师语录风格 -->
<div class="mt-8 sm:mt-12 md:mt-16 text-center px-3">
<div class="inline-block max-w-xl bg-gradient-to-br from-white via-primary-50 to-secondary-50 dark:from-primary-950/20 dark:via-secondary-950/20 dark:to-black p-4 sm:p-5 transform -rotate-1 border border-primary-200 dark:border-primary-800 shadow-md sm:shadow-lg shadow-primary-200/30 dark:shadow-none rounded-lg">
<blockquote class="text-base sm:text-lg text-primary-700 dark:text-primary-300 italic">
"最美的景观并非仅存于远方,而在于观察者如何用心去发现"
</blockquote>
<div class="mt-3 text-[10px] sm:text-xs text-accent-500 dark:text-accent-400 tracking-wider font-mono bg-white/80 dark:bg-black/50 py-0.5 sm:py-1 rounded-full">
— 河北风光摄影集 —
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</MainLayout>
<style>
.hide-scrollbar::-webkit-scrollbar {
display: none;
}
.hide-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
/* 彩色动画效果 */
@keyframes gradientShift {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
.animate-gradient {
background-size: 200% 200%;
animation: gradientShift 8s ease infinite;
}
/* 鼠标悬停卡片发光效果 */
.card-glow {
transition: box-shadow 0.3s ease-in-out;
}
.card-glow:hover {
box-shadow: 0 0 15px var(--color-primary-400, rgba(251, 191, 36, 0.4));
}
.dark .card-glow:hover {
box-shadow: 0 0 15px var(--color-primary-500, rgba(245, 158, 11, 0.4));
}
@media (prefers-color-scheme: dark) {
.dark-mode-filter {
filter: brightness(0.8) contrast(1.2);
}
}
/* 标签悬停效果 */
.tag-hover {
transform: translateY(0);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.tag-hover:hover {
transform: translateY(-2px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.dark .tag-hover:hover {
box-shadow: 0 2px 8px rgba(255, 255, 255, 0.1);
}
/* Flexbox排序支持 */
#attractions-grid {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
gap: 1.5rem;
}
#attractions-grid > div {
order: 0;
}
#attractions-grid > div.hidden-card {
display: none !important;
}
/* 为筛选中的卡片移除过渡效果 */
.attraction-card {
transition: none;
}
/* 移动端触摸友好样式 */
@media (max-width: 640px) {
.tag-hover:active {
transform: scale(0.95);
opacity: 0.8;
}
.card-glow:active {
transform: scale(0.98);
opacity: 0.9;
}
/* 提高移动端按钮触摸区域 */
button,
a[href],
input[type="submit"],
input[type="button"] {
min-height: 44px;
min-width: 44px;
}
/* 改善移动端文字间距 */
p, h1, h2, h3, h4, h5, h6 {
letter-spacing: -0.01em;
}
}
/* 移动端筛选抽屉样式 */
body.overflow-hidden {
overflow: hidden;
}
#mobile-filter-drawer {
will-change: transform;
}
@keyframes slideIn {
from { transform: translateX(100%); }
to { transform: translateX(0); }
}
.mobile-tag-item,
.mobile-city-tag {
position: relative;
overflow: hidden;
}
.mobile-tag-item::after,
.mobile-city-tag::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.1);
transform: translateY(100%);
transition: transform 0.3s;
}
.mobile-tag-item:active::after,
.mobile-city-tag:active::after {
transform: translateY(0);
}
/* 确保移动端搜索框在不同屏幕尺寸下都能正常显示 */
@media (max-width: 480px) {
#search-form-mobile input {
font-size: 14px;
padding-left: 10px;
padding-right: 30px;
}
#mobile-filter-toggle {
padding: 6px 12px;
font-size: 12px;
}
#mobile-filter-toggle svg {
width: 14px;
height: 14px;
}
}
</style>
<script>
document.addEventListener('DOMContentLoaded', () => {
// 客户端交互逻辑
const searchForm = document.getElementById('search-form');
const searchFormMobile = document.getElementById('search-form-mobile');
const searchInput = document.querySelector('#search-form input[name="search"]') as HTMLInputElement;
const searchInputMobile = document.querySelector('#search-form-mobile input[name="search"]') as HTMLInputElement;
const desktopTagElements = document.querySelectorAll('.desktop-tag-item, .desktop-city-tag');
const mobileTagElements = document.querySelectorAll('.mobile-tag-item, .mobile-city-tag');
const attractionCards = document.querySelectorAll('.attraction-card');
const noResultsMessage = document.getElementById('no-results-message');
const searchTermMessage = document.getElementById('search-term-message');
const attractionsGrid = document.getElementById('attractions-grid');
// 移动端筛选抽屉元素
const mobileFilterToggle = document.getElementById('mobile-filter-toggle');
const mobileFilterDrawer = document.getElementById('mobile-filter-drawer');
const mobileFilterBackdrop = document.getElementById('mobile-filter-backdrop');
const mobileFilterClose = document.getElementById('mobile-filter-close');
const mobileFilterApply = document.getElementById('mobile-filter-apply');
// 打开移动端筛选抽屉
if (mobileFilterToggle && mobileFilterDrawer) {
mobileFilterToggle.addEventListener('click', () => {
mobileFilterDrawer.classList.remove('translate-x-full');
document.body.classList.add('overflow-hidden'); // 防止背景滚动
});
}
// 关闭移动端筛选抽屉
const closeFilterDrawer = () => {
if (mobileFilterDrawer) {
mobileFilterDrawer.classList.add('translate-x-full');
document.body.classList.remove('overflow-hidden');
}
};
// 点击背景关闭抽屉
if (mobileFilterBackdrop) {
mobileFilterBackdrop.addEventListener('click', closeFilterDrawer);
}
// 点击关闭按钮关闭抽屉
if (mobileFilterClose) {
mobileFilterClose.addEventListener('click', closeFilterDrawer);
}
// 点击应用按钮关闭抽屉并应用筛选
if (mobileFilterApply) {
mobileFilterApply.addEventListener('click', () => {
closeFilterDrawer();
// 筛选逻辑已经在标签点击时处理
});
}
// 同步桌面端和移动端的搜索内容
const syncSearchInputs = (from: HTMLInputElement, to: HTMLInputElement) => {
if (from && to) {
to.value = from.value;
}
};
// 筛选状态UI元素
const filterStatus = document.getElementById('filter-status');
const searchFilter = document.getElementById('search-filter');
const tagFilter = document.getElementById('tag-filter');
const cityFilter = document.getElementById('city-filter');
const searchFilterText = document.getElementById('search-filter-text');
const tagFilterText = document.getElementById('tag-filter-text');
const cityFilterText = document.getElementById('city-filter-text');
const resetFiltersBtn = document.getElementById('reset-filters');
// 客户端解析URL查询参数
const urlParams = new URLSearchParams(window.location.search);
const searchParamFromUrl = urlParams.get('search');
const tagParamFromUrl = urlParams.get('tag');
const cityParamFromUrl = urlParams.get('city');
const pageParam = urlParams.get('page');
const currentPage = pageParam ? parseInt(pageParam) : 1;
// 当前筛选状态
let currentTag = tagParamFromUrl || '';
let currentCity = cityParamFromUrl || '';
let currentSearch = searchParamFromUrl || '';
// 更新浏览器历史记录,不刷新页面
function updateHistory() {
const params = new URLSearchParams();
if (currentSearch) params.set('search', currentSearch);
if (currentTag) params.set('tag', currentTag);
if (currentCity) params.set('city', currentCity);
const newUrl = `${window.location.pathname}${params.toString() ? '?' + params.toString() : ''}`;
window.history.pushState({ search: currentSearch, tag: currentTag, city: currentCity }, '', newUrl);
}
// 显示当前筛选状态
function updateFilterStatusUI() {
let hasFilters = false;
// 处理搜索筛选
if (currentSearch && searchFilter && searchFilterText) {
searchFilterText.textContent = `搜索: ${currentSearch}`;
searchFilter.classList.remove('hidden');
hasFilters = true;
} else if (searchFilter) {
searchFilter.classList.add('hidden');
}
// 处理标签筛选
if (currentTag && tagFilter && tagFilterText) {
tagFilterText.textContent = `标签: ${currentTag}`;
tagFilter.classList.remove('hidden');
hasFilters = true;
} else if (tagFilter) {
tagFilter.classList.add('hidden');
}
// 处理城市筛选
if (currentCity && cityFilter && cityFilterText) {
cityFilterText.textContent = `城市: ${currentCity}`;
cityFilter.classList.remove('hidden');
hasFilters = true;
} else if (cityFilter) {
cityFilter.classList.add('hidden');
}
// 显示或隐藏整个筛选状态区域
if (hasFilters && filterStatus) {
filterStatus.classList.remove('hidden');
} else if (filterStatus) {
filterStatus.classList.add('hidden');
}
}
// 客户端筛选函数
function filterAttractions() {
const searchValue = currentSearch.toLowerCase();
const tagValue = currentTag.toLowerCase();
const cityValue = currentCity.toLowerCase();
let matchCount = 0;
const matchedCards: HTMLElement[] = [];
// 获取所有卡片容器
const cardContainers = attractionsGrid ? Array.from(attractionsGrid.children) as HTMLElement[] : [];
// 如果没有筛选条件,显示所有卡片
if (!searchValue && !tagValue && !cityValue) {
if (noResultsMessage) {
noResultsMessage.classList.add('hidden');
}
if (attractionsGrid) {
attractionsGrid.classList.remove('hidden');
}
// 显示所有卡片并重置排序
cardContainers.forEach((container) => {
container.classList.remove('hidden-card');
container.style.order = '';
});
return cardContainers.length;
}
// 遍历所有景点卡片进行筛选
cardContainers.forEach((container) => {
const cardTitle = container.querySelector('.attraction-title')?.textContent?.toLowerCase() || '';
const cardDesc = container.querySelector('.attraction-desc')?.textContent?.toLowerCase() || '';
const cardTags = container.querySelector('.attraction-tags')?.textContent?.toLowerCase() || '';
// 获取所有城市标签现在city是数组
const cityElements = container.querySelectorAll('.attraction-city');
let cardCities: string[] = [];
cityElements.forEach((element) => {
const cityText = element?.textContent?.toLowerCase() || '';
if (cityText) {
cardCities.push(cityText);
}
});
// 如果没有找到城市元素,使用旧的单一城市元素方式作为兼容
if (cardCities.length === 0) {
const singleCityText = container.querySelector('.attraction-city')?.textContent?.toLowerCase() || '';
if (singleCityText) {
cardCities.push(singleCityText);
}
}
// 匹配逻辑:满足所有已提供的筛选条件
let isMatch = true;
// 搜索词筛选
if (searchValue && !(
cardTitle.includes(searchValue) ||
cardDesc.includes(searchValue) ||
cardTags.includes(searchValue) ||
cardCities.some(city => city.includes(searchValue))
)) {
isMatch = false;
}
// 标签筛选
if (tagValue && !cardTags.includes(tagValue)) {
isMatch = false;
}
// 城市筛选 - 检查cityValue是否包含在任何一个城市中
if (cityValue && !cardCities.some(city => city.includes(cityValue))) {
isMatch = false;
}
if (isMatch) {
matchCount++;
matchedCards.push(container);
container.classList.remove('hidden-card');
} else {
container.classList.add('hidden-card');
}
});
// 重新排序匹配的卡片,使它们从第一个位置开始依次排列
matchedCards.forEach((container, index) => {
container.style.order = index.toString();
});
// 更新无结果消息显示
if (matchCount === 0) {
// 显示无结果消息
if (noResultsMessage) {
noResultsMessage.classList.remove('hidden');
// 更新无结果消息中的搜索词
if (searchTermMessage) {
let messageText = '抱歉,未找到相关景点。';
if (searchValue) {
messageText += ` 搜索词: "${searchValue}"`;
}
if (tagValue) {
messageText += ` 标签: "${tagValue}"`;
}
if (cityValue) {
messageText += ` 城市: "${cityValue}"`;
}
messageText += '。请尝试其他条件或浏览所有景点。';
searchTermMessage.textContent = messageText;
}
}
// 隐藏景点网格
if (attractionsGrid) {
attractionsGrid.classList.add('hidden');
}
} else {
// 隐藏无结果消息
if (noResultsMessage) {
noResultsMessage.classList.add('hidden');
}
// 显示景点网格
if (attractionsGrid) {
attractionsGrid.classList.remove('hidden');
}
}
return matchCount;
}
// 处理点击标签/城市筛选的函数
function handleFilterClick(type: 'tag' | 'city', value: string) {
// 如果点击了已选中的标签/城市,则取消选择
if ((type === 'tag' && value === currentTag) || (type === 'city' && value === currentCity)) {
if (type === 'tag') {
currentTag = '';
} else {
currentCity = '';
}
} else {
// 否则,选择新标签/城市
if (type === 'tag') {
currentTag = value;
} else {
currentCity = value;
}
}
// 更新UI和执行筛选
updateFilterStatusUI();
updateTagsHighlight(); // 更新标签高亮状态
filterAttractions();
updateHistory();
}
// 新增:更新标签和城市的高亮状态
function updateTagsHighlight() {
// 处理桌面端标签
if (desktopTagElements) {
updateElementsHighlight(desktopTagElements);
}
// 处理移动端标签
if (mobileTagElements) {
updateElementsHighlight(mobileTagElements);
}
}
// 辅助函数:为给定元素集合更新高亮状态
function updateElementsHighlight(elements: NodeListOf<Element>) {
elements.forEach((tag) => {
// 找出是城市还是标签
const isTag = tag.textContent?.includes('#');
// 提取文本内容,并移除计数部分和#号
let tagText = tag.textContent?.trim() || '';
tagText = tagText.replace(/\(\d+\)$/, '').trim();
if (isTag) {
tagText = tagText.replace(/^#/, '');
// 处理标签高亮
if (tagText === currentTag) {
// 添加高亮样式
tag.classList.add('bg-accent-500/70', 'dark:bg-accent-500/50', 'text-white', 'dark:text-white', 'shadow-md');
tag.classList.remove('bg-accent-100', 'dark:bg-accent-900/30', 'text-accent-800', 'dark:text-accent-200');
} else {
// 移除高亮样式
tag.classList.remove('bg-accent-500/70', 'dark:bg-accent-500/50', 'text-white', 'dark:text-white', 'shadow-md');
tag.classList.add('bg-accent-100', 'dark:bg-accent-900/30', 'text-accent-800', 'dark:text-accent-200');
}
} else {
// 处理城市高亮
if (tagText === currentCity) {
// 添加高亮样式
tag.classList.add('bg-secondary-500/70', 'dark:bg-secondary-500/50', 'text-white', 'dark:text-white', 'shadow-md');
tag.classList.remove('bg-secondary-100', 'dark:bg-secondary-900/30', 'text-secondary-800', 'dark:text-secondary-200');
} else {
// 移除高亮样式
tag.classList.remove('bg-secondary-500/70', 'dark:bg-secondary-500/50', 'text-white', 'dark:text-white', 'shadow-md');
tag.classList.add('bg-secondary-100', 'dark:bg-secondary-900/30', 'text-secondary-800', 'dark:text-secondary-200');
}
}
});
}
// 处理标签点击事件 - 桌面端
function attachTagClickEvents(elements: NodeListOf<Element>) {
elements.forEach((tag) => {
// 添加触摸友好类
tag.classList.add('tag-hover');
tag.addEventListener('click', () => {
// 找出是城市还是标签
const isTag = tag.textContent?.includes('#');
// 提取文本内容,并移除计数部分和#号
let tagText = tag.textContent?.trim() || '';
// 移除计数信息 (数字部分)
tagText = tagText.replace(/\(\d+\)$/, '').trim();
// 对于标签,还需要移除#号
if (isTag) {
tagText = tagText.replace(/^#/, '');
}
// 添加点击视觉反馈
tag.classList.add('scale-95', 'opacity-70');
setTimeout(() => {
tag.classList.remove('scale-95', 'opacity-70');
}, 200);
if (tagText) {
// 在客户端处理筛选,不刷新页面
handleFilterClick(isTag ? 'tag' : 'city', tagText);
}
});
});
}
// 为桌面端和移动端标签添加点击事件处理
if (desktopTagElements) attachTagClickEvents(desktopTagElements);
if (mobileTagElements) attachTagClickEvents(mobileTagElements);
// 初始化更新筛选状态UI、应用筛选
updateFilterStatusUI();
// 如果URL中有筛选参数执行筛选
if (searchParamFromUrl || tagParamFromUrl || cityParamFromUrl) {
if (searchParamFromUrl) {
if (searchInput) searchInput.value = searchParamFromUrl;
if (searchInputMobile) searchInputMobile.value = searchParamFromUrl;
}
// 设置当前的标签和城市
if (tagParamFromUrl) currentTag = tagParamFromUrl;
if (cityParamFromUrl) currentCity = cityParamFromUrl;
// 更新标签高亮
updateTagsHighlight();
// 立即触发筛选
filterAttractions();
}
// 窗口历史记录弹出状态处理
window.addEventListener('popstate', (event) => {
// 从历史状态中恢复筛选条件
if (event.state) {
currentSearch = event.state.search || '';
currentTag = event.state.tag || '';
currentCity = event.state.city || '';
// 更新搜索框值
if (searchInput) searchInput.value = currentSearch;
if (searchInputMobile) searchInputMobile.value = currentSearch;
} else {
// 如果没有状态(例如回到初始页面),清除所有筛选
currentSearch = '';
currentTag = '';
currentCity = '';
if (searchInput) searchInput.value = '';
if (searchInputMobile) searchInputMobile.value = '';
}
// 更新UI和执行筛选
updateFilterStatusUI();
updateTagsHighlight(); // 更新标签高亮状态
filterAttractions();
});
// 重置筛选按钮
if (resetFiltersBtn) {
resetFiltersBtn.addEventListener('click', () => {
// 清除所有筛选条件
currentSearch = '';
currentTag = '';
currentCity = '';
// 清空搜索框
if (searchInput) searchInput.value = '';
if (searchInputMobile) searchInputMobile.value = '';
// 获取所有卡片容器
const cardContainers = attractionsGrid ? Array.from(attractionsGrid.children) as HTMLElement[] : [];
// 重置所有卡片的排序和显示状态
cardContainers.forEach((container, index) => {
container.classList.remove('hidden-card');
container.style.order = index.toString(); // 恢复原始顺序
});
// 更新UI状态
updateFilterStatusUI();
updateTagsHighlight(); // 更新标签高亮状态
// 更新URL不带查询参数
window.history.pushState({}, '', window.location.pathname);
});
}
// 处理桌面端搜索功能
if (searchForm && searchInput) {
searchForm.addEventListener('submit', (e) => {
e.preventDefault(); // 阻止默认提交
const query = searchInput.value.trim();
if (!query) {
return;
}
// 更新当前搜索词
currentSearch = query;
// 同步到移动端搜索框
if (searchInputMobile) searchInputMobile.value = query;
// 执行筛选并更新UI
updateFilterStatusUI();
filterAttractions();
updateHistory();
});
}
// 处理移动端搜索功能
if (searchFormMobile && searchInputMobile) {
searchFormMobile.addEventListener('submit', (e) => {
e.preventDefault(); // 阻止默认提交
const query = searchInputMobile.value.trim();
if (!query) {
return;
}
// 更新当前搜索词
currentSearch = query;
// 同步到桌面端搜索框
if (searchInput) searchInput.value = query;
// 执行筛选并更新UI
updateFilterStatusUI();
filterAttractions();
updateHistory();
// 如果筛选抽屉是打开的,则关闭它
closeFilterDrawer();
});
}
// 为所有景点卡片添加悬停发光效果
if (attractionCards) {
attractionCards.forEach(card => {
card.classList.add('card-glow');
});
}
// 监听系统暗色模式变化
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
const handleDarkModeChange = (e: MediaQueryListEvent | MediaQueryList) => {
const isDarkMode = e.matches;
document.documentElement.classList.toggle('dark', isDarkMode);
// 添加或移除图片的过滤器以适应暗色模式
const images = document.querySelectorAll('img');
images.forEach(img => {
img.classList.toggle('dark-mode-filter', isDarkMode);
});
// 调整彩色元素的亮度
const colorElements = document.querySelectorAll('[class*="text-"]:not([class*="text-white"]):not([class*="text-gray"]):not([class*="text-black"])');
colorElements.forEach(element => {
element.classList.toggle('brightness-90', isDarkMode);
});
};
// 初始化时检查系统主题
handleDarkModeChange(darkModeMediaQuery);
// 监听系统主题变化
darkModeMediaQuery.addEventListener('change', handleDarkModeChange);
// 页面加载后初始化标签高亮状态
updateTagsHighlight();
});
</script>