Files
web/src/app/(root)/header.tsx
2025-03-14 12:40:51 +08:00

435 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
import {ReactNode, useCallback, useEffect, useMemo, useState} from 'react'
import Link from 'next/link'
import Wrap from '@/components/wrap'
export type HeaderProps = {}
export default function Header(props: HeaderProps) {
// ======================
// 滚动条状态
// ======================
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`}/>,
], [])
// ======================
// 覆盖状态
// ======================
const [overlay, setOverlay] = useState(false)
useEffect(() => {
setOverlay(scroll || menu)
}, [menu, scroll])
// ======================
// 渲染组件
// ======================
const LinkItem = (props: {
text: string
href: string
}) => (
<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>
)
const MenuItem = (props: {
text: string
page: number
}) => (
<li className={`group relative`}>
<button
onPointerEnter={() => {
setMenu(true)
setPage(props.page)
}}
onPointerLeave={() => {
setMenu(false)
}}
className={[
`h-full px-4 flex gap-3 items-center cursor-pointer text-lg`,
`transition-colors duration-200 ease-in-out`,
menu && page === props.page
? `text-blue-500`
: ``,
].join(' ')}
>
<span>{props.text}</span>
<img
src={`/header/down.svg`}
alt={`drop_menu`}
className={[
`transition-transform duration-200 ease-in-out`,
menu && page === props.page
? `rotate-180`
: ``,
].join(' ')}
/>
</button>
<div className={[
`absolute bottom-0 w-full h-0.5 bg-transparent pointer-events-none`,
`transition-colors duration-200`,
menu && page === props.page ? `bg-blue-500` : '',
].join(' ')}>
</div>
</li>
)
return (
<header className={[
`fixed top-0 w-full z-10`,
].join(' ')}>
<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`}>
<img src={`/logo.svg`} alt={`logo`} className={`w-16 max-md:w-12 h-16 max-md:h-12 rounded-full bg-gray-100`}/>
</Link>
{/* 菜单 */}
<nav>
<ul className="h-full flex items-stretch max-lg:hidden">
<LinkItem text={`首页`} href={`/`}/>
<MenuItem text={`产品订购`} page={0}/>
<MenuItem text={`业务场景`} page={1}/>
<MenuItem text={`帮助中心`} page={2}/>
<LinkItem text={`企业服务`} href={`#`}/>
<LinkItem text={`推广返利`} href={`#`}/>
</ul>
</nav>
</div>
{/* 登录 */}
<div className={`flex items-center`}>
<a
href="#"
className={`w-24 h-12 flex items-center justify-center lg:text-lg font-medium`}
>
<span></span>
</a>
<a
href="#"
className={`w-20 lg:w-24 h-10 lg:h-12 bg-gradient-to-r from-blue-500 to-cyan-400 rounded-sm flex items-center justify-center lg:text-lg font-medium text-white`}
>
<span></span>
</a>
</div>
</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>
</header>
)
}
function ProductMenu() {
// ====================
// Tab 选项
// ====================
type TabType = 'domestic' | 'oversea'
const [type, setType] = useState<TabType>('domestic')
const Tab = (props: {
type: TabType,
children: ReactNode
}) => {
return (
<li role="tab">
<button
className={[
`p-8 text-lg cursor-pointer border-r`,
type === props.type ? `bg-gradient-to-r from-transparent to-blue-200 border-blue-400` : `border-gray-200`,
].join(' ')}
onClick={() => setType(props.type)}
>
{props.children}
</button>
</li>
)
}
// ====================
// 渲染组件
// ====================
return (
<Wrap className="flex">
<ul role="tablist" className="w-48">
<Tab type="domestic"></Tab>
<Tab type="oversea"></Tab>
</ul>
<div className="flex-1">
{type === 'domestic'
? (
<section role="tabpanel" className="flex gap-16 mr-16">
<div className="w-64 flex flex-col gap-6">
<h3 className="font-bold mb-2 flex items-center gap-3">
<img src={`/prod.svg`} alt={`产品`} className={`w-10 h-=10`}/>
<span></span>
</h3>
<div className="flex flex-col gap-2">
<p className="flex gap-2">
<span>IP</span>
<span className="text-orange-500 text-xs text-light px-2 py-1 bg-orange-50 rounded-full">
45%
</span>
</p>
<p className="text-gray-400 text-sm">
300+
</p>
</div>
<div className="flex flex-col gap-2">
<p className="flex gap-2">
<span>IP</span>
<span className="text-orange-500 text-xs text-light px-2 py-1 bg-orange-50 rounded-full">
45%
</span>
</p>
<p className="text-gray-400 text-sm">
IPI资源覆盖全国
</p>
</div>
<div>
<p className="flex gap-2">
<span>IP</span>
<span className="text-orange-500 text-xs text-light px-2 py-1 bg-orange-50 rounded-full">
45%
</span>
</p>
</div>
</div>
<div className="w-64 flex flex-col gap-4">
<h3 className="font-bold mb-2 flex items-center gap-3">
<img src="/custom.svg" 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>
) : (
<section role="tabpanel">
</section>
)
}
</div>
<aside className="w-64">
<h3 className="flex gap-3 items-center mb-4">
<img src={`/anno.svg`} alt={`公告`} className={`w-10 h-10`}/>
<span></span>
</h3>
<div className="flex flex-col gap-2">
<p>线</p>
<p className="text-gray-400 text-sm">
1.使使
使
</p>
<p className="text-gray-400 text-sm">
2.使使
使
</p>
</div>
</aside>
</Wrap>
)
}
function SolutionMenu() {
const SolutionItem = (props: {
icon: string
title: string
desc: string
}) => {
return (
<div
className={[
`h-full p-4 flex gap-4 items-start rounded-md cursor-pointer`,
`transition-colors duration-200 hover:bg-blue-100`,
].join(' ')}
>
<img src={`/header/solution/${props.icon}.svg`} alt={props.title} className="w-10 h-10"/>
<div className="flex flex-col gap-1">
<h3 className="font-bold">{props.title}</h3>
<p className="text-gray-400 text-sm">{props.desc}</p>
</div>
</div>
)
}
return (
<Wrap className="grid grid-cols-4 auto-rows-fr gap-4">
<SolutionItem
icon="01"
title="数据抓取"
desc="高效采集和分析大量数据,助力企业获取大量情报"
/>
<SolutionItem
icon="02"
title="广告验证"
desc="确保广告点击和展示数据的真实性,帮助企业,提升广告效果"
/>
<SolutionItem
icon="03"
title="市场调查"
desc="收集全网行业数据,了解竞争对手动态"
/>
<SolutionItem
icon="04"
title="SEO优化"
desc="收集搜索引擎情报,提高网站在搜索引擎的排名"
/>
<SolutionItem
icon="05"
title="品牌保护"
desc="保护品牌商标打造,优质品牌形象,为企业获得更多用户"
/>
<SolutionItem
icon="06"
title="价格监控"
desc="利用优质代理IP实时监控行业价格信息提高市场竞争力"
/>
<SolutionItem
icon="07"
title="金融数据"
desc="快速获取金融市场实时数据,帮助投资者分析趋势,使其做出精准决策"
/>
<SolutionItem
icon="08"
title="网站测试"
desc="在不同环境下对网站进行性能和兼容的测试,让用户有高质量体验"
/>
</Wrap>
)
}
function HelpMenu() {
const Column = (props: {
icon: string,
title: string,
items: {
lead: string
href: string
}[]
}) => (
<div className="flex flex-col gap-4">
<h3 className="font-bold flex gap-3 items-center">
<img src={`/header/help/${props.icon}.svg`} alt={props.title} className="w-10 h-10"/>
<span>{props.title}</span>
</h3>
<ul className=" text-gray-500 text-sm flex flex-col items-end gap-2">
{props.items.map((item, index) => (
<li key={index}><Link href={item.href}>{item.lead}</Link></li>
))}
</ul>
</div>
)
return (
<Wrap className="w-full grid grid-cols-4 gap-4 justify-items-center">
<Column
icon="01"
title="提取IP"
items={[
{lead: '短效动态IP提取', href: '#'},
{lead: '长效静态IP提取', href: '#'},
]}
/>
<Column
icon="02"
title="使用教程"
items={[
{lead: '快速入手', href: '#'},
{lead: '代码下载', href: '#'},
{lead: 'API文档', href: '#'},
]}
/>
<Column
icon="03"
title="产品功能"
items={[
{lead: '常见问题', href: '#'},
{lead: '产品介绍', href: '#'},
{lead: '行业资讯', href: '#'},
]}
/>
<img src={`/header/help/banner.webp`} alt={`banner`} className={``}/>
</Wrap>
)
}