feat: 添加导航徽章
This commit is contained in:
@@ -59,8 +59,7 @@ import {
|
||||
FiAlertCircle,
|
||||
} from 'react-icons/fi';
|
||||
import MyFutureEvents from './components/MyFutureEvents';
|
||||
import InvestmentCalendarChakra from './components/InvestmentCalendarChakra';
|
||||
import InvestmentPlansAndReviews from './components/InvestmentPlansAndReviews';
|
||||
import InvestmentPlanningCenter from './components/InvestmentPlanningCenter';
|
||||
|
||||
export default function CenterDashboard() {
|
||||
const { user } = useAuth();
|
||||
@@ -81,26 +80,21 @@ export default function CenterDashboard() {
|
||||
const [realtimeQuotes, setRealtimeQuotes] = useState({});
|
||||
const [followingEvents, setFollowingEvents] = useState([]);
|
||||
const [eventComments, setEventComments] = useState([]);
|
||||
const [subscriptionInfo, setSubscriptionInfo] = useState({ type: 'free', status: 'active', days_left: 999, is_active: true });
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
const [quotesLoading, setQuotesLoading] = useState(false);
|
||||
|
||||
const loadData = useCallback(async () => {
|
||||
try {
|
||||
setRefreshing(true);
|
||||
const base = (process.env.NODE_ENV === 'production' ? '' : process.env.REACT_APP_API_URL || 'http://49.232.185.254:5001');
|
||||
const ts = Date.now();
|
||||
const [w, e, c, s] = await Promise.all([
|
||||
const [w, e, c] = await Promise.all([
|
||||
fetch(base + `/api/account/watchlist?_=${ts}`, { credentials: 'include', cache: 'no-store', headers: { 'Cache-Control': 'no-cache' } }),
|
||||
fetch(base + `/api/account/events/following?_=${ts}`, { credentials: 'include', cache: 'no-store', headers: { 'Cache-Control': 'no-cache' } }),
|
||||
fetch(base + `/api/account/events/comments?_=${ts}`, { credentials: 'include', cache: 'no-store', headers: { 'Cache-Control': 'no-cache' } }),
|
||||
fetch(base + `/api/subscription/current?_=${ts}`, { credentials: 'include', cache: 'no-store', headers: { 'Cache-Control': 'no-cache' } }),
|
||||
]);
|
||||
const jw = await w.json();
|
||||
const je = await e.json();
|
||||
const jc = await c.json();
|
||||
const js = await s.json();
|
||||
if (jw.success) {
|
||||
setWatchlist(Array.isArray(jw.data) ? jw.data : []);
|
||||
// 加载实时行情
|
||||
@@ -110,18 +104,15 @@ export default function CenterDashboard() {
|
||||
}
|
||||
if (je.success) setFollowingEvents(Array.isArray(je.data) ? je.data : []);
|
||||
if (jc.success) setEventComments(Array.isArray(jc.data) ? jc.data : []);
|
||||
if (js.success) setSubscriptionInfo(js.data);
|
||||
} catch (err) {
|
||||
// ❌ 移除 toast,仅 console 输出
|
||||
logger.error('Center', 'loadData', err, {
|
||||
userId: user?.id,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
setRefreshing(false);
|
||||
}
|
||||
}, [user]); // ✅ 移除 toast 依赖
|
||||
}, [user]);
|
||||
|
||||
// 加载实时行情
|
||||
const loadRealtimeQuotes = useCallback(async () => {
|
||||
@@ -235,96 +226,11 @@ export default function CenterDashboard() {
|
||||
return (
|
||||
<Box bg={sectionBg} minH="100vh">
|
||||
<Box px={{ base: 4, md: 8 }} py={6} maxW="1400px" mx="auto">
|
||||
{/* 头部 */}
|
||||
<Flex justify="space-between" align="center" mb={8}>
|
||||
<VStack align="start" spacing={1}>
|
||||
<Heading size="lg" color={textColor}>
|
||||
个人中心
|
||||
</Heading>
|
||||
<Text color={secondaryText} fontSize="sm">
|
||||
管理您的自选股、事件关注和互动记录
|
||||
</Text>
|
||||
</VStack>
|
||||
<Button
|
||||
leftIcon={<FiRefreshCw />}
|
||||
onClick={loadData}
|
||||
isLoading={refreshing}
|
||||
loadingText="刷新中"
|
||||
variant="solid"
|
||||
colorScheme="blue"
|
||||
size="sm"
|
||||
>
|
||||
刷新数据
|
||||
</Button>
|
||||
</Flex>
|
||||
|
||||
{/* 统计卡片 */}
|
||||
<SimpleGrid columns={{ base: 1, sm: 2, md: 4 }} spacing={4} mb={8}>
|
||||
<Card bg={cardBg} shadow="sm">
|
||||
<CardBody>
|
||||
<Stat>
|
||||
<StatLabel color={secondaryText}>自选股票</StatLabel>
|
||||
<StatNumber fontSize="2xl">{watchlist.length}</StatNumber>
|
||||
<StatHelpText>
|
||||
<Icon as={FiTrendingUp} color="green.500" mr={1} />
|
||||
关注市场动态
|
||||
</StatHelpText>
|
||||
</Stat>
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
||||
<Card bg={cardBg} shadow="sm">
|
||||
<CardBody>
|
||||
<Stat>
|
||||
<StatLabel color={secondaryText}>关注事件</StatLabel>
|
||||
<StatNumber fontSize="2xl">{followingEvents.length}</StatNumber>
|
||||
<StatHelpText>
|
||||
<Icon as={FiActivity} color="blue.500" mr={1} />
|
||||
追踪热点事件
|
||||
</StatHelpText>
|
||||
</Stat>
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
||||
<Card bg={cardBg} shadow="sm">
|
||||
<CardBody>
|
||||
<Stat>
|
||||
<StatLabel color={secondaryText}>我的评论</StatLabel>
|
||||
<StatNumber fontSize="2xl">{eventComments.length}</StatNumber>
|
||||
<StatHelpText>
|
||||
<Icon as={FiMessageSquare} color="purple.500" mr={1} />
|
||||
参与讨论
|
||||
</StatHelpText>
|
||||
</Stat>
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
||||
<Card bg={cardBg} shadow="sm" cursor="pointer" onClick={() => navigate('/home/pages/account/subscription')} _hover={{ transform: 'translateY(-2px)', shadow: 'lg' }} transition="all 0.2s">
|
||||
<CardBody>
|
||||
<Stat>
|
||||
<StatLabel color={secondaryText}>订阅状态</StatLabel>
|
||||
<StatNumber fontSize="xl" color={subscriptionInfo.type === 'free' ? 'gray.500' : subscriptionInfo.type === 'pro' ? 'blue.500' : 'purple.500'}>
|
||||
{subscriptionInfo.type === 'free' ? '免费版' : subscriptionInfo.type === 'pro' ? 'Pro版' : 'Max版'}
|
||||
</StatNumber>
|
||||
<StatHelpText>
|
||||
<Icon as={FiStar} color={subscriptionInfo.type === 'free' ? 'gray.400' : 'orange.400'} mr={1} />
|
||||
{subscriptionInfo.type === 'free' ? '点击升级' : `剩余${subscriptionInfo.days_left}天`}
|
||||
</StatHelpText>
|
||||
</Stat>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</SimpleGrid>
|
||||
|
||||
{/* 投资日历 */}
|
||||
<Box mb={8}>
|
||||
<InvestmentCalendarChakra />
|
||||
</Box>
|
||||
|
||||
{/* 主要内容区域 */}
|
||||
<Grid templateColumns={{ base: '1fr', lg: '1fr 2fr' }} gap={6}>
|
||||
{/* 左侧:自选股 */}
|
||||
<Grid templateColumns={{ base: '1fr', md: '1fr 1fr', lg: 'repeat(3, 1fr)' }} gap={6} mb={8}>
|
||||
{/* 左列:自选股票 */}
|
||||
<VStack spacing={6} align="stretch">
|
||||
<Card bg={cardBg} shadow="md">
|
||||
<Card bg={cardBg} shadow="md" height="600px" display="flex" flexDirection="column">
|
||||
<CardHeader pb={4}>
|
||||
<Flex justify="space-between" align="center">
|
||||
<HStack>
|
||||
@@ -335,26 +241,16 @@ export default function CenterDashboard() {
|
||||
</Badge>
|
||||
{quotesLoading && <Spinner size="sm" color="blue.500" />}
|
||||
</HStack>
|
||||
<HStack>
|
||||
<IconButton
|
||||
icon={<FiRefreshCw />}
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={loadRealtimeQuotes}
|
||||
isLoading={quotesLoading}
|
||||
aria-label="刷新行情"
|
||||
/>
|
||||
<IconButton
|
||||
icon={<FiPlus />}
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => navigate('/stock-analysis/overview')}
|
||||
aria-label="添加自选股"
|
||||
/>
|
||||
</HStack>
|
||||
<IconButton
|
||||
icon={<FiPlus />}
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => navigate('/stock-analysis/overview')}
|
||||
aria-label="添加自选股"
|
||||
/>
|
||||
</Flex>
|
||||
</CardHeader>
|
||||
<CardBody pt={0}>
|
||||
<CardBody pt={0} flex="1" overflowY="auto">
|
||||
{watchlist.length === 0 ? (
|
||||
<Center py={8}>
|
||||
<VStack spacing={3}>
|
||||
@@ -440,86 +336,12 @@ export default function CenterDashboard() {
|
||||
)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
||||
{/* 订阅管理 */}
|
||||
<Card bg={cardBg} shadow="md">
|
||||
<CardHeader pb={4}>
|
||||
<Flex justify="space-between" align="center">
|
||||
<HStack>
|
||||
<Icon as={FiStar} color={subscriptionInfo.type === 'free' ? 'gray.500' : subscriptionInfo.type === 'pro' ? 'blue.500' : 'purple.500'} boxSize={5} />
|
||||
<Heading size="md">我的订阅</Heading>
|
||||
<Badge
|
||||
colorScheme={subscriptionInfo.type === 'free' ? 'gray' : subscriptionInfo.type === 'pro' ? 'blue' : 'purple'}
|
||||
variant="subtle"
|
||||
>
|
||||
{subscriptionInfo.type === 'free' ? '免费版' : subscriptionInfo.type === 'pro' ? 'Pro版' : 'Max版'}
|
||||
</Badge>
|
||||
</HStack>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
colorScheme={subscriptionInfo.type === 'free' ? 'blue' : 'purple'}
|
||||
onClick={() => navigate('/home/pages/account/subscription')}
|
||||
>
|
||||
{subscriptionInfo.type === 'free' ? '升级' : '管理'}
|
||||
</Button>
|
||||
</Flex>
|
||||
</CardHeader>
|
||||
<CardBody pt={0}>
|
||||
<VStack align="stretch" spacing={4}>
|
||||
<Box p={4} borderRadius="md" bg={subscriptionInfo.type === 'free' ? 'gray.50' : subscriptionInfo.type === 'pro' ? 'blue.50' : 'purple.50'} border="1px" borderColor={subscriptionInfo.type === 'free' ? 'gray.200' : subscriptionInfo.type === 'pro' ? 'blue.200' : 'purple.200'}>
|
||||
<HStack justify="space-between">
|
||||
<VStack align="start" spacing={1}>
|
||||
<Text fontSize="sm" fontWeight="medium" color={textColor}>
|
||||
当前套餐
|
||||
</Text>
|
||||
<Text fontSize="lg" fontWeight="bold" color={subscriptionInfo.type === 'free' ? 'gray.600' : subscriptionInfo.type === 'pro' ? 'blue.600' : 'purple.600'}>
|
||||
{subscriptionInfo.type === 'free' ? '免费版' : subscriptionInfo.type === 'pro' ? 'Pro版本' : 'Max版本'}
|
||||
</Text>
|
||||
</VStack>
|
||||
<VStack align="end" spacing={1}>
|
||||
<Text fontSize="sm" color={secondaryText}>
|
||||
{subscriptionInfo.type === 'free' ? '永久免费' : subscriptionInfo.is_active ? '已激活' : '已过期'}
|
||||
</Text>
|
||||
{subscriptionInfo.type !== 'free' && (
|
||||
<Text fontSize="xs" color={subscriptionInfo.days_left > 7 ? 'green.500' : 'orange.500'}>
|
||||
剩余 {subscriptionInfo.days_left} 天
|
||||
</Text>
|
||||
)}
|
||||
</VStack>
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
{subscriptionInfo.type === 'free' ? (
|
||||
<VStack spacing={2}>
|
||||
<Text fontSize="sm" color={secondaryText} textAlign="center">
|
||||
升级到Pro或Max版本,解锁更多功能
|
||||
</Text>
|
||||
<HStack spacing={2}>
|
||||
<Button size="xs" colorScheme="blue" variant="outline" onClick={() => navigate('/home/pages/account/subscription')}>
|
||||
Pro ¥0.01/月
|
||||
</Button>
|
||||
<Button size="xs" colorScheme="purple" variant="outline" onClick={() => navigate('/home/pages/account/subscription')}>
|
||||
Max ¥0.1/月
|
||||
</Button>
|
||||
</HStack>
|
||||
</VStack>
|
||||
) : (
|
||||
<Box textAlign="center">
|
||||
<Text fontSize="sm" color={subscriptionInfo.is_active ? 'green.600' : 'orange.600'}>
|
||||
{subscriptionInfo.is_active ? '订阅服务正常' : '订阅已过期,请续费'}
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
</VStack>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</VStack>
|
||||
|
||||
{/* 右侧:事件相关 */}
|
||||
{/* 中列:关注事件 */}
|
||||
<VStack spacing={6} align="stretch">
|
||||
{/* 关注事件 */}
|
||||
<Card bg={cardBg} shadow="md">
|
||||
<Card bg={cardBg} shadow="md" height="600px" display="flex" flexDirection="column">
|
||||
<CardHeader pb={4}>
|
||||
<Flex justify="space-between" align="center">
|
||||
<HStack>
|
||||
@@ -538,7 +360,7 @@ export default function CenterDashboard() {
|
||||
</Button>
|
||||
</Flex>
|
||||
</CardHeader>
|
||||
<CardBody pt={0}>
|
||||
<CardBody pt={0} flex="1" overflowY="auto">
|
||||
{followingEvents.length === 0 ? (
|
||||
<Center py={8}>
|
||||
<VStack spacing={3}>
|
||||
@@ -651,10 +473,12 @@ export default function CenterDashboard() {
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
||||
{/* 移除“未来事件”板块,根据需求不再展示 */}
|
||||
</VStack>
|
||||
|
||||
{/* 右列:我的评论 */}
|
||||
<VStack spacing={6} align="stretch">
|
||||
{/* 我的评论 */}
|
||||
<Card bg={cardBg} shadow="md">
|
||||
<Card bg={cardBg} shadow="md" height="600px" display="flex" flexDirection="column">
|
||||
<CardHeader pb={4}>
|
||||
<Flex justify="space-between" align="center">
|
||||
<HStack>
|
||||
@@ -666,7 +490,7 @@ export default function CenterDashboard() {
|
||||
</HStack>
|
||||
</Flex>
|
||||
</CardHeader>
|
||||
<CardBody pt={0}>
|
||||
<CardBody pt={0} flex="1" overflowY="auto">
|
||||
{eventComments.length === 0 ? (
|
||||
<Center py={8}>
|
||||
<VStack spacing={3}>
|
||||
@@ -723,9 +547,9 @@ export default function CenterDashboard() {
|
||||
</VStack>
|
||||
</Grid>
|
||||
|
||||
{/* 我的复盘和计划 */}
|
||||
<Box mt={8}>
|
||||
<InvestmentPlansAndReviews />
|
||||
{/* 投资规划中心(整合了日历、计划、复盘) */}
|
||||
<Box>
|
||||
<InvestmentPlanningCenter />
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user