feat(ui): 接入股票名称展示,实装表单币种自动绑定及编辑删除功能

This commit is contained in:
2026-04-28 12:51:20 +08:00
parent 7cd084d4b3
commit ea57b4629a
9 changed files with 655 additions and 155 deletions
+2 -1
View File
@@ -37,8 +37,9 @@ export async function syncAllStockPrices() {
const latestPrice = dataArr[3];
if (latestPrice && !isNaN(Number(latestPrice)) && Number(latestPrice) > 0) {
const stockName = dataArr[1] || null;
await db.update(assets)
.set({ latestPrice: latestPrice })
.set({ latestPrice: latestPrice, name: stockName })
.where(eq(assets.id, asset.id));
successCount++;
}
+5
View File
@@ -8,6 +8,7 @@ import { desc, eq } from 'drizzle-orm';
interface Position {
assetId: string;
symbol: string;
name: string | null;
type: string;
quantity: string;
baseCurrency: string;
@@ -82,6 +83,7 @@ export async function getPortfolioPositions(): Promise<Position[]> {
txCurrency: transactions.txCurrency,
assetId: transactions.assetId,
assetSymbol: assets.symbol,
assetName: assets.name,
assetType: assets.type,
assetBaseCurrency: assets.baseCurrency,
assetLatestPrice: assets.latestPrice,
@@ -101,6 +103,7 @@ export async function getPortfolioPositions(): Promise<Position[]> {
const holdings = new Map<string, {
assetId: string;
symbol: string;
name: string | null;
type: string;
quantity: Big;
baseCurrency: string;
@@ -117,6 +120,7 @@ export async function getPortfolioPositions(): Promise<Position[]> {
holdings.set(tx.assetId, {
assetId: tx.assetId,
symbol: tx.assetSymbol || tx.assetId,
name: tx.assetName,
type: tx.assetType || 'CASH',
quantity: new Big('0'),
baseCurrency: tx.assetBaseCurrency || '',
@@ -190,6 +194,7 @@ export async function getPortfolioPositions(): Promise<Position[]> {
result.push({
assetId: holding.assetId,
symbol: holding.symbol,
name: holding.name,
type: holding.type,
quantity: holding.quantity.toString(),
baseCurrency: holding.baseCurrency,
+19 -2
View File
@@ -1,10 +1,11 @@
'use client';
import { useState, useTransition } from 'react';
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,
@@ -61,10 +62,26 @@ export function AddAssetDialog() {
symbol: '',
type: 'STOCK' as const,
exchange: 'US',
baseCurrency: '',
baseCurrency: 'USD',
},
});
const exchangeValue = useWatch({ control: form.control, name: 'exchange' });
useEffect(() => {
if (!exchangeValue) return;
const currencyMap: Record<string, string> = {
'US': 'USD',
'HKEX': 'HKD',
'SSE': 'CNY',
'SZSE': 'CNY',
};
const currency = currencyMap[exchangeValue];
if (currency) {
form.setValue('baseCurrency', currency, { shouldValidate: true });
}
}, [exchangeValue, form]);
function onSubmit(values: AddAssetForm) {
startTransition(async () => {
const result = await createAsset(values);
@@ -1,10 +1,11 @@
'use client';
import { useState, useTransition } from 'react';
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,
@@ -49,8 +50,10 @@ type AddTransactionForm = z.infer<typeof addTransactionSchema>;
interface Asset {
id: string;
symbol: string;
name: string | null;
type: string;
baseCurrency: string;
exchange: string | null;
}
interface AddTransactionDialogProps {
@@ -65,6 +68,13 @@ const txTypeLabels: Record<string, string> = {
FEE: '手续费 (FEE)',
};
const exchangeToCurrencyMap: Record<string, string> = {
'US': 'USD',
'HKEX': 'HKD',
'SSE': 'CNY',
'SZSE': 'CNY',
};
export function AddTransactionDialog({ assets }: AddTransactionDialogProps) {
const [open, setOpen] = useState(false);
const [isPending, startTransition] = useTransition();
@@ -83,6 +93,17 @@ export function AddTransactionDialog({ assets }: AddTransactionDialogProps) {
},
});
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({
@@ -136,7 +157,7 @@ export function AddTransactionDialog({ assets }: AddTransactionDialogProps) {
) : (
assets.map((asset) => (
<SelectItem key={asset.id} value={asset.id}>
{asset.symbol}
{asset.symbol}{asset.name ? ` (${asset.name})` : ''}
</SelectItem>
))
)}