Files
juip-web/src/components/productDropdown.tsx

185 lines
4.6 KiB
TypeScript
Raw Normal View History

2026-08-13 18:28:20 +08:00
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="海量全球住宅优质IP4G/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>
)
}