目录结构与表单组件结构调整
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -48,7 +48,6 @@ export default function Header(props: HeaderProps) {
|
||||
<HelpMenu key={`help`}/>,
|
||||
], [])
|
||||
|
||||
|
||||
// ======================
|
||||
// 渲染组件
|
||||
// ======================
|
||||
|
||||
244
src/app/(root)/collect/_client/form-section.tsx
Normal file
244
src/app/(root)/collect/_client/form-section.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
{/* 左右的边框 */}
|
||||
|
||||
Reference in New Issue
Block a user