feat: Heritage Globe - 中国文物全球分布地图

- Next.js + MapLibre GL 3D 地球
- CARTO dark-matter vector 底图
- 动态星空背景(闪烁、漂移、流星)
- 经纬网格线(graticule)
- 文物数据图层与朝代/类别筛选
- 中国风信息弹窗
- 中英双语 i18n
- LayerPanel + MapControls 浮动面板
This commit is contained in:
freedakgmail
2026-07-01 01:37:18 +08:00
commit a550d6d0fd
35 changed files with 11760 additions and 0 deletions
+29
View File
@@ -0,0 +1,29 @@
'use client';
import { useLocale } from 'next-intl';
import { useRouter, usePathname } from 'next/navigation';
import { Globe } from 'lucide-react';
export function LanguageSwitcher() {
const locale = useLocale();
const router = useRouter();
const pathname = usePathname();
const toggleLanguage = () => {
const newLocale = locale === 'zh' ? 'en' : 'zh';
// 替换 URL 中的语言前缀
const newPath = pathname.replace(`/${locale}`, `/${newLocale}`);
router.push(newPath);
};
return (
<button
onClick={toggleLanguage}
className="flex items-center gap-1.5 px-2.5 py-1.5 rounded-md text-sm font-medium text-[var(--map-text-muted)] hover:text-white hover:bg-white/10 transition-colors"
aria-label="Switch language"
>
<Globe className="w-4 h-4" />
<span>{locale === 'zh' ? 'EN' : '中文'}</span>
</button>
);
}
+73
View File
@@ -0,0 +1,73 @@
'use client';
import { useState } from 'react';
import { useTranslations } from 'next-intl';
import Link from 'next/link';
import { Menu, X, Globe2 } from 'lucide-react';
import { LanguageSwitcher } from '@/components/ui/LanguageSwitcher';
export function Navbar() {
const t = useTranslations('nav');
const [menuOpen, setMenuOpen] = useState(false);
return (
<nav className="absolute top-0 left-0 right-0 z-20 h-14 bg-[#06060c]/90 backdrop-blur-md border-b border-[var(--map-border)]">
<div className="flex items-center justify-between h-full px-4">
{/* Left: hamburger + brand */}
<div className="flex items-center gap-3">
<button
onClick={() => setMenuOpen(!menuOpen)}
className="w-8 h-8 flex items-center justify-center rounded-md hover:bg-white/10 transition-colors"
aria-label={menuOpen ? 'Close navigation' : 'Open navigation'}
>
{menuOpen ? <X className="w-5 h-5" /> : <Menu className="w-5 h-5" />}
</button>
<Link href="/" className="flex items-center gap-2 text-[var(--map-text)] hover:text-white transition-colors">
<div className="w-7 h-7 rounded-full bg-gradient-to-br from-amber-500 to-red-600 flex items-center justify-center">
<Globe2 className="w-4 h-4 text-white" />
</div>
<span className="font-semibold text-sm tracking-wide hidden sm:inline">{t('title')}</span>
</Link>
</div>
{/* Center nav link (like Plants link in reference) */}
<div className="hidden md:flex items-center gap-1">
<Link
href="/"
className="flex items-center gap-2 px-3 py-1.5 rounded-md text-sm font-medium text-[var(--map-text-muted)] hover:text-white hover:bg-white/10 transition-colors"
>
<Globe2 className="w-4 h-4" />
<span>{t('mapLink')}</span>
</Link>
</div>
{/* Right: language + sign in */}
<div className="flex items-center gap-2">
<LanguageSwitcher />
<button className="hidden sm:flex items-center px-3 py-1.5 rounded-md text-sm font-medium text-[var(--map-text-muted)] hover:text-white hover:bg-white/10 transition-colors">
{t('signIn')}
</button>
</div>
</div>
{/* Expandable nav links */}
{menuOpen && (
<div className="absolute top-14 left-0 right-0 bg-[#0b0f1e]/95 border-b border-[var(--map-border)] px-4 py-3 flex flex-col gap-1">
<Link href="/" className="text-sm text-[var(--map-text-muted)] hover:text-white py-1.5 px-2 rounded-md hover:bg-white/10 transition-colors">
{t('home')}
</Link>
<Link href="/" className="text-sm text-[var(--map-text-muted)] hover:text-white py-1.5 px-2 rounded-md hover:bg-white/10 transition-colors">
{t('about')}
</Link>
<Link href="/" className="text-sm text-[var(--map-text-muted)] hover:text-white py-1.5 px-2 rounded-md hover:bg-white/10 transition-colors">
{t('privacy')}
</Link>
<Link href="/" className="text-sm text-[var(--map-text-muted)] hover:text-white py-1.5 px-2 rounded-md hover:bg-white/10 transition-colors">
{t('terms')}
</Link>
</div>
)}
</nav>
);
}