产品和提取页面
This commit is contained in:
311
src/app/(root)/collect/form-section.tsx
Normal file
311
src/app/(root)/collect/form-section.tsx
Normal file
@@ -0,0 +1,311 @@
|
||||
'use client'
|
||||
import { z } from 'zod'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form'
|
||||
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
const schema = z.object({
|
||||
type: z.enum([`num`, `time`]),
|
||||
order: z.number(),
|
||||
region: z.string(),
|
||||
provider: z.string(),
|
||||
proto: z.string(),
|
||||
distinct: z.string(),
|
||||
format: z.enum([`txt`, `json`]),
|
||||
separator: z.string(),
|
||||
count: z.number(),
|
||||
})
|
||||
|
||||
type FormSectionProps = {}
|
||||
|
||||
export default function FormSection(props: FormSectionProps) {
|
||||
|
||||
const form = useForm<z.infer<typeof schema>>({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
type: `num`,
|
||||
order: 0,
|
||||
region: ``,
|
||||
provider: ``,
|
||||
proto: ``,
|
||||
distinct: ``,
|
||||
format: `txt`,
|
||||
separator: `,`,
|
||||
count: 0,
|
||||
},
|
||||
})
|
||||
|
||||
const onSubmit = (values: z.infer<typeof schema>) => {
|
||||
console.log(values)
|
||||
// 在这里处理表单提交
|
||||
}
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className={`p-8 bg-white flex flex-col gap-4 rounded-lg`}>
|
||||
<ul role={`tablist`} className={`p-2 w-fit flex gap-2 bg-gray-100 rounded-lg`}>
|
||||
<li role={`tab`}>
|
||||
<button type="button" className={`px-4 h-10 bg-white rounded-md shadow-sm`}>
|
||||
短效动态IP提取
|
||||
</button>
|
||||
</li>
|
||||
<li role={`tab`}>
|
||||
<button type="button" className={`px-4 h-10 rounded-md`}>
|
||||
长效静态IP提取
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<p className={`px-4 h-10 bg-orange-50 flex gap-3 items-center rounded-lg`}>
|
||||
<img src={`/collect/warn.svg`} alt={`warn`} aria-hidden className={`w-5 h-5`} />
|
||||
<span className={`text-sm`}>提取IP前需要将本机IP添加到白名单后才可使用</span>
|
||||
</p>
|
||||
|
||||
<div className={`flex flex-col gap-y-4`}>
|
||||
{/* 套餐类型 */}
|
||||
<div className="flex items-center">
|
||||
<FormLabel className="w-24 flex-shrink-0">套餐类型</FormLabel>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="type"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1">
|
||||
<FormControl>
|
||||
<RadioGroup
|
||||
onValueChange={field.onChange}
|
||||
defaultValue={field.value}
|
||||
className="flex gap-4"
|
||||
>
|
||||
<div className={`px-4 h-10 border rounded-lg flex items-center`}>
|
||||
<RadioGroupItem value="num" id="num" className="mr-2" />
|
||||
<label htmlFor="num">包量套餐</label>
|
||||
</div>
|
||||
<div className={`px-4 h-10 border rounded-lg flex items-center`}>
|
||||
<RadioGroupItem value="time" id="time" className="mr-2" />
|
||||
<label htmlFor="time">包时套餐</label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 已购套餐 */}
|
||||
<div className="flex items-center">
|
||||
<FormLabel className="w-24 flex-shrink-0">已购套餐</FormLabel>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="order"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1">
|
||||
<FormControl>
|
||||
<Select
|
||||
onValueChange={value => field.onChange(Number(value))}
|
||||
value={String(field.value)}
|
||||
>
|
||||
<SelectTrigger className="h-10">
|
||||
<SelectValue placeholder="选择您的套餐" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="0">基础IP套餐</SelectItem>
|
||||
<SelectItem value="1">高级IP套餐</SelectItem>
|
||||
<SelectItem value="2">企业IP套餐</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 地区筛选 */}
|
||||
<div className="flex items-center">
|
||||
<FormLabel className="w-24 flex-shrink-0">地区筛选</FormLabel>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="region"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1">
|
||||
<FormControl>
|
||||
<Select onValueChange={field.onChange} value={field.value}>
|
||||
<SelectTrigger className="h-10">
|
||||
<SelectValue placeholder="选择地区" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="cn">中国大陆</SelectItem>
|
||||
<SelectItem value="hk">香港</SelectItem>
|
||||
<SelectItem value="us">美国</SelectItem>
|
||||
<SelectItem value="all">不限</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 运营商筛选 */}
|
||||
<div className="flex items-center">
|
||||
<FormLabel className="w-24 flex-shrink-0">运营商筛选</FormLabel>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="provider"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1">
|
||||
<FormControl>
|
||||
<Select onValueChange={field.onChange} value={field.value}>
|
||||
<SelectTrigger className="h-10">
|
||||
<SelectValue placeholder="选择运营商" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="telecom">电信</SelectItem>
|
||||
<SelectItem value="mobile">移动</SelectItem>
|
||||
<SelectItem value="unicom">联通</SelectItem>
|
||||
<SelectItem value="all">不限</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 协议类型 */}
|
||||
<div className="flex items-center">
|
||||
<FormLabel className="w-24 flex-shrink-0">协议类型</FormLabel>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="proto"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1">
|
||||
<FormControl>
|
||||
<Select onValueChange={field.onChange} value={field.value}>
|
||||
<SelectTrigger className="h-10">
|
||||
<SelectValue placeholder="选择协议类型" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="http">HTTP</SelectItem>
|
||||
<SelectItem value="https">HTTPS</SelectItem>
|
||||
<SelectItem value="socks5">SOCKS5</SelectItem>
|
||||
<SelectItem value="all">不限</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 去重选项 */}
|
||||
<div className="flex items-center">
|
||||
<FormLabel className="w-24 flex-shrink-0">去重选项</FormLabel>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="distinct"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1">
|
||||
<FormControl>
|
||||
<Select onValueChange={field.onChange} value={field.value}>
|
||||
<SelectTrigger className="h-10">
|
||||
<SelectValue placeholder="选择去重方式" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">不去重</SelectItem>
|
||||
<SelectItem value="ip">按IP去重</SelectItem>
|
||||
<SelectItem value="domain">按域名去重</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 导出格式 */}
|
||||
<div className="flex items-center">
|
||||
<FormLabel className="w-24 flex-shrink-0">导出格式</FormLabel>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="format"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1">
|
||||
<FormControl>
|
||||
<RadioGroup
|
||||
onValueChange={field.onChange}
|
||||
defaultValue={field.value}
|
||||
className="flex gap-4"
|
||||
>
|
||||
<div className={`px-4 h-10 border rounded-lg flex items-center`}>
|
||||
<RadioGroupItem value="txt" id="txt" className="mr-2" />
|
||||
<label htmlFor="txt">TXT格式</label>
|
||||
</div>
|
||||
<div className={`px-4 h-10 border rounded-lg flex items-center`}>
|
||||
<RadioGroupItem value="json" id="json" className="mr-2" />
|
||||
<label htmlFor="json">JSON格式</label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 分隔符 */}
|
||||
<div className="flex items-center">
|
||||
<FormLabel className="w-24 flex-shrink-0">分隔符</FormLabel>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="separator"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1">
|
||||
<FormControl>
|
||||
<Input {...field} className="h-10" placeholder="输入分隔符,默认为逗号" />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 提取数量 */}
|
||||
<div className="flex items-center">
|
||||
<FormLabel className="w-24 flex-shrink-0">提取数量</FormLabel>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="count"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1">
|
||||
<FormControl>
|
||||
<Input
|
||||
type="number"
|
||||
{...field}
|
||||
onChange={e => field.onChange(Number(e.target.value))}
|
||||
className="h-10"
|
||||
placeholder="输入提取数量"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end mt-6">
|
||||
<Button type="submit" className="w-32 h-10 bg-blue-500 text-white rounded-lg">提取IP</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
19
src/app/(root)/collect/page.tsx
Normal file
19
src/app/(root)/collect/page.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import BreadCrumb from '@/components/bread-crumb'
|
||||
import Wrap from '@/components/wrap'
|
||||
import FormSection from '@/app/(root)/collect/form-section'
|
||||
|
||||
export type CollectPageProps = {}
|
||||
|
||||
export default function CollectPage(props: CollectPageProps) {
|
||||
return (
|
||||
<main className={`mt-20 flex flex-col gap-4`}>
|
||||
<Wrap className="flex flex-col py-8 gap-8">
|
||||
<BreadCrumb items={[
|
||||
{label: 'IP 提取', href: '/collect'},
|
||||
]}/>
|
||||
<h2 className={`text-3xl text-center`}>提取 IP</h2>
|
||||
<FormSection/>
|
||||
</Wrap>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
123
src/app/(root)/footer.tsx
Normal file
123
src/app/(root)/footer.tsx
Normal file
@@ -0,0 +1,123 @@
|
||||
import Wrap from '@/components/wrap'
|
||||
|
||||
export type FooterProps = {}
|
||||
|
||||
export default function Footer(props: FooterProps) {
|
||||
return (
|
||||
<footer className="bg-gray-900 text-white overflow-hidden">
|
||||
<Wrap className="flex flex-col px-4 py-8 lg:p-12">
|
||||
<div className={`flex-auto overflow-hidden flex flex-wrap justify-between`}>
|
||||
<div className="flex flex-col lg:items-center gap-2 lg:gap-6 max-lg:w-1/2">
|
||||
<img src="/qrcode.svg" alt="logo" className="flex-none w-20 h-20 sm:w-44 sm:h-44 bg-gray-100"/>
|
||||
<span className="text-sm font-light">关注我们查看更多资讯</span>
|
||||
</div>
|
||||
|
||||
<div className={`flex flex-col gap-2 lg:gap-4 max-lg:w-1/2`}>
|
||||
<h3>商务合作</h3>
|
||||
<p className={`text-sm text-gray-500 font-light`}>大客户经理:张经理</p>
|
||||
<p className={`text-sm text-gray-500 font-light`}>电话/微信:18751847847</p>
|
||||
<p className={`text-sm text-gray-500 font-light`}>QQ号:800180559</p>
|
||||
<h3 className={`hidden sm:block`}>服务保障</h3>
|
||||
<p className={`text-sm text-gray-500 font-light hidden sm:block`}>售前服务</p>
|
||||
<p className={`text-sm text-gray-500 font-light hidden sm:block`}>技术支持</p>
|
||||
</div>
|
||||
|
||||
<SiteNavList
|
||||
title={`站点导航`}
|
||||
items={[
|
||||
{name: `产品订购`, href: `#`},
|
||||
{name: `获取代理`, href: `#`},
|
||||
{name: `帮助中心`, href: `#`},
|
||||
{name: `企业服务`, href: `#`},
|
||||
{name: `推广返利`, href: `#`},
|
||||
]}
|
||||
/>
|
||||
<SiteNavList
|
||||
title={`国内代理`}
|
||||
items={[
|
||||
{name: `短效代理`, href: `#`},
|
||||
{name: `长效代理`, href: `#`},
|
||||
{name: `固定IP代理`, href: `#`},
|
||||
]}
|
||||
/>
|
||||
<SiteNavList
|
||||
title={`全球代理`}
|
||||
items={[
|
||||
{name: `动态代理`, href: `#`},
|
||||
{name: `静态代理`, href: `#`},
|
||||
]}
|
||||
/>
|
||||
<SiteNavList
|
||||
title={`使用案例`}
|
||||
items={[
|
||||
{name: `数据抓取`, href: `#`},
|
||||
{name: `媒体矩阵`, href: `#`},
|
||||
{name: `广告验证`, href: `#`},
|
||||
{name: `价格监控`, href: `#`},
|
||||
{name: `市场调研`, href: `#`},
|
||||
{name: `金融数据`, href: `#`},
|
||||
{name: `SEO优化`, href: `#`},
|
||||
{name: `网站测试`, href: `#`},
|
||||
]}
|
||||
/>
|
||||
<SiteNavList
|
||||
title={`获取代理`}
|
||||
items={[
|
||||
{name: `API提取`, href: `#`},
|
||||
{name: `代码示例`, href: `#`},
|
||||
{name: `热门地区`, href: `#`},
|
||||
]}
|
||||
/>
|
||||
<SiteNavList
|
||||
title={`代理资讯`}
|
||||
items={[
|
||||
{name: `产品功能`, href: `#`},
|
||||
{name: `使用教程`, href: `#`},
|
||||
{name: `行业资讯`, href: `#`},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={`flex-none mt-6 pt-6 border-t border-gray-300 flex flex-col items-center`}>
|
||||
<p className={`text-sm font-light`}>
|
||||
声明:啊啊HTTP仅提供代理IP服务;严禁用户使用啊啊HTTP从事任何违法犯罪行为,产生的相关责任用户自负,对此啊啊HTTP不承担任何法律责任。
|
||||
<a href="#">自律公约</a>
|
||||
</p>
|
||||
<p className={`text-sm mt-3 font-light`}>
|
||||
南京啊啊啊啊科技有限公司 版权所有网站地图
|
||||
地址:啊啊啊啊啊啊啊啊啊大街57号楚翘城7幢404-405室</p>
|
||||
<p className={`text-sm mt-3 font-light`}>
|
||||
电信业务经营许可证:B1-11111111
|
||||
苏ICP备111111111号-1
|
||||
苏公网安备11111111111111号
|
||||
</p>
|
||||
</div>
|
||||
</Wrap>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
|
||||
function SiteNavList(props: {
|
||||
title: string
|
||||
items: {
|
||||
name: string
|
||||
href: string
|
||||
}[]
|
||||
}) {
|
||||
return (
|
||||
<div className={`max-lg:mt-4 w-full lg:w-auto`}>
|
||||
<h3>{props.title}</h3>
|
||||
<ul
|
||||
className={[
|
||||
`mt-1 flex flex-wrap gap-2 font-light`,
|
||||
`lg:mt-4 lg:h-[184px] lg:flex-col lg:gap-4 lg:gap-x-6`,
|
||||
].join(' ')}>
|
||||
{props.items.map((item, index) => (
|
||||
<li key={index}>
|
||||
<a href={item.href} className={`text-sm text-gray-500`}>{item.name}</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
434
src/app/(root)/header.tsx
Normal file
434
src/app/(root)/header.tsx
Normal file
@@ -0,0 +1,434 @@
|
||||
'use client'
|
||||
import {ReactNode, useCallback, useEffect, useMemo, useState} from 'react'
|
||||
import Link from 'next/link'
|
||||
import Wrap from '@/components/wrap'
|
||||
|
||||
export type HeaderProps = {}
|
||||
|
||||
export default function Header(props: HeaderProps) {
|
||||
|
||||
// ======================
|
||||
// 滚动条状态
|
||||
// ======================
|
||||
|
||||
const [scroll, setScroll] = useState(false) // Changed to false for client-side rendering
|
||||
|
||||
const handleScroll = useCallback(() => {
|
||||
setScroll(window.scrollY > 48)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
// Initialize scroll state on client
|
||||
setScroll(window.scrollY > 48)
|
||||
window.addEventListener('scroll', handleScroll)
|
||||
return () => {
|
||||
window.removeEventListener('scroll', handleScroll)
|
||||
}
|
||||
}, [handleScroll])
|
||||
|
||||
// ======================
|
||||
// 菜单状态
|
||||
// ======================
|
||||
|
||||
const [menu, setMenu] = useState(false)
|
||||
const [page, setPage] = useState(0)
|
||||
const pages = useMemo(() => [
|
||||
<ProductMenu key={`product`}/>,
|
||||
<SolutionMenu key={`solution`}/>,
|
||||
<HelpMenu key={`help`}/>,
|
||||
], [])
|
||||
|
||||
// ======================
|
||||
// 覆盖状态
|
||||
// ======================
|
||||
|
||||
const [overlay, setOverlay] = useState(false)
|
||||
useEffect(() => {
|
||||
setOverlay(scroll || menu)
|
||||
}, [menu, scroll])
|
||||
|
||||
// ======================
|
||||
// 渲染组件
|
||||
// ======================
|
||||
|
||||
const LinkItem = (props: {
|
||||
text: string
|
||||
href: string
|
||||
}) => (
|
||||
<li className={`group relative`}>
|
||||
<Link
|
||||
href={props.href}
|
||||
className={[
|
||||
`h-full px-4 flex items-center text-lg`,
|
||||
`transition-colors duration-200 ease-in-out`,
|
||||
`hover:text-blue-500`,
|
||||
].join(' ')}
|
||||
>
|
||||
{props.text}
|
||||
</Link>
|
||||
<div className={[
|
||||
`absolute bottom-0 w-full h-0.5 bg-transparent `,
|
||||
`transition-colors duration-200`,
|
||||
`group-hover:bg-blue-500`,
|
||||
].join(' ')}>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
|
||||
const MenuItem = (props: {
|
||||
text: string
|
||||
page: number
|
||||
}) => (
|
||||
<li className={`group relative`}>
|
||||
<button
|
||||
onPointerEnter={() => {
|
||||
setMenu(true)
|
||||
setPage(props.page)
|
||||
}}
|
||||
onPointerLeave={() => {
|
||||
setMenu(false)
|
||||
}}
|
||||
className={[
|
||||
`h-full px-4 flex gap-3 items-center cursor-pointer text-lg`,
|
||||
`transition-colors duration-200 ease-in-out`,
|
||||
menu && page === props.page
|
||||
? `text-blue-500`
|
||||
: ``,
|
||||
].join(' ')}
|
||||
>
|
||||
<span>{props.text}</span>
|
||||
<img
|
||||
src={`/header/down.svg`}
|
||||
alt={`drop_menu`}
|
||||
className={[
|
||||
`transition-transform duration-200 ease-in-out`,
|
||||
menu && page === props.page
|
||||
? `rotate-180`
|
||||
: ``,
|
||||
].join(' ')}
|
||||
/>
|
||||
</button>
|
||||
<div className={[
|
||||
`absolute bottom-0 w-full h-0.5 bg-transparent pointer-events-none`,
|
||||
`transition-colors duration-200`,
|
||||
menu && page === props.page ? `bg-blue-500` : '',
|
||||
].join(' ')}>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
)
|
||||
|
||||
return (
|
||||
<header className={[
|
||||
`fixed top-0 w-full z-10`,
|
||||
].join(' ')}>
|
||||
<div className={[
|
||||
``,
|
||||
`transition-[background, shadow] duration-200 ease-in-out`,
|
||||
menu
|
||||
? `bg-[#fffe] backdrop-blur-sm`
|
||||
: scroll
|
||||
? `bg-[#fffe] backdrop-blur-sm shadow-lg`
|
||||
: `bg-transparent shadow-none`,
|
||||
].join(' ')}>
|
||||
<Wrap className="h-20 max-md:h-16 flex justify-between">
|
||||
<div className="flex justify-between gap-8">
|
||||
{/* logo */}
|
||||
<Link href="/public" className={`flex items-center`}>
|
||||
<img src={`/logo.svg`} alt={`logo`} className={`w-16 max-md:w-12 h-16 max-md:h-12 rounded-full bg-gray-100`}/>
|
||||
</Link>
|
||||
|
||||
{/* 菜单 */}
|
||||
<nav>
|
||||
<ul className="h-full flex items-stretch max-lg:hidden">
|
||||
<LinkItem text={`首页`} href={`/`}/>
|
||||
<MenuItem text={`产品订购`} page={0}/>
|
||||
<MenuItem text={`业务场景`} page={1}/>
|
||||
<MenuItem text={`帮助中心`} page={2}/>
|
||||
<LinkItem text={`企业服务`} href={`#`}/>
|
||||
<LinkItem text={`推广返利`} href={`#`}/>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
{/* 登录 */}
|
||||
<div className={`flex items-center`}>
|
||||
<a
|
||||
href="#"
|
||||
className={`w-24 h-12 flex items-center justify-center lg:text-lg font-medium`}
|
||||
>
|
||||
<span>登录</span>
|
||||
</a>
|
||||
<a
|
||||
href="#"
|
||||
className={`w-20 lg:w-24 h-10 lg:h-12 bg-gradient-to-r from-blue-500 to-cyan-400 rounded-sm flex items-center justify-center lg:text-lg font-medium text-white`}
|
||||
>
|
||||
<span>注册</span>
|
||||
</a>
|
||||
</div>
|
||||
</Wrap>
|
||||
</div>
|
||||
|
||||
{/* 下拉菜单 */}
|
||||
<div
|
||||
className={[
|
||||
`shadow-lg`,
|
||||
`overflow-hidden bg-[#fffe] backdrop-blur-sm`,
|
||||
`transition-[opacity,padding,height] transition-discrete duration-200 ease-in-out`,
|
||||
menu
|
||||
? `delay-[0s,0s,0s] opacity-100 py-8 h-auto`
|
||||
: `delay-[0s,0s,0.2s] opacity-0 py-0 h-0`,
|
||||
].join(' ')}
|
||||
onPointerEnter={() => setMenu(true)}
|
||||
onPointerLeave={() => setMenu(false)}
|
||||
>
|
||||
{pages[page]}
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
function ProductMenu() {
|
||||
|
||||
// ====================
|
||||
// Tab 选项
|
||||
// ====================
|
||||
|
||||
type TabType = 'domestic' | 'oversea'
|
||||
const [type, setType] = useState<TabType>('domestic')
|
||||
|
||||
const Tab = (props: {
|
||||
type: TabType,
|
||||
children: ReactNode
|
||||
}) => {
|
||||
return (
|
||||
<li role="tab">
|
||||
<button
|
||||
className={[
|
||||
`p-8 text-lg cursor-pointer border-r`,
|
||||
type === props.type ? `bg-gradient-to-r from-transparent to-blue-200 border-blue-400` : `border-gray-200`,
|
||||
].join(' ')}
|
||||
onClick={() => setType(props.type)}
|
||||
>
|
||||
{props.children}
|
||||
</button>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
// ====================
|
||||
// 渲染组件
|
||||
// ====================
|
||||
|
||||
return (
|
||||
<Wrap className="flex">
|
||||
<ul role="tablist" className="w-48">
|
||||
<Tab type="domestic">国内代理</Tab>
|
||||
<Tab type="oversea">海外代理</Tab>
|
||||
</ul>
|
||||
<div className="flex-1">
|
||||
{type === 'domestic'
|
||||
? (
|
||||
<section role="tabpanel" className="flex gap-16 mr-16">
|
||||
<div className="w-64 flex flex-col gap-6">
|
||||
<h3 className="font-bold mb-2 flex items-center gap-3">
|
||||
<img src={`/prod.svg`} alt={`产品`} className={`w-10 h-=10`}/>
|
||||
<span>代理产品</span>
|
||||
</h3>
|
||||
<div className="flex flex-col gap-2">
|
||||
<p className="flex gap-2">
|
||||
<span>短效动态IP</span>
|
||||
<span className="text-orange-500 text-xs text-light px-2 py-1 bg-orange-50 rounded-full">
|
||||
折扣45%
|
||||
</span>
|
||||
</p>
|
||||
<p className="text-gray-400 text-sm">
|
||||
全国300+城市级定位节点
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<p className="flex gap-2">
|
||||
<span>长效静态IP</span>
|
||||
<span className="text-orange-500 text-xs text-light px-2 py-1 bg-orange-50 rounded-full">
|
||||
折扣45%
|
||||
</span>
|
||||
</p>
|
||||
<p className="text-gray-400 text-sm">
|
||||
IPI资源覆盖全国
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="flex gap-2">
|
||||
<span>固定IP</span>
|
||||
<span className="text-orange-500 text-xs text-light px-2 py-1 bg-orange-50 rounded-full">
|
||||
折扣45%
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-64 flex flex-col gap-4">
|
||||
<h3 className="font-bold mb-2 flex items-center gap-3">
|
||||
<img src="/custom.svg" alt="定制" className="w-10 h-10"/>
|
||||
<span>业务定制</span>
|
||||
</h3>
|
||||
<div className="flex flex-col gap-2">
|
||||
<p>优质/企业/精选IP</p>
|
||||
<p className="text-gray-400 text-sm">
|
||||
超 1000 家企业共同信赖之选!大客户经理全
|
||||
程 1 对 1 沟通,随时为您排忧解难,提供 24
|
||||
小时不间断支持
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
) : (
|
||||
<section role="tabpanel">
|
||||
|
||||
</section>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<aside className="w-64">
|
||||
<h3 className="flex gap-3 items-center mb-4">
|
||||
<img src={`/anno.svg`} alt={`公告`} className={`w-10 h-10`}/>
|
||||
<span>网站公告</span>
|
||||
</h3>
|
||||
<div className="flex flex-col gap-2">
|
||||
<p>官网最新上线,体验再升级!</p>
|
||||
<p className="text-gray-400 text-sm">
|
||||
1.新增多样使用功能,新增多样使用
|
||||
新增多样使用功能
|
||||
</p>
|
||||
<p className="text-gray-400 text-sm">
|
||||
2.新增多样使用功能,新增多样使用
|
||||
新增多样使用功能
|
||||
</p>
|
||||
</div>
|
||||
</aside>
|
||||
</Wrap>
|
||||
)
|
||||
}
|
||||
|
||||
function SolutionMenu() {
|
||||
|
||||
const SolutionItem = (props: {
|
||||
icon: string
|
||||
title: string
|
||||
desc: string
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={[
|
||||
`h-full p-4 flex gap-4 items-start rounded-md cursor-pointer`,
|
||||
`transition-colors duration-200 hover:bg-blue-100`,
|
||||
].join(' ')}
|
||||
>
|
||||
<img src={`/header/solution/${props.icon}.svg`} alt={props.title} className="w-10 h-10"/>
|
||||
<div className="flex flex-col gap-1">
|
||||
<h3 className="font-bold">{props.title}</h3>
|
||||
<p className="text-gray-400 text-sm">{props.desc}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Wrap className="grid grid-cols-4 auto-rows-fr gap-4">
|
||||
<SolutionItem
|
||||
icon="01"
|
||||
title="数据抓取"
|
||||
desc="高效采集和分析大量数据,助力企业获取大量情报"
|
||||
/>
|
||||
<SolutionItem
|
||||
icon="02"
|
||||
title="广告验证"
|
||||
desc="确保广告点击和展示数据的真实性,帮助企业,提升广告效果"
|
||||
/>
|
||||
<SolutionItem
|
||||
icon="03"
|
||||
title="市场调查"
|
||||
desc="收集全网行业数据,了解竞争对手动态"
|
||||
/>
|
||||
<SolutionItem
|
||||
icon="04"
|
||||
title="SEO优化"
|
||||
desc="收集搜索引擎情报,提高网站在搜索引擎的排名"
|
||||
/>
|
||||
<SolutionItem
|
||||
icon="05"
|
||||
title="品牌保护"
|
||||
desc="保护品牌商标打造,优质品牌形象,为企业获得更多用户"
|
||||
/>
|
||||
<SolutionItem
|
||||
icon="06"
|
||||
title="价格监控"
|
||||
desc="利用优质代理IP,实时监控行业价格信息,提高市场竞争力"
|
||||
/>
|
||||
<SolutionItem
|
||||
icon="07"
|
||||
title="金融数据"
|
||||
desc="快速获取金融市场实时数据,帮助投资者分析趋势,使其做出精准决策"
|
||||
/>
|
||||
<SolutionItem
|
||||
icon="08"
|
||||
title="网站测试"
|
||||
desc="在不同环境下对网站进行性能和兼容的测试,让用户有高质量体验"
|
||||
/>
|
||||
</Wrap>
|
||||
)
|
||||
}
|
||||
|
||||
function HelpMenu() {
|
||||
|
||||
const Column = (props: {
|
||||
icon: string,
|
||||
title: string,
|
||||
items: {
|
||||
lead: string
|
||||
href: string
|
||||
}[]
|
||||
}) => (
|
||||
<div className="flex flex-col gap-4">
|
||||
<h3 className="font-bold flex gap-3 items-center">
|
||||
<img src={`/header/help/${props.icon}.svg`} alt={props.title} className="w-10 h-10"/>
|
||||
<span>{props.title}</span>
|
||||
</h3>
|
||||
<ul className=" text-gray-500 text-sm flex flex-col items-end gap-2">
|
||||
{props.items.map((item, index) => (
|
||||
<li key={index}><Link href={item.href}>{item.lead}</Link></li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<Wrap className="w-full grid grid-cols-4 gap-4 justify-items-center">
|
||||
<Column
|
||||
icon="01"
|
||||
title="提取IP"
|
||||
items={[
|
||||
{lead: '短效动态IP提取', href: '#'},
|
||||
{lead: '长效静态IP提取', href: '#'},
|
||||
]}
|
||||
/>
|
||||
<Column
|
||||
icon="02"
|
||||
title="使用教程"
|
||||
items={[
|
||||
{lead: '快速入手', href: '#'},
|
||||
{lead: '代码下载', href: '#'},
|
||||
{lead: 'API文档', href: '#'},
|
||||
]}
|
||||
/>
|
||||
<Column
|
||||
icon="03"
|
||||
title="产品功能"
|
||||
items={[
|
||||
{lead: '常见问题', href: '#'},
|
||||
{lead: '产品介绍', href: '#'},
|
||||
{lead: '行业资讯', href: '#'},
|
||||
]}
|
||||
/>
|
||||
<img src={`/header/help/banner.webp`} alt={`banner`} className={``}/>
|
||||
</Wrap>
|
||||
)
|
||||
}
|
||||
22
src/app/(root)/layout.tsx
Normal file
22
src/app/(root)/layout.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import Header from '@/app/(root)/header'
|
||||
import Footer from '@/app/(root)/footer'
|
||||
import {ReactNode} from 'react'
|
||||
|
||||
export type RootLayoutProps = {
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
export default function RootLayout(props: RootLayoutProps) {
|
||||
return (
|
||||
<div className={`overflow-auto flex flex-col items-stretch relative`}>
|
||||
{/* 页头 */}
|
||||
<Header/>
|
||||
|
||||
{/* 正文 */}
|
||||
{props.children}
|
||||
|
||||
{/* 页脚 */}
|
||||
<Footer/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
215
src/app/(root)/page.tsx
Normal file
215
src/app/(root)/page.tsx
Normal file
@@ -0,0 +1,215 @@
|
||||
import {ReactNode} from 'react'
|
||||
import Wrap from '@/components/wrap'
|
||||
import Image from 'next/image'
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<main className={`flex flex-col gap-16 lg:gap-32 mb-16 lg:mb-32`}>
|
||||
|
||||
{/* banner */}
|
||||
<section className={`w-full bg-[url('/banner.webp')] bg-cover bg-[center_right_40%]`}>
|
||||
<Wrap className={`pt-64 pb-48 max-md:pt-32 max-md:pb-24`}>
|
||||
<h1 className={`text-4xl font-medium`}>安全,稳定,快速,合规的代理服务器</h1>
|
||||
<p className={`mt-10 text-gray-500`}>遍布全国的代理服务器节点为用户提供智能可靠的IP代理服务</p>
|
||||
|
||||
<div className={`mt-24 max-md:mt-14 flex gap-8 max-md:flex-col`}>
|
||||
<p className={`flex gap-4 items-center`}>
|
||||
<Image src={`/check.svg`} alt={`checkbox`} width={24} height={24}/>
|
||||
<span className={`lg:text-lg font-light`}>全国200+城市节点</span>
|
||||
</p>
|
||||
<p className={`flex gap-4 items-center`}>
|
||||
<Image src={`/check.svg`} alt={`checkbox`} width={24} height={24}/>
|
||||
<span className={`lg:text-lg font-light`}>300+城市级精准定位</span>
|
||||
</p>
|
||||
<p className={`flex gap-4 items-center`}>
|
||||
<Image src={`/check.svg`} alt={`checkbox`} width={24} height={24}/>
|
||||
<span className={`lg:text-lg font-light`}>低延迟&高并发提取</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
className={[
|
||||
`mt-32 max-md:mt-20 w-96 max-md:w-full h-16 md:h-24 rounded-lg shadow-lg`,
|
||||
`bg-gradient-to-r from-blue-500 to-cyan-400 text-white text-xl lg:text-4xl font-medium`,
|
||||
].join(' ')}>
|
||||
免费试用
|
||||
</button>
|
||||
</Wrap>
|
||||
</section>
|
||||
|
||||
{/* 数据展示 */}
|
||||
<Section title={`覆盖全国的IP资源及超大的带宽线路`}>
|
||||
<ul className={`shadow-[0_0_20px_4px] shadow-blue-50 p-8 flex max-lg:flex-col`}>
|
||||
<li className={`flex-1 flex flex-col items-center justify-center lg:border-r max-lg:mb-4 border-gray-200`}>
|
||||
<p className={`text-xl`}>全国城市线路数量</p>
|
||||
<p className={`mt-9 max-lg:mt-2 text-5xl bg-gradient-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2`}>350+</p>
|
||||
<div className={`lg:hidden w-24 border-b mt-4 border-gray-200`}></div>
|
||||
</li>
|
||||
<li className={`flex-1 flex flex-col items-center justify-center lg:border-r max-lg:mb-4 border-gray-200`}>
|
||||
<p className={`text-xl`}>每日更新IP数量</p>
|
||||
<p className={`mt-9 max-lg:mt-2 text-5xl bg-gradient-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2`}>1,350,129</p>
|
||||
<div className={`lg:hidden w-24 border-b mt-4 border-gray-200`}></div>
|
||||
</li>
|
||||
<li className={`flex-1 flex flex-col items-center justify-center lg:border-r max-lg:mb-4 border-gray-200`}>
|
||||
<p className={`text-xl`}>用户量</p>
|
||||
<p className={`mt-9 max-lg:mt-2 text-5xl bg-gradient-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2`}>26,578</p>
|
||||
<div className={`lg:hidden w-24 border-b mt-4 border-gray-200`}></div>
|
||||
</li>
|
||||
<li className={`flex-1 flex flex-col items-center justify-center`}>
|
||||
<p className={`text-xl`}>IP可用率</p>
|
||||
<p className={`mt-9 max-lg:mt-2 text-5xl bg-gradient-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2`}>99%</p>
|
||||
</li>
|
||||
</ul>
|
||||
<img src={`/map.webp`} alt={`map`} className="w-[1200px]"/>
|
||||
</Section>
|
||||
|
||||
|
||||
{/* 优势 1 */}
|
||||
<Section title={`HTTP安全合规的代理IP资源池`}>
|
||||
<ul
|
||||
className={[
|
||||
`grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8`,
|
||||
].join(' ')}>
|
||||
<Sec3Item
|
||||
icon={`s1-1`} title={`短期动态IP池`} terms={[
|
||||
{icon: `s1-check`, text: `IP时效3-30分钟(可定制)`},
|
||||
{icon: `s1-check`, text: `支持高并发提取`},
|
||||
]}/>
|
||||
<Sec3Item
|
||||
icon={`s1-2`} title={`长期静态IP池`} terms={[
|
||||
{icon: `s1-check`, text: `IP覆盖全国各地`},
|
||||
{icon: `s1-check`, text: `平均响应时长:0.03s`},
|
||||
]}/>
|
||||
<Sec3Item
|
||||
icon={`s1-3`} title={`固定IP池`} terms={[
|
||||
{icon: `s1-check`, text: `稳定长输不掉线`},
|
||||
{icon: `s1-check`, text: `全国热门静态IP线路`},
|
||||
]}/>
|
||||
<Sec3Item
|
||||
icon={`s1-4`} title={`企业级定制池`} terms={[
|
||||
{icon: `s1-check`, text: `可视化监控设计`},
|
||||
{icon: `s1-check`, text: `技术团队现场支持`},
|
||||
]}/>
|
||||
</ul>
|
||||
</Section>
|
||||
|
||||
{/* 优势 2 */}
|
||||
<Section title={`HTTP 产品优势`}>
|
||||
<div className={`flex gap-36`}>
|
||||
<ul className={`flex-1 flex flex-col gap-6`}>
|
||||
<Sec4Item icon={`s4-1-1`} title={`安全合规`} description={`国内三大运营商支持`}/>
|
||||
<Sec4Item icon={`s4-1-2`} title={`稳定链接`} description={`IP纯净度高达99.9%`}/>
|
||||
<Sec4Item icon={`s4-1-3`} title={`超匿名性`} description={`稳定传输,保护隐私安全`}/>
|
||||
</ul>
|
||||
<img src={`/s4-1-main.webp`} alt={`s2-1-main`} className={`w-0 flex-1 object-contain max-lg:hidden`}/>
|
||||
</div>
|
||||
|
||||
<div className={`flex gap-36`}>
|
||||
<img src={`/s4-2-main.webp`} alt={`s2-1-main`} className={`w-0 flex-1 object-contain max-lg:hidden`}/>
|
||||
<ul className={`flex-1 flex flex-col gap-6`}>
|
||||
<Sec4Item icon={`s4-2-1`} title={`API接口文档`} description={`与第三方软件轻松集成`}/>
|
||||
<Sec4Item icon={`s4-2-2`} title={`多种编程语言代码`} description={`C语言、GO语言、Python...`}/>
|
||||
<Sec4Item icon={`s4-2-3`} title={`双重认证方式`} description={`API提取+账密认证`}/>
|
||||
</ul>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* 行业资讯 */}
|
||||
<Section title={`行业资讯`}>
|
||||
<div className={`flex gap-8 max-md:gap-4`}>
|
||||
<button className={`px-4 max-md:-mx-4`}>
|
||||
<img src={`/next.svg`} alt={`prev`} className={`rotate-180`}/>
|
||||
</button>
|
||||
|
||||
<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(' ')}>
|
||||
<img src="/s3-main.webp" 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 font-medium`}>我是标题</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`}>
|
||||
更多详情
|
||||
<img src={`/next.svg`} alt={`more`} className={`h-4 fill-gray-400`}/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button className={`px-4 max-md:-mx-4`}>
|
||||
<img src={`/next.svg`} alt={`prev`}/>
|
||||
</button>
|
||||
</div>
|
||||
</Section>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
function Section(props: {
|
||||
title: string
|
||||
children: ReactNode
|
||||
}) {
|
||||
|
||||
return (
|
||||
<section>
|
||||
<div className={`max-w-[1232px] mx-auto px-4 flex flex-col items-stretch`}>
|
||||
<h2 className={`text-center text-3xl font-medium mb-8 lg:mb-24`}>{props.title}</h2>
|
||||
{props.children}
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
function Sec3Item(props: {
|
||||
icon: string,
|
||||
title: string,
|
||||
terms: {
|
||||
icon: string,
|
||||
text: string,
|
||||
}[]
|
||||
}) {
|
||||
return (
|
||||
<li
|
||||
className={[
|
||||
`p-8 flex flex-col gap-5 shadow-[4px_4px_20px_4px] shadow-blue-50 bg-white rounded-lg`,
|
||||
`max-md:items-center`,
|
||||
].join(' ')}>
|
||||
<img src={`/${props.icon}.webp`} alt={`s1-1`} aria-hidden className="w-44 h-44 object-cover"/>
|
||||
<h3 className={`text-xl font-medium`}>{props.title}</h3>
|
||||
<div className={`flex flex-col gap-3`}>
|
||||
{props.terms.map((item, index) => {
|
||||
return (
|
||||
<p key={index} className={`text-sm text-gray-500 flex gap-3 items-center`}>
|
||||
<img src={`/${item.icon}.svg`} alt={`check`} aria-hidden className={`w-5 h-5`}/>
|
||||
<span>{item.text}</span>
|
||||
</p>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
function Sec4Item(props: {
|
||||
icon: string
|
||||
title: string
|
||||
description: string
|
||||
}) {
|
||||
return (
|
||||
<li className={`flex gap-8 items-center p-4 lg:p-8 shadow-[4px_4px_20px_4px] shadow-blue-50 rounded-lg`}>
|
||||
<img src={`/${props.icon}.webp`} alt={`s2-1-1`} aria-hidden className="w-24 h-24 object-contain"/>
|
||||
<div className={`flex flex-col gap-3`}>
|
||||
<h3 className={`text-xl font-medium`}>{props.title}</h3>
|
||||
<p>{props.description}</p>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
43
src/app/(root)/product/combo.tsx
Normal file
43
src/app/(root)/product/combo.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
'use client'
|
||||
import {useState} from 'react'
|
||||
|
||||
export function Combo(props: {
|
||||
name: string
|
||||
level?: {
|
||||
number: number
|
||||
discount: number
|
||||
}[]
|
||||
}) {
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
return (
|
||||
<li>
|
||||
<p className={`flex justify-between items-center`}>
|
||||
<span>{props.name}</span>
|
||||
<button
|
||||
className={`text-gray-500 text-sm`}
|
||||
onClick={() => setOpen(!open)}
|
||||
>
|
||||
{open ? '收起' : '展开'}
|
||||
</button>
|
||||
</p>
|
||||
{props.level && (
|
||||
<ul className={[
|
||||
`flex flex-col gap-3 overflow-hidden`,
|
||||
`transition-[opacity,padding,max-height] transition-discrete duration-200 ease-in-out`,
|
||||
open
|
||||
? 'delay-[0s, 0s] opacity-100 py-3 max-h-80'
|
||||
: 'delay-[0s, 0.2s] opacity-0 p-0 max-h-0',
|
||||
].join(' ')}>
|
||||
{props.level.map((item, index) => (
|
||||
<li key={index} className={`flex flex-row justify-between items-center`}>
|
||||
<span className={`text-gray-500 text-sm`}>{item.number}</span>
|
||||
<span className={`text-orange-500 text-xs text-light px-2 py-1 bg-orange-50 rounded-full`}>赠送 {item.discount} %</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</li>
|
||||
)
|
||||
}
|
||||
259
src/app/(root)/product/page.tsx
Normal file
259
src/app/(root)/product/page.tsx
Normal file
@@ -0,0 +1,259 @@
|
||||
import BreadCrumb from '@/components/bread-crumb'
|
||||
import Wrap from '@/components/wrap'
|
||||
import {Combo} from '@/app/(root)/product/combo'
|
||||
|
||||
|
||||
export type ProductPageProps = {}
|
||||
|
||||
export default function ProductPage(props: ProductPageProps) {
|
||||
return (
|
||||
<main className={`mt-20`}>
|
||||
<Wrap className="flex flex-col py-8 gap-8">
|
||||
<BreadCrumb items={[
|
||||
{label: '产品中心', href: '/product'},
|
||||
]}/>
|
||||
|
||||
<h2 className={`text-3xl text-center`}>多种套餐选择</h2>
|
||||
|
||||
<ul role={`tablist`} className={`flex justify-center items-stretch bg-white rounded-lg`}>
|
||||
<li role={`tab`}>
|
||||
<button className={`h-14 px-8 text-lg`}>短效动态套餐</button>
|
||||
</li>
|
||||
<li role={`tab`}>
|
||||
<button className={`h-14 px-8 text-lg`}>长效静态套餐</button>
|
||||
</li>
|
||||
<li role={`tab`}>
|
||||
<button className={`h-14 px-8 text-lg`}>固定套餐</button>
|
||||
</li>
|
||||
<li role={`tab`}>
|
||||
<button className={`h-14 px-8 text-lg`}>定制套餐</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<section role={`tabpanel`} className={`flex flex-row bg-white`}>
|
||||
<Left/>
|
||||
<Center/>
|
||||
<Right/>
|
||||
</section>
|
||||
</Wrap>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
function Left() {
|
||||
return (
|
||||
<div className="flex-none basis-56 p-8 flex flex-col gap-4">
|
||||
<img src={`/product/banner.webp`} alt={`banner`} className={`w-full`}/>
|
||||
<h3 className={`text-lg`}>包量套餐</h3>
|
||||
<ul className={`flex flex-col gap-3`}>
|
||||
<Combo name={`3分钟`} level={[
|
||||
{number: 30000, discount: 10},
|
||||
{number: 80000, discount: 20},
|
||||
{number: 200000, discount: 30},
|
||||
{number: 450000, discount: 40},
|
||||
{number: 1000000, discount: 50},
|
||||
{number: 1600000, discount: 65},
|
||||
]}/>
|
||||
<Combo name={`5分钟`} level={[
|
||||
{number: 30000, discount: 10},
|
||||
{number: 80000, discount: 20},
|
||||
{number: 200000, discount: 30},
|
||||
{number: 450000, discount: 40},
|
||||
{number: 1000000, discount: 50},
|
||||
{number: 1600000, discount: 65},
|
||||
]}/>
|
||||
<Combo name={`10分钟`} level={[
|
||||
{number: 30000, discount: 10},
|
||||
{number: 80000, discount: 20},
|
||||
{number: 200000, discount: 30},
|
||||
{number: 450000, discount: 40},
|
||||
{number: 1000000, discount: 50},
|
||||
{number: 1600000, discount: 65},
|
||||
]}/>
|
||||
<Combo name={`15分钟`} level={[
|
||||
{number: 30000, discount: 10},
|
||||
{number: 80000, discount: 20},
|
||||
{number: 200000, discount: 30},
|
||||
{number: 450000, discount: 40},
|
||||
{number: 1000000, discount: 50},
|
||||
{number: 1600000, discount: 65},
|
||||
]}/>
|
||||
<Combo name={`30分钟`} level={[
|
||||
{number: 30000, discount: 10},
|
||||
{number: 80000, discount: 20},
|
||||
{number: 200000, discount: 30},
|
||||
{number: 450000, discount: 40},
|
||||
{number: 1000000, discount: 50},
|
||||
{number: 1600000, discount: 65},
|
||||
]}/>
|
||||
</ul>
|
||||
<div className={`border-b border-gray-200`}></div>
|
||||
<h3 className={`text-lg`}>包时套餐</h3>
|
||||
<ul className={`flex flex-col gap-3`}>
|
||||
<li className={`flex justify-between`}>
|
||||
<span className={`text-sm text-gray-500`}>7天</span>
|
||||
<span className={`text-orange-500 text-xs text-light px-2 py-1 bg-orange-50 rounded-full`}>9折</span>
|
||||
</li>
|
||||
<li className={`flex justify-between`}>
|
||||
<span className={`text-sm text-gray-500`}>30天</span>
|
||||
<span className={`text-orange-500 text-xs text-light px-2 py-1 bg-orange-50 rounded-full`}>8折</span>
|
||||
</li>
|
||||
<li className={`flex justify-between`}>
|
||||
<span className={`text-sm text-gray-500`}>90天</span>
|
||||
<span className={`text-orange-500 text-xs text-light px-2 py-1 bg-orange-50 rounded-full`}>7折</span>
|
||||
</li>
|
||||
<li className={`flex justify-between`}>
|
||||
<span className={`text-sm text-gray-500`}>180天</span>
|
||||
<span className={`text-orange-500 text-xs text-light px-2 py-1 bg-orange-50 rounded-full`}>6折</span>
|
||||
</li>
|
||||
<li className={`flex justify-between`}>
|
||||
<span className={`text-sm text-gray-500`}>360天</span>
|
||||
<span className={`text-orange-500 text-xs text-light px-2 py-1 bg-orange-50 rounded-full`}>5折</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Center() {
|
||||
return (
|
||||
<div className={`flex-auto p-8 flex flex-col relative gap-4 `}>
|
||||
|
||||
<h3>计费方式</h3>
|
||||
<div className={`grid grid-cols-2 auto-cols-fr place-items-stretch gap-4`}>
|
||||
<button className={`p-4 bg-blue-50 border border-blue-500 rounded-lg flex flex-col items-start gap-2 cursor-pointer`}>
|
||||
<h4>包量套餐</h4>
|
||||
<p className={`text-sm text-gray-500`}>适用于短期或不定期高提取业务场景</p>
|
||||
</button>
|
||||
<button className={`p-4 bg-blue-50 border border-blue-500 rounded-lg flex flex-col items-start gap-2 cursor-pointer`}>
|
||||
<h4>包时套餐</h4>
|
||||
<p className={`text-sm text-gray-500`}>适用于每日提取量稳定的业务场景</p>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h3 className={`mt-2`}>IP 时效</h3>
|
||||
<div className={`grid grid-cols-5 auto-cols-fr place-items-stretch gap-4`}>
|
||||
<button className={`p-4 bg-blue-50 border border-blue-500 rounded-lg flex flex-col gap-2 cursor-pointer`}>
|
||||
<span>3 分钟</span>
|
||||
<span className={`text-sm text-gray-500`}>¥0.005/IP</span>
|
||||
</button>
|
||||
<button className={`p-4 bg-blue-50 border border-blue-500 rounded-lg flex flex-col gap-2 cursor-pointer`}>
|
||||
<span>3 分钟</span>
|
||||
<span className={`text-sm text-gray-500`}>¥0.005/IP</span>
|
||||
</button>
|
||||
<button className={`p-4 bg-blue-50 border border-blue-500 rounded-lg flex flex-col gap-2 cursor-pointer`}>
|
||||
<span>3 分钟</span>
|
||||
<span className={`text-sm text-gray-500`}>¥0.005/IP</span>
|
||||
</button>
|
||||
<button className={`p-4 bg-blue-50 border border-blue-500 rounded-lg flex flex-col gap-2 cursor-pointer`}>
|
||||
<span>3 分钟</span>
|
||||
<span className={`text-sm text-gray-500`}>¥0.005/IP</span>
|
||||
</button>
|
||||
<button className={`p-4 bg-blue-50 border border-blue-500 rounded-lg flex flex-col gap-2 cursor-pointer`}>
|
||||
<span>3 分钟</span>
|
||||
<span className={`text-sm text-gray-500`}>¥0.005/IP</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 赠送 IP 数 */}
|
||||
<h3 className={`mt-2`}>赠送IP总数</h3>
|
||||
<div className={`flex gap-4`}>
|
||||
<button className={`h-10 w-10 border border-gray-200 rounded-sm flex items-center justify-center text-lg`}>-</button>
|
||||
<input type="number" className={`w-40 h-10 border border-gray-200 rounded-sm`}/>
|
||||
<button className={`h-10 w-10 border border-gray-200 rounded-sm flex items-center justify-center text-lg`}>+</button>
|
||||
</div>
|
||||
|
||||
{/* 产品特性 */}
|
||||
<h3 className={`mt-2`}>产品特性</h3>
|
||||
<div className={`grid grid-cols-3 auto-rows-fr gap-y-4`}>
|
||||
<p className={`flex gap-2 items-center`}>
|
||||
<img src={`/product/check.svg`} alt={`check`} aria-hidden className={`w-4 h-4`}/>
|
||||
<span className={`text-sm text-gray-500`}>支持高并发提取</span>
|
||||
</p>
|
||||
<p className={`flex gap-2 items-center`}>
|
||||
<img src={`/product/check.svg`} alt={`check`} aria-hidden className={`w-4 h-4`}/>
|
||||
<span className={`text-sm text-gray-500`}>指定省份、城市或混播</span>
|
||||
</p>
|
||||
<p className={`flex gap-2 items-center`}>
|
||||
<img src={`/product/check.svg`} alt={`check`} aria-hidden className={`w-4 h-4`}/>
|
||||
<span className={`text-sm text-gray-500`}>账密+白名单验证</span>
|
||||
</p>
|
||||
<p className={`flex gap-2 items-center`}>
|
||||
<img src={`/product/check.svg`} alt={`check`} aria-hidden className={`w-4 h-4`}/>
|
||||
<span className={`text-sm text-gray-500`}>完备的API接口</span>
|
||||
</p>
|
||||
<p className={`flex gap-2 items-center`}>
|
||||
<img src={`/product/check.svg`} alt={`check`} aria-hidden className={`w-4 h-4`}/>
|
||||
<span className={`text-sm text-gray-500`}>IP时效3-30分钟(可定制)</span>
|
||||
</p>
|
||||
<p className={`flex gap-2 items-center`}>
|
||||
<img src={`/product/check.svg`} alt={`check`} aria-hidden className={`w-4 h-4`}/>
|
||||
<span className={`text-sm text-gray-500`}>IP资源定期筛选</span>
|
||||
</p>
|
||||
<p className={`flex gap-2 items-center`}>
|
||||
<img src={`/product/check.svg`} alt={`check`} aria-hidden className={`w-4 h-4`}/>
|
||||
<span className={`text-sm text-gray-500`}>完备的API接口</span>
|
||||
</p>
|
||||
<p className={`flex gap-2 items-center`}>
|
||||
<img src={`/product/check.svg`} alt={`check`} aria-hidden className={`w-4 h-4`}/>
|
||||
<span className={`text-sm text-gray-500`}>包量/包时计费方式</span>
|
||||
</p>
|
||||
<p className={`flex gap-2 items-center`}>
|
||||
<img src={`/product/check.svg`} alt={`check`} aria-hidden className={`w-4 h-4`}/>
|
||||
<span className={`text-sm text-gray-500`}>每日去重量:500万</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 左右的边框 */}
|
||||
<div className={`absolute inset-0 my-8 border-l border-r border-gray-200 pointer-events-none`}></div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Right() {
|
||||
return (
|
||||
<div className={`flex-none basis-80 p-8 flex flex-col gap-4`}>
|
||||
<h3>订单详情</h3>
|
||||
<ul className={`flex flex-col gap-4`}>
|
||||
<li className={`flex justify-between items-center`}>
|
||||
<span className={`text-sm text-gray-500`}>套餐名称</span>
|
||||
<span className={`text-sm`}>包量套餐</span>
|
||||
</li>
|
||||
<li className={`flex justify-between items-center`}>
|
||||
<span className={`text-sm text-gray-500`}>套餐时长</span>
|
||||
<span className={`text-sm`}>3分钟</span>
|
||||
</li>
|
||||
<li className={`flex justify-between items-center`}>
|
||||
<span className={`text-sm text-gray-500`}>购买 IP 量</span>
|
||||
<span className={`text-sm`}>1000个</span>
|
||||
</li>
|
||||
<li className={`flex justify-between items-center`}>
|
||||
<span className={`text-sm text-gray-500`}>实到 IP 量</span>
|
||||
<span className={`text-sm`}>1000个</span>
|
||||
</li>
|
||||
<li className={`flex justify-between items-center`}>
|
||||
<span className={`text-sm text-gray-500`}>原价</span>
|
||||
<span className={`text-sm`}>¥50</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div className={`border-b border-gray-200`}></div>
|
||||
<p className={`flex justify-between items-center`}>
|
||||
<span>实付价格</span>
|
||||
<span className={`text-xl text-orange-500`}>¥50</span>
|
||||
</p>
|
||||
<div className={`flex gap-4`}>
|
||||
<button className={`flex-1 p-3 bg-blue-50 border border-blue-500 rounded-lg flex justify-center items-center gap-2`}>
|
||||
<img src={`/product/alipay.svg`} alt={`alipay`} className={`w-5 h-5`}/>
|
||||
<span className={`text-sm`}>支付宝</span>
|
||||
</button>
|
||||
<button className={`flex-1 p-3 bg-blue-50 border border-blue-500 rounded-lg flex justify-center items-center gap-2`}>
|
||||
<img src={`/product/wechat.svg`} alt={`wechat`} className={`w-5 h-5`}/>
|
||||
<span className={`text-sm`}>微信</span>
|
||||
</button>
|
||||
</div>
|
||||
<button className={`mt-4 h-12 bg-blue-500 text-white rounded-lg`}>
|
||||
立即支付
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user