首页菜单栏下拉页面调整高度1024适配
This commit is contained in:
@@ -8,7 +8,7 @@ import banner from '@/assets/header/help/banner.webp'
|
|||||||
|
|
||||||
export default function HelpMenu() {
|
export default function HelpMenu() {
|
||||||
return (
|
return (
|
||||||
<Wrap className="w-full grid grid-cols-4 gap-4 justify-items-center">
|
<Wrap className="w-full grid grid-cols-4 gap-4 justify-items-center h-75">
|
||||||
<Column
|
<Column
|
||||||
icon={h01}
|
icon={h01}
|
||||||
title="提取IP"
|
title="提取IP"
|
||||||
|
|||||||
@@ -31,14 +31,16 @@ export function LinkItem(props: {
|
|||||||
export function MenuItem(props: {
|
export function MenuItem(props: {
|
||||||
text: string
|
text: string
|
||||||
active: boolean
|
active: boolean
|
||||||
onEnter: () => void
|
onEnter: (e: React.PointerEvent) => void
|
||||||
onLeave: () => void
|
onLeave: (e: React.PointerEvent) => void
|
||||||
|
onTouchStart?: (e: React.TouchEvent) => void
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<li className="group relative">
|
<li className="group relative">
|
||||||
<button
|
<button
|
||||||
onPointerEnter={props.onEnter}
|
onPointerEnter={props.onEnter}
|
||||||
onPointerLeave={props.onLeave}
|
onPointerLeave={props.onLeave}
|
||||||
|
onTouchStart={props.onTouchStart}
|
||||||
className={[
|
className={[
|
||||||
`h-full px-4 flex gap-3 items-center cursor-pointer text-lg`,
|
`h-full px-4 flex gap-3 items-center cursor-pointer text-lg`,
|
||||||
`transition-colors duration-200 ease-in-out`,
|
`transition-colors duration-200 ease-in-out`,
|
||||||
@@ -70,3 +72,26 @@ export function MenuItem(props: {
|
|||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 移动端
|
||||||
|
export function MobileLinkItem(props: {
|
||||||
|
text: string
|
||||||
|
href: string
|
||||||
|
onClick?: () => void
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<li>
|
||||||
|
<Link
|
||||||
|
href={props.href}
|
||||||
|
onClick={props.onClick}
|
||||||
|
className={[
|
||||||
|
`block px-4 py-2 text-lg rounded-md`,
|
||||||
|
`transition-colors duration-200 ease-in-out`,
|
||||||
|
`hover:bg-blue-50 hover:text-blue-600`,
|
||||||
|
].join(' ')}
|
||||||
|
>
|
||||||
|
{props.text}
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import prod from '@/assets/header/product/prod.svg'
|
|||||||
import custom from '@/assets/header/product/custom.svg'
|
import custom from '@/assets/header/product/custom.svg'
|
||||||
import {HeaderContext} from '@/app/(home)/@header/_client/provider'
|
import {HeaderContext} from '@/app/(home)/@header/_client/provider'
|
||||||
import {useSearchParams} from 'next/navigation'
|
import {useSearchParams} from 'next/navigation'
|
||||||
|
import {useRouter} from 'next/navigation'
|
||||||
|
|
||||||
type TabType = 'domestic' | 'oversea'
|
type TabType = 'domestic' | 'oversea'
|
||||||
|
|
||||||
@@ -100,9 +101,20 @@ export default function ProductMenu() {
|
|||||||
const urlType = searchParams?.get('type')
|
const urlType = searchParams?.get('type')
|
||||||
console.log('URL参数:', urlType)
|
console.log('URL参数:', urlType)
|
||||||
}, [searchParams])
|
}, [searchParams])
|
||||||
|
// 响应式布局
|
||||||
|
const [isMobile, setIsMobile] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const checkMobile = () => {
|
||||||
|
setIsMobile(window.innerWidth <= 1024)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkMobile()
|
||||||
|
window.addEventListener('resize', checkMobile)
|
||||||
|
return () => window.removeEventListener('resize', checkMobile)
|
||||||
|
}, [])
|
||||||
return (
|
return (
|
||||||
<Wrap className="flex">
|
<Wrap className="flex h-75">
|
||||||
<ul role="tablist" className="w-48">
|
<ul role="tablist" className="w-48">
|
||||||
<Tab selected={type === 'domestic'} onSelect={() => setType('domestic')}>国内代理</Tab>
|
<Tab selected={type === 'domestic'} onSelect={() => setType('domestic')}>国内代理</Tab>
|
||||||
<Tab selected={type === 'oversea'} onSelect={() => setType('oversea')}>海外代理</Tab>
|
<Tab selected={type === 'oversea'} onSelect={() => setType('oversea')}>海外代理</Tab>
|
||||||
@@ -145,12 +157,21 @@ export function DomesticLink(props: {
|
|||||||
active?: boolean
|
active?: boolean
|
||||||
}) {
|
}) {
|
||||||
const ctx = useContext(HeaderContext)
|
const ctx = useContext(HeaderContext)
|
||||||
|
const router = useRouter()
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
throw new Error(`HeaderContext not found`)
|
throw new Error(`HeaderContext not found`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onClick = () => {
|
// const onClick = () => {
|
||||||
|
// ctx.setMenu(false)
|
||||||
|
// }
|
||||||
|
const onClick = (e: React.MouseEvent) => {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
ctx.setMenu(false)
|
ctx.setMenu(false)
|
||||||
|
setTimeout(() => {
|
||||||
|
router.push(props.href)
|
||||||
|
}, ctx.isMobile ? 100 : 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import {createContext, ReactNode, useCallback, useContext, useEffect, useMemo, useState} from 'react'
|
import {createContext, ReactNode, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import {LinkItem, MenuItem} from './navs'
|
import {LinkItem, MenuItem} from './navs'
|
||||||
@@ -14,6 +14,7 @@ import UserCenter from '@/components/composites/user-center'
|
|||||||
|
|
||||||
export const HeaderContext = createContext<{
|
export const HeaderContext = createContext<{
|
||||||
setMenu: (value: boolean) => void
|
setMenu: (value: boolean) => void
|
||||||
|
isMobile: boolean
|
||||||
} | null>(null)
|
} | null>(null)
|
||||||
|
|
||||||
export type ProviderProps = {}
|
export type ProviderProps = {}
|
||||||
@@ -22,7 +23,6 @@ export default function Provider(props: ProviderProps) {
|
|||||||
// ======================
|
// ======================
|
||||||
// 滚动条状态
|
// 滚动条状态
|
||||||
// ======================
|
// ======================
|
||||||
|
|
||||||
const [scroll, setScroll] = useState(false) // Changed to false for client-side rendering
|
const [scroll, setScroll] = useState(false) // Changed to false for client-side rendering
|
||||||
|
|
||||||
const handleScroll = useCallback(() => {
|
const handleScroll = useCallback(() => {
|
||||||
@@ -44,6 +44,55 @@ export default function Provider(props: ProviderProps) {
|
|||||||
|
|
||||||
const [menu, setMenu] = useState(false)
|
const [menu, setMenu] = useState(false)
|
||||||
const [page, setPage] = useState(0)
|
const [page, setPage] = useState(0)
|
||||||
|
const menuRef = useRef<HTMLDivElement>(null)
|
||||||
|
// 屏幕1024时
|
||||||
|
const [isMobile, setIsMobile] = useState(false)
|
||||||
|
|
||||||
|
// 点击外部关闭菜单
|
||||||
|
useEffect(() => {
|
||||||
|
const handleClickOutside = (e: MouseEvent | TouchEvent) => {
|
||||||
|
if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
|
||||||
|
const target = e.target as HTMLElement
|
||||||
|
// 排除所有交互元素
|
||||||
|
if (!target.closest('a, button, [role="button"], [role="tab"]')) {
|
||||||
|
setMenu(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener('mousedown', handleClickOutside)
|
||||||
|
document.addEventListener('touchstart', handleClickOutside)
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('mousedown', handleClickOutside)
|
||||||
|
document.removeEventListener('touchstart', handleClickOutside)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleMenuEnter = (pageIndex: number) => {
|
||||||
|
if (isMobile) {
|
||||||
|
// 移动端: 点击切换当前菜单
|
||||||
|
if (menu && page === pageIndex) {
|
||||||
|
// 添加关闭动画延迟
|
||||||
|
setTimeout(() => setMenu(false), 100)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setPage(pageIndex)
|
||||||
|
// 先设置page,再设置menu为true
|
||||||
|
setTimeout(() => setMenu(true), 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// 桌面端: 悬停打开
|
||||||
|
setPage(pageIndex)
|
||||||
|
setMenu(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMenuLeave = useCallback(() => {
|
||||||
|
if (!isMobile) {
|
||||||
|
// 桌面端: 添加延迟关闭以避免意外关闭
|
||||||
|
setMenu(false)
|
||||||
|
}
|
||||||
|
}, [isMobile])
|
||||||
const pages = useMemo(() => [
|
const pages = useMemo(() => [
|
||||||
<ProductMenu key="product"/>,
|
<ProductMenu key="product"/>,
|
||||||
<SolutionMenu key="solution"/>,
|
<SolutionMenu key="solution"/>,
|
||||||
@@ -60,9 +109,23 @@ export default function Provider(props: ProviderProps) {
|
|||||||
// render
|
// render
|
||||||
// ======================
|
// ======================
|
||||||
|
|
||||||
|
// 屏幕1024时响应式处理
|
||||||
|
useEffect(() => {
|
||||||
|
const checkMobile = () => {
|
||||||
|
const mobile = window.innerWidth <= 1024
|
||||||
|
setIsMobile(mobile)
|
||||||
|
// 移除自动关闭菜单的逻辑,让交互逻辑处理菜单状态
|
||||||
|
}
|
||||||
|
checkMobile()
|
||||||
|
window.addEventListener('resize', checkMobile)
|
||||||
|
return () => window.removeEventListener('resize', checkMobile)
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HeaderContext.Provider value={{setMenu}}>
|
<HeaderContext.Provider value={{setMenu, isMobile}}>
|
||||||
<div className={[
|
<div
|
||||||
|
ref={menuRef}
|
||||||
|
className={[
|
||||||
`transition-[background, shadow] duration-200 ease-in-out`,
|
`transition-[background, shadow] duration-200 ease-in-out`,
|
||||||
menu
|
menu
|
||||||
? `bg-[#fffe] backdrop-blur-sm`
|
? `bg-[#fffe] backdrop-blur-sm`
|
||||||
@@ -84,35 +147,23 @@ export default function Provider(props: ProviderProps) {
|
|||||||
<MenuItem
|
<MenuItem
|
||||||
text="产品订购"
|
text="产品订购"
|
||||||
active={menu && page === 0}
|
active={menu && page === 0}
|
||||||
onEnter={() => {
|
onEnter={() => handleMenuEnter(0)}
|
||||||
setMenu(true)
|
onLeave={handleMenuLeave}
|
||||||
setPage(0)
|
onTouchStart={() => handleMenuEnter(0)}
|
||||||
}}
|
|
||||||
onLeave={() => {
|
|
||||||
return setMenu(false)
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
text="业务场景"
|
text="业务场景"
|
||||||
active={menu && page === 1}
|
active={menu && page === 1}
|
||||||
onEnter={() => {
|
onEnter={() => handleMenuEnter(1)}
|
||||||
setMenu(true)
|
onLeave={handleMenuLeave}
|
||||||
setPage(1)
|
onTouchStart={() => handleMenuEnter(1)}
|
||||||
}}
|
|
||||||
onLeave={() => {
|
|
||||||
return setMenu(false)
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
text="帮助中心"
|
text="帮助中心"
|
||||||
active={menu && page === 2}
|
active={menu && page === 2}
|
||||||
onEnter={() => {
|
onEnter={() => handleMenuEnter(2)}
|
||||||
setMenu(true)
|
onLeave={handleMenuLeave}
|
||||||
setPage(2)
|
onTouchStart={() => handleMenuEnter(2)}
|
||||||
}}
|
|
||||||
onLeave={() => {
|
|
||||||
return setMenu(false)
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<LinkItem
|
<LinkItem
|
||||||
text="企业服务"
|
text="企业服务"
|
||||||
@@ -168,9 +219,10 @@ export default function Provider(props: ProviderProps) {
|
|||||||
menu
|
menu
|
||||||
? `delay-[0s,0s,0s] opacity-100 py-8 h-auto`
|
? `delay-[0s,0s,0s] opacity-100 py-8 h-auto`
|
||||||
: `delay-[0s,0s,0.2s] opacity-0 py-0 h-0`,
|
: `delay-[0s,0s,0.2s] opacity-0 py-0 h-0`,
|
||||||
|
isMobile && `max-lg:transition-all max-lg:duration-300`,
|
||||||
].join(' ')}
|
].join(' ')}
|
||||||
onPointerEnter={() => setMenu(true)}
|
onPointerEnter={() => !isMobile && setMenu(true)}
|
||||||
onPointerLeave={() => setMenu(false)}
|
onPointerLeave={() => !isMobile && setMenu(false)}
|
||||||
>
|
>
|
||||||
{pages[page]}
|
{pages[page]}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {StaticImageData} from 'next/image'
|
|||||||
|
|
||||||
export default function SolutionMenu() {
|
export default function SolutionMenu() {
|
||||||
return (
|
return (
|
||||||
<Wrap className="grid grid-cols-4 auto-rows-fr gap-4">
|
<Wrap className="grid grid-cols-4 auto-rows-fr gap-4 h-75">
|
||||||
<SolutionItem
|
<SolutionItem
|
||||||
icon={s01}
|
icon={s01}
|
||||||
title="数据抓取"
|
title="数据抓取"
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import {ReactNode} from 'react'
|
import {ReactNode, useContext} from 'react'
|
||||||
import {merge} from '@/lib/utils'
|
import {merge} from '@/lib/utils'
|
||||||
import {Tabs, TabsContent, TabsList, TabsTrigger} from '@/components/ui/tabs'
|
import {Tabs, TabsContent, TabsList, TabsTrigger} from '@/components/ui/tabs'
|
||||||
import LongForm from '@/components/composites/purchase/long/form'
|
import LongForm from '@/components/composites/purchase/long/form'
|
||||||
import ShortForm from '@/components/composites/purchase/short/form'
|
import ShortForm from '@/components/composites/purchase/short/form'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import {useSearchParams} from 'next/navigation'
|
import {useSearchParams} from 'next/navigation'
|
||||||
|
import {HeaderContext} from '@/app/(home)/@header/_client/provider'
|
||||||
|
import {useRouter} from 'next/navigation'
|
||||||
|
|
||||||
export type TabType = 'short' | 'long' | 'fixed' | 'custom'
|
export type TabType = 'short' | 'long' | 'fixed' | 'custom'
|
||||||
|
|
||||||
@@ -38,6 +40,15 @@ function Tab(props: {
|
|||||||
value: string
|
value: string
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
}) {
|
}) {
|
||||||
|
const ctx = useContext(HeaderContext)
|
||||||
|
const router = useRouter()
|
||||||
|
const handleClick = (e: React.MouseEvent) => {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
if (ctx) {
|
||||||
|
ctx.setMenu(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
href={{
|
href={{
|
||||||
@@ -52,7 +63,8 @@ function Tab(props: {
|
|||||||
`w-36 h-12 text-base font-normal flex-none`,
|
`w-36 h-12 text-base font-normal flex-none`,
|
||||||
`data-[state=active]:text-primary data-[state=active]:bg-primary-muted`,
|
`data-[state=active]:text-primary data-[state=active]:bg-primary-muted`,
|
||||||
)}
|
)}
|
||||||
value={props.value}>
|
value={props.value}
|
||||||
|
onClick={handleClick}>
|
||||||
{props.children}
|
{props.children}
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
Reference in New Issue
Block a user