调整首页布局 & 首页移动端适配
This commit is contained in:
@@ -1,11 +1,17 @@
|
||||
import { useRef, useState, useSyncExternalStore } from "react"
|
||||
import { NavLink } from "react-router-dom"
|
||||
import { Menu, User } from "lucide-react"
|
||||
import { useEffect, useRef, useState, useSyncExternalStore } from "react"
|
||||
import { NavLink, useNavigate } from "react-router-dom"
|
||||
import { AUTH_EVENT, isAuthed } from "@/api/token"
|
||||
import ProductDropdown from "@/components/productDropdown"
|
||||
import ProfileOrLogin from "@/components/profileOrLogin"
|
||||
import Wrap from "@/components/wrap"
|
||||
import { useMediaQuery } from "@/lib/hooks/useMediaQuery"
|
||||
import { cn } from "@/lib/utils"
|
||||
import MobileMenu from "./_components/header/menu-mobile"
|
||||
|
||||
export default function Header() {
|
||||
const navigate = useNavigate()
|
||||
const isWideDesktop = useMediaQuery("(min-width: 80rem)")
|
||||
const scroll = useSyncExternalStore(
|
||||
callback => {
|
||||
window.addEventListener("scroll", callback)
|
||||
@@ -22,6 +28,20 @@ export default function Header() {
|
||||
const handleEnter = () => setOpenMenu(true)
|
||||
const handleLeave = () => setOpenMenu(false)
|
||||
|
||||
// 移动端状态
|
||||
const [mobileMenu, setMobileMenu] = useState(false)
|
||||
const [authed, setAuthed] = useState(isAuthed)
|
||||
|
||||
useEffect(() => {
|
||||
const update = () => setAuthed(isAuthed())
|
||||
window.addEventListener("focus", update)
|
||||
window.addEventListener(AUTH_EVENT, update)
|
||||
return () => {
|
||||
window.removeEventListener("focus", update)
|
||||
window.removeEventListener(AUTH_EVENT, update)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<header className={cn("fixed top-0 left-0 w-screen z-50 flex flex-col")}>
|
||||
<div
|
||||
@@ -33,8 +53,21 @@ export default function Header() {
|
||||
: `bg-transparent shadow-none`,
|
||||
)}
|
||||
>
|
||||
<Wrap className="h-20 max-md:h-16 flex justify-between pl-15 pr-15">
|
||||
<nav className="flex items-center justify-between gap-4 lg:gap-8">
|
||||
<Wrap className="h-20 max-md:h-16 flex justify-between pl-15 pr-15 max-md:pl-4 max-md:pr-4">
|
||||
<nav className="flex items-center gap-4 lg:gap-8">
|
||||
{/* logo + 网站名称 */}
|
||||
<NavLink to="/" className="flex items-center gap-2 shrink-0">
|
||||
<img
|
||||
src="/favicon.svg"
|
||||
alt="聚IP"
|
||||
className="w-10 h-10 max-md:w-8 max-md:h-8 object-contain"
|
||||
/>
|
||||
<span className="text-2xl max-md:text-xl font-bold bg-linear-to-r from-blue-600 to-cyan-500 bg-clip-text text-transparent">
|
||||
聚IP
|
||||
</span>
|
||||
</NavLink>
|
||||
|
||||
{/* 桌面端菜单 */}
|
||||
<ul className="h-full items-stretch hidden lg:flex">
|
||||
<NavItem to="/">首页</NavItem>
|
||||
<MenuItem
|
||||
@@ -43,16 +76,50 @@ export default function Header() {
|
||||
onMouseEnter={handleEnter}
|
||||
onMouseLeave={handleLeave}
|
||||
>
|
||||
<ProductDropdown onNavigate={() => setOpenMenu(false)} />
|
||||
<ProductDropdown
|
||||
size={isWideDesktop ? "lg" : "sm"}
|
||||
onNavigate={() => setOpenMenu(false)}
|
||||
/>
|
||||
</MenuItem>
|
||||
<NavItem to="/ipline">IP线路表</NavItem>
|
||||
<NavItem to="/softdownload">软件下载</NavItem>
|
||||
<NavItem to="/help">帮助/教程</NavItem>
|
||||
</ul>
|
||||
</nav>
|
||||
<ProfileOrLogin />
|
||||
|
||||
{/* 桌面端登录/注册 */}
|
||||
<div className="max-lg:hidden flex items-center">
|
||||
<ProfileOrLogin />
|
||||
</div>
|
||||
|
||||
{/* 移动端:未登录显示用户图标+汉堡菜单,已登录仅显示汉堡菜单 */}
|
||||
<div className="lg:hidden flex items-center gap-2">
|
||||
{!authed && (
|
||||
<button
|
||||
type="button"
|
||||
aria-label="登录/注册"
|
||||
onClick={() => navigate("/login")}
|
||||
className="p-2 text-slate-700 hover:text-blue-500 transition-colors cursor-pointer"
|
||||
>
|
||||
<User className="w-6 h-6" />
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
aria-label="打开菜单"
|
||||
onClick={() => setMobileMenu(true)}
|
||||
className="p-2 text-slate-700 hover:text-blue-500 transition-colors cursor-pointer"
|
||||
>
|
||||
<Menu className="w-6 h-6" />
|
||||
</button>
|
||||
</div>
|
||||
</Wrap>
|
||||
</div>
|
||||
|
||||
{/* 移动端左侧菜单栏 */}
|
||||
{mobileMenu && (
|
||||
<MobileMenu authed={authed} onClose={() => setMobileMenu(false)} />
|
||||
)}
|
||||
</header>
|
||||
)
|
||||
}
|
||||
@@ -147,15 +214,11 @@ const MenuItem = ({
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
"absolute top-full left-0 pt-3 w-screen max-w-4xl transition-all duration-200 z-50",
|
||||
"fixed left-1/2 top-20 -translate-x-1/2 w-[min(56rem,calc(100vw-2rem))] transition-all duration-200 z-50",
|
||||
isOpen
|
||||
? "opacity-100 visible translate-y-0"
|
||||
: "opacity-0 invisible -translate-y-2 pointer-events-none",
|
||||
)}
|
||||
style={{
|
||||
left: "50%",
|
||||
transform: `translateX(-7%)`,
|
||||
}}
|
||||
>
|
||||
<div className="bg-white rounded-xl shadow-2xl border border-gray-100/80 p-6 overflow-visible">
|
||||
{children}
|
||||
|
||||
Reference in New Issue
Block a user