完善登录与状态回显处理

This commit is contained in:
2025-03-28 15:00:46 +08:00
parent e16ef8e509
commit a16faadaab
17 changed files with 463 additions and 285 deletions

View File

@@ -1,5 +1,5 @@
import Link from "next/link"
import Image from "next/image"
import Image, {StaticImageData} from 'next/image'
import Wrap from "@/components/wrap"
import h01 from '@/assets/header/help/01.svg'
import h02 from '@/assets/header/help/02.svg'
@@ -44,7 +44,7 @@ export default function HelpMenu() {
}
function Column(props: {
icon: any
icon: StaticImageData
title: string
items: {
lead: string
@@ -64,4 +64,4 @@ function Column(props: {
</ul>
</div>
)
}
}

View File

@@ -0,0 +1,72 @@
import Image from "next/image"
import Link from "next/link"
import down from "@/assets/header/down.svg"
export function LinkItem(props: {
text: string
href: string
}) {
return (
<li className={`group relative`}>
<Link
href={props.href}
className={[
`h-full px-4 flex items-center text-lg`,
`transition-colors duration-200 ease-in-out`,
`hover:text-blue-500`,
].join(' ')}
>
{props.text}
</Link>
<div className={[
`absolute bottom-0 w-full h-0.5 bg-transparent `,
`transition-colors duration-200`,
`group-hover:bg-blue-500`,
].join(' ')}>
</div>
</li>
)
}
export function MenuItem(props: {
text: string
active: boolean
onEnter: () => void
onLeave: () => void
}) {
return (
<li className={`group relative`}>
<button
onPointerEnter={props.onEnter}
onPointerLeave={props.onLeave}
className={[
`h-full px-4 flex gap-3 items-center cursor-pointer text-lg`,
`transition-colors duration-200 ease-in-out`,
props.active
? `text-blue-500`
: ``,
].join(' ')}
>
<span>{props.text}</span>
<Image
src={down}
alt={`drop_menu`}
className={[
`transition-transform duration-200 ease-in-out`,
props.active
? `rotate-180`
: ``,
].join(' ')}
/>
</button>
<div
className={[
`absolute bottom-0 w-full h-0.5 pointer-events-none`,
`transition-colors duration-200`,
props.active
? `bg-blue-500`
: 'bg-transparent',
].join(' ')} />
</li>
)
}

View File

@@ -1,16 +1,89 @@
'use client'
import {useContext, useState} from 'react'
import {ReactNode, useContext, useState} from 'react'
import Wrap from '@/components/wrap'
import Image from 'next/image'
import anno from '@/assets/header/product/anno.svg'
import {Domestic, Oversea, Tab} from '@/app/(root)/@header/_server/product'
import Link from 'next/link'
import {merge} from '@/lib/utils'
import {HeaderContext} from '@/app/(root)/@header/page'
import prod from '@/assets/header/product/prod.svg'
import custom from '@/assets/header/product/custom.svg'
import {HeaderContext} from '@/app/(root)/@header/_client/provider'
type TabType = 'domestic' | 'oversea'
export function Tab(props: {
selected: boolean
onSelect: () => void
children: ReactNode
}) {
return (
<li role="tab">
<button
className={[
`p-8 text-lg cursor-pointer border-r`,
props.selected ? `bg-gradient-to-r from-transparent to-blue-200 border-blue-400` : `border-gray-200`,
].join(' ')}
onClick={props.onSelect}
>
{props.children}
</button>
</li>
)
}
export function Domestic(props: {}) {
return (
<section role="tabpanel" className="flex gap-16 mr-16">
<div className="w-64 flex flex-col">
<h3 className="mb-6 font-bold flex items-center gap-3">
<Image src={prod} alt={`产品`} className={`w-10 h-=10`}/>
<span></span>
</h3>
<DomesticLink
label={`动态IP`}
desc={`全国300+城市级定位节点`}
href={`/product?type=dynamic`}
discount={45}
/>
<DomesticLink
label={`长效静态IP`}
desc={`IP 资源覆盖全国`}
href={`/product?type=dynamic`}
discount={45}
/>
<DomesticLink
label={`固定IP`}
desc={`全国300+城市级定位节点`}
href={`/product?type=static`}
discount={45}
/>
</div>
<div className="w-64 flex flex-col gap-4">
<h3 className="font-bold mb-2 flex items-center gap-3">
<Image src={custom} alt="定制" className="w-10 h-10"/>
<span></span>
</h3>
<div className="flex flex-col gap-2">
<p>//IP</p>
<p className="text-gray-400 text-sm">
1000
1 1 24
</p>
</div>
</div>
</section>
)
}
export function Oversea(props: {}) {
return (
<section role="tabpanel">
</section>
)
}
export default function ProductMenu() {
const [type, setType] = useState<TabType>('domestic')

View File

@@ -0,0 +1,144 @@
'use client'
import {createContext, ReactNode, useCallback, useEffect, useMemo, useState} from 'react'
import Link from 'next/link'
import Image from 'next/image'
import {LinkItem, MenuItem} from './navs'
import SolutionMenu from './solution'
import ProductMenu from './product'
import HelpMenu from './help'
import Wrap from '@/components/wrap'
import logo from '@/assets/logo.webp'
export const HeaderContext = createContext<{
setMenu: (value: boolean) => void
} | null>(null)
export type ProviderProps = {
userCenter: ReactNode
}
export default function Provider(props: ProviderProps) {
// ======================
// 滚动条状态
// ======================
const [scroll, setScroll] = useState(false) // Changed to false for client-side rendering
const handleScroll = useCallback(() => {
setScroll(window.scrollY > 48)
}, [])
useEffect(() => {
// Initialize scroll state on client
setScroll(window.scrollY > 48)
window.addEventListener('scroll', handleScroll)
return () => {
window.removeEventListener('scroll', handleScroll)
}
}, [handleScroll])
// ======================
// 菜单状态
// ======================
const [menu, setMenu] = useState(false)
const [page, setPage] = useState(0)
const pages = useMemo(() => [
<ProductMenu key={`product`}/>,
<SolutionMenu key={`solution`}/>,
<HelpMenu key={`help`}/>,
], [])
// ======================
// 渲染组件
// ======================
return (
<HeaderContext.Provider value={{setMenu}}>
<div className={[
`transition-[background, shadow] duration-200 ease-in-out`,
menu
? `bg-[#fffe] backdrop-blur-sm`
: scroll
? `bg-[#fffe] backdrop-blur-sm shadow-lg`
: `bg-transparent shadow-none`,
].join(' ')}>
<Wrap className="h-20 max-md:h-16 flex justify-between">
<div className="flex justify-between gap-8">
{/* logo */}
<Link href="/public" className={`flex items-center`}>
<Image src={logo} alt={`logo`} height={48}/>
</Link>
{/* 菜单 */}
<nav>
<ul className="h-full flex items-stretch max-lg:hidden">
<LinkItem text={`首页`} href={`/`}/>
<MenuItem
text={`产品订购`}
active={menu && page === 0}
onEnter={() => {
setMenu(true)
setPage(0)
}}
onLeave={() => {
return setMenu(false)
}}
/>
<MenuItem
text={`业务场景`}
active={menu && page === 1}
onEnter={() => {
setMenu(true)
setPage(1)
}}
onLeave={() => {
return setMenu(false)
}}
/>
<MenuItem
text={`帮助中心`}
active={menu && page === 2}
onEnter={() => {
setMenu(true)
setPage(2)
}}
onLeave={() => {
return setMenu(false)
}}
/>
<LinkItem
text={`企业服务`} href={`#`}/>
<LinkItem
text={`推广返利`} href={`#`}/>
</ul>
</nav>
</div>
{/* 登录 */}
{props.userCenter}
</Wrap>
</div>
{/* 下拉菜单 */}
<div
className={[
`shadow-lg`,
`overflow-hidden bg-[#fffe] backdrop-blur-sm`,
`transition-[opacity,padding,height] transition-discrete duration-200 ease-in-out`,
menu
? `delay-[0s,0s,0s] opacity-100 py-8 h-auto`
: `delay-[0s,0s,0.2s] opacity-0 py-0 h-0`,
].join(' ')}
onPointerEnter={() => setMenu(true)}
onPointerLeave={() => setMenu(false)}
>
{pages[page]}
</div>
</HeaderContext.Provider>
)
}