2025-04-08 11:21:58 +08:00
|
|
|
|
'use client'
|
|
|
|
|
|
import {FormField} from '@/components/ui/form'
|
|
|
|
|
|
import {RadioGroup} from '@/components/ui/radio-group'
|
|
|
|
|
|
import {Input} from '@/components/ui/input'
|
|
|
|
|
|
import {Button} from '@/components/ui/button'
|
|
|
|
|
|
import {Minus, Plus} from 'lucide-react'
|
2025-05-22 14:59:22 +08:00
|
|
|
|
import FormOption from '@/components/composites/purchase/option'
|
2025-04-08 11:21:58 +08:00
|
|
|
|
import Image from 'next/image'
|
2025-12-04 19:07:48 +08:00
|
|
|
|
import check from '../_assets/check.svg'
|
2025-12-16 17:10:30 +08:00
|
|
|
|
import {useFormContext, useWatch} from 'react-hook-form'
|
2025-05-22 14:59:22 +08:00
|
|
|
|
import {Schema} from '@/components/composites/purchase/short/form'
|
2025-06-09 19:04:25 +08:00
|
|
|
|
import {Card} from '@/components/ui/card'
|
2025-04-08 11:21:58 +08:00
|
|
|
|
|
2026-04-16 14:41:42 +08:00
|
|
|
|
export default function Center({
|
|
|
|
|
|
priceMap,
|
|
|
|
|
|
liveList,
|
|
|
|
|
|
expireList,
|
|
|
|
|
|
}: {
|
|
|
|
|
|
priceMap: Map<string, string>
|
|
|
|
|
|
liveList: string[]
|
|
|
|
|
|
expireList: string[]
|
|
|
|
|
|
}) {
|
2025-05-22 14:59:22 +08:00
|
|
|
|
const form = useFormContext<Schema>()
|
2025-12-16 17:10:30 +08:00
|
|
|
|
const type = useWatch({name: 'type'})
|
2026-04-16 14:41:42 +08:00
|
|
|
|
const expire = useWatch({name: 'expire'})
|
|
|
|
|
|
const isTime = type === '1'
|
|
|
|
|
|
|
2025-04-08 11:21:58 +08:00
|
|
|
|
return (
|
2025-06-09 19:04:25 +08:00
|
|
|
|
<Card className="flex-auto p-6 flex flex-col gap-6 relative">
|
2025-04-08 11:21:58 +08:00
|
|
|
|
|
|
|
|
|
|
{/* 计费方式 */}
|
2025-05-22 14:59:22 +08:00
|
|
|
|
<FormField<Schema, 'type'>
|
2025-06-07 11:49:57 +08:00
|
|
|
|
className="flex flex-col gap-4"
|
|
|
|
|
|
name="type"
|
|
|
|
|
|
label="计费方式">
|
2025-04-08 11:21:58 +08:00
|
|
|
|
{({id, field}) => (
|
|
|
|
|
|
<RadioGroup
|
|
|
|
|
|
id={id}
|
2026-04-16 14:41:42 +08:00
|
|
|
|
value={field.value}
|
|
|
|
|
|
onValueChange={(v) => {
|
|
|
|
|
|
field.onChange(v)
|
|
|
|
|
|
if (v === '2') {
|
|
|
|
|
|
form.setValue('expire', '0')
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (expireList.length > 0) {
|
|
|
|
|
|
form.setValue('expire', expireList[0])
|
|
|
|
|
|
form.setValue('daily_limit', 2000)
|
|
|
|
|
|
}
|
|
|
|
|
|
}}
|
2025-06-09 19:04:25 +08:00
|
|
|
|
className="flex gap-4 max-md:flex-col">
|
2025-04-08 11:21:58 +08:00
|
|
|
|
|
|
|
|
|
|
<FormOption
|
|
|
|
|
|
id={`${id}-2`}
|
|
|
|
|
|
value="2"
|
|
|
|
|
|
label="包量套餐"
|
|
|
|
|
|
description="适用于短期或不定期高提取业务场景"
|
|
|
|
|
|
compare={field.value}/>
|
|
|
|
|
|
|
|
|
|
|
|
<FormOption
|
|
|
|
|
|
id={`${id}-1`}
|
|
|
|
|
|
value="1"
|
|
|
|
|
|
label="包时套餐"
|
|
|
|
|
|
description="适用于每日提取量稳定的业务场景"
|
|
|
|
|
|
compare={field.value}/>
|
|
|
|
|
|
|
|
|
|
|
|
</RadioGroup>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</FormField>
|
|
|
|
|
|
|
|
|
|
|
|
{/* IP 时效 */}
|
2025-05-22 14:59:22 +08:00
|
|
|
|
<FormField<Schema, 'live'>
|
2025-06-07 11:49:57 +08:00
|
|
|
|
className="space-y-4"
|
|
|
|
|
|
name="live"
|
|
|
|
|
|
label="IP 时效">
|
2025-04-08 11:21:58 +08:00
|
|
|
|
{({id, field}) => (
|
|
|
|
|
|
<RadioGroup
|
|
|
|
|
|
id={id}
|
2026-04-16 14:41:42 +08:00
|
|
|
|
value={field.value}
|
2025-04-08 11:21:58 +08:00
|
|
|
|
onValueChange={field.onChange}
|
2025-06-10 09:31:05 +08:00
|
|
|
|
className="grid grid-cols-[repeat(auto-fill,minmax(120px,1fr))] gap-4">
|
2026-04-16 14:41:42 +08:00
|
|
|
|
{liveList.map((live) => {
|
|
|
|
|
|
const params = new URLSearchParams()
|
|
|
|
|
|
params.set('mode', isTime ? 'time' : 'quota')
|
|
|
|
|
|
params.set('live', live)
|
|
|
|
|
|
params.set('expire', isTime ? expire : '0')
|
|
|
|
|
|
const price = priceMap.get(params.toString())
|
|
|
|
|
|
const minutes = Number(live)
|
|
|
|
|
|
const hours = minutes / 60
|
|
|
|
|
|
const label = minutes % 60 === 0 ? `${hours} 小时` : `${minutes} 分钟`
|
|
|
|
|
|
return (
|
|
|
|
|
|
<FormOption
|
|
|
|
|
|
key={live}
|
|
|
|
|
|
id={`${id}-${live}`}
|
|
|
|
|
|
value={live}
|
|
|
|
|
|
label={label}
|
|
|
|
|
|
description={price && `¥${price}${!isTime ? '/IP' : ''}`}
|
|
|
|
|
|
compare={field.value}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)
|
|
|
|
|
|
})}
|
2025-04-08 11:21:58 +08:00
|
|
|
|
</RadioGroup>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</FormField>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 根据套餐类型显示不同表单项 */}
|
2026-04-16 14:41:42 +08:00
|
|
|
|
{!isTime ? (
|
2025-04-08 11:21:58 +08:00
|
|
|
|
/* 包量:IP 购买数量 */
|
|
|
|
|
|
<FormField
|
2025-06-07 11:49:57 +08:00
|
|
|
|
className="space-y-4"
|
|
|
|
|
|
name="quota"
|
|
|
|
|
|
label="IP 购买数量">
|
2025-04-08 11:21:58 +08:00
|
|
|
|
{({id, field}) => (
|
2025-06-07 11:49:57 +08:00
|
|
|
|
<div className="flex gap-2 items-center">
|
2025-04-08 11:21:58 +08:00
|
|
|
|
<Button
|
2025-06-07 11:49:57 +08:00
|
|
|
|
theme="outline"
|
2025-04-08 11:21:58 +08:00
|
|
|
|
type="button"
|
2025-06-07 11:49:57 +08:00
|
|
|
|
className="h-10 w-10 border border-gray-200 rounded-sm flex items-center justify-center text-lg"
|
2025-12-16 17:10:30 +08:00
|
|
|
|
onClick={() => form.setValue('quota', Math.max(10000, Number(field.value) - 5000))}
|
|
|
|
|
|
disabled={Number(field.value) === 10000}>
|
2025-04-08 11:21:58 +08:00
|
|
|
|
<Minus/>
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
<Input
|
|
|
|
|
|
{...field}
|
|
|
|
|
|
id={id}
|
|
|
|
|
|
type="number"
|
2025-06-07 11:49:57 +08:00
|
|
|
|
className="w-40 h-10 border border-gray-200 rounded-sm text-center"
|
2025-12-16 17:10:30 +08:00
|
|
|
|
min={10000}
|
2025-12-18 12:28:29 +08:00
|
|
|
|
step={5000}
|
|
|
|
|
|
onBlur={(e) => {
|
|
|
|
|
|
const value = Number(e.target.value)
|
|
|
|
|
|
if (value < 10000) {
|
|
|
|
|
|
form.setValue('quota', 10000)
|
|
|
|
|
|
}
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
2025-04-08 11:21:58 +08:00
|
|
|
|
<Button
|
2025-06-07 11:49:57 +08:00
|
|
|
|
theme="outline"
|
2025-04-08 11:21:58 +08:00
|
|
|
|
type="button"
|
2025-06-07 11:49:57 +08:00
|
|
|
|
className="h-10 w-10 border border-gray-200 rounded-sm flex items-center justify-center text-lg"
|
2025-12-16 17:10:30 +08:00
|
|
|
|
onClick={() => form.setValue('quota', Number(field.value) + 5000)}>
|
2025-04-08 11:21:58 +08:00
|
|
|
|
<Plus/>
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</FormField>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<>
|
|
|
|
|
|
{/* 包时:套餐时效 */}
|
2026-04-16 14:41:42 +08:00
|
|
|
|
<FormField className="space-y-4" name="expire" label="套餐时效">
|
2025-04-08 11:21:58 +08:00
|
|
|
|
{({id, field}) => (
|
2026-04-16 14:41:42 +08:00
|
|
|
|
<RadioGroup id={id} value={field.value} onValueChange={field.onChange} className="flex gap-4 flex-wrap">
|
|
|
|
|
|
{expireList.map(day => (
|
|
|
|
|
|
<FormOption
|
|
|
|
|
|
key={day}
|
|
|
|
|
|
id={`${id}-${day}`}
|
|
|
|
|
|
value={day}
|
|
|
|
|
|
label={`${day} 天`}
|
|
|
|
|
|
compare={field.value}
|
|
|
|
|
|
/>
|
|
|
|
|
|
))}
|
2025-04-08 11:21:58 +08:00
|
|
|
|
</RadioGroup>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</FormField>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 包时:每日提取上限 */}
|
|
|
|
|
|
<FormField
|
2025-06-07 11:49:57 +08:00
|
|
|
|
className="space-y-4"
|
2025-12-17 18:46:45 +08:00
|
|
|
|
name="daily_limit"
|
2025-06-07 11:49:57 +08:00
|
|
|
|
label="每日提取上限">
|
2025-04-08 11:21:58 +08:00
|
|
|
|
{({id, field}) => (
|
2025-06-07 11:49:57 +08:00
|
|
|
|
<div className="flex gap-2 items-center">
|
2025-04-08 11:21:58 +08:00
|
|
|
|
<Button
|
2025-06-07 11:49:57 +08:00
|
|
|
|
theme="outline"
|
2025-04-08 11:21:58 +08:00
|
|
|
|
type="button"
|
2025-06-07 11:49:57 +08:00
|
|
|
|
className="h-10 w-10 border border-gray-200 rounded-sm flex items-center justify-center text-lg"
|
2026-04-16 14:41:42 +08:00
|
|
|
|
onClick={() => form.setValue('daily_limit', Math.max(2000, Number(field.value) - 1000))}
|
|
|
|
|
|
disabled={Number(field.value) === 2000}>
|
2025-04-08 11:21:58 +08:00
|
|
|
|
<Minus/>
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
<Input
|
|
|
|
|
|
{...field}
|
|
|
|
|
|
id={id}
|
|
|
|
|
|
type="number"
|
2025-06-07 11:49:57 +08:00
|
|
|
|
className="w-40 h-10 border border-gray-200 rounded-sm text-center"
|
2026-04-16 14:41:42 +08:00
|
|
|
|
min={2000}
|
|
|
|
|
|
step={1000}
|
2025-12-18 12:28:29 +08:00
|
|
|
|
onBlur={(e) => {
|
|
|
|
|
|
const value = Number(e.target.value)
|
2026-04-16 14:41:42 +08:00
|
|
|
|
if (value < 2000) {
|
|
|
|
|
|
form.setValue('daily_limit', 2000)
|
2025-12-18 12:28:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
}}
|
2025-04-08 11:21:58 +08:00
|
|
|
|
/>
|
|
|
|
|
|
<Button
|
2025-06-07 11:49:57 +08:00
|
|
|
|
theme="outline"
|
2025-04-08 11:21:58 +08:00
|
|
|
|
type="button"
|
2025-06-07 11:49:57 +08:00
|
|
|
|
className="h-10 w-10 border border-gray-200 rounded-sm flex items-center justify-center text-lg"
|
2026-04-16 14:41:42 +08:00
|
|
|
|
onClick={() => form.setValue('daily_limit', Number(field.value) + 1000)}>
|
2025-04-08 11:21:58 +08:00
|
|
|
|
<Plus/>
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</FormField>
|
|
|
|
|
|
</>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{/* 产品特性 */}
|
2025-06-07 11:49:57 +08:00
|
|
|
|
<div className="space-y-6">
|
2025-04-08 11:21:58 +08:00
|
|
|
|
<h3>产品特性</h3>
|
2025-06-09 19:04:25 +08:00
|
|
|
|
<div className="grid grid-cols-2 md:grid-cols-3 auto-rows-fr gap-4 md:gap-y-6">
|
2025-06-07 11:49:57 +08:00
|
|
|
|
<p className="flex gap-2 items-center">
|
|
|
|
|
|
<Image src={check} alt="check" aria-hidden className="w-4 h-4"/>
|
|
|
|
|
|
<span className="text-sm text-gray-500">支持高并发提取</span>
|
2025-04-08 11:21:58 +08:00
|
|
|
|
</p>
|
2025-06-07 11:49:57 +08:00
|
|
|
|
<p className="flex gap-2 items-center">
|
|
|
|
|
|
<Image src={check} alt="check" aria-hidden className="w-4 h-4"/>
|
|
|
|
|
|
<span className="text-sm text-gray-500">指定省份、城市或混播</span>
|
2025-04-08 11:21:58 +08:00
|
|
|
|
</p>
|
2025-06-07 11:49:57 +08:00
|
|
|
|
<p className="flex gap-2 items-center">
|
|
|
|
|
|
<Image src={check} alt="check" aria-hidden className="w-4 h-4"/>
|
|
|
|
|
|
<span className="text-sm text-gray-500">账密+白名单验证</span>
|
2025-04-08 11:21:58 +08:00
|
|
|
|
</p>
|
2025-06-07 11:49:57 +08:00
|
|
|
|
<p className="flex gap-2 items-center">
|
|
|
|
|
|
<Image src={check} alt="check" aria-hidden className="w-4 h-4"/>
|
|
|
|
|
|
<span className="text-sm text-gray-500">完备的API接口</span>
|
2025-04-08 11:21:58 +08:00
|
|
|
|
</p>
|
2025-06-07 11:49:57 +08:00
|
|
|
|
<p className="flex gap-2 items-center">
|
|
|
|
|
|
<Image src={check} alt="check" aria-hidden className="w-4 h-4"/>
|
|
|
|
|
|
<span className="text-sm text-gray-500">IP时效3-30分钟(可定制)</span>
|
2025-04-08 11:21:58 +08:00
|
|
|
|
</p>
|
2025-06-07 11:49:57 +08:00
|
|
|
|
<p className="flex gap-2 items-center">
|
|
|
|
|
|
<Image src={check} alt="check" aria-hidden className="w-4 h-4"/>
|
|
|
|
|
|
<span className="text-sm text-gray-500">IP资源定期筛选</span>
|
2025-04-08 11:21:58 +08:00
|
|
|
|
</p>
|
2025-06-07 11:49:57 +08:00
|
|
|
|
<p className="flex gap-2 items-center">
|
|
|
|
|
|
<Image src={check} alt="check" aria-hidden className="w-4 h-4"/>
|
|
|
|
|
|
<span className="text-sm text-gray-500">包量/包时计费方式</span>
|
2025-04-08 11:21:58 +08:00
|
|
|
|
</p>
|
2025-06-07 11:49:57 +08:00
|
|
|
|
<p className="flex gap-2 items-center">
|
|
|
|
|
|
<Image src={check} alt="check" aria-hidden className="w-4 h-4"/>
|
|
|
|
|
|
<span className="text-sm text-gray-500">每日去重量:500万</span>
|
2025-04-08 11:21:58 +08:00
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-06-09 19:04:25 +08:00
|
|
|
|
</Card>
|
2025-04-08 11:21:58 +08:00
|
|
|
|
)
|
|
|
|
|
|
}
|