Initial commit

This commit is contained in:
2025-10-11 11:55:25 +08:00
parent 467dad8449
commit 8107dee8d3
2879 changed files with 610575 additions and 0 deletions

View File

@@ -0,0 +1,364 @@
// src/views/TradingSimulation/components/AccountOverview.js - 账户概览组件(现代化版本)
import React from 'react';
import {
Grid,
GridItem,
Stat,
StatLabel,
StatNumber,
StatHelpText,
StatArrow,
Box,
Text,
Badge,
VStack,
HStack,
Progress,
useColorModeValue,
Icon,
Flex,
SimpleGrid,
Card,
CardBody,
CardHeader
} from '@chakra-ui/react';
import { FiTrendingUp, FiTrendingDown, FiDollarSign, FiPieChart, FiTarget, FiActivity } from 'react-icons/fi';
// 导入现有的图表组件
import DonutChart from '../../../components/Charts/DonutChart';
import IconBox from '../../../components/Icons/IconBox';
export default function AccountOverview({ account }) {
const textColor = useColorModeValue('gray.700', 'white');
const secondaryColor = useColorModeValue('gray.500', 'gray.400');
const profitColor = account?.totalProfit >= 0 ? 'green.500' : 'red.500';
const profitBg = useColorModeValue(
account?.totalProfit >= 0 ? 'green.50' : 'red.50',
account?.totalProfit >= 0 ? 'green.900' : 'red.900'
);
if (!account) {
return (
<Box p={4}>
<Text color={secondaryColor}>暂无账户数据</Text>
</Box>
);
}
// 安全地计算资产配置比例
const cashRatio = account?.totalAssets > 0 ? (account.availableCash / account.totalAssets) * 100 : 0;
const stockRatio = account?.totalAssets > 0 ? (account.marketValue / account.totalAssets) * 100 : 0;
// 格式化数字
const formatCurrency = (amount) => {
return new Intl.NumberFormat('zh-CN', {
style: 'currency',
currency: 'CNY',
minimumFractionDigits: 2,
maximumFractionDigits: 2
}).format(amount || 0);
};
const formatPercent = (percent) => {
return `${(percent || 0) >= 0 ? '+' : ''}${(percent || 0).toFixed(2)}%`;
};
// 安全地准备资产配置饼图数据
const assetAllocationData = account?.totalAssets > 0 ? [
(account.availableCash / account.totalAssets) * 100,
(account.marketValue / account.totalAssets) * 100
] : [100, 0];
const assetAllocationOptions = {
labels: ['现金资产', '股票资产'],
colors: ['#4299E1', '#48BB78'],
chart: {
width: "100%",
height: "280px"
},
states: {
hover: {
filter: {
type: "none",
},
},
},
legend: {
show: true,
position: 'bottom',
fontSize: '12px',
labels: {
colors: textColor
}
},
dataLabels: {
enabled: true,
style: {
fontSize: '12px',
fontWeight: 'bold',
colors: ['#fff']
},
formatter: function (val) {
return (val || 0).toFixed(1) + "%"
}
},
plotOptions: {
pie: {
expandOnClick: false,
donut: {
labels: {
show: false,
},
},
},
},
fill: {
colors: ['#4299E1', '#48BB78'],
},
tooltip: {
enabled: true,
theme: "dark",
y: {
formatter: function(val) {
return formatCurrency(val / 100 * (account?.totalAssets || 0))
}
}
},
};
return (
<SimpleGrid columns={{ base: 1, lg: 2 }} spacing={6}>
{/* 左侧:主要资产数据 */}
<VStack spacing={6} align="stretch">
{/* 核心资产指标 */}
<SimpleGrid columns={{ base: 2, md: 2 }} spacing={4}>
{/* 总资产卡片 */}
<Card minH="120px">
<CardBody>
<Flex direction="row" align="center" justify="center" w="100%">
<Stat me="auto">
<StatLabel
fontSize="sm"
color={secondaryColor}
fontWeight="bold"
pb=".1rem"
>
总资产
</StatLabel>
<Flex>
<StatNumber fontSize="lg" color={textColor} fontWeight="bold">
{formatCurrency(account?.totalAssets)}
</StatNumber>
</Flex>
</Stat>
<IconBox
as="div"
h={"45px"}
w={"45px"}
bg="linear-gradient(90deg, #4481EB 0%, #04BEFE 100%)"
icon={<Icon as={FiDollarSign} color="white" w="24px" h="24px" />}
/>
</Flex>
</CardBody>
</Card>
{/* 总收益卡片 */}
<Card minH="120px">
<CardBody>
<Flex direction="row" align="center" justify="center" w="100%">
<Stat me="auto">
<StatLabel
fontSize="sm"
color={secondaryColor}
fontWeight="bold"
pb=".1rem"
>
总收益
</StatLabel>
<Flex align="center">
<StatNumber fontSize="lg" color={profitColor} fontWeight="bold">
{formatCurrency(account?.totalProfit)}
</StatNumber>
<StatArrow
type={(account?.totalProfit || 0) >= 0 ? 'increase' : 'decrease'}
color={profitColor}
ml={2}
/>
</Flex>
<StatHelpText color={profitColor} fontSize="sm" fontWeight="bold">
{formatPercent(account?.totalProfitPercent)}
</StatHelpText>
</Stat>
<IconBox
as="div"
h={"45px"}
w={"45px"}
bg={(account?.totalProfit || 0) >= 0
? "linear-gradient(90deg, #4FD1C7 0%, #81E6D9 100%)"
: "linear-gradient(90deg, #FEB2B2 0%, #F56565 100%)"
}
icon={
<Icon
as={(account?.totalProfit || 0) >= 0 ? FiTrendingUp : FiTrendingDown}
color="white"
w="24px"
h="24px"
/>
}
/>
</Flex>
</CardBody>
</Card>
{/* 可用资金卡片 */}
<Card minH="120px">
<CardBody>
<Flex direction="row" align="center" justify="center" w="100%">
<Stat me="auto">
<StatLabel
fontSize="sm"
color={secondaryColor}
fontWeight="bold"
pb=".1rem"
>
可用资金
</StatLabel>
<Flex>
<StatNumber fontSize="lg" color={textColor} fontWeight="bold">
{formatCurrency(account?.availableCash)}
</StatNumber>
</Flex>
<StatHelpText color={secondaryColor} fontSize="xs">
现金占比: {(cashRatio || 0).toFixed(1)}%
</StatHelpText>
</Stat>
<IconBox
as="div"
h={"45px"}
w={"45px"}
bg="linear-gradient(90deg, #FFE57F 0%, #FFB74D 100%)"
icon={<Icon as={FiTarget} color="white" w="24px" h="24px" />}
/>
</Flex>
</CardBody>
</Card>
{/* 持仓市值卡片 */}
<Card minH="120px">
<CardBody>
<Flex direction="row" align="center" justify="center" w="100%">
<Stat me="auto">
<StatLabel
fontSize="sm"
color={secondaryColor}
fontWeight="bold"
pb=".1rem"
>
持仓市值
</StatLabel>
<Flex>
<StatNumber fontSize="lg" color={textColor} fontWeight="bold">
{formatCurrency(account?.marketValue)}
</StatNumber>
</Flex>
<StatHelpText color={secondaryColor} fontSize="xs">
持仓占比: {(stockRatio || 0).toFixed(1)}%
</StatHelpText>
</Stat>
<IconBox
as="div"
h={"45px"}
w={"45px"}
bg="linear-gradient(90deg, #9F7AEA 0%, #805AD5 100%)"
icon={<Icon as={FiPieChart} color="white" w="24px" h="24px" />}
/>
</Flex>
</CardBody>
</Card>
</SimpleGrid>
{/* 风险等级和账户状态 */}
<Card>
<CardBody>
<VStack spacing={4}>
<HStack justify="space-between" w="full">
<HStack spacing={3}>
<IconBox
as="div"
h={"35px"}
w={"35px"}
bg="linear-gradient(90deg, #F093FB 0%, #F5576C 100%)"
icon={<Icon as={FiActivity} color="white" w="16px" h="16px" />}
/>
<VStack align="start" spacing={0}>
<Text fontSize="sm" fontWeight="bold" color={textColor}>账户状态</Text>
<Text fontSize="xs" color={secondaryColor}>风险等级: 中等</Text>
</VStack>
</HStack>
<Badge
colorScheme={(account?.totalProfit || 0) >= 0 ? 'green' : 'red'}
variant="solid"
borderRadius="full"
px={3}
py={1}
>
{(account?.totalProfit || 0) >= 0 ? '盈利中' : '亏损中'}
</Badge>
</HStack>
<HStack justify="space-between" w="full" fontSize="xs" color={secondaryColor}>
<Text>创建时间: {account?.createdAt ? new Date(account.createdAt).toLocaleDateString('zh-CN') : '-'}</Text>
<Text>最后更新: {account?.lastUpdated ? new Date(account.lastUpdated).toLocaleString('zh-CN') : '-'}</Text>
</HStack>
</VStack>
</CardBody>
</Card>
</VStack>
{/* 右侧:资产配置图表 */}
<Card>
<CardHeader>
<HStack justify="space-between" w="full">
<Text fontSize="lg" fontWeight="bold" color={textColor}>
资产配置
</Text>
<Badge colorScheme="blue" variant="outline">
实时更新
</Badge>
</HStack>
</CardHeader>
<CardBody>
<Box h="280px">
<DonutChart
chartData={assetAllocationData}
chartOptions={assetAllocationOptions}
/>
</Box>
{/* 详细配置信息 */}
<VStack spacing={3} mt={4}>
<HStack justify="space-between" w="full">
<HStack spacing={2}>
<Box w={3} h={3} bg="blue.400" borderRadius="sm" />
<Text fontSize="sm" color={textColor}>现金资产</Text>
</HStack>
<Text fontSize="sm" fontWeight="bold" color={textColor}>
{formatCurrency(account?.availableCash)}
</Text>
</HStack>
<HStack justify="space-between" w="full">
<HStack spacing={2}>
<Box w={3} h={3} bg="green.400" borderRadius="sm" />
<Text fontSize="sm" color={textColor}>股票资产</Text>
</HStack>
<Text fontSize="sm" fontWeight="bold" color={textColor}>
{formatCurrency(account?.marketValue)}
</Text>
</HStack>
</VStack>
</CardBody>
</Card>
</SimpleGrid>
);
}