动态生成购买套餐 & 取消初次进后台修改密码的弹窗 & 添加总折扣字段 & 发布v1.5.0版本

This commit is contained in:
Eamon-meng
2026-04-16 14:41:42 +08:00
parent 319baea5e8
commit 5607217625
25 changed files with 619 additions and 273 deletions

View File

@@ -10,19 +10,21 @@ import check from '../_assets/check.svg'
import {useFormContext, useWatch} from 'react-hook-form'
import {Schema} from '@/components/composites/purchase/short/form'
import {Card} from '@/components/ui/card'
import {useEffect} from 'react'
export default function Center() {
export default function Center({
priceMap,
liveList,
expireList,
}: {
priceMap: Map<string, string>
liveList: string[]
expireList: string[]
}) {
const form = useFormContext<Schema>()
const type = useWatch({name: 'type'})
useEffect(() => {
if (type === '1') {
form.setValue('daily_limit', 2000)
}
else {
form.setValue('quota', 10000)
}
}, [type, form])
const expire = useWatch({name: 'expire'})
const isTime = type === '1'
return (
<Card className="flex-auto p-6 flex flex-col gap-6 relative">
@@ -34,8 +36,17 @@ export default function Center() {
{({id, field}) => (
<RadioGroup
id={id}
defaultValue={field.value}
onValueChange={field.onChange}
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)
}
}}
className="flex gap-4 max-md:flex-col">
<FormOption
@@ -64,21 +75,35 @@ export default function Center() {
{({id, field}) => (
<RadioGroup
id={id}
defaultValue={field.value}
value={field.value}
onValueChange={field.onChange}
className="grid grid-cols-[repeat(auto-fill,minmax(120px,1fr))] gap-4">
<FormOption id={`${id}-3`} value="3" label="3 分钟" description="¥0.005/IP" compare={field.value}/>
<FormOption id={`${id}-5`} value="5" label="5 分钟" description="¥0.01/IP" compare={field.value}/>
<FormOption id={`${id}-10`} value="10" label="10 分钟" description="¥0.02/IP" compare={field.value}/>
<FormOption id={`${id}-20`} value="15" label="15 分钟" description="¥0.03/IP" compare={field.value}/>
<FormOption id={`${id}-30`} value="30" label="30 分钟" description="¥0.06/IP" compare={field.value}/>
{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}
/>
)
})}
</RadioGroup>
)}
</FormField>
{/* 根据套餐类型显示不同表单项 */}
{type === '2' ? (
{!isTime ? (
/* 包量IP 购买数量 */
<FormField
className="space-y-4"
@@ -121,23 +146,18 @@ export default function Center() {
) : (
<>
{/* 包时:套餐时效 */}
<FormField
className="space-y-4"
name="expire"
label="套餐时效">
<FormField className="space-y-4" name="expire" label="套餐时效">
{({id, field}) => (
<RadioGroup
id={id}
defaultValue={field.value}
onValueChange={field.onChange}
className="flex gap-4 flex-wrap">
<FormOption id={`${id}-7`} value="7" label="7天" compare={field.value}/>
<FormOption id={`${id}-15`} value="15" label="15天" compare={field.value}/>
<FormOption id={`${id}-30`} value="30" label="30天" compare={field.value}/>
<FormOption id={`${id}-90`} value="90" label="90天" compare={field.value}/>
<FormOption id={`${id}-180`} value="180" label="180天" compare={field.value}/>
<FormOption id={`${id}-365`} value="365" label="365天" compare={field.value}/>
<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}
/>
))}
</RadioGroup>
)}
</FormField>
@@ -153,8 +173,8 @@ export default function Center() {
theme="outline"
type="button"
className="h-10 w-10 border border-gray-200 rounded-sm flex items-center justify-center text-lg"
onClick={() => form.setValue('daily_limit', Math.max(2_000, Number(field.value) - 1_000))}
disabled={Number(field.value) === 2_000}>
onClick={() => form.setValue('daily_limit', Math.max(2000, Number(field.value) - 1000))}
disabled={Number(field.value) === 2000}>
<Minus/>
</Button>
<Input
@@ -162,12 +182,12 @@ export default function Center() {
id={id}
type="number"
className="w-40 h-10 border border-gray-200 rounded-sm text-center"
min={2_000}
step={1_000}
min={2000}
step={1000}
onBlur={(e) => {
const value = Number(e.target.value)
if (value < 2_000) {
form.setValue('daily_limit', 2_000)
if (value < 2000) {
form.setValue('daily_limit', 2000)
}
}}
/>
@@ -175,7 +195,7 @@ export default function Center() {
theme="outline"
type="button"
className="h-10 w-10 border border-gray-200 rounded-sm flex items-center justify-center text-lg"
onClick={() => form.setValue('daily_limit', Number(field.value) + 1_000)}>
onClick={() => form.setValue('daily_limit', Number(field.value) + 1000)}>
<Plus/>
</Button>
</div>
@@ -212,10 +232,6 @@ export default function Center() {
<Image src={check} 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">
<Image src={check} 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">
<Image src={check} alt="check" aria-hidden className="w-4 h-4"/>
<span className="text-sm text-gray-500">/</span>