优化用户数据初始化时机
This commit is contained in:
18
src/app/(home)/_components/header/common.tsx
Normal file
18
src/app/(home)/_components/header/common.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import {createContext} from 'react'
|
||||
import Image, {StaticImageData} from 'next/image'
|
||||
|
||||
export const HeaderContext = createContext<{
|
||||
setMenu: (value: boolean) => void
|
||||
} | null>(null)
|
||||
|
||||
export function FragmentTitle(props: {
|
||||
text: string
|
||||
img: StaticImageData
|
||||
}) {
|
||||
return (
|
||||
<h3 className="font-bold flex items-center gap-3">
|
||||
<Image src={props.img} alt="icon" aria-hidden className="size-8 lg:size-9"/>
|
||||
<span>{props.text}</span>
|
||||
</h3>
|
||||
)
|
||||
}
|
||||
81
src/app/(home)/_components/header/menu-help.tsx
Normal file
81
src/app/(home)/_components/header/menu-help.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import Link from 'next/link'
|
||||
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'
|
||||
import h03 from '@/assets/header/help/03.svg'
|
||||
import banner from '@/assets/header/help/banner.webp'
|
||||
import {FragmentTitle, HeaderContext} from './common'
|
||||
import {useContext} from 'react'
|
||||
import {useRouter} from 'next/navigation'
|
||||
|
||||
export default function HelpMenu() {
|
||||
return (
|
||||
<Wrap className="w-full grid sm:grid-cols-3 lg:grid-cols-4 gap-4 justify-items-start">
|
||||
<Column
|
||||
icon={h01}
|
||||
title="提取 IP"
|
||||
items={[
|
||||
{lead: '短效 IP 提取', href: '/collect'},
|
||||
{lead: '长效 IP 提取', href: '#'},
|
||||
]}
|
||||
/>
|
||||
<Column
|
||||
icon={h02}
|
||||
title="使用教程"
|
||||
items={[
|
||||
{lead: '快速入手', href: '/help/tutorials/quick-start'},
|
||||
{lead: '代码下载', href: '#'},
|
||||
{lead: 'API文档', href: '#'},
|
||||
]}
|
||||
/>
|
||||
<Column
|
||||
icon={h03}
|
||||
title="产品功能"
|
||||
items={[
|
||||
{lead: '常见问题', href: '#'},
|
||||
{lead: '产品介绍', href: '#'},
|
||||
{lead: '行业资讯', href: '#'},
|
||||
]}
|
||||
/>
|
||||
<Image src={banner} alt="banner" className="hidden lg:block"/>
|
||||
</Wrap>
|
||||
)
|
||||
}
|
||||
|
||||
function Column(props: {
|
||||
icon: StaticImageData
|
||||
title: string
|
||||
items: {
|
||||
lead: string
|
||||
href: string
|
||||
}[]
|
||||
}) {
|
||||
const ctx = useContext(HeaderContext) // 获取菜单上下文
|
||||
const router = useRouter()
|
||||
|
||||
if (!ctx) {
|
||||
throw new Error(`HeaderContext not found`)
|
||||
}
|
||||
return (
|
||||
<div className="flex-1 flex flex-col gap-4">
|
||||
<FragmentTitle img={props.icon} text={props.title}/>
|
||||
<ul className="flex flex-col gap-2">
|
||||
{props.items.map((item, index) => (
|
||||
<li key={index}>
|
||||
<Link
|
||||
href={item.href}
|
||||
className="px-4 py-2"
|
||||
onClick={() => {
|
||||
ctx.setMenu(false) // 点击时关闭菜单
|
||||
router.push(item.href) // 跳转页面
|
||||
}}
|
||||
>
|
||||
{item.lead}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
31
src/app/(home)/_components/header/menu-mobile.tsx
Normal file
31
src/app/(home)/_components/header/menu-mobile.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import ProductMenu from './menu-product'
|
||||
import HelpMenu from './menu-help'
|
||||
import SolutionMenu from './menu-solution'
|
||||
|
||||
export type MobileMenuProps = {}
|
||||
|
||||
export default function MobileMenu(props: MobileMenuProps) {
|
||||
return (
|
||||
<div className="flex flex-col gap-8">
|
||||
<div className="flex flex-col gap-4">
|
||||
<ProductMenu/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<MenuTitle title="帮助中心"/>
|
||||
<HelpMenu/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<MenuTitle title="业务场景"/>
|
||||
<SolutionMenu/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function MenuTitle(props: {title: string}) {
|
||||
return (
|
||||
<h3 className="text-xl text-weak px-4">
|
||||
{props.title}
|
||||
</h3>
|
||||
)
|
||||
}
|
||||
150
src/app/(home)/_components/header/menu-product.tsx
Normal file
150
src/app/(home)/_components/header/menu-product.tsx
Normal file
@@ -0,0 +1,150 @@
|
||||
'use client'
|
||||
import {ReactNode, useContext, useState} from 'react'
|
||||
import Wrap from '@/components/wrap'
|
||||
import anno from '@/assets/header/product/anno.svg'
|
||||
import Link from 'next/link'
|
||||
import {merge} from '@/lib/utils'
|
||||
import prod from '@/assets/header/product/prod.svg'
|
||||
import custom from '@/assets/header/product/custom.svg'
|
||||
import {useRouter} from 'next/navigation'
|
||||
import {FragmentTitle, HeaderContext} from './common'
|
||||
|
||||
type TabType = 'domestic' | 'oversea'
|
||||
|
||||
export default function ProductMenu() {
|
||||
const [type, setType] = useState<TabType>('domestic')
|
||||
|
||||
return (
|
||||
<Wrap className="flex flex-col items-stretch lg:flex-row gap-4 lg:gap-0">
|
||||
<ul role="tablist" className="flex-none lg:basis-36 flex lg:flex-col">
|
||||
<Tab selected={type === 'domestic'} onSelect={() => setType('domestic')}>国内代理</Tab>
|
||||
<Tab selected={type === 'oversea'} onSelect={() => setType('oversea')}>海外代理</Tab>
|
||||
</ul>
|
||||
{type === 'domestic'
|
||||
? (
|
||||
<Domestic/>
|
||||
) : (
|
||||
<Oversea/>
|
||||
)
|
||||
}
|
||||
<aside className="w-full lg:w-64 hidden lg:block">
|
||||
<FragmentTitle img={anno} text="网站公告"/>
|
||||
<div className="flex flex-col gap-2 p-4">
|
||||
<p>官网最新上线,体验再升级!</p>
|
||||
<p className="text-gray-400 text-sm">
|
||||
1.新增多样使用功能,新增多样使用
|
||||
新增多样使用功能
|
||||
</p>
|
||||
<p className="text-gray-400 text-sm">
|
||||
2.新增多样使用功能,新增多样使用
|
||||
新增多样使用功能
|
||||
</p>
|
||||
</div>
|
||||
</aside>
|
||||
</Wrap>
|
||||
)
|
||||
}
|
||||
|
||||
export function Tab(props: {
|
||||
selected: boolean
|
||||
onSelect: () => void
|
||||
children: ReactNode
|
||||
}) {
|
||||
return (
|
||||
<li role="tab" className="flex-1 lg:flex-none">
|
||||
<button
|
||||
className={[
|
||||
`w-full p-4 lg:p-6 text-base lg:text-lg cursor-pointer border-b lg:border-b-0 lg:border-r flex justify-center`,
|
||||
props.selected ? `bg-gradient-to-b lg: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-auto flex flex-col lg:flex-row justify-evenly gap-3 lg:gap-0">
|
||||
<div className="w-full lg:w-64 flex flex-col">
|
||||
<FragmentTitle img={prod} text="代理产品"/>
|
||||
<DomesticLink
|
||||
label="短效动态 IP"
|
||||
desc="全国300+城市级定位节点"
|
||||
href="/product?type=short"
|
||||
discount={45}
|
||||
/>
|
||||
<DomesticLink
|
||||
label="长效动态 IP"
|
||||
desc="IP 资源覆盖全国"
|
||||
href="/product?type=long"
|
||||
discount={45}
|
||||
/>
|
||||
<DomesticLink
|
||||
label="静态 IP"
|
||||
desc="全国300+城市级定位节点"
|
||||
href="/product?type=fixed"
|
||||
discount={45}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full lg:w-64 flex flex-col lg:max-lg:hidden">
|
||||
<FragmentTitle img={custom} text="业务定制"/>
|
||||
<DomesticLink
|
||||
label="优质/企业/精选IP"
|
||||
desc="超 1000 家企业共同信赖之选!大客户经理全程 1 对 1 沟通,随时为您排忧解难,提供 24 小时不间断支持"
|
||||
href="/customized"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export function Oversea(props: {}) {
|
||||
return (
|
||||
<section role="tabpanel" className="flex-auto flex items-center justify-center">
|
||||
<p className="text-2xl text-primary">产品即将上线,敬请期待~</p>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export function DomesticLink(props: {
|
||||
label: string
|
||||
desc: string
|
||||
href: string
|
||||
discount?: number
|
||||
}) {
|
||||
const router = useRouter()
|
||||
const ctx = useContext(HeaderContext)
|
||||
if (!ctx) {
|
||||
throw new Error(`HeaderContext not found`)
|
||||
}
|
||||
|
||||
const onClick = () => {
|
||||
ctx.setMenu(false)
|
||||
router.push(props.href)
|
||||
}
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={props.href}
|
||||
className={merge(
|
||||
`transition-colors duration-150 ease-in-out`,
|
||||
`p-4 rounded-lg flex flex-col gap-1 hover:bg-blue-50`,
|
||||
)}
|
||||
onClick={onClick}>
|
||||
<p className="flex gap-2">
|
||||
<span>{props.label}</span>
|
||||
{props.discount && (
|
||||
<span className="text-orange-500 text-xs text-light px-2 py-1 bg-orange-50 rounded-full">
|
||||
折扣 {props.discount}%
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
<p className="text-gray-400 text-sm">
|
||||
{props.desc}
|
||||
</p>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
100
src/app/(home)/_components/header/menu-solution.tsx
Normal file
100
src/app/(home)/_components/header/menu-solution.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
import Wrap from '@/components/wrap'
|
||||
import s01 from '@/assets/header/solution/01.svg'
|
||||
import s02 from '@/assets/header/solution/02.svg'
|
||||
import s03 from '@/assets/header/solution/03.svg'
|
||||
import s04 from '@/assets/header/solution/04.svg'
|
||||
import s05 from '@/assets/header/solution/05.svg'
|
||||
import s06 from '@/assets/header/solution/06.svg'
|
||||
import s07 from '@/assets/header/solution/07.svg'
|
||||
import s08 from '@/assets/header/solution/08.svg'
|
||||
import {StaticImageData} from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import {useRouter} from 'next/navigation'
|
||||
import {useContext} from 'react'
|
||||
import {FragmentTitle, HeaderContext} from './common'
|
||||
|
||||
export default function SolutionMenu() {
|
||||
return (
|
||||
<Wrap className="h-full grid grid-cols-2 lg:grid-cols-4 pl=2 auto-rows-fr gap-4">
|
||||
<SolutionItem
|
||||
icon={s01}
|
||||
title="数据抓取"
|
||||
desc="高效采集和分析大量数据,助力企业获取大量情报"
|
||||
href="/data-capture"
|
||||
/>
|
||||
<SolutionItem
|
||||
icon={s02}
|
||||
title="广告验证"
|
||||
desc="确保广告点击和展示数据的真实性,帮助企业,提升广告效果"
|
||||
href="#"
|
||||
/>
|
||||
<SolutionItem
|
||||
icon={s03}
|
||||
title="市场调查"
|
||||
desc="收集全网行业数据,了解竞争对手动态"
|
||||
href="#"
|
||||
/>
|
||||
<SolutionItem
|
||||
icon={s04}
|
||||
title="SEO优化"
|
||||
desc="收集搜索引擎情报,提高网站在搜索引擎的排名"
|
||||
href="#"
|
||||
/>
|
||||
<SolutionItem
|
||||
icon={s05}
|
||||
title="品牌保护"
|
||||
desc="保护品牌商标打造,优质品牌形象,为企业获得更多用户"
|
||||
href="#"
|
||||
/>
|
||||
<SolutionItem
|
||||
icon={s06}
|
||||
title="价格监控"
|
||||
desc="利用优质代理IP,实时监控行业价格信息,提高市场竞争力"
|
||||
href="#"
|
||||
/>
|
||||
<SolutionItem
|
||||
icon={s07}
|
||||
title="金融数据"
|
||||
desc="快速获取金融市场实时数据,帮助投资者分析趋势,使其做出精准决策"
|
||||
href="#"
|
||||
/>
|
||||
<SolutionItem
|
||||
icon={s08}
|
||||
title="网站测试"
|
||||
desc="在不同环境下对网站进行性能和兼容的测试,让用户有高质量体验"
|
||||
href="#"
|
||||
/>
|
||||
</Wrap>
|
||||
)
|
||||
}
|
||||
|
||||
function SolutionItem(props: {
|
||||
icon: StaticImageData
|
||||
title: string
|
||||
desc: string
|
||||
href: string
|
||||
}) {
|
||||
const router = useRouter()
|
||||
const ctx = useContext(HeaderContext)
|
||||
if (!ctx) {
|
||||
throw new Error(`HeaderContext not found`)
|
||||
}
|
||||
|
||||
const onClick = () => {
|
||||
ctx.setMenu(false)
|
||||
router.push(props.href)
|
||||
}
|
||||
return (
|
||||
<Link
|
||||
href={props.href}
|
||||
className={[
|
||||
`flex flex-col gap-2 items-start rounded-md cursor-pointer`,
|
||||
`transition-colors duration-200 hover:bg-blue-50`,
|
||||
].join(' ')}
|
||||
onClick={onClick}
|
||||
>
|
||||
<FragmentTitle img={props.icon} text={props.title}/>
|
||||
<p className="text-gray-400 text-sm">{props.desc}</p>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user