185 lines
4.6 KiB
TypeScript
185 lines
4.6 KiB
TypeScript
import { useNavigate } from "react-router-dom"
|
||
import { cn } from "@/lib/utils"
|
||
|
||
const SIZE_STYLES = {
|
||
lg: {
|
||
wrap: "space-y-4",
|
||
grid: "grid-cols-3 gap-4",
|
||
card: "gap-3 p-3",
|
||
title: "text-sm",
|
||
titleGap: "gap-2",
|
||
desc: "text-xs mt-1",
|
||
tag: "px-2 py-0.5",
|
||
footer: "gap-3 pt-3",
|
||
footerLink: "text-sm",
|
||
},
|
||
sm: {
|
||
wrap: "space-y-3",
|
||
grid: "grid-cols-3 gap-3",
|
||
card: "gap-2 p-2",
|
||
title: "text-xs",
|
||
titleGap: "gap-1.5",
|
||
desc: "text-[11px] mt-0.5",
|
||
tag: "px-1.5 py-0.5",
|
||
footer: "gap-2 pt-2",
|
||
footerLink: "text-xs",
|
||
},
|
||
} as const
|
||
|
||
export default function ProductDropdown({
|
||
size = "lg",
|
||
onNavigate,
|
||
}: {
|
||
size?: keyof typeof SIZE_STYLES
|
||
onNavigate?: () => void
|
||
}) {
|
||
const navigate = useNavigate()
|
||
const s = SIZE_STYLES[size]
|
||
|
||
const handleClick = (path: string) => {
|
||
navigate(path)
|
||
onNavigate?.()
|
||
}
|
||
|
||
return (
|
||
<div className={s.wrap}>
|
||
<div className={cn("grid", s.grid)}>
|
||
<ProductCard
|
||
size={s}
|
||
title="动静态IP"
|
||
desc="千万级动态短效IP,自动去重,高并发采集"
|
||
tag="热门"
|
||
onClick={() => handleClick("/product?tab=short")}
|
||
/>
|
||
<ProductCard
|
||
size={s}
|
||
title="新HTTP/S5"
|
||
desc="IP可用时长从数小时到365天全覆盖"
|
||
tag="稳定"
|
||
onClick={() => window.open("https://lanhuip.com", "_blank")}
|
||
/>
|
||
<ProductCard
|
||
size={s}
|
||
title="HTTP/S5"
|
||
desc="海量全球住宅优质IP,4G/5G真实手机IP"
|
||
tag="全球"
|
||
onClick={() => handleClick("/product/http")}
|
||
/>
|
||
<ProductCard
|
||
size={s}
|
||
title="软路由成本价"
|
||
desc="云端自动切换IP,更易于调用与开发"
|
||
onClick={() => handleClick("/product/routeros")}
|
||
/>
|
||
<ProductCard
|
||
size={s}
|
||
title="动态Vps"
|
||
desc="IP独享,高带宽,灵活控制存活周期"
|
||
onClick={() => window.open("http://vps.juip.com", "_blank")}
|
||
/>
|
||
</div>
|
||
|
||
<div
|
||
className={cn(
|
||
"flex items-center flex-wrap border-t border-gray-100",
|
||
s.footer,
|
||
)}
|
||
>
|
||
<span className="text-xs font-medium text-gray-400">热门推荐:</span>
|
||
<button
|
||
onClick={() => handleClick("/product?tab=short")}
|
||
className={cn(
|
||
"text-blue-600 hover:underline whitespace-nowrap",
|
||
s.footerLink,
|
||
)}
|
||
>
|
||
短效代理
|
||
</button>
|
||
<button
|
||
onClick={() => handleClick("/product?tab=global")}
|
||
className={cn(
|
||
"text-blue-600 hover:underline whitespace-nowrap",
|
||
s.footerLink,
|
||
)}
|
||
>
|
||
海外代理
|
||
</button>
|
||
<button
|
||
onClick={() => handleClick("/product?tab=tunnel")}
|
||
className={cn(
|
||
"text-blue-600 hover:underline whitespace-nowrap",
|
||
s.footerLink,
|
||
)}
|
||
>
|
||
隧道代理
|
||
</button>
|
||
<span className="text-xs text-gray-300">|</span>
|
||
<button
|
||
onClick={() => handleClick("/product")}
|
||
className={cn(
|
||
"text-orange-500 hover:underline font-medium whitespace-nowrap",
|
||
s.footerLink,
|
||
)}
|
||
>
|
||
业务定制 →
|
||
</button>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
function ProductCard({
|
||
size,
|
||
title,
|
||
desc,
|
||
tag,
|
||
onClick,
|
||
}: {
|
||
size: (typeof SIZE_STYLES)[keyof typeof SIZE_STYLES]
|
||
title: string
|
||
desc: string
|
||
tag?: string
|
||
onClick: () => void
|
||
}) {
|
||
return (
|
||
<button
|
||
onClick={onClick}
|
||
className={cn(
|
||
"group flex items-start rounded-lg hover:bg-gray-50 transition-all cursor-pointer border border-transparent hover:border-blue-200 text-left w-full",
|
||
size.card,
|
||
)}
|
||
>
|
||
<div className="flex-1 min-w-0">
|
||
<div className={cn("flex items-center flex-wrap", size.titleGap)}>
|
||
<span
|
||
className={cn(
|
||
"font-medium text-gray-800 group-hover:text-blue-600 transition-colors whitespace-nowrap",
|
||
size.title,
|
||
)}
|
||
>
|
||
{title}
|
||
</span>
|
||
{tag && (
|
||
<span
|
||
className={cn(
|
||
"text-[10px] bg-blue-100 text-blue-600 rounded-full shrink-0",
|
||
size.tag,
|
||
)}
|
||
>
|
||
{tag}
|
||
</span>
|
||
)}
|
||
</div>
|
||
<p
|
||
className={cn(
|
||
"text-gray-500 leading-relaxed line-clamp-2",
|
||
size.desc,
|
||
)}
|
||
>
|
||
{desc}
|
||
</p>
|
||
</div>
|
||
</button>
|
||
)
|
||
}
|