Files
web/src/app/(home)/_components/header/menu-product.tsx

163 lines
5.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
import {ReactNode, useContext, useEffect, useState} from 'react'
import Wrap from '@/components/wrap'
import Image, {StaticImageData} from 'next/image'
import Link from 'next/link'
import {merge} from '@/lib/utils'
import prod from '@/assets/header/product/prod.svg'
import custom from '@/assets/header/product/custom.svg'
import {useRouter} from 'next/navigation'
import {HeaderContext} from './common'
import {Product} from '@/lib/models/product'
import {listProductHome} from '@/actions/product'
export type ProductItem = Product
type TabType = 'domestic' | 'oversea'
export default function ProductMenu() {
const [type, setType] = useState<TabType>('domestic')
return (
<Wrap className="flex flex-col items-stretch lg:flex-row gap-4 lg:gap-0">
<ul role="tablist" className="flex-none lg:basis-36 flex lg:flex-col">
<Tab selected={type === 'domestic'} onSelect={() => setType('domestic')}></Tab>
<Tab selected={type === 'oversea'} onSelect={() => setType('oversea')}></Tab>
</ul>
{type === 'domestic'
? (
<Domestic/>
) : (
<Oversea/>
)
}
</Wrap>
)
}
export function Tab(props: {
selected: boolean
onSelect: () => void
children: ReactNode
}) {
return (
<li role="tab" className="flex-1 lg:flex-none">
<button
className={[
`w-full p-4 lg:p-6 text-base lg:text-lg cursor-pointer border-b lg:border-b-0 lg:border-r flex justify-center`,
props.selected ? `bg-linear-to-b lg:bg-linear-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: {}) {
const [productList, setProductList] = useState<Product[]>([])
useEffect(() => {
const fetchProducts = async () => {
const res = await listProductHome({})
if (res.success) {
setProductList(res.data)
}
}
fetchProducts()
}, [])
const shortProduct = productList.find(p => p.name?.includes('短效') || p.code === 'short')
const longProduct = productList.find(p => p.name?.includes('长效') || p.code === 'long')
return (
<section role="tabpanel" className="flex-auto">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-3">
<div className="grid grid-cols-1 gap-3">
{shortProduct && (
<ProductCard
icon={prod}
label="短效动态 IP"
discount="最低4.5折"
desc="全国 300+ 城市级定位节点IP 池资源充足自动高频切换。适用于数据采集、市场调研、SEO 优化等高并发场景。稳定可靠,响应迅速,助力业务高效运转。"
href={`/product?type=${shortProduct.code}`}
/>
)}
{longProduct && (
<ProductCard
icon={prod}
label="长效动态 IP"
discount="最低4.5折"
desc="IP 存活时长可达数小时至数天,连接稳定不掉线。适用于账号养号、社交运营、电商管理等需要持续在线的场景。优质线路保障,为您的长期业务保驾护航。"
href={`/product?type=${longProduct.code}`}
/>
)}
</div>
<div className="flex flex-col gap-3">
<ProductCard
icon={custom}
label="业务定制"
discount="1V1 专属服务"
desc="超 1000 家企业共同信赖之选!大客户经理全程 1 对 1 沟通,随时为您排忧解难,提供 24 小时不间断支持"
href="/custom"
/>
</div>
</div>
</section>
)
}
export function Oversea(props: {}) {
return (
<section role="tabpanel" className="flex-auto flex items-center justify-center">
<p className="text-2xl text-primary">线~</p>
</section>
)
}
export function ProductCard(props: {
icon: StaticImageData
label: string
discount: string
desc: string
href: string
}) {
const router = useRouter()
const ctx = useContext(HeaderContext)
if (!ctx) {
throw new Error(`HeaderContext not found`)
}
const onClick = () => {
ctx.setMenu(false)
router.push(props.href)
}
return (
<Link
href={props.href}
className={merge(
`transition-colors duration-150 ease-in-out`,
`p-4 rounded-lg flex flex-col gap-1 hover:bg-blue-50`,
)}
onClick={onClick}
>
<div className="flex items-start gap-3">
<div className="flex-none">
<Image src={props.icon} alt="" width={30} height={30}/>
</div>
<div className="flex-1">
<div className="flex items-center justify-between gap-3">
<span className="font-bold">{props.label}</span>
<span className="text-xs font-medium text-orange-600 bg-orange-50 px-2 py-1 rounded-full">
{props.discount}
</span>
</div>
<div className="mt-2 text-sm text-gray-400 space-y-1">
{props.desc}
</div>
</div>
</div>
</Link>
)
}