|
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
import React, { useState, useEffect } from "react";
|
|
|
|
|
import type { NavItem } from "../types";
|
|
|
|
|
import { LangSwitch } from "./LangSwitch";
|
|
|
|
|
|
|
|
|
|
export interface MainNavProps {
|
|
|
|
|
items: NavItem[];
|
|
|
|
|
basePath?: string; // e.g. /zh-CN
|
|
|
|
|
locale?: string; // zh-CN | en
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function MainNav({ items, basePath = "", locale = "zh-CN" }: MainNavProps) {
|
|
|
|
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
|
|
|
|
const [currentPath, setCurrentPath] = useState("");
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const updatePath = () => {
|
|
|
|
|
setCurrentPath(window.location.pathname);
|
|
|
|
|
};
|
|
|
|
|
updatePath();
|
|
|
|
|
|
|
|
|
|
// 监听浏览器前进/后退
|
|
|
|
|
window.addEventListener("popstate", updatePath);
|
|
|
|
|
|
|
|
|
|
// 监听点击事件(处理 Next.js 客户端导航)
|
|
|
|
|
const handleClick = (e: MouseEvent) => {
|
|
|
|
|
const target = e.target as HTMLElement;
|
|
|
|
|
const link = target.closest("a");
|
|
|
|
|
if (link && link.href) {
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
updatePath();
|
|
|
|
|
}, 100);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
document.addEventListener("click", handleClick);
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
window.removeEventListener("popstate", updatePath);
|
|
|
|
|
document.removeEventListener("click", handleClick);
|
|
|
|
|
};
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
const brandFull = locale === "en" ? "SensiGuard Technologies" : "衡感智能";
|
|
|
|
|
|
|
|
|
|
const isActive = (href: string) => {
|
|
|
|
|
if (!currentPath) return false;
|
|
|
|
|
if (href.startsWith("#")) return false;
|
|
|
|
|
|
|
|
|
|
const fullPath = `${basePath}${href}`;
|
|
|
|
|
// 首页匹配
|
|
|
|
|
if (href === "/" || href === "") {
|
|
|
|
|
return currentPath === basePath || currentPath === `${basePath}/`;
|
|
|
|
|
}
|
|
|
|
|
// 精确匹配或作为路径前缀匹配
|
|
|
|
|
return currentPath === fullPath || currentPath.startsWith(`${fullPath}/`);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<header className="w-full fixed top-0 left-0 right-0 z-50 bg-white/95 border-b border-[#dfe4ee] backdrop-blur-md shadow-[0_2px_16px_rgba(14,37,74,0.08)]">
|
|
|
|
|
<div className="max-w-[1280px] mx-auto px-4 lg:px-8 py-3.5 md:py-5 flex items-center justify-between">
|
|
|
|
|
<a href={basePath || "/"} className="flex items-center gap-2 md:gap-3 no-underline flex-shrink-0">
|
|
|
|
|
<img
|
|
|
|
|
src="/img/Log.jpg"
|
|
|
|
|
alt={brandFull}
|
|
|
|
|
className="h-8 w-auto md:h-10"
|
|
|
|
|
/>
|
|
|
|
|
<div
|
|
|
|
|
className={`font-semibold text-[#0f1f3f] text-xs sm:text-sm md:text-base hidden sm:block ${
|
|
|
|
|
locale === "en" ? "tracking-[0.12em] uppercase" : "tracking-[0.08em]"
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
{brandFull}
|
|
|
|
|
</div>
|
|
|
|
|
</a>
|
|
|
|
|
|
|
|
|
|
{/* PC端导航菜单 */}
|
|
|
|
|
<nav className="hidden md:flex items-center gap-5 lg:gap-7">
|
|
|
|
|
{items.map((item) => {
|
|
|
|
|
const isCta = item.href === "/contact";
|
|
|
|
|
const isAnchor = item.href.startsWith("#");
|
|
|
|
|
const href = isAnchor ? item.href : `${basePath}${item.href}`;
|
|
|
|
|
const active = isActive(item.href);
|
|
|
|
|
return (
|
|
|
|
|
<a
|
|
|
|
|
key={item.label}
|
|
|
|
|
href={href}
|
|
|
|
|
className={`no-underline transition-all duration-200 text-sm lg:text-base leading-none ${
|
|
|
|
|
isCta
|
|
|
|
|
? `inline-flex items-center justify-center px-4 lg:px-5 py-2 rounded-full font-medium tracking-[0.1em] ${
|
|
|
|
|
active
|
|
|
|
|
? "bg-[#0f3c88] text-white shadow-[0_6px_14px_rgba(15,60,136,0.25)]"
|
|
|
|
|
: "border border-[#0f3c88] text-[#0f3c88] hover:bg-[#0f3c88] hover:text-white"
|
|
|
|
|
}`
|
|
|
|
|
: `relative group px-2 lg:px-3 py-1 tracking-[0.1em] ${
|
|
|
|
|
active || item.href === "/"
|
|
|
|
|
? "text-[#0f3c88]"
|
|
|
|
|
: "text-[#1b1f2a] hover:text-[#0f3c88]"
|
|
|
|
|
}`
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
{item.label}
|
|
|
|
|
{!isCta && (
|
|
|
|
|
<span
|
|
|
|
|
className={`pointer-events-none absolute left-0 right-0 -bottom-2 h-[2px] rounded-full transform transition-all duration-200 ${
|
|
|
|
|
active
|
|
|
|
|
? "opacity-100 scale-100 bg-[#0f3c88]"
|
|
|
|
|
: "opacity-0 scale-75 group-hover:opacity-100 group-hover:scale-100 group-hover:bg-[#0f3c88]"
|
|
|
|
|
}`}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
</a>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</nav>
|
|
|
|
|
|
|
|
|
|
<div className="flex items-center gap-3">
|
|
|
|
|
<LangSwitch basePath={basePath} locale={locale} />
|
|
|
|
|
|
|
|
|
|
{/* 移动端菜单按钮 */}
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
|
|
|
|
className="md:hidden p-2 text-[#1c2538] hover:text-[#0f3c88] focus:outline-none"
|
|
|
|
|
aria-label="Toggle menu"
|
|
|
|
|
>
|
|
|
|
|
<svg
|
|
|
|
|
className="w-6 h-6"
|
|
|
|
|
fill="none"
|
|
|
|
|
strokeLinecap="round"
|
|
|
|
|
strokeLinejoin="round"
|
|
|
|
|
strokeWidth="2"
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
>
|
|
|
|
|
{mobileMenuOpen ? (
|
|
|
|
|
<path d="M6 18L18 6M6 6l12 12" />
|
|
|
|
|
) : (
|
|
|
|
|
<path d="M4 6h16M4 12h16M4 18h16" />
|
|
|
|
|
)}
|
|
|
|
|
</svg>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 移动端导航菜单 */}
|
|
|
|
|
{mobileMenuOpen && (
|
|
|
|
|
<div className="md:hidden bg-white border-t border-[#dfe4ee] max-h-[calc(100vh-80px)] overflow-y-auto shadow-[0_10px_24px_rgba(14,37,74,0.12)]">
|
|
|
|
|
<nav className="max-w-[1200px] mx-auto px-4 md:px-6 py-4 space-y-2">
|
|
|
|
|
{items.map((item) => {
|
|
|
|
|
const isCta = item.href === "/contact";
|
|
|
|
|
const isAnchor = item.href.startsWith("#");
|
|
|
|
|
const href = isAnchor ? item.href : `${basePath}${item.href}`;
|
|
|
|
|
const active = isActive(item.href);
|
|
|
|
|
return (
|
|
|
|
|
<a
|
|
|
|
|
key={item.label}
|
|
|
|
|
href={href}
|
|
|
|
|
className={`block py-2 px-2 rounded-md ${
|
|
|
|
|
isCta
|
|
|
|
|
? `border ${
|
|
|
|
|
active
|
|
|
|
|
? "border-transparent bg-[#0f3c88] text-white"
|
|
|
|
|
: "border-[#0f3c88] text-[#0f3c88] bg-transparent"
|
|
|
|
|
}`
|
|
|
|
|
: active
|
|
|
|
|
? "text-[#0f3c88] font-medium bg-[#eef3fb]"
|
|
|
|
|
: "text-[#1d2332] hover:text-[#0f3c88] hover:bg-[#f3f6fb]"
|
|
|
|
|
}`}
|
|
|
|
|
onClick={() => setMobileMenuOpen(false)}
|
|
|
|
|
>
|
|
|
|
|
{item.label}
|
|
|
|
|
</a>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</nav>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</header>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|