157 lines
6.5 KiB
TypeScript
157 lines
6.5 KiB
TypeScript
'use client';
|
||
import Link from 'next/link';
|
||
import { usePathname } from 'next/navigation';
|
||
import { useState } from 'react';
|
||
import { LanguageType } from '../lib/content';
|
||
|
||
interface NavigationProps {
|
||
/** 🔧 修改 #1:currentLang 从 string 改为 LanguageType */
|
||
currentLang: LanguageType;
|
||
|
||
/** 🔧 修改 #2:setCurrentLang 返回值改为 void(原来写成了 string),签名也保持只接受 LanguageType */
|
||
setCurrentLang: (lang: LanguageType) => void;
|
||
|
||
content: {
|
||
nav: {
|
||
home: string;
|
||
services: string;
|
||
solutions: string;
|
||
pricing: string;
|
||
news: string;
|
||
contact: string;
|
||
};
|
||
};
|
||
|
||
/** 🔧 修改 #3:createLocalizedPath 的签名改为 (path: string) => string */
|
||
createLocalizedPath?: (path: string) => string;
|
||
}
|
||
|
||
export default function Navigation({
|
||
currentLang,
|
||
setCurrentLang,
|
||
content,
|
||
createLocalizedPath,
|
||
}: NavigationProps) {
|
||
const pathname = usePathname();
|
||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
||
|
||
// Helper to build localized URLs
|
||
const getLocalizedPath = (path: string) => {
|
||
return createLocalizedPath ? createLocalizedPath(path) : path;
|
||
};
|
||
|
||
const navItems = [
|
||
{ label: content.nav.home, href: getLocalizedPath('/') },
|
||
{ label: content.nav.services, href: getLocalizedPath('/services') },
|
||
{ label: content.nav.solutions, href: getLocalizedPath('/solutions') },
|
||
{ label: content.nav.pricing, href: getLocalizedPath('/pricing') },
|
||
{ label: content.nav.news, href: getLocalizedPath('/news') },
|
||
{ label: content.nav.contact, href: getLocalizedPath('/contact') },
|
||
];
|
||
|
||
return (
|
||
<nav className="relative z-10 px-6 py-4 border-b border-cyan-500/20 backdrop-blur-sm">
|
||
<div className="max-w-7xl mx-auto flex justify-between items-center">
|
||
{/* Logo */}
|
||
<Link
|
||
href="/"
|
||
className="text-2xl font-bold bg-gradient-to-r from-cyan-400 to-blue-500 bg-clip-text text-transparent hover:scale-105 transition-transform duration-300"
|
||
>
|
||
CloudAgency
|
||
</Link>
|
||
|
||
{/* Desktop Nav */}
|
||
<div className="hidden md:flex space-x-8">
|
||
{navItems.map((item, idx) => {
|
||
const isActive = pathname === item.href;
|
||
return (
|
||
<Link
|
||
key={idx}
|
||
href={item.href}
|
||
className={`relative group transition-colors duration-300 ${
|
||
isActive ? 'text-cyan-400' : 'text-gray-300 hover:text-cyan-400'
|
||
}`}
|
||
>
|
||
{item.label}
|
||
<span
|
||
className={`absolute -bottom-1 left-0 h-0.5 bg-cyan-400 transition-all duration-300 ${
|
||
isActive ? 'w-full' : 'w-0 group-hover:w-full'
|
||
}`}
|
||
/>
|
||
</Link>
|
||
);
|
||
})}
|
||
</div>
|
||
|
||
{/* Right side */}
|
||
<div className="flex items-center space-x-4">
|
||
{/* 🔧 修改 #4:onChange 时将 value 强制断言为 LanguageType */}
|
||
<select
|
||
value={currentLang}
|
||
onChange={(e) => setCurrentLang(e.target.value as LanguageType)}
|
||
className="bg-gray-800 border border-cyan-500/30 rounded-lg px-3 py-1 text-sm focus:outline-none focus:border-cyan-400 transition-colors"
|
||
>
|
||
<option value="zh-CN">简体中文</option>
|
||
<option value="zh-TW">繁體中文</option>
|
||
<option value="en">English</option>
|
||
</select>
|
||
|
||
{/* Mobile menu button */}
|
||
<button
|
||
className="md:hidden text-gray-300 hover:text-cyan-400 transition-colors"
|
||
onClick={() => setIsMobileMenuOpen((o) => !o)}
|
||
>
|
||
<svg
|
||
className="w-6 h-6"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
viewBox="0 0 24 24"
|
||
>
|
||
{isMobileMenuOpen ? (
|
||
<path
|
||
strokeLinecap="round"
|
||
strokeLinejoin="round"
|
||
strokeWidth={2}
|
||
d="M6 18L18 6M6 6l12 12"
|
||
/>
|
||
) : (
|
||
<path
|
||
strokeLinecap="round"
|
||
strokeLinejoin="round"
|
||
strokeWidth={2}
|
||
d="M4 6h16M4 12h16M4 18h16"
|
||
/>
|
||
)}
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Mobile Menu */}
|
||
{isMobileMenuOpen && (
|
||
<div className="md:hidden absolute top-full left-0 right-0 bg-gray-900/95 backdrop-blur-sm border-b border-cyan-500/20">
|
||
<div className="px-6 py-4 space-y-4">
|
||
{navItems.map((item, idx) => {
|
||
const isActive = pathname === item.href;
|
||
return (
|
||
<Link
|
||
key={idx}
|
||
href={item.href}
|
||
className={`block py-2 transition-colors duration-300 ${
|
||
isActive
|
||
? 'text-cyan-400'
|
||
: 'text-gray-300 hover:text-cyan-400'
|
||
}`}
|
||
onClick={() => setIsMobileMenuOpen(false)}
|
||
>
|
||
{item.label}
|
||
</Link>
|
||
);
|
||
})}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</nav>
|
||
);
|
||
}
|