'use client'; import { useState, useTransition, useEffect } from 'react'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; import { useRouter } from 'next/navigation'; import { useWatch } from 'react-hook-form'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from '@/components/ui/form'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Plus } from 'lucide-react'; import { toast } from 'sonner'; import { createTransaction } from '@/actions/transaction'; import { formatDateForDatetimeLocal, parseDateTimeLocalToUTC_v2 } from '@/libs/utils'; const addTransactionSchema = z.object({ assetId: z.string().uuid('请选择一个资产'), txType: z.enum(['BUY', 'SELL', 'DIVIDEND', 'AIRDROP', 'FEE']), quantity: z.string().regex(/^-?\d+(\.\d+)?$/, '数量必须是数字字符串'), price: z.string().regex(/^-?\d+(\.\d+)?$/, '价格必须是数字字符串'), fee: z.string().regex(/^-?\d+(\.\d+)?$/, '手续费必须是数字字符串'), txCurrency: z.string().min(1, '交易币种不能为空'), executedAt: z.date(), }); type AddTransactionForm = z.infer; interface Asset { id: string; symbol: string; name: string | null; type: string; baseCurrency: string; exchange: string | null; } interface AddTransactionDialogProps { assets: Asset[]; } const txTypeLabels: Record = { BUY: '买入 (BUY)', SELL: '卖出 (SELL)', DIVIDEND: '分红 (DIVIDEND)', AIRDROP: '空投 (AIRDROP)', FEE: '手续费 (FEE)', }; const exchangeToCurrencyMap: Record = { 'US': 'USD', 'HKEX': 'HKD', 'SSE': 'CNY', 'SZSE': 'CNY', }; export function AddTransactionDialog({ assets }: AddTransactionDialogProps) { const [open, setOpen] = useState(false); const [isPending, startTransition] = useTransition(); const router = useRouter(); const form = useForm({ resolver: zodResolver(addTransactionSchema), defaultValues: { assetId: '', txType: 'BUY' as const, quantity: '', price: '', fee: '0', txCurrency: 'USD', executedAt: new Date(), }, }); const selectedAssetId = useWatch({ control: form.control, name: 'assetId' }); useEffect(() => { if (!selectedAssetId) return; const selectedAsset = assets.find((a) => a.id === selectedAssetId); if (selectedAsset) { const currency = exchangeToCurrencyMap[selectedAsset.exchange || ''] || selectedAsset.baseCurrency || 'USD'; form.setValue('txCurrency', currency, { shouldValidate: true }); } }, [selectedAssetId, assets, form]); function onSubmit(values: AddTransactionForm) { startTransition(async () => { const result = await createTransaction({ assetId: values.assetId, txType: values.txType, quantity: String(values.quantity), price: String(values.price), fee: String(values.fee), txCurrency: values.txCurrency, executedAt: values.executedAt, exchangeRate: '1', }); if (result.success) { toast.success('流水记录成功'); setOpen(false); form.reset(); router.refresh(); } }); } return ( 添加交易流水 录入一笔新的交易记录到系统中
( 标的资产 )} /> ( 交易类型 )} />
( 数量 )} /> ( 价格 )} />
( 手续费 )} /> ( 交易币种 )} />
( 执行时间 { const value = e.target.value; if (!value || value.trim() === '') { field.onChange(null); return; } const [datePart, timePart] = value.split('T'); if (!datePart) { field.onChange(null); return; } const [yearStr, monthStr, dayStr] = datePart.split('-'); const year = parseInt(yearStr, 10); const month = parseInt(monthStr, 10) - 1; const day = parseInt(dayStr, 10); if (isNaN(year) || isNaN(month) || isNaN(day)) { field.onChange(null); return; } let hours = 0; let minutes = 0; if (timePart) { const timeParts = timePart.split(':'); hours = parseInt(timeParts[0], 10) || 0; minutes = parseInt(timeParts[1], 10) || 0; } const parsedDate = parseDateTimeLocalToUTC_v2(year, month, day, hours, minutes); if (isNaN(parsedDate.getTime())) { field.onChange(null); return; } field.onChange(parsedDate); }} value={ field.value ? formatDateForDatetimeLocal(field.value) : '' } /> )} />
); }