实现响应式导航栏组件

This commit is contained in:
2025-06-18 17:57:12 +08:00
parent ba7d22168d
commit 39f30fcfa9
29 changed files with 742 additions and 223 deletions

View File

@@ -1,11 +1,154 @@
import Provider from '@/app/(home)/@header/_client/provider'
'use client'
import UserCenter from '@/components/composites/user-center'
import {useClientStore, useProfileStore} from '@/app/stores'
import {buttonVariants} from '@/components/ui/button'
import {
Navigation,
NavigationIndicator,
NavigationLink,
NavigationLinkItem,
NavigationGroup,
NavigationTriggerItem,
NavigationMenuViewport,
} from '@/components/ui/navigation-menu'
import Wrap from '@/components/wrap'
import {merge} from '@/lib/utils'
import {useState, useCallback, useEffect, useContext} from 'react'
import Image from 'next/image'
import logo from '@/assets/logo.webp'
import ProductMenu, {Domestic} from '@/app/(home)/@header/_client/product'
import SolutionMenu from '@/app/(home)/@header/_client/solution'
import HelpMenu from '@/app/(home)/@header/_client/help'
import {MenuIcon} from 'lucide-react'
export type HeaderProps = {}
export default async function Header(props: HeaderProps) {
export default function Header(props: HeaderProps) {
// ======================
// 背景显示状态
// ======================
const [expand, setExpand] = useState(false)
const [scroll, setScroll] = useState(false)
const handleScroll = useCallback(() => {
setScroll(window.scrollY > 48)
}, [])
useEffect(() => {
setScroll(window.scrollY > 48)
window.addEventListener('scroll', handleScroll)
return () => {
window.removeEventListener('scroll', handleScroll)
}
}, [handleScroll])
// ======================
// 移动端
// ======================
const lg = useClientStore(state => state.breakpoint.lg)
// ======================
// render
// ======================
return (
<header className="fixed top-0 w-full z-10">
<Provider/>
<header
data-expand={expand}
data-scroll={scroll}
data-effect={expand || scroll}
className="group/header w-full fixed left-0 top-0 z-20"
>
<Navigation
onValueChange={(value) => {
setExpand(!!value)
}}
>
<div className={merge(
`transition-[background-color,backdrop-filter,box-shadow] duration-200 ease-in-out`,
`bg-transparent backdrop-blur-none shadow-none`,
`group-data-[effect=true]/header:bg-card/90`,
`group-data-[effect=true]/header:backdrop-blur-sm`,
`group-data-[scroll=true]/header:shadow-lg`,
)}>
<Wrap className={merge('h-14 md:h-16 flex justify-between items-stretch')}>
<div className="flex items-stretch lg:flex-row-reverse">
{lg ? (
<NavigationGroup className="gap-0 flex">
<NavigationLinkItem href="/" text="首页"/>
<NavigationTriggerItem text="产品订购" className="h-80">
<ProductMenu/>
</NavigationTriggerItem>
<NavigationTriggerItem text="业务场景" className="h-80">
<SolutionMenu/>
</NavigationTriggerItem>
<NavigationTriggerItem text="帮助中心" className="h-80">
<HelpMenu/>
</NavigationTriggerItem>
<NavigationLinkItem href="/" text="企业服务"/>
<NavigationLinkItem href="/" text="推广返利"/>
<NavigationIndicator/>
</NavigationGroup>
) : (
<NavigationGroup className="flex">
<NavigationTriggerItem
suffix={false}
text={<MenuIcon/>}
className={merge(
`flex flex-col items-start gap-6`,
)}
>
<ProductMenu/>
<SolutionMenu/>
<HelpMenu/>
<NavigationLink href="/" text="企业服务"/>
<NavigationLink href="/" text="推广返利"/>
</NavigationTriggerItem>
</NavigationGroup>
)}
<NavigationLink href="/" text={<Image src={logo} alt="logo" height={36}/>}/>
</div>
<AccountRegion/>
</Wrap>
</div>
<NavigationMenuViewport className={merge(
`bg-card/90 backdrop-blur-sm shadow-lg`,
`transition-[padding,opacity,max-height]`,
`group-data-[expand=false]/header:delay-[0s,0s,0.2s] group-data-[expand=true]/header:delay-0`,
`p-0 group-data-[expand=true]/header:py-4`,
`opacity-0 group-data-[effect=true]/header:opacity-100`,
`max-h-0 group-data-[effect=true]/header:max-h-[calc(100vh-56px)] overflow-auto`,
)}/>
</Navigation>
</header>
)
}
function AccountRegion() {
const profile = useProfileStore(state => state.profile)
return (
<div className="self-center">
{profile ? (
<UserCenter profile={profile}/>
) : (
<NavigationLink
href="/login"
text="登录 / 注册"
classNameOverride={buttonVariants({
theme: 'gradient',
className: 'h-10 lg:h-12',
})}/>
)}
</div>
)
}