产品页面跳转与性能优化

This commit is contained in:
2025-03-24 11:45:54 +08:00
parent 906693be10
commit 60155e9d9d
8 changed files with 230 additions and 198 deletions

View File

@@ -1,11 +1,13 @@
'use client'
import { useState, ReactNode } from "react"
import Wrap from "@/components/wrap"
import Image from "next/image"
import prod from '@/assets/header/product/prod.svg'
import custom from '@/assets/header/product/custom.svg'
import {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'
type TabType = 'domestic' | 'oversea'
@@ -22,68 +24,15 @@ export default function ProductMenu() {
<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">
<Image src={prod} 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">
<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>
<Domestic/>
) : (
<section role="tabpanel">
</section>
<Oversea/>
)
}
</div>
<aside className="w-64">
<h3 className="flex gap-3 items-center mb-4">
<Image src={anno} alt={`公告`} className={`w-10 h-10`} />
<Image src={anno} alt={`公告`} className={`w-10 h-10`}/>
<span></span>
</h3>
<div className="flex flex-col gap-2">
@@ -102,26 +51,35 @@ export default function ProductMenu() {
)
}
function Tab(props: {
selected: boolean
onSelect: () => void
children: ReactNode
export function DomesticLink(props: {
label: string
desc: string
href: string
discount: number
}) {
const ctx = useContext(HeaderContext)
if (!ctx) {
throw new Error(`HeaderContext not found`)
}
const onClick = () => {
ctx.setMenu(false)
}
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>
<Link href={props.href} className={merge(
`transition-colors duration-150 ease-in-out`,
`p-4 rounded-lg flex flex-col gap-2 hover:bg-blue-50`,
)} onClick={onClick}>
<p className="flex gap-2">
<span>{props.label}</span>
<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>
)
}
}

View File

@@ -8,6 +8,7 @@ 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'
export default function SolutionMenu() {
@@ -58,7 +59,7 @@ export default function SolutionMenu() {
}
function SolutionItem(props: {
icon: any
icon: StaticImageData
title: string
desc: string
}) {
@@ -66,7 +67,7 @@ function SolutionItem(props: {
<div
className={[
`h-full p-4 flex gap-4 items-start rounded-md cursor-pointer`,
`transition-colors duration-200 hover:bg-blue-100`,
`transition-colors duration-200 hover:bg-blue-50`,
].join(' ')}
>
<Image src={props.icon} alt={props.title} className="w-10 h-10" />
@@ -76,4 +77,4 @@ function SolutionItem(props: {
</div>
</div>
)
}
}

View File

@@ -0,0 +1,80 @@
import {ReactNode} from 'react'
import Image from 'next/image'
import prod from '@/assets/header/product/prod.svg'
import Link from 'next/link'
import custom from '@/assets/header/product/custom.svg'
import {DomesticLink} from '@/app/(root)/@header/_client/product'
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>
)
}

View File

@@ -1,5 +1,5 @@
'use client'
import {useCallback, useEffect, useMemo, useState} from 'react'
import {createContext, useCallback, useEffect, useMemo, useState} from 'react'
import Link from 'next/link'
import Image from 'next/image'
import {LinkItem, MenuItem} from './_server/navs'
@@ -9,6 +9,10 @@ import HelpMenu from './_client/help'
import Wrap from '@/components/wrap'
import logo from '@/assets/logo.webp'
export const HeaderContext = createContext<{
setMenu: (value: boolean) => void
} | null>(null)
export type HeaderProps = {}
export default function Header(props: HeaderProps) {
@@ -44,111 +48,112 @@ export default function Header(props: HeaderProps) {
<HelpMenu key={`help`}/>,
], [])
// ======================
// 渲染组件
// ======================
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`}>
<Image src={logo} alt={`logo`} className={`w-16 max-md:w-12 h-16 max-md:h-12 rounded-full`}/>
</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>
{/* 登录 */}
<div className={`flex items-center`}>
<Link
href="/login"
className={`w-24 h-12 flex items-center justify-center lg:text-lg`}
>
<span></span>
</Link>
<Link
href="/login"
className={[
`w-20 lg:w-24 h-10 lg:h-12 bg-gradient-to-r rounded-sm flex items-center justify-center lg:text-lg text-white`,
`transition-colors duration-200 ease-in-out`,
`from-blue-500 to-cyan-400 hover:from-blue-500 hover:to-cyan-300`,
].join(' ')}
>
<span></span>
</Link>
</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`,
<HeaderContext.Provider value={{setMenu}}>
<header className={`fixed top-0 w-full z-10`}>
<div className={[
`transition-[background, shadow] 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>
? `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="/" 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>
{/* 登录 */}
<div className={`flex items-center`}>
<Link
href="/login"
className={`w-24 h-12 flex items-center justify-center lg:text-lg`}
>
<span></span>
</Link>
<Link
href="/login"
className={[
`w-20 lg:w-24 h-10 lg:h-12 bg-gradient-to-r rounded-sm flex items-center justify-center lg:text-lg text-white`,
`transition-colors duration-200 ease-in-out`,
`from-blue-500 to-cyan-400 hover:from-blue-500 hover:to-cyan-300`,
].join(' ')}
>
<span></span>
</Link>
</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>
</HeaderContext.Provider>
)
}

View File

@@ -8,7 +8,7 @@ export type RootLayoutProps = {
export default function RootLayout(props: RootLayoutProps) {
return (
<div className={`overflow-auto bg-white flex flex-col items-stretch relative`}>
<div className={`overflow-auto bg-blue-50 flex flex-col items-stretch relative`}>
{/* 页头 */}
<Header/>

View File

@@ -30,7 +30,7 @@ export default function ProductPage(props: ProductPageProps) {
</li>
</ul>
<section role={`tabpanel`} className={`flex flex-row bg-white`}>
<section role={`tabpanel`} className={`flex flex-row bg-white rounded-lg`}>
<Left/>
<Center/>
<Right/>

View File

@@ -20,7 +20,7 @@ export default function RootLayout({
}>) {
return (
<html lang="zh-Cn">
<body className={`${font.className} bg-blue-50`}>
<body className={`${font.className}`}>
{children}
<Toaster position={'top-center'}/>
</body>

View File

@@ -1,12 +0,0 @@
import {NextRequest, NextResponse} from 'next/server'
import {cookies} from 'next/headers'
export async function GET(req: NextRequest) {
const store = await cookies()
store.set('test','test')
return NextResponse.json(JSON.stringify({
'test': 'value',
}))
}