目录结构与表单组件结构调整

This commit is contained in:
2025-03-24 12:29:52 +08:00
parent 60155e9d9d
commit e16ef8e509
9 changed files with 530 additions and 615 deletions

View File

@@ -14,11 +14,7 @@ import {
} from '@/components/ui/card'
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/form'
import {zodResolver} from '@hookform/resolvers/zod'
import {useForm} from 'react-hook-form'
@@ -29,6 +25,7 @@ import {login} from '@/actions/auth/login'
import {useRouter} from 'next/navigation'
import {toast} from 'sonner'
import {ApiResponse} from '@/lib/api'
import {Label} from '@/components/ui/label'
export type LoginPageProps = {}
@@ -38,6 +35,7 @@ const formSchema = zod.object({
password: zod.string().min(1, '请输入验证码'),
remember: zod.boolean().default(false),
})
type FormValues = zod.infer<typeof formSchema>
export default function LoginPage(props: LoginPageProps) {
@@ -202,89 +200,70 @@ export default function LoginPage(props: LoginPageProps) {
</CardHeader>
<CardContent className={`px-8`}>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
<FormField
control={form.control}
name="username"
render={({field}) => (
<FormItem>
<FormLabel></FormLabel>
<FormControl>
<Input
{...field}
type="tel"
placeholder="请输入手机号码"
autoComplete="tel-national"
/>
</FormControl>
<FormMessage/>
</FormItem>
)}
/>
<Form<FormValues> className="space-y-6" onSubmit={onSubmit} form={form}>
<FormField name="username" label={`手机号码`}>
{({id, field}) => (
<Input
{...field}
id={id}
type="tel"
placeholder="请输入手机号码"
autoComplete="tel-national"
/>
)}
</FormField>
<FormField
control={form.control}
name="password"
render={({field}) => (
<FormItem>
<FormLabel></FormLabel>
<div className="flex space-x-4">
<FormControl>
<Input
{...field}
className="h-12"
placeholder="请输入验证码"
/>
</FormControl>
<Button
type="button"
variant="outline"
className="whitespace-nowrap h-12"
onClick={checkUsername}
disabled={countdown > 0}
>
{countdown > 0 ? `${countdown}秒后重发` : '获取验证码'}
</Button>
</div>
<FormMessage/>
</FormItem>
)}
/>
<FormField name="password" label={`验证码`}>
{({id, field}) => (
<div className="flex space-x-4">
<Input
{...field}
id={id}
className="h-12"
placeholder="请输入验证码"
/>
<Button
type="button"
variant="outline"
className="whitespace-nowrap h-12"
onClick={checkUsername}
disabled={countdown > 0}
>
{countdown > 0 ? `${countdown}秒后重发` : '获取验证码'}
</Button>
</div>
)}
</FormField>
<FormField
control={form.control}
name="remember"
render={({field}) => (
<FormItem className="flex flex-row items-start space-x-2 space-y-0">
<FormControl>
<Checkbox
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
<div className="space-y-1 leading-none">
<FormLabel></FormLabel>
</div>
</FormItem>
)}
/>
<FormField name="remember">
{({id, field}) => (
<div className="flex flex-row items-start space-x-2 space-y-0">
<Checkbox
id={id}
checked={field.value}
onCheckedChange={field.onChange}
/>
<div className="space-y-1 leading-none">
<Label></Label>
</div>
</div>
)}
</FormField>
<div className="flex flex-col gap-3">
<Button
className="w-full h-12 text-lg"
type="submit"
variant="gradient"
disabled={submitting}
>
{submitting ? '登录中...' : '注册 / 登录'}
</Button>
<div className="flex flex-col gap-3">
<Button
className="w-full h-12 text-lg"
type="submit"
variant="gradient"
disabled={submitting}
>
{submitting ? '登录中...' : '注册 / 登录'}
</Button>
<p className="text-xs text-center text-gray-500">
<a href="#" className="text-blue-600 hover:text-blue-500"></a><a href="#" className="text-blue-600 hover:text-blue-500"></a>
</p>
</div>
</form>
<p className="text-xs text-center text-gray-500">
<a href="#" className="text-blue-600 hover:text-blue-500"></a><a href="#" className="text-blue-600 hover:text-blue-500"></a>
</p>
</div>
</Form>
</CardContent>
</Card>

View File

@@ -48,7 +48,6 @@ export default function Header(props: HeaderProps) {
<HelpMenu key={`help`}/>,
], [])
// ======================
// 渲染组件
// ======================

View File

@@ -0,0 +1,244 @@
'use client'
import {z} from 'zod'
import {zodResolver} from '@hookform/resolvers/zod'
import {Form, FormField, FormLabel} 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'
import {useForm} from 'react-hook-form'
const formSchema = 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 FormValues = z.infer<typeof formSchema>
type FormSectionProps = {}
export default function FormSection(props: FormSectionProps) {
const form = useForm<FormValues>({
resolver: zodResolver(formSchema),
defaultValues: {
type: `num`,
order: 0,
region: ``,
provider: ``,
proto: ``,
distinct: ``,
format: `txt`,
separator: `,`,
count: 0,
},
})
const onSubmit = (values: z.infer<typeof formSchema>) => {
console.log(values)
// 在这里处理表单提交
}
return (
<Form
form={form}
onSubmit={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">
<FormField<FormValues> name="type" label={`套餐类型`}>
{({id, field}) => (
<RadioGroup
id={id}
onValueChange={field.onChange}
defaultValue={field.value as string}
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>
)}
</FormField>
</div>
{/* 已购套餐 */}
<div className="flex items-center">
<FormField name="order" label={`已购套餐`}>
{({field}) => (
<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>
)}
</FormField>
</div>
{/* 地区筛选 */}
<div className="flex items-center">
<FormField name="region" label={`地区筛选`}>
{({field}) => (
<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>
)}
</FormField>
</div>
{/* 运营商筛选 */}
<div className="flex items-center">
<FormField name="provider" label={`运营商筛选`}>
{({field}) => (
<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>
)}
</FormField>
</div>
{/* 协议类型 */}
<div className="flex items-center">
<FormField name="proto" label={`协议类型`}>
{({field}) => (
<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>
)}
</FormField>
</div>
{/* 去重选项 */}
<div className="flex items-center">
<FormField name="distinct" label={`去重选项`}>
{({field}) => (
<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>
)}
</FormField>
</div>
{/* 导出格式 */}
<div className="flex items-center">
<FormField name="format" label={`导出格式`}>
{({id, field}) => (
<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={`${id}-v-txt`} className="mr-2"/>
<FormLabel htmlFor={`${id}-v-txt`}>TXT格式</FormLabel>
</div>
<div className={`px-4 h-10 border rounded-lg flex items-center`}>
<RadioGroupItem value="json" id={`${id}-v-json`} className="mr-2"/>
<FormLabel htmlFor={`${id}-v-json`}>JSON格式</FormLabel>
</div>
</RadioGroup>
)}
</FormField>
</div>
{/* 分隔符 */}
<div className="flex items-center">
<FormField name="separator" label={`分隔符`}>
{({id, field}) => (
<Input {...field} id={id} className="h-10" placeholder="输入分隔符,默认为逗号"/>
)}
</FormField>
</div>
{/* 提取数量 */}
<div className="flex items-center">
<FormField name="count" label={`提取数量`}>
{({id, field}) => (
<Input
{...field}
id={id}
type="number"
onChange={e => field.onChange(Number(e.target.value))}
className="h-10"
placeholder="输入提取数量"
/>
)}
</FormField>
</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>
)
}

View File

@@ -1,311 +0,0 @@
'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>
)
}

View File

@@ -1,6 +1,6 @@
import BreadCrumb from '@/components/bread-crumb'
import Wrap from '@/components/wrap'
import FormSection from '@/app/(root)/collect/form-section'
import FormSection from '@/app/(root)/collect/_client/form-section'
export type CollectPageProps = {}
@@ -11,7 +11,6 @@ export default function CollectPage(props: CollectPageProps) {
<BreadCrumb items={[
{label: 'IP 提取', href: '/collect'},
]}/>
<h2 className={`text-3xl text-center`}> IP</h2>
<FormSection/>
</Wrap>
</main>

View File

@@ -1,6 +1,6 @@
import BreadCrumb from '@/components/bread-crumb'
import Wrap from '@/components/wrap'
import {Combo} from '@/app/(root)/product/combo'
import {Combo} from '@/app/(root)/product/_client/combo'
export type ProductPageProps = {}
@@ -13,8 +13,6 @@ export default function ProductPage(props: ProductPageProps) {
{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>
@@ -117,91 +115,99 @@ function Left() {
function Center() {
return (
<div className={`flex-auto p-8 flex flex-col relative gap-4 `}>
<div className={`flex-auto p-8 flex flex-col relative gap-8`}>
<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 className={`space-y-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>
</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 className={`space-y-4`}>
<h3>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>
</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 className={`space-y-4`}>
<h3>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>
</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 className={`space-y-6`}>
<h3></h3>
<div className={`grid grid-cols-3 auto-rows-fr gap-y-6`}>
<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>
{/* 左右的边框 */}