2025-04-08 11:21:58 +08:00
|
|
|
'use client'
|
|
|
|
|
import {FormLabel} from '@/components/ui/form'
|
|
|
|
|
import {merge} from '@/lib/utils'
|
|
|
|
|
import {RadioGroupItem} from '@/components/ui/radio-group'
|
|
|
|
|
import {ReactNode} from 'react'
|
|
|
|
|
|
|
|
|
|
export type FormOptionProps = {
|
|
|
|
|
id: string
|
|
|
|
|
value: string
|
|
|
|
|
label?: string
|
|
|
|
|
description?: string
|
2026-06-18 15:11:28 +08:00
|
|
|
price?: string
|
|
|
|
|
discount?: number
|
2025-04-08 11:21:58 +08:00
|
|
|
compare: string
|
|
|
|
|
className?: string
|
|
|
|
|
children?: ReactNode
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default function FormOption(props: FormOptionProps) {
|
2026-06-18 15:11:28 +08:00
|
|
|
// 安全地解析价格
|
|
|
|
|
const priceNum = props.price ? parseFloat(props.price) : NaN
|
|
|
|
|
const isValidPrice = !isNaN(priceNum) && priceNum > 0
|
|
|
|
|
|
|
|
|
|
const discount = typeof props.discount === 'number' ? props.discount : undefined
|
|
|
|
|
const hasDiscount = isValidPrice && discount !== undefined && discount < 100
|
|
|
|
|
const discountedPrice = hasDiscount ? priceNum * discount / 100 : null
|
|
|
|
|
|
|
|
|
|
const formatPrice = (price: number | string): string => {
|
|
|
|
|
const num = typeof price === 'string' ? parseFloat(price) : price
|
|
|
|
|
// 如果是 NaN 或无效值,返回空字符串
|
|
|
|
|
if (isNaN(num) || !isFinite(num)) return ''
|
|
|
|
|
|
|
|
|
|
const str = num.toString()
|
|
|
|
|
if (!str.includes('.')) return str
|
|
|
|
|
|
|
|
|
|
const decimal = str.split('.')[1]
|
|
|
|
|
if (/^0+$/.test(decimal)) return Math.floor(num).toString()
|
|
|
|
|
if (decimal.length <= 2) return str
|
|
|
|
|
|
|
|
|
|
return num.toFixed(4).replace(/\.?0+$/, '')
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-07 11:49:57 +08:00
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
<FormLabel
|
|
|
|
|
htmlFor={props.id}
|
|
|
|
|
className={merge(
|
|
|
|
|
`transition-colors duration-150 ease-in-out`,
|
|
|
|
|
`px-6 py-4 border rounded-md flex flex-col gap-2 cursor-pointer`,
|
|
|
|
|
props.compare === props.value ? `bg-primary/10 border-primary` : `border-gray-200`,
|
|
|
|
|
props.className,
|
|
|
|
|
)}>
|
|
|
|
|
{props.children ? props.children : (
|
|
|
|
|
<>
|
|
|
|
|
<span>{props.label}</span>
|
2026-06-18 15:11:28 +08:00
|
|
|
{props.description && !isValidPrice && (
|
|
|
|
|
<p className="text-sm text-gray-500">{props.description}</p>
|
|
|
|
|
)}
|
|
|
|
|
{isValidPrice && (
|
2026-06-23 14:31:50 +08:00
|
|
|
<div className="flex items-center flex-col">
|
2026-06-18 15:11:28 +08:00
|
|
|
{hasDiscount ? (
|
|
|
|
|
<>
|
|
|
|
|
<p className="text-sm font-medium">¥{formatPrice(discountedPrice!)}/IP</p>
|
|
|
|
|
<p className="text-sm text-gray-500 line-through">原价:{formatPrice(props.price!)}/IP</p>
|
|
|
|
|
{/* <span className="text-xs font-medium text-orange-600 bg-orange-50 px-1.5 py-0.5 rounded-full">
|
|
|
|
|
{props.discount}折
|
|
|
|
|
</span> */}
|
|
|
|
|
</>
|
|
|
|
|
) : (
|
|
|
|
|
<p className="text-sm">¥{formatPrice(props.price!)}/IP</p>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2025-06-07 11:49:57 +08:00
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
</FormLabel>
|
|
|
|
|
<RadioGroupItem id={props.id} value={props.value} className="hidden"/>
|
|
|
|
|
</>
|
|
|
|
|
)
|
2025-04-08 11:21:58 +08:00
|
|
|
}
|