feat: 任务 1: 集成 TradingSimulation 追踪事件任务 2: 传递 tradingEvents 到子组件

This commit is contained in:
zdl
2025-10-29 14:24:39 +08:00
parent 8632e40c94
commit e5ab99bae6
5 changed files with 139 additions and 16 deletions

View File

@@ -64,20 +64,38 @@ const calculateChange = (currentPrice, avgPrice) => {
return { change, changePercent };
};
export default function PositionsList({ positions, account, onSellStock }) {
export default function PositionsList({ positions, account, onSellStock, tradingEvents }) {
const [selectedPosition, setSelectedPosition] = useState(null);
const [sellQuantity, setSellQuantity] = useState(0);
const [orderType, setOrderType] = useState('MARKET');
const [limitPrice, setLimitPrice] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [hasTracked, setHasTracked] = React.useState(false);
const { isOpen, onOpen, onClose } = useDisclosure();
const toast = useToast();
const cardBg = useColorModeValue('white', 'gray.800');
const textColor = useColorModeValue('gray.700', 'white');
const secondaryColor = useColorModeValue('gray.500', 'gray.400');
// 🎯 追踪持仓查看 - 组件加载时触发一次
React.useEffect(() => {
if (!hasTracked && positions && positions.length > 0 && tradingEvents && tradingEvents.trackSimulationHoldingsViewed) {
const totalMarketValue = positions.reduce((sum, pos) => sum + (pos.marketValue || pos.quantity * pos.currentPrice || 0), 0);
const totalCost = positions.reduce((sum, pos) => sum + (pos.totalCost || pos.quantity * pos.avgPrice || 0), 0);
const totalProfit = positions.reduce((sum, pos) => sum + (pos.profit || 0), 0);
tradingEvents.trackSimulationHoldingsViewed({
count: positions.length,
totalValue: totalMarketValue,
totalCost,
profitLoss: totalProfit,
});
setHasTracked(true);
}
}, [positions, tradingEvents, hasTracked]);
// 格式化货币
const formatCurrency = (amount) => {
return new Intl.NumberFormat('zh-CN', {
@@ -102,6 +120,17 @@ export default function PositionsList({ positions, account, onSellStock }) {
setSelectedPosition(position);
setSellQuantity(position.availableQuantity); // 默认全部可卖数量
setLimitPrice(position.currentPrice?.toString() || position.avgPrice.toString());
// 🎯 追踪卖出按钮点击
if (tradingEvents && tradingEvents.trackSellButtonClicked) {
tradingEvents.trackSellButtonClicked({
stockCode: position.stockCode,
stockName: position.stockName,
quantity: position.quantity,
profitLoss: position.profit || 0,
}, 'holdings');
}
onOpen();
};
@@ -110,6 +139,8 @@ export default function PositionsList({ positions, account, onSellStock }) {
if (!selectedPosition || sellQuantity <= 0) return;
setIsLoading(true);
const price = orderType === 'LIMIT' ? parseFloat(limitPrice) : selectedPosition.currentPrice || selectedPosition.avgPrice;
try {
const result = await onSellStock(
selectedPosition.stockCode,
@@ -126,6 +157,20 @@ export default function PositionsList({ positions, account, onSellStock }) {
orderType,
orderId: result.orderId
});
// 🎯 追踪卖出成功
if (tradingEvents && tradingEvents.trackSimulationOrderPlaced) {
tradingEvents.trackSimulationOrderPlaced({
stockCode: selectedPosition.stockCode,
stockName: selectedPosition.stockName,
direction: 'sell',
quantity: sellQuantity,
price,
orderType,
success: true,
});
}
toast({
title: '卖出成功',
description: `已卖出 ${selectedPosition.stockName} ${sellQuantity}`,
@@ -142,6 +187,21 @@ export default function PositionsList({ positions, account, onSellStock }) {
quantity: sellQuantity,
orderType
});
// 🎯 追踪卖出失败
if (tradingEvents && tradingEvents.trackSimulationOrderPlaced) {
tradingEvents.trackSimulationOrderPlaced({
stockCode: selectedPosition.stockCode,
stockName: selectedPosition.stockName,
direction: 'sell',
quantity: sellQuantity,
price,
orderType,
success: false,
errorMessage: error.message,
});
}
toast({
title: '卖出失败',
description: error.message,