优化页头样式与操控 & 优化首页推荐文章部分 & 修复后台未正确流式加载问题

This commit is contained in:
2025-12-18 17:10:20 +08:00
parent fe5a9d2d48
commit e2c9ea7fbf
6 changed files with 97 additions and 82 deletions

View File

@@ -1,7 +1,6 @@
'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'
@@ -27,20 +26,7 @@ export default function ProductMenu() {
<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>
)
}
@@ -69,27 +55,24 @@ 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="代理产品"/>
<FragmentTitle img={prod} text="短效 IP"/>
<DomesticLink
label="短效动态 IP"
desc="全国300+城市级定位节点"
desc="全国 300+ 城市级定位节点IP 池资源充足自动高频切换。适用于数据采集、市场调研、SEO 优化等高并发场景。稳定可靠,响应迅速,助力业务高效运转。"
href="/product?type=short"
discount={45}
/>
</div>
<div className="w-full lg:w-64 flex flex-col">
<FragmentTitle img={prod} text="长效 IP"/>
<DomesticLink
label="长效动态 IP"
desc="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">
<div className="w-full lg:w-64 flex flex-col">
<FragmentTitle img={custom} text="业务定制"/>
<DomesticLink
label="优质/企业/精选IP"

View File

@@ -1,5 +1,5 @@
'use client'
import {useMemo, useState, PointerEvent, ComponentProps, useSyncExternalStore, use, Suspense} from 'react'
import {useMemo, useState, PointerEvent, ComponentProps, useSyncExternalStore, use, Suspense, MouseEvent} from 'react'
import Link from 'next/link'
import Image from 'next/image'
import {HeaderContext} from './_components/header/common'
@@ -43,10 +43,20 @@ export default function Header(props: HeaderProps) {
<MobileMenu key="mobile"/>,
], [])
const toggleMobileMenu = () => {
if (menu) {
setMenu(false)
}
else {
setPage(3)
setMenu(true)
}
}
const enterMenu = (i: number) => {
return (e: PointerEvent) => {
setPage(i)
if (e.pointerType === 'mouse' || page !== i) {
if (page !== i) {
setMenu(true)
}
else {
@@ -55,18 +65,16 @@ export default function Header(props: HeaderProps) {
}
}
const exitMenu = (e: PointerEvent) => {
if (e.pointerType === 'mouse') {
setMenu(false)
}
const leaveMenu = (e: PointerEvent) => {
setMenu(false)
}
const enterMenuContent = (e: PointerEvent) => {
setMenu(true)
}
const exitMenuContent = (e: PointerEvent) => {
if (e.pointerType === 'mouse') {
const leaveMenuContent = (e: PointerEvent) => {
if (page != 3) {
setMenu(false)
}
}
@@ -115,19 +123,19 @@ export default function Header(props: HeaderProps) {
text="产品订购"
active={menu && page === 0}
onPointerEnter={enterMenu(0)}
onPointerLeave={exitMenu}
onPointerLeave={leaveMenu}
/>
<MenuItem
text="业务场景"
active={menu && page === 1}
onPointerEnter={enterMenu(1)}
onPointerLeave={exitMenu}
onPointerLeave={leaveMenu}
/>
<MenuItem
text="帮助中心"
active={menu && page === 2}
onPointerEnter={enterMenu(2)}
onPointerLeave={exitMenu}
onPointerLeave={leaveMenu}
/>
<LinkItem
text="业务定制"
@@ -138,8 +146,7 @@ export default function Header(props: HeaderProps) {
<Button
theme="ghost"
className="lg:hidden size-10"
onPointerEnter={enterMenu(3)}
onPointerLeave={exitMenu}
onClick={toggleMobileMenu}
>
<MenuIcon/>
</Button>
@@ -168,7 +175,7 @@ export default function Header(props: HeaderProps) {
: `delay-[0s,0s] opacity-0 py-0 pointer-events-none`,
)}
onPointerEnter={enterMenuContent}
onPointerLeave={exitMenuContent}
onPointerLeave={leaveMenuContent}
>
{pages[page]}
</div>

View File

@@ -1,16 +1,15 @@
import {ReactNode} from 'react'
import Wrap from '@/components/wrap'
import Image, {StaticImageData} from 'next/image'
import Link from 'next/link'
import check_main from '@/assets/check-main.svg'
import banner from './_assets/banner.webp'
import map from './_assets/map.webp'
import next from './_assets/next.svg'
import s1_1 from './_assets/s1-1.webp'
import s1_2 from './_assets/s1-2.webp'
import s1_3 from './_assets/s1-3.webp'
import s1_4 from './_assets/s1-4.webp'
import s1_check from './_assets/s1-check.svg'
import s3_main from './_assets/s3-main.webp'
import s4_1_main from './_assets/s4-1-main.webp'
import s4_2_main from './_assets/s4-2-main.webp'
import s4_1_1 from './_assets/s4-1-1.webp'
@@ -20,6 +19,7 @@ import s4_2_1 from './_assets/s4-2-1.webp'
import s4_2_2 from './_assets/s4-2-2.webp'
import s4_2_3 from './_assets/s4-2-3.webp'
import FreeTrial from '@/components/free-trial'
import {BookOpen, Smartphone, Settings, HelpCircle, Newspaper, FileText} from 'lucide-react'
export default function Home() {
return (
@@ -141,37 +141,27 @@ export default function Home() {
{/* 推荐文章 */}
<Section title="推荐文章">
<div className="flex gap-8 max-md:gap-4">
<button className="px-4 max-md:-mx-4">
<Image src={next} alt="prev" className="rotate-180"/>
</button>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
<ArticleCard
icon={<BookOpen className="w-12 h-12"/>}
title="浏览器设置代理教程"
description="快速上手5分钟学会在浏览器中配置代理服务器"
href="/docs/browser-proxy"
/>
<div
className={[
`shadow-[4px_4px_20px_4px] shadow-blue-50 rounded-lg`,
`flex p-14 md:gap-14 max-md:flex-col max-md:p-4`,
].join(' ')}>
<Image src={s3_main} alt="tumb" className="w-2/3 md:flex-1 md:w-0 object-cover max-md:self-center"/>
<div className="flex-2 flex flex-col justify-between gap-4">
<h3 className="flex justify-between">
<span className="text-xl"></span>
<sub className="text-sm text-gray-500">2025-03-04</sub>
</h3>
<p className="text-gray-400 md:leading-12">
...
</p>
<div className="flex justify-end">
<a href="#" className="text-sm text-gray-500 flex items-center gap-4">
<Image src={next} alt="more" className="h-4 fill-gray-400"/>
</a>
</div>
</div>
</div>
<ArticleCard
icon={<Smartphone className="w-12 h-12"/>}
title="Windows10 代理配置"
description="详细图文教程,帮助你在 Windows 系统中设置代理"
href="/docs/windows10-proxy"
/>
<button className="px-4 max-md:-mx-4">
<Image src={next} alt="prev"/>
</button>
<ArticleCard
icon={<HelpCircle className="w-12 h-12"/>}
title="常见问题总览"
description="解决使用过程中遇到的各类问题,快速找到答案"
href="/docs/faq-general"
/>
</div>
</Section>
</main>
@@ -222,6 +212,43 @@ function Sec3Item(props: {
)
}
function ArticleCard(props: {
icon: ReactNode
title: string
description: string
href: string
}) {
return (
<Link
href={props.href}
className={[
`group block p-8 shadow-[4px_4px_20px_4px] shadow-blue-50 rounded-lg bg-white`,
`transition-all duration-200`,
].join(' ')}
>
<div className="flex flex-col items-center text-center gap-6">
<div className="p-4 rounded-xl bg-linear-to-br from-blue-500 to-cyan-400 text-white group-hover:scale-110 transition-transform">
{props.icon}
</div>
<div>
<h3 className="text-xl font-semibold mb-3 group-hover:text-blue-600 transition-colors">
{props.title}
</h3>
<p className="text-gray-500 text-sm leading-relaxed">
{props.description}
</p>
</div>
<div className="mt-2 text-blue-500 text-sm flex items-center gap-2 group-hover:gap-3 transition-all">
<span></span>
<span className="text-lg"></span>
</div>
</div>
</Link>
)
}
function Sec4Item(props: {
icon: StaticImageData
title: string

View File

@@ -1,4 +1,4 @@
import {ReactNode} from 'react'
import {ReactNode, Suspense} from 'react'
import {Metadata} from 'next'
export async function generateMetadata(): Promise<Metadata> {
@@ -12,5 +12,9 @@ export type BillsLayoutProps = {
}
export default async function BillsLayout(props: BillsLayoutProps) {
return props.children
return (
<Suspense>
{props.children}
</Suspense>
)
}

View File

@@ -18,7 +18,7 @@ export default async function DashboardPage(props: DashboardPageProps) {
if (!resp.success) {
return (
<div className="col-start-4 row-start-3 row-span-2 flex justify-center items-center">
</div>
)
}